2025-01-13 08:45:08 +00:00
|
|
|
|
import { Subscriber } from 'cesium-extends'
|
2024-12-09 06:44:52 +00:00
|
|
|
|
import chroma from 'chroma-js'
|
|
|
|
|
import { parseWKT } from '@/utils/parseWKT'
|
|
|
|
|
import { difference } from 'lodash'
|
|
|
|
|
import { polygonGradient } from '@/js/polygonGradient'
|
|
|
|
|
import { polygonMaterial } from '@/js/polygon'
|
|
|
|
|
import { centerOfMass } from '@turf/turf'
|
2025-01-20 01:42:19 +00:00
|
|
|
|
import { useTree } from '@/utils/tree'
|
2024-12-09 06:44:52 +00:00
|
|
|
|
|
|
|
|
|
import { useHangjingPopup } from './hangjingPopup'
|
|
|
|
|
|
|
|
|
|
const { createPopup } = useHangjingPopup()
|
2025-01-20 01:42:19 +00:00
|
|
|
|
const { filterTreeNodeByField } = useTree()
|
2024-12-09 06:44:52 +00:00
|
|
|
|
|
|
|
|
|
export const useHjPolygon = (polygonMap: Map<string | number, any>) => {
|
|
|
|
|
let subscriber: Subscriber | null = null
|
|
|
|
|
|
|
|
|
|
const colors = new Map()
|
|
|
|
|
|
2025-01-20 01:42:19 +00:00
|
|
|
|
function addHangjingPolygon(ids, data) {
|
2024-12-09 06:44:52 +00:00
|
|
|
|
subscriber = new Subscriber(viewer, {
|
|
|
|
|
pickResult: {
|
|
|
|
|
enable: true,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
2025-01-20 01:42:19 +00:00
|
|
|
|
// const ids = data.map(item => item.id)
|
2024-12-09 06:44:52 +00:00
|
|
|
|
const addIds = difference(ids, [...polygonMap.keys()])
|
|
|
|
|
const removeIds = difference([...polygonMap.keys()], ids)
|
|
|
|
|
// 添加
|
|
|
|
|
if (addIds.length > 0) {
|
2025-01-20 01:42:19 +00:00
|
|
|
|
const nodes = filterTreeNodeByField({
|
|
|
|
|
treeData: data.value,
|
|
|
|
|
params: addIds,
|
|
|
|
|
paramName: 'dataId',
|
|
|
|
|
})
|
|
|
|
|
nodes.forEach(({ data: hjData, dataId: id }) => {
|
|
|
|
|
// const item = data.find(item => item.id === id)
|
|
|
|
|
if (hjData) {
|
|
|
|
|
if (hjData.zoneList.length > 0) {
|
|
|
|
|
hjData.zoneList.forEach(zone => {
|
2024-12-09 06:44:52 +00:00
|
|
|
|
addPolygon(zone, id)
|
|
|
|
|
})
|
|
|
|
|
} else {
|
2025-01-20 01:42:19 +00:00
|
|
|
|
addPolygon(hjData)
|
2024-12-09 06:44:52 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
// 删除
|
|
|
|
|
if (removeIds.length > 0) {
|
|
|
|
|
console.log(removeIds, 'removeIds')
|
|
|
|
|
removeIds.forEach(id => {
|
|
|
|
|
removeEventSub(polygonMap.get(id))
|
|
|
|
|
removeHangjingPolygon(id)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function addPolygon(item, parentId: number | null = null) {
|
2025-01-20 01:42:19 +00:00
|
|
|
|
const { id, geom, title } = item
|
2024-12-09 06:44:52 +00:00
|
|
|
|
const feature = parseWKT(geom)
|
|
|
|
|
const position = feature.coordinates[0].map(pos => {
|
|
|
|
|
return Cesium.Cartesian3.fromDegrees(...pos)
|
|
|
|
|
})
|
|
|
|
|
|
2025-01-20 01:42:19 +00:00
|
|
|
|
const labels = addTextAlongCurve('Cesium中文垂直排列测试', position)
|
|
|
|
|
|
2024-12-09 06:44:52 +00:00
|
|
|
|
// console.log(item, id, position, 'id, position, color')
|
|
|
|
|
// const randomColor =
|
|
|
|
|
// '#' + Math.random().toString(16).substring(2, 8).padEnd(6, '0')
|
|
|
|
|
|
|
|
|
|
// if (!colors.has(id)) {
|
|
|
|
|
// const randomColor = chroma.random().alpha(0.5).hex()
|
|
|
|
|
// colors.set(id, randomColor)
|
|
|
|
|
// }
|
|
|
|
|
// if (zoneId) {
|
|
|
|
|
// if()
|
|
|
|
|
// }
|
2025-01-20 01:42:19 +00:00
|
|
|
|
|
2024-12-09 06:44:52 +00:00
|
|
|
|
const curId = parentId || id
|
|
|
|
|
|
|
|
|
|
if (!colors.has(curId)) {
|
|
|
|
|
const randomColor = chroma.random().brighten().alpha(0.6).hex()
|
|
|
|
|
colors.set(curId, randomColor)
|
|
|
|
|
}
|
|
|
|
|
const color = colors.get(curId)
|
|
|
|
|
|
|
|
|
|
const polygon = addPrimitivePolygon({
|
|
|
|
|
id,
|
|
|
|
|
color: color,
|
|
|
|
|
position,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const centerPoint = centerOfMass(feature).geometry.coordinates
|
2025-01-13 08:45:08 +00:00
|
|
|
|
|
|
|
|
|
const centerEntity = addPolygonCenter(centerPoint)
|
2024-12-09 06:44:52 +00:00
|
|
|
|
addEventSub({
|
|
|
|
|
parentId,
|
|
|
|
|
id,
|
|
|
|
|
entity: polygon,
|
|
|
|
|
hjData: { ...item, centerPoint },
|
|
|
|
|
})
|
|
|
|
|
if (parentId) {
|
|
|
|
|
if (polygonMap.get(parentId)) {
|
|
|
|
|
polygonMap.get(parentId).push(polygon)
|
|
|
|
|
} else {
|
|
|
|
|
polygonMap.set(parentId, [polygon])
|
|
|
|
|
}
|
2025-01-13 08:45:08 +00:00
|
|
|
|
polygonMap.get(parentId).push(centerEntity)
|
2024-12-09 06:44:52 +00:00
|
|
|
|
} else {
|
|
|
|
|
polygonMap.set(id, polygon)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// console.log(polygonMap, 'polygonMap')
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-13 08:45:08 +00:00
|
|
|
|
function addPolygonCenter(centerPoint: number[]) {
|
|
|
|
|
return viewer.entities.add({
|
|
|
|
|
show: false,
|
|
|
|
|
position: Cesium.Cartesian3.fromDegrees(...centerPoint),
|
|
|
|
|
point: {
|
|
|
|
|
pixelSize: 10,
|
|
|
|
|
color: Cesium.Color.RED,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-09 06:44:52 +00:00
|
|
|
|
function addPrimitivePolygon({
|
|
|
|
|
id,
|
|
|
|
|
position,
|
|
|
|
|
color,
|
|
|
|
|
}: {
|
|
|
|
|
id: string
|
|
|
|
|
position: Cesium.Cartesian3[]
|
|
|
|
|
color: string
|
|
|
|
|
}): Cesium.Primitive {
|
|
|
|
|
// const polygonOptions = {
|
|
|
|
|
// extrudedHeight: 5000,
|
|
|
|
|
// polygonHierarchy: new Cesium.PolygonHierarchy(position),
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// const geometry = new Cesium.PolygonGeometry(polygonOptions)
|
|
|
|
|
|
|
|
|
|
// const geometryInstance = new Cesium.GeometryInstance({
|
|
|
|
|
// geometry: geometry,
|
|
|
|
|
// id,
|
|
|
|
|
// })
|
|
|
|
|
|
|
|
|
|
// const primitive = new Cesium.Primitive({
|
|
|
|
|
// releaseGeometryInstances: false,
|
|
|
|
|
// asynchronous: false,
|
|
|
|
|
// geometryInstances: [geometryInstance],
|
|
|
|
|
// appearance: new Cesium.EllipsoidSurfaceAppearance({
|
|
|
|
|
// material: polygonGradient(color),
|
|
|
|
|
// }),
|
|
|
|
|
// })
|
|
|
|
|
// return viewer.scene.primitives.add(primitive)
|
|
|
|
|
|
|
|
|
|
return viewer.entities.add({
|
|
|
|
|
id,
|
2025-01-13 08:45:08 +00:00
|
|
|
|
polyline: {
|
|
|
|
|
positions: position,
|
|
|
|
|
width: 1,
|
|
|
|
|
material: Cesium.Color.RED,
|
|
|
|
|
},
|
2024-12-09 06:44:52 +00:00
|
|
|
|
polygon: {
|
|
|
|
|
hierarchy: new Cesium.PolygonHierarchy(position),
|
2025-01-13 08:45:08 +00:00
|
|
|
|
material: Cesium.Color.fromCssColorString(color),
|
|
|
|
|
// material: polygonMaterial(color),
|
2024-12-09 06:44:52 +00:00
|
|
|
|
// material: polygonGradient(color),
|
|
|
|
|
// heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
function removeHangjingPolygon(id: string) {
|
|
|
|
|
if (polygonMap.has(id)) {
|
|
|
|
|
const polygon = polygonMap.get(id)
|
|
|
|
|
if (polygon instanceof Array) {
|
|
|
|
|
polygon.forEach(item => {
|
|
|
|
|
// viewer.scene.primitives.remove(item)
|
|
|
|
|
viewer.entities.remove(item)
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
// viewer.scene.primitives.remove(polygonMap.get(id))
|
|
|
|
|
viewer.entities.remove(polygonMap.get(id))
|
|
|
|
|
}
|
|
|
|
|
// viewer.entities.remove(hangjingMap.get(id))
|
|
|
|
|
polygonMap.delete(id)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function addEventSub({
|
|
|
|
|
parentId,
|
|
|
|
|
id,
|
|
|
|
|
entity,
|
|
|
|
|
hjData,
|
|
|
|
|
}: {
|
|
|
|
|
parentId: number | null
|
|
|
|
|
id: number
|
|
|
|
|
entity: Cesium.Entity
|
|
|
|
|
hjData: Record<string, any>
|
|
|
|
|
}) {
|
|
|
|
|
subscriber &&
|
|
|
|
|
subscriber.add(
|
|
|
|
|
entity,
|
|
|
|
|
(movement, entity) => {
|
|
|
|
|
// console.log(movement, 'movement')
|
|
|
|
|
// console.log(hjData, 'hjData')
|
|
|
|
|
const position = Cesium.Cartesian3.fromDegrees(...hjData.centerPoint)
|
|
|
|
|
;(polygonMap.has(id) || polygonMap.has(parentId)) &&
|
|
|
|
|
createPopup(id, { _value: position }, hjData)
|
|
|
|
|
},
|
|
|
|
|
'LEFT_CLICK'
|
|
|
|
|
)
|
|
|
|
|
// console.log(subscriber, '-------')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function removeEventSub(entity: Cesium.Entity) {
|
|
|
|
|
subscriber && subscriber.remove(entity, 'LEFT_CLICK')
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
addHangjingPolygon,
|
|
|
|
|
removeHangjingPolygon,
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-01-20 01:42:19 +00:00
|
|
|
|
|
|
|
|
|
function addTextAlongCurve(text, polygonPoints) {
|
|
|
|
|
// 创建 Billboard 集合
|
|
|
|
|
const billboardCollection = viewer.scene.primitives.add(
|
|
|
|
|
new Cesium.BillboardCollection()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 创建文字绘制的辅助函数
|
|
|
|
|
function createTextTexture(text, angle) {
|
|
|
|
|
const canvas = document.createElement('canvas')
|
|
|
|
|
const context = canvas.getContext('2d')
|
|
|
|
|
|
|
|
|
|
// 设置 Canvas 大小
|
|
|
|
|
canvas.width = 20
|
|
|
|
|
canvas.height = 20
|
|
|
|
|
|
|
|
|
|
// 设置文字样式
|
|
|
|
|
context.font = '20px sans-serif' // 支持中文
|
|
|
|
|
context.fillStyle = 'blue'
|
|
|
|
|
context.textAlign = 'center'
|
|
|
|
|
context.textBaseline = 'middle'
|
|
|
|
|
|
|
|
|
|
// 将文字绘制到 Canvas,并进行旋转
|
|
|
|
|
context.clearRect(0, 0, canvas.width, canvas.height)
|
|
|
|
|
context.save()
|
|
|
|
|
|
|
|
|
|
// 旋转文字
|
|
|
|
|
context.translate(canvas.width / 2, canvas.height / 2)
|
|
|
|
|
context.rotate(angle)
|
|
|
|
|
context.fillText(text, 0, 0)
|
|
|
|
|
|
|
|
|
|
context.restore()
|
|
|
|
|
|
|
|
|
|
return canvas
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 动态生成字符标注
|
|
|
|
|
function generateLabels(cameraHeight) {
|
|
|
|
|
billboardCollection.removeAll() // 清除之前的标注
|
|
|
|
|
|
|
|
|
|
let charIndex = 0 // 当前字符索引
|
|
|
|
|
for (let i = 0; i < polygonPoints.length - 1; i++) {
|
|
|
|
|
const start = polygonPoints[i] // 当前边的起点
|
|
|
|
|
const end = polygonPoints[i + 1] // 当前边的终点
|
|
|
|
|
|
|
|
|
|
// 计算线段的方向向量
|
|
|
|
|
const direction = Cesium.Cartesian3.subtract(
|
|
|
|
|
end,
|
|
|
|
|
start,
|
|
|
|
|
new Cesium.Cartesian3()
|
|
|
|
|
)
|
|
|
|
|
Cesium.Cartesian3.normalize(direction, direction)
|
|
|
|
|
|
|
|
|
|
// 计算垂直于线段的方向向量
|
|
|
|
|
const perpendicular = Cesium.Cartesian3.cross(
|
|
|
|
|
direction,
|
|
|
|
|
Cesium.Cartesian3.UNIT_Z, // 使用 Z 轴(垂直地球表面)计算垂直方向
|
|
|
|
|
new Cesium.Cartesian3()
|
|
|
|
|
)
|
|
|
|
|
Cesium.Cartesian3.normalize(perpendicular, perpendicular)
|
|
|
|
|
|
|
|
|
|
// 计算线段的长度
|
|
|
|
|
const length = Cesium.Cartesian3.distance(start, end)
|
|
|
|
|
|
|
|
|
|
// 动态调整字符间隔,基于相机高度
|
|
|
|
|
const baseSpacing = 50 // 基础字符间隔
|
|
|
|
|
const charSpacing = Math.max((baseSpacing * cameraHeight) / 5000000, 30)
|
|
|
|
|
|
|
|
|
|
// 按字符间隔放置文字
|
|
|
|
|
let distance = 0
|
|
|
|
|
while (distance < length && charIndex < text.length) {
|
|
|
|
|
// 计算字符的位置
|
|
|
|
|
const fraction = distance / length // 当前字符在边上的位置比例
|
|
|
|
|
const position = Cesium.Cartesian3.lerp(
|
|
|
|
|
start,
|
|
|
|
|
end,
|
|
|
|
|
fraction,
|
|
|
|
|
new Cesium.Cartesian3()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 计算旋转角度,使文字垂直于线段
|
|
|
|
|
const angle = Math.atan2(perpendicular.y, perpendicular.x)
|
|
|
|
|
|
|
|
|
|
// 创建带旋转的文字纹理
|
|
|
|
|
const canvas = createTextTexture(text[charIndex], angle)
|
|
|
|
|
|
|
|
|
|
// 添加 Billboard 显示文字
|
|
|
|
|
billboardCollection.add({
|
|
|
|
|
position: position,
|
|
|
|
|
image: canvas, // 使用生成的文字纹理
|
|
|
|
|
// pixelOffset: new Cesium.Cartesian2(10, 0),
|
|
|
|
|
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 更新字符索引和距离
|
|
|
|
|
charIndex++
|
|
|
|
|
console.log(charSpacing)
|
|
|
|
|
distance += charSpacing * 2000
|
|
|
|
|
// distance *= 2
|
|
|
|
|
console.log(distance)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果字符已用完,跳出循环
|
|
|
|
|
if (charIndex >= text.length) {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 初始化标注(根据初始相机高度)
|
|
|
|
|
generateLabels(viewer.camera.positionCartographic.height)
|
|
|
|
|
|
|
|
|
|
// 监听相机变化事件,动态更新字符间隔
|
|
|
|
|
viewer.camera.changed.addEventListener(() => {
|
|
|
|
|
const cameraHeight = viewer.camera.positionCartographic.height
|
|
|
|
|
generateLabels(cameraHeight)
|
|
|
|
|
})
|
|
|
|
|
}
|