import * as Cesium from 'cesium'
|
import { MapTooltip } from '@ztzf/utils'
|
import { ToolBase } from '../ToolBase'
|
import { getBoundsFromLngLats, rectanglePositionsFromBounds } from '../shapeUtils'
|
|
const DEFAULT_STYLE = {
|
fill: Cesium.Color.fromBytes(45, 140, 240, 99),
|
outline: Cesium.Color.fromBytes(45, 140, 240, 255),
|
}
|
const resolveStyle = style => ({
|
fill: style?.fill || DEFAULT_STYLE.fill,
|
outline: style?.outline || DEFAULT_STYLE.outline,
|
})
|
|
const normalizePoints = points =>
|
(points || []).map(point => ({
|
lng: point.lng ?? point.longitude,
|
lat: point.lat ?? point.latitude,
|
}))
|
|
export class EditRectangleTool extends ToolBase {
|
constructor(viewer, options = {}) {
|
super(viewer)
|
this.tooltip = new MapTooltip(viewer)
|
this.dataSource = null
|
this.bounds = null
|
this.rectangleEntity = null
|
this.outlineEntity = null
|
this.cornerEntities = []
|
this.dragIndex = null
|
this.fixedCorner = null
|
this.style = resolveStyle(options?.style)
|
}
|
|
start(points = []) {
|
const normalized = normalizePoints(points)
|
this.bounds = getBoundsFromLngLats(normalized)
|
if (!this.bounds) return
|
this.dataSource = new Cesium.CustomDataSource('edit-rectangle')
|
this.viewer.dataSources.add(this.dataSource)
|
this.createEntities()
|
this.initHandler()
|
}
|
|
initHandler() {
|
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas)
|
this.handler.setInputAction(evt => this.handleLeftDown(evt), Cesium.ScreenSpaceEventType.LEFT_DOWN)
|
this.handler.setInputAction(evt => this.handleMouseMove(evt), Cesium.ScreenSpaceEventType.MOUSE_MOVE)
|
this.handler.setInputAction(() => this.handleLeftUp(), Cesium.ScreenSpaceEventType.LEFT_UP)
|
this.tooltip.show('拖动顶点编辑', { x: 0, y: 0 })
|
}
|
|
handleLeftDown(evt) {
|
const picked = this.viewer.scene.pick(evt.position)?.id
|
if (!picked || picked.customType !== 'rect-corner') return
|
this.dragIndex = picked.customData.index
|
const corners = this.getCornerCartographics()
|
this.fixedCorner = corners[(this.dragIndex + 2) % 4]
|
this.disableMapControl()
|
}
|
|
handleMouseMove(evt) {
|
this.tooltip.move(evt.endPosition)
|
if (this.dragIndex == null) return
|
const position = this.getPositionFromScreen(evt.endPosition)
|
if (!position) return
|
const moved = Cesium.Cartographic.fromCartesian(position)
|
this.bounds = {
|
west: Math.min(moved.longitude, this.fixedCorner.longitude),
|
east: Math.max(moved.longitude, this.fixedCorner.longitude),
|
south: Math.min(moved.latitude, this.fixedCorner.latitude),
|
north: Math.max(moved.latitude, this.fixedCorner.latitude),
|
}
|
this.updateCorners()
|
}
|
|
handleLeftUp() {
|
if (this.dragIndex == null) return
|
this.dragIndex = null
|
this.fixedCorner = null
|
this.enableMapControl()
|
const positions = rectanglePositionsFromBounds(this.bounds)
|
this.notify('getPolygonPositions', positions)
|
}
|
|
createEntities() {
|
const hierarchy = new Cesium.CallbackProperty(() => {
|
const positions = rectanglePositionsFromBounds(this.bounds)
|
return new Cesium.PolygonHierarchy(positions)
|
}, false)
|
this.rectangleEntity = this.dataSource.entities.add({
|
polygon: {
|
hierarchy,
|
material: this.style.fill,
|
outline: false,
|
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
},
|
})
|
this.outlineEntity = this.dataSource.entities.add({
|
polyline: {
|
positions: new Cesium.CallbackProperty(() => {
|
const positions = rectanglePositionsFromBounds(this.bounds)
|
return positions.length ? [...positions, positions[0]] : []
|
}, false),
|
clampToGround: true,
|
width: 2,
|
material: this.style.outline,
|
},
|
})
|
this.createCorners()
|
}
|
|
createCorners() {
|
const positions = rectanglePositionsFromBounds(this.bounds)
|
this.cornerEntities = positions.map((position, index) =>
|
this.dataSource.entities.add({
|
position,
|
point: {
|
pixelSize: 12,
|
color: Cesium.Color.WHITE,
|
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
},
|
customType: 'rect-corner',
|
customData: { index },
|
})
|
)
|
}
|
|
updateCorners() {
|
const positions = rectanglePositionsFromBounds(this.bounds)
|
this.cornerEntities.forEach((entity, index) => {
|
entity.position = positions[index]
|
})
|
}
|
|
getPositions() {
|
return rectanglePositionsFromBounds(this.bounds)
|
}
|
|
getCornerCartographics() {
|
return rectanglePositionsFromBounds(this.bounds).map(position => Cesium.Cartographic.fromCartesian(position))
|
}
|
|
getPositionFromScreen(screenPosition) {
|
const scene = this.viewer.scene
|
const cartesian = scene.pickPosition(screenPosition)
|
if (cartesian) return cartesian
|
return scene.camera.pickEllipsoid(screenPosition, scene.globe.ellipsoid)
|
}
|
|
setStyle(style) {
|
this.style = resolveStyle(style)
|
if (this.rectangleEntity?.polygon) {
|
this.rectangleEntity.polygon.material = this.style.fill
|
}
|
if (this.outlineEntity?.polyline) {
|
this.outlineEntity.polyline.material = this.style.outline
|
}
|
}
|
|
disableMapControl() {
|
const controller = this.viewer.scene.screenSpaceCameraController
|
controller.enableRotate = false
|
controller.enableTranslate = false
|
controller.enableZoom = false
|
}
|
|
enableMapControl() {
|
const controller = this.viewer.scene.screenSpaceCameraController
|
controller.enableRotate = true
|
controller.enableTranslate = true
|
controller.enableZoom = true
|
}
|
|
destroy() {
|
if (this.dataSource) {
|
this.dataSource.entities.removeAll()
|
this.viewer.dataSources.remove(this.dataSource)
|
this.dataSource = null
|
}
|
if (this.handler) {
|
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOWN)
|
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE)
|
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_UP)
|
this.handler.destroy()
|
this.handler = null
|
}
|
this.tooltip?.destroy()
|
this.tooltip = null
|
this.bounds = null
|
this.dragIndex = null
|
this.fixedCorner = null
|
this.cornerEntities = []
|
}
|
}
|