From ab3aa61521fcdbe90493f98845ba4725f3087d96 Mon Sep 17 00:00:00 2001 From: muxuming1 Date: Thu, 14 Aug 2025 16:51:48 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84generatePoints?= =?UTF-8?q?=E6=96=B9=E6=B3=95=EF=BC=8C=E4=BD=BF=E7=94=A8Cesium=E7=9A=843D?= =?UTF-8?q?=E5=9D=90=E6=A0=87=E7=B3=BB=E7=BB=9F=E8=AE=A1=E7=AE=97=E5=9C=86?= =?UTF-8?q?=E5=91=A8=E7=82=B9=20=E6=94=B9=E8=BF=9B=E7=AE=97=E6=B3=95?= =?UTF-8?q?=E4=BB=A5=E8=80=83=E8=99=91=E5=9C=B0=E7=90=83=E6=9B=B2=E7=8E=87?= =?UTF-8?q?=EF=BC=8C=E4=BD=BF=E7=94=A8ENU=E5=B1=80=E9=83=A8=E5=9D=90?= =?UTF-8?q?=E6=A0=87=E7=B3=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/polygon/circle.ts | 80 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/src/polygon/circle.ts b/src/polygon/circle.ts index 6cd438e..aa0b1fb 100644 --- a/src/polygon/circle.ts +++ b/src/polygon/circle.ts @@ -61,24 +61,86 @@ export default class Circle extends Base { const radius = Utils.MathDistance(center, pnt2); - const res = this.generatePoints(center, radius); + const res = this.generatePoints(positions); const temp = [].concat(...res); const cartesianPoints = this.cesium.Cartesian3.fromDegreesArray(temp); return cartesianPoints; } - - generatePoints(center, radius) { - let x, y, angle; + generatePoints(positions) { 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); + // 计算局部坐标系(东-北-上) + 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++) { - angle = (Math.PI * 2 * i) / 100; - x = center[0] + radius * Math.cos(angle); - y = center[1] + radius * Math.sin(angle); - points.push([x, y]); + const angle = (Math.PI * 2 * i) / 100; + const cosAngle = Math.cos(angle); + const sinAngle = Math.sin(angle); + + // 计算偏移量(基于米) + 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; } - getPoints() { return this.points; } From 20d5741675a9e2a4a0ca81106bb3c7d7e55f878f Mon Sep 17 00:00:00 2001 From: muxuming1 Date: Thu, 11 Sep 2025 16:07:52 +0800 Subject: [PATCH 2/3] =?UTF-8?q?upd:=20entity=E6=94=B9=E4=B8=BAdatasource?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/base.ts | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/base.ts b/src/base.ts index 1e3444c..71a38bc 100644 --- a/src/base.ts +++ b/src/base.ts @@ -36,6 +36,7 @@ export default class Base { styleCache: GeometryStyle | undefined; minPointsForShape: number = 0; tempLineEntity: CesiumTypeOnly.Entity; + dataSource: CesiumTypeOnly.CustomDataSource; constructor(cesium: CesiumTypeOnly, viewer: CesiumTypeOnly.Viewer, style?: GeometryStyle) { this.cesium = cesium; @@ -49,7 +50,8 @@ export default class Base { // Disable default behavior for double-clicking on entities. viewer.trackedEntity = undefined; viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(this.cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); - + this.dataSource = new this.cesium.CustomDataSource("entity"); + viewer.dataSources.add(this.dataSource); this.onClick(); } @@ -134,6 +136,7 @@ export default class Base { this.eventDispatcher.dispatchEvent('editEnd', this.getPoints()); } } 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. if (hitEntities && activeEntity.id === pickedObject.id.id) { const pickedGraphics = this.type === 'line' ? pickedObject.id.polyline : pickedObject.id.polygon; @@ -182,7 +185,7 @@ export default class Base { finishDrawing() { // 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.type === 'polygon' && this.lineEntity && this.viewer.entities.remove(this.lineEntity); + this.type === 'polygon' && this.lineEntity && this.dataSource.entities.remove(this.lineEntity); this.removeMoveListener(); // Editable upon initial drawing completion. @@ -238,7 +241,7 @@ export default class Base { }; if (!this.polygonEntity) { const style = this.style as PolygonStyle; - this.polygonEntity = this.viewer.entities.add({ + this.polygonEntity = this.dataSource.entities.add({ polygon: new this.cesium.PolygonGraphics({ hierarchy: new this.cesium.CallbackProperty(callback, false), show: true, @@ -247,7 +250,7 @@ export default class Base { }); // 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: { positions: new this.cesium.CallbackProperty(() => { return [...this.geometryPoints, this.geometryPoints[0]]; @@ -281,12 +284,12 @@ export default class Base { removeTempLine() { if (this.tempLineEntity) { - this.viewer.entities.remove(this.tempLineEntity); + this.dataSource.entities.remove(this.tempLineEntity); } } addLineEntity(style: LineStyle) { - const entity = this.viewer.entities.add({ + const entity = this.dataSource.entities.add({ polyline: { positions: new this.cesium.CallbackProperty(() => this.geometryPoints, false), width: style.lineWidth, @@ -323,7 +326,7 @@ export default class Base { // }, // }); - return this.viewer.entities.add({ + return this.dataSource.entities.add({ position, point: { pixelSize: 10, @@ -385,7 +388,7 @@ export default class Base { removeControlPoints() { if (this.controlPoints.length > 0) { 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.MOUSE_MOVE); @@ -574,7 +577,7 @@ export default class Base { setTimeout(() => { const graphics = entity.polygon || entity.polyline || entity.billboard; let startAlpha: number; - let material = graphics.material; + const material = graphics.material; if (material) { if (material.image && material.color.alpha !== undefined) { // 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. 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, // update the point at index movingPointIndex in the points array with the newPosition, // 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 startPointRight = this.cesium.Cartesian3.midpoint(midPoint, points[1], new this.cesium.Cartesian3()); - let endPointLeft = points[3]; - let endPointRight = points[2]; + const endPointLeft = points[3]; + const endPointRight = points[2]; const t = elapsedTime / duration; const controlPoint = this.getBezierControlPointforGrowthAnimation(); - let curveControlPointsLeft = [startPointLeft, controlPoint.left, endPointLeft]; - let curveControlPointsRight = [startPointRight, controlPoint.right, endPointRight]; + const curveControlPointsLeft = [startPointLeft, controlPoint.left, endPointLeft]; + const curveControlPointsRight = [startPointRight, controlPoint.right, endPointRight]; const newPositionLeft = this.getNewPosition(curveControlPointsLeft, t); const newPositionRight = this.getNewPosition(curveControlPointsRight, t); @@ -800,7 +803,7 @@ export default class Base { return this.cesium.Cartesian3.fromDegrees(p[0], p[1]); }); - let newPosition = this.interpolateAlongCurve(curvePoints, t); + const newPosition = this.interpolateAlongCurve(curvePoints, t); return newPosition; } @@ -819,13 +822,13 @@ export default class Base { remove() { if (this.type === 'polygon') { - this.viewer.entities.remove(this.polygonEntity); - this.viewer.entities.remove(this.outlineEntity); + this.dataSource.entities.remove(this.polygonEntity); + this.dataSource.entities.remove(this.outlineEntity); this.polygonEntity = null; this.outlineEntity = null; this.lineEntity = null; } else if (this.type === 'line') { - this.viewer.entities.remove(this.lineEntity); + this.dataSource.entities.remove(this.lineEntity); } this.removeClickListener(); this.removeMoveListener(); From 5251862c94f4b5c65b68dc000e53839bb33e0f8f Mon Sep 17 00:00:00 2001 From: muxuming1 Date: Tue, 16 Sep 2025 10:00:30 +0800 Subject: [PATCH 3/3] =?UTF-8?q?upd:=20=E5=A2=9E=E5=8A=A0=E5=8D=8A=E5=BE=84?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/polygon/circle.ts | 208 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) diff --git a/src/polygon/circle.ts b/src/polygon/circle.ts index aa0b1fb..7bb4aa1 100644 --- a/src/polygon/circle.ts +++ b/src/polygon/circle.ts @@ -8,6 +8,8 @@ import { PolygonStyle } from '../interface'; export default class Circle extends Base { points: Cartesian3[] = []; freehand: boolean; + radius: number = 0; // 添加半径属性 + radiusLabelEntity: any; // 添加半径标签实体 constructor(cesium: any, viewer: any, style?: PolygonStyle) { 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. */ @@ -40,6 +132,7 @@ export default class Circle extends Base { const geometryPoints = this.createGraphic(tempPoints); this.setGeometryPoints(geometryPoints); this.drawPolygon(); + this.updateRadiusLabel(); // 更新半径标签 } /** @@ -50,6 +143,8 @@ export default class Circle extends Base { const geometryPoints = this.createGraphic(this.points); this.setGeometryPoints(geometryPoints); this.drawPolygon(); + this.updateRadiusLabel(); // 更新半径标签 + this.updateRadiusLabelPosition(); // 确保标签位置跟随圆心 } createGraphic(positions: Cartesian3[]) { @@ -60,12 +155,14 @@ export default class Circle extends Base { const pnt2 = lnglatPoints[1]; const radius = Utils.MathDistance(center, pnt2); + this.radius = radius; // 保存半径数据 const res = this.generatePoints(positions); const temp = [].concat(...res); const cartesianPoints = this.cesium.Cartesian3.fromDegreesArray(temp); return cartesianPoints; } + generatePoints(positions) { const points = []; const degreesPerRadian = 180.0 / Math.PI; @@ -81,6 +178,8 @@ export default class Circle extends Base { // 计算两点之间的距离作为半径(米) const radius = this.cesium.Cartesian3.distance(centerCartesian, secondPointCartesian); + this.radius = radius; // 保存半径数据(米为单位) + // 计算局部坐标系(东-北-上) const localFrame = this.cesium.Transforms.eastNorthUpToFixedFrame(centerCartesian); // 提取东向和北向单位向量 @@ -141,7 +240,116 @@ export default class Circle extends Base { } return points; } + getPoints() { 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; + } + } }