ts/src/views/Hangjing/hooks/hangjingPolygon.ts

345 lines
9.6 KiB
TypeScript
Raw Normal View History

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)
})
}