| | |
| | | import * as Cesium from 'cesium' |
| | | import * as Cesium from 'cesium' |
| | | import { MapTooltip } from '../Tooltip' |
| | | import { ToolBase } from '../ToolBase' |
| | | import { buildEllipsePositions, cartesianToLocal } from '../shapeUtils' |
| | |
| | | this.tooltip = new MapTooltip(viewer) |
| | | this.dataSource = null |
| | | this.centerCartesian = null |
| | | this.radius = 0 |
| | | this.radiusLevel1 = 0 |
| | | this.radiusLevel2 = 0 |
| | | this.radiusLevel3 = 0 |
| | | this.dragType = null |
| | | this.circleEntity = null |
| | | this.circleLevel1Entity = null |
| | | this.circleLevel2Entity = null |
| | | this.circleLevel3Entity = null |
| | | this.circleLevel1OutlineEntity = null |
| | | this.circleLevel2OutlineEntity = null |
| | | this.circleLevel3OutlineEntity = null |
| | | this.centerEntity = null |
| | | this.radiusEntity = null |
| | | this.radiusLevel1Entity = null |
| | | this.radiusLevel2Entity = null |
| | | this.radiusLevel3Entity = null |
| | | } |
| | | |
| | | start(points = []) { |
| | | const normalized = normalizePoints(points) |
| | | start(data = []) { |
| | | const inputPoints = Array.isArray(data?.points) ? data.points : data |
| | | const normalized = normalizePoints(inputPoints) |
| | | if (!normalized.length) return |
| | | const centerLng = normalized.reduce((sum, p) => sum + p.lng, 0) / normalized.length |
| | | const centerLat = normalized.reduce((sum, p) => sum + p.lat, 0) / normalized.length |
| | | this.centerCartesian = Cesium.Cartesian3.fromDegrees(centerLng, centerLat) |
| | | this.radius = this.estimateRadius(normalized) |
| | | const radii = Array.isArray(data?.meta?.bufferRadii) ? data.meta.bufferRadii : null |
| | | if (radii?.length) { |
| | | const [r1, r2, r3] = radii |
| | | this.radiusLevel1 = Number.isFinite(r1) && r1 > 0 ? r1 : 1 |
| | | this.radiusLevel2 = Number.isFinite(r2) && r2 > 0 ? Math.max(r2, this.radiusLevel1) : this.radiusLevel1 |
| | | this.radiusLevel3 = Number.isFinite(r3) && r3 > 0 ? Math.max(r3, this.radiusLevel2) : this.radiusLevel2 |
| | | } else { |
| | | this.radiusLevel3 = this.estimateRadius(normalized) |
| | | this.radiusLevel2 = this.radiusLevel3 |
| | | this.radiusLevel1 = this.radiusLevel3 |
| | | } |
| | | |
| | | this.dataSource = new Cesium.CustomDataSource('edit-buffer-circle') |
| | | this.viewer.dataSources.add(this.dataSource) |
| | |
| | | const picked = this.viewer.scene.pick(evt.position)?.id |
| | | if (!picked) return |
| | | if (picked.customType === 'circle-center') this.dragType = 'center' |
| | | if (picked.customType === 'circle-radius') this.dragType = 'radius' |
| | | if (picked.customType === 'circle-radius-1') this.dragType = 'radius-1' |
| | | if (picked.customType === 'circle-radius-2') this.dragType = 'radius-2' |
| | | if (picked.customType === 'circle-radius-3') this.dragType = 'radius-3' |
| | | if (this.dragType) this.disableMapControl() |
| | | } |
| | | |
| | |
| | | |
| | | if (this.dragType === 'center') { |
| | | this.centerCartesian = position |
| | | this.updateRadiusPoint() |
| | | this.updateRadiusPoints() |
| | | return |
| | | } |
| | | |
| | | const local = cartesianToLocal(this.centerCartesian, position) |
| | | this.radius = Math.max(1, Math.sqrt(local.x * local.x + local.y * local.y)) |
| | | this.updateRadiusPoint() |
| | | const radius = Math.max(1, Math.sqrt(local.x * local.x + local.y * local.y)) |
| | | if (this.dragType === 'radius-1') { |
| | | this.radiusLevel1 = radius |
| | | if (this.radiusLevel2 < this.radiusLevel1) this.radiusLevel2 = this.radiusLevel1 |
| | | if (this.radiusLevel3 < this.radiusLevel2) this.radiusLevel3 = this.radiusLevel2 |
| | | } |
| | | if (this.dragType === 'radius-2') { |
| | | this.radiusLevel2 = Math.max(radius, this.radiusLevel1) |
| | | if (this.radiusLevel3 < this.radiusLevel2) this.radiusLevel3 = this.radiusLevel2 |
| | | } |
| | | if (this.dragType === 'radius-3') { |
| | | this.radiusLevel3 = Math.max(radius, this.radiusLevel2) |
| | | } |
| | | this.updateRadiusPoints() |
| | | } |
| | | |
| | | handleLeftUp() { |
| | | if (!this.dragType) return |
| | | this.dragType = null |
| | | this.enableMapControl() |
| | | const positions = buildEllipsePositions(this.centerCartesian, this.radius, this.radius) |
| | | this.notify('getPolygonPositions', positions) |
| | | const positions = buildEllipsePositions(this.centerCartesian, this.radiusLevel3, this.radiusLevel3) |
| | | this.notify('getPolygonPositions', { |
| | | positions, |
| | | meta: { |
| | | center: this.centerCartesian, |
| | | bufferRadii: [this.radiusLevel1, this.radiusLevel2, this.radiusLevel3], |
| | | }, |
| | | }) |
| | | } |
| | | |
| | | createEntities() { |
| | | this.circleEntity = this.dataSource.entities.add({ |
| | | this.circleLevel1Entity = this.dataSource.entities.add({ |
| | | position: new Cesium.CallbackProperty(() => this.centerCartesian, false), |
| | | ellipse: { |
| | | semiMajorAxis: new Cesium.CallbackProperty(() => this.radius, false), |
| | | semiMinorAxis: new Cesium.CallbackProperty(() => this.radius, false), |
| | | material: Cesium.Color.fromBytes(45, 140, 240, 99), |
| | | outline: true, |
| | | outlineColor: Cesium.Color.fromBytes(45, 140, 240, 255), |
| | | outlineWidth: 2, |
| | | semiMajorAxis: new Cesium.CallbackProperty(() => this.radiusLevel1, false), |
| | | semiMinorAxis: new Cesium.CallbackProperty(() => this.radiusLevel1, false), |
| | | material: Cesium.Color.fromBytes(247, 20, 20, 128), |
| | | outline: false, |
| | | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, |
| | | }, |
| | | }) |
| | | this.circleLevel1OutlineEntity = this.dataSource.entities.add({ |
| | | polyline: { |
| | | positions: new Cesium.CallbackProperty(() => { |
| | | if (!this.centerCartesian || this.radiusLevel1 <= 0) return [] |
| | | const positions = buildEllipsePositions(this.centerCartesian, this.radiusLevel1, this.radiusLevel1) |
| | | return positions.length ? [...positions, positions[0]] : positions |
| | | }, false), |
| | | clampToGround: true, |
| | | width: 2, |
| | | material: Cesium.Color.fromBytes(255, 0, 0, 255), |
| | | }, |
| | | }) |
| | | |
| | | this.circleLevel2Entity = this.dataSource.entities.add({ |
| | | position: new Cesium.CallbackProperty(() => this.centerCartesian, false), |
| | | ellipse: { |
| | | semiMajorAxis: new Cesium.CallbackProperty(() => this.radiusLevel2, false), |
| | | semiMinorAxis: new Cesium.CallbackProperty(() => this.radiusLevel2, false), |
| | | material: Cesium.Color.fromBytes(255, 235, 59, 128), |
| | | outline: false, |
| | | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, |
| | | }, |
| | | }) |
| | | this.circleLevel2OutlineEntity = this.dataSource.entities.add({ |
| | | polyline: { |
| | | positions: new Cesium.CallbackProperty(() => { |
| | | if (!this.centerCartesian || this.radiusLevel2 <= 0) return [] |
| | | const positions = buildEllipsePositions(this.centerCartesian, this.radiusLevel2, this.radiusLevel2) |
| | | return positions.length ? [...positions, positions[0]] : positions |
| | | }, false), |
| | | clampToGround: true, |
| | | width: 2, |
| | | material: Cesium.Color.fromBytes(255, 235, 59, 255), |
| | | }, |
| | | }) |
| | | |
| | | this.circleLevel3Entity = this.dataSource.entities.add({ |
| | | position: new Cesium.CallbackProperty(() => this.centerCartesian, false), |
| | | ellipse: { |
| | | semiMajorAxis: new Cesium.CallbackProperty(() => this.radiusLevel3, false), |
| | | semiMinorAxis: new Cesium.CallbackProperty(() => this.radiusLevel3, false), |
| | | material: Cesium.Color.fromBytes(25, 178, 230, 128), |
| | | outline: false, |
| | | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, |
| | | }, |
| | | }) |
| | | this.circleLevel3OutlineEntity = this.dataSource.entities.add({ |
| | | polyline: { |
| | | positions: new Cesium.CallbackProperty(() => { |
| | | if (!this.centerCartesian || this.radiusLevel3 <= 0) return [] |
| | | const positions = buildEllipsePositions(this.centerCartesian, this.radiusLevel3, this.radiusLevel3) |
| | | return positions.length ? [...positions, positions[0]] : positions |
| | | }, false), |
| | | clampToGround: true, |
| | | width: 2, |
| | | material: Cesium.Color.fromBytes(0, 251, 255, 255), |
| | | }, |
| | | }) |
| | | |
| | |
| | | customType: 'circle-center', |
| | | }) |
| | | |
| | | this.radiusEntity = this.dataSource.entities.add({ |
| | | position: new Cesium.CallbackProperty(() => this.getRadiusPoint(), false), |
| | | this.radiusLevel1Entity = this.dataSource.entities.add({ |
| | | position: new Cesium.CallbackProperty(() => this.getRadiusPoint(this.radiusLevel1), false), |
| | | point: { |
| | | pixelSize: 10, |
| | | color: Cesium.Color.fromBytes(255, 255, 255, 200), |
| | | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, |
| | | disableDepthTestDistance: Number.POSITIVE_INFINITY, |
| | | }, |
| | | customType: 'circle-radius', |
| | | customType: 'circle-radius-1', |
| | | }) |
| | | |
| | | this.radiusLevel2Entity = this.dataSource.entities.add({ |
| | | position: new Cesium.CallbackProperty(() => this.getRadiusPoint(this.radiusLevel2), false), |
| | | point: { |
| | | pixelSize: 10, |
| | | color: Cesium.Color.fromBytes(255, 255, 255, 200), |
| | | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, |
| | | disableDepthTestDistance: Number.POSITIVE_INFINITY, |
| | | }, |
| | | customType: 'circle-radius-2', |
| | | }) |
| | | |
| | | this.radiusLevel3Entity = this.dataSource.entities.add({ |
| | | position: new Cesium.CallbackProperty(() => this.getRadiusPoint(this.radiusLevel3), false), |
| | | point: { |
| | | pixelSize: 10, |
| | | color: Cesium.Color.fromBytes(255, 255, 255, 200), |
| | | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, |
| | | disableDepthTestDistance: Number.POSITIVE_INFINITY, |
| | | }, |
| | | customType: 'circle-radius-3', |
| | | }) |
| | | } |
| | | |
| | | updateRadiusPoint() { |
| | | if (!this.radiusEntity) return |
| | | this.radiusEntity.position = this.getRadiusPoint() |
| | | updateRadiusPoints() { |
| | | if (this.radiusLevel1Entity) this.radiusLevel1Entity.position = this.getRadiusPoint(this.radiusLevel1) |
| | | if (this.radiusLevel2Entity) this.radiusLevel2Entity.position = this.getRadiusPoint(this.radiusLevel2) |
| | | if (this.radiusLevel3Entity) this.radiusLevel3Entity.position = this.getRadiusPoint(this.radiusLevel3) |
| | | } |
| | | |
| | | getPositions() { |
| | | if (!this.centerCartesian) return [] |
| | | return buildEllipsePositions(this.centerCartesian, this.radius, this.radius) |
| | | return { |
| | | positions: buildEllipsePositions(this.centerCartesian, this.radiusLevel3, this.radiusLevel3), |
| | | meta: { |
| | | center: this.centerCartesian, |
| | | bufferRadii: [this.radiusLevel1, this.radiusLevel2, this.radiusLevel3], |
| | | }, |
| | | } |
| | | } |
| | | |
| | | getRadiusPoint() { |
| | | getRadiusPoint(radius) { |
| | | const frame = Cesium.Transforms.eastNorthUpToFixedFrame(this.centerCartesian) |
| | | const local = new Cesium.Cartesian3(this.radius, 0, 0) |
| | | const local = new Cesium.Cartesian3(radius, 0, 0) |
| | | return Cesium.Matrix4.multiplyByPoint(frame, local, new Cesium.Cartesian3()) |
| | | } |
| | | |
| | |
| | | this.tooltip?.destroy() |
| | | this.tooltip = null |
| | | this.centerCartesian = null |
| | | this.radius = 0 |
| | | this.radiusLevel1 = 0 |
| | | this.radiusLevel2 = 0 |
| | | this.radiusLevel3 = 0 |
| | | this.circleLevel1OutlineEntity = null |
| | | this.circleLevel2OutlineEntity = null |
| | | this.circleLevel3OutlineEntity = null |
| | | this.dragType = null |
| | | } |
| | | } |