This commit is contained in:
jackbyebye1024 2025-09-16 02:00:38 +00:00 committed by GitHub
commit d8268fdd98
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 298 additions and 25 deletions

View File

@ -36,6 +36,7 @@ export default class Base {
styleCache: GeometryStyle | undefined; styleCache: GeometryStyle | undefined;
minPointsForShape: number = 0; minPointsForShape: number = 0;
tempLineEntity: CesiumTypeOnly.Entity; tempLineEntity: CesiumTypeOnly.Entity;
dataSource: CesiumTypeOnly.CustomDataSource;
constructor(cesium: CesiumTypeOnly, viewer: CesiumTypeOnly.Viewer, style?: GeometryStyle) { constructor(cesium: CesiumTypeOnly, viewer: CesiumTypeOnly.Viewer, style?: GeometryStyle) {
this.cesium = cesium; this.cesium = cesium;
@ -49,7 +50,8 @@ export default class Base {
// Disable default behavior for double-clicking on entities. // Disable default behavior for double-clicking on entities.
viewer.trackedEntity = undefined; viewer.trackedEntity = undefined;
viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(this.cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(this.cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
this.dataSource = new this.cesium.CustomDataSource("entity");
viewer.dataSources.add(this.dataSource);
this.onClick(); this.onClick();
} }
@ -134,6 +136,7 @@ export default class Base {
this.eventDispatcher.dispatchEvent('editEnd', this.getPoints()); this.eventDispatcher.dispatchEvent('editEnd', this.getPoints());
} }
} else if (this.state === 'static') { } else if (this.state === 'static') {
console.log('here')
//When drawing multiple shapes, the click events for all shapes are triggered. Only when hitting a completed shape should it enter editing mode. //When drawing multiple shapes, the click events for all shapes are triggered. Only when hitting a completed shape should it enter editing mode.
if (hitEntities && activeEntity.id === pickedObject.id.id) { if (hitEntities && activeEntity.id === pickedObject.id.id) {
const pickedGraphics = this.type === 'line' ? pickedObject.id.polyline : pickedObject.id.polygon; const pickedGraphics = this.type === 'line' ? pickedObject.id.polyline : pickedObject.id.polygon;
@ -182,7 +185,7 @@ export default class Base {
finishDrawing() { finishDrawing() {
// Some polygons draw a separate line between the first two points before drawing the complete shape; // Some polygons draw a separate line between the first two points before drawing the complete shape;
// this line should be removed after drawing is complete. // this line should be removed after drawing is complete.
this.type === 'polygon' && this.lineEntity && this.viewer.entities.remove(this.lineEntity); this.type === 'polygon' && this.lineEntity && this.dataSource.entities.remove(this.lineEntity);
this.removeMoveListener(); this.removeMoveListener();
// Editable upon initial drawing completion. // Editable upon initial drawing completion.
@ -238,7 +241,7 @@ export default class Base {
}; };
if (!this.polygonEntity) { if (!this.polygonEntity) {
const style = this.style as PolygonStyle; const style = this.style as PolygonStyle;
this.polygonEntity = this.viewer.entities.add({ this.polygonEntity = this.dataSource.entities.add({
polygon: new this.cesium.PolygonGraphics({ polygon: new this.cesium.PolygonGraphics({
hierarchy: new this.cesium.CallbackProperty(callback, false), hierarchy: new this.cesium.CallbackProperty(callback, false),
show: true, show: true,
@ -247,7 +250,7 @@ export default class Base {
}); });
// Due to limitations in PolygonGraphics outlining, a separate line style is drawn. // Due to limitations in PolygonGraphics outlining, a separate line style is drawn.
this.outlineEntity = this.viewer.entities.add({ this.outlineEntity = this.dataSource.entities.add({
polyline: { polyline: {
positions: new this.cesium.CallbackProperty(() => { positions: new this.cesium.CallbackProperty(() => {
return [...this.geometryPoints, this.geometryPoints[0]]; return [...this.geometryPoints, this.geometryPoints[0]];
@ -281,12 +284,12 @@ export default class Base {
removeTempLine() { removeTempLine() {
if (this.tempLineEntity) { if (this.tempLineEntity) {
this.viewer.entities.remove(this.tempLineEntity); this.dataSource.entities.remove(this.tempLineEntity);
} }
} }
addLineEntity(style: LineStyle) { addLineEntity(style: LineStyle) {
const entity = this.viewer.entities.add({ const entity = this.dataSource.entities.add({
polyline: { polyline: {
positions: new this.cesium.CallbackProperty(() => this.geometryPoints, false), positions: new this.cesium.CallbackProperty(() => this.geometryPoints, false),
width: style.lineWidth, width: style.lineWidth,
@ -323,7 +326,7 @@ export default class Base {
// }, // },
// }); // });
return this.viewer.entities.add({ return this.dataSource.entities.add({
position, position,
point: { point: {
pixelSize: 10, pixelSize: 10,
@ -385,7 +388,7 @@ export default class Base {
removeControlPoints() { removeControlPoints() {
if (this.controlPoints.length > 0) { if (this.controlPoints.length > 0) {
this.controlPoints.forEach((entity: CesiumTypeOnly.Entity) => { this.controlPoints.forEach((entity: CesiumTypeOnly.Entity) => {
this.viewer.entities.remove(entity); this.dataSource.entities.remove(entity);
}); });
this.controlPointsEventHandler.removeInputAction(this.cesium.ScreenSpaceEventType.LEFT_DOWN); this.controlPointsEventHandler.removeInputAction(this.cesium.ScreenSpaceEventType.LEFT_DOWN);
this.controlPointsEventHandler.removeInputAction(this.cesium.ScreenSpaceEventType.MOUSE_MOVE); this.controlPointsEventHandler.removeInputAction(this.cesium.ScreenSpaceEventType.MOUSE_MOVE);
@ -574,7 +577,7 @@ export default class Base {
setTimeout(() => { setTimeout(() => {
const graphics = entity.polygon || entity.polyline || entity.billboard; const graphics = entity.polygon || entity.polyline || entity.billboard;
let startAlpha: number; let startAlpha: number;
let material = graphics.material; const material = graphics.material;
if (material) { if (material) {
if (material.image && material.color.alpha !== undefined) { if (material.image && material.color.alpha !== undefined) {
// Texture material, setting the alpha channel in the color of the custom ImageFlowMaterialProperty. // Texture material, setting the alpha channel in the color of the custom ImageFlowMaterialProperty.
@ -712,7 +715,7 @@ export default class Base {
// The face-arrow determined by three points, with the animation starting from the midpoint of the line connecting the first two points. // The face-arrow determined by three points, with the animation starting from the midpoint of the line connecting the first two points.
startPoint = this.cesium.Cartesian3.midpoint(points[0], points[1], new this.cesium.Cartesian3()); startPoint = this.cesium.Cartesian3.midpoint(points[0], points[1], new this.cesium.Cartesian3());
} }
let endPoint = points[movingPointIndex]; const endPoint = points[movingPointIndex];
// To dynamically add points between the startPoint and endPoint, consistent with the initial drawing logic, // To dynamically add points between the startPoint and endPoint, consistent with the initial drawing logic,
// update the point at index movingPointIndex in the points array with the newPosition, // update the point at index movingPointIndex in the points array with the newPosition,
// generate the arrow, and execute the animation. // generate the arrow, and execute the animation.
@ -754,12 +757,12 @@ export default class Base {
const startPointLeft = this.cesium.Cartesian3.midpoint(points[0], midPoint, new this.cesium.Cartesian3()); const startPointLeft = this.cesium.Cartesian3.midpoint(points[0], midPoint, new this.cesium.Cartesian3());
const startPointRight = this.cesium.Cartesian3.midpoint(midPoint, points[1], new this.cesium.Cartesian3()); const startPointRight = this.cesium.Cartesian3.midpoint(midPoint, points[1], new this.cesium.Cartesian3());
let endPointLeft = points[3]; const endPointLeft = points[3];
let endPointRight = points[2]; const endPointRight = points[2];
const t = elapsedTime / duration; const t = elapsedTime / duration;
const controlPoint = this.getBezierControlPointforGrowthAnimation(); const controlPoint = this.getBezierControlPointforGrowthAnimation();
let curveControlPointsLeft = [startPointLeft, controlPoint.left, endPointLeft]; const curveControlPointsLeft = [startPointLeft, controlPoint.left, endPointLeft];
let curveControlPointsRight = [startPointRight, controlPoint.right, endPointRight]; const curveControlPointsRight = [startPointRight, controlPoint.right, endPointRight];
const newPositionLeft = this.getNewPosition(curveControlPointsLeft, t); const newPositionLeft = this.getNewPosition(curveControlPointsLeft, t);
const newPositionRight = this.getNewPosition(curveControlPointsRight, t); const newPositionRight = this.getNewPosition(curveControlPointsRight, t);
@ -800,7 +803,7 @@ export default class Base {
return this.cesium.Cartesian3.fromDegrees(p[0], p[1]); return this.cesium.Cartesian3.fromDegrees(p[0], p[1]);
}); });
let newPosition = this.interpolateAlongCurve(curvePoints, t); const newPosition = this.interpolateAlongCurve(curvePoints, t);
return newPosition; return newPosition;
} }
@ -819,13 +822,13 @@ export default class Base {
remove() { remove() {
if (this.type === 'polygon') { if (this.type === 'polygon') {
this.viewer.entities.remove(this.polygonEntity); this.dataSource.entities.remove(this.polygonEntity);
this.viewer.entities.remove(this.outlineEntity); this.dataSource.entities.remove(this.outlineEntity);
this.polygonEntity = null; this.polygonEntity = null;
this.outlineEntity = null; this.outlineEntity = null;
this.lineEntity = null; this.lineEntity = null;
} else if (this.type === 'line') { } else if (this.type === 'line') {
this.viewer.entities.remove(this.lineEntity); this.dataSource.entities.remove(this.lineEntity);
} }
this.removeClickListener(); this.removeClickListener();
this.removeMoveListener(); this.removeMoveListener();

View File

@ -8,6 +8,8 @@ import { PolygonStyle } from '../interface';
export default class Circle extends Base { export default class Circle extends Base {
points: Cartesian3[] = []; points: Cartesian3[] = [];
freehand: boolean; freehand: boolean;
radius: number = 0; // 添加半径属性
radiusLabelEntity: any; // 添加半径标签实体
constructor(cesium: any, viewer: any, style?: PolygonStyle) { constructor(cesium: any, viewer: any, style?: PolygonStyle) {
super(cesium, viewer, style); super(cesium, viewer, style);
@ -32,6 +34,96 @@ export default class Circle extends Base {
} }
} }
/**
*
*/
calculateLabelPosition(): Cartesian3 {
if (this.points.length < 1) {
return this.points[0];
}
const centerCartesian = this.points[0];
// 计算局部坐标系(东-北-上)
const localFrame = this.cesium.Transforms.eastNorthUpToFixedFrame(centerCartesian);
// 提取东向单位向量(右侧方向)
const eastVector = this.cesium.Cartesian3.fromElements(
localFrame[0],
localFrame[1],
localFrame[2],
new this.cesium.Cartesian3(),
);
// 归一化方向向量
this.cesium.Cartesian3.normalize(eastVector, eastVector);
// 计算半径的一半距离的偏移量(向右)
const halfRadiusOffset = this.cesium.Cartesian3.multiplyByScalar(
eastVector,
this.radius / 2, // 半径的一半
new this.cesium.Cartesian3(),
);
// 计算标签位置(圆心 + 向右偏移半径的一半)
const labelPosition = this.cesium.Cartesian3.add(
centerCartesian,
halfRadiusOffset,
new this.cesium.Cartesian3(),
);
// 投影到椭球表面
const surfacePosition = this.cesium.Ellipsoid.WGS84.scaleToGeodeticSurface(
labelPosition,
new this.cesium.Cartesian3(),
);
return surfacePosition || labelPosition;
}
/**
*
*/
createRadiusLabel() {
if (this.points.length >= 1 && this.radius > 0) {
const labelPosition = this.calculateLabelPosition();
const radiusText = this.formatRadiusText(this.radius);
if (!this.radiusLabelEntity) {
this.radiusLabelEntity = this.dataSource.entities.add({
position: labelPosition,
label: {
text: radiusText,
font: '16pt sans-serif',
fillColor: this.cesium.Color.WHITE,
outlineColor: this.cesium.Color.BLACK,
outlineWidth: 2,
style: this.cesium.LabelStyle.FILL_AND_OUTLINE,
pixelOffset: new this.cesium.Cartesian2(0, 0),
horizontalOrigin: this.cesium.HorizontalOrigin.CENTER,
verticalOrigin: this.cesium.VerticalOrigin.CENTER,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
heightReference: this.cesium.HeightReference.CLAMP_TO_GROUND
}
});
} else {
// 更新现有标签的文本和位置
this.radiusLabelEntity.position = labelPosition;
this.radiusLabelEntity.label.text = radiusText;
}
}
}
/**
*
*/
updateRadiusLabelPosition() {
if (this.radiusLabelEntity && this.points.length >= 1 && this.radius > 0) {
const labelPosition = this.calculateLabelPosition();
this.radiusLabelEntity.position = labelPosition;
}
}
/** /**
* Draw a shape based on mouse movement points during the initial drawing. * Draw a shape based on mouse movement points during the initial drawing.
*/ */
@ -40,6 +132,7 @@ export default class Circle extends Base {
const geometryPoints = this.createGraphic(tempPoints); const geometryPoints = this.createGraphic(tempPoints);
this.setGeometryPoints(geometryPoints); this.setGeometryPoints(geometryPoints);
this.drawPolygon(); this.drawPolygon();
this.updateRadiusLabel(); // 更新半径标签
} }
/** /**
@ -50,6 +143,8 @@ export default class Circle extends Base {
const geometryPoints = this.createGraphic(this.points); const geometryPoints = this.createGraphic(this.points);
this.setGeometryPoints(geometryPoints); this.setGeometryPoints(geometryPoints);
this.drawPolygon(); this.drawPolygon();
this.updateRadiusLabel(); // 更新半径标签
this.updateRadiusLabelPosition(); // 确保标签位置跟随圆心
} }
createGraphic(positions: Cartesian3[]) { createGraphic(positions: Cartesian3[]) {
@ -60,21 +155,88 @@ export default class Circle extends Base {
const pnt2 = lnglatPoints[1]; const pnt2 = lnglatPoints[1];
const radius = Utils.MathDistance(center, pnt2); const radius = Utils.MathDistance(center, pnt2);
this.radius = radius; // 保存半径数据
const res = this.generatePoints(center, radius); const res = this.generatePoints(positions);
const temp = [].concat(...res); const temp = [].concat(...res);
const cartesianPoints = this.cesium.Cartesian3.fromDegreesArray(temp); const cartesianPoints = this.cesium.Cartesian3.fromDegreesArray(temp);
return cartesianPoints; return cartesianPoints;
} }
generatePoints(center, radius) { generatePoints(positions) {
let x, y, angle;
const points = []; const points = [];
const degreesPerRadian = 180.0 / Math.PI;
// 验证输入格式
if (!positions || positions.length !== 2) {
console.error('输入参数格式错误,需要包含两个世界坐标点');
return points;
}
// 提取中心点和第二点均为Cartesian3世界坐标
const centerCartesian = positions[0];
const secondPointCartesian = positions[1];
// 计算两点之间的距离作为半径(米)
const radius = this.cesium.Cartesian3.distance(centerCartesian, secondPointCartesian);
this.radius = radius; // 保存半径数据(米为单位)
// 计算局部坐标系(东-北-上)
const localFrame = this.cesium.Transforms.eastNorthUpToFixedFrame(centerCartesian);
// 提取东向和北向单位向量
const eastVector = this.cesium.Cartesian3.fromElements(
localFrame[0],
localFrame[1],
localFrame[2],
new this.cesium.Cartesian3(),
);
const northVector = this.cesium.Cartesian3.fromElements(
localFrame[4],
localFrame[5],
localFrame[6],
new this.cesium.Cartesian3(),
);
// 归一化方向向量
this.cesium.Cartesian3.normalize(eastVector, eastVector);
this.cesium.Cartesian3.normalize(northVector, northVector);
// 生成圆周点
for (let i = 0; i <= 100; i++) { for (let i = 0; i <= 100; i++) {
angle = (Math.PI * 2 * i) / 100; const angle = (Math.PI * 2 * i) / 100;
x = center[0] + radius * Math.cos(angle); const cosAngle = Math.cos(angle);
y = center[1] + radius * Math.sin(angle); const sinAngle = Math.sin(angle);
points.push([x, y]);
// 计算偏移量(基于米)
const eastOffset = this.cesium.Cartesian3.multiplyByScalar(
eastVector,
radius * cosAngle,
new this.cesium.Cartesian3(),
);
const northOffset = this.cesium.Cartesian3.multiplyByScalar(
northVector,
radius * sinAngle,
new this.cesium.Cartesian3(),
);
// 计算目标点
const targetPoint = this.cesium.Cartesian3.add(
centerCartesian,
this.cesium.Cartesian3.add(eastOffset, northOffset, new this.cesium.Cartesian3()),
new this.cesium.Cartesian3(),
);
// 投影到椭球表面
const surfacePoint = this.cesium.Ellipsoid.WGS84.scaleToGeodeticSurface(
targetPoint,
new this.cesium.Cartesian3(),
);
// 转换回经纬度
const cartographic = this.cesium.Cartographic.fromCartesian(surfacePoint);
const longitude = cartographic.longitude * degreesPerRadian;
const latitude = cartographic.latitude * degreesPerRadian;
points.push([longitude, latitude]);
} }
return points; return points;
} }
@ -82,4 +244,112 @@ export default class Circle extends Base {
getPoints() { getPoints() {
return this.points; return this.points;
} }
/**
*
* @returns {number}
*/
getRadius(): number {
return this.radius;
}
/**
*
* @param {number} radius
*/
setRadius(radius: number): void {
this.radius = radius;
}
/**
*
* @returns {number}
*/
getDiameter(): number {
return this.radius * 2;
}
/**
*
* @returns {number}
*/
getArea(): number {
return Math.PI * this.radius * this.radius;
}
/**
*
* @returns {number}
*/
getCircumference(): number {
return 2 * Math.PI * this.radius;
}
/**
*
*/
updateRadiusLabel() {
if (this.points.length >= 1 && this.radius > 0) {
this.createRadiusLabel();
}
}
/**
*
*/
formatRadiusText(radius: number): string {
if (radius >= 1000) {
return `半径: ${(radius / 1000).toFixed(2)} km`;
} else {
return `半径: ${radius.toFixed(2)} m`;
}
}
/**
* drawPolygon方法
*/
drawPolygon() {
super.drawPolygon();
this.updateRadiusLabel();
}
/**
* draggable方法
*/
draggable() {
super.draggable();
// 监听拖动事件,确保标签位置实时更新
if (this.dragEventHandler) {
const originalMouseMoveHandler = this.dragEventHandler.getInputAction(this.cesium.ScreenSpaceEventType.MOUSE_MOVE);
this.dragEventHandler.setInputAction((event) => {
if (originalMouseMoveHandler) {
originalMouseMoveHandler(event);
}
// 在拖动过程中实时更新标签位置
this.updateRadiusLabelPosition();
}, this.cesium.ScreenSpaceEventType.MOUSE_MOVE);
}
}
/**
* remove方法
*/
remove() {
if (this.radiusLabelEntity) {
this.dataSource.entities.remove(this.radiusLabelEntity);
this.radiusLabelEntity = null;
}
super.remove();
}
/**
* /
*/
showRadiusLabel(show: boolean = true) {
if (this.radiusLabelEntity) {
this.radiusLabelEntity.label.show = show;
}
}
} }