ts/src/views/Hangjing/hooks/hangjingPolygon.ts
严争鸣 ab89bed57d add
2025-01-20 09:42:19 +08:00

345 lines
9.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Subscriber } from 'cesium-extends'
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'
import { useTree } from '@/utils/tree'
import { useHangjingPopup } from './hangjingPopup'
const { createPopup } = useHangjingPopup()
const { filterTreeNodeByField } = useTree()
export const useHjPolygon = (polygonMap: Map<string | number, any>) => {
let subscriber: Subscriber | null = null
const colors = new Map()
function addHangjingPolygon(ids, data) {
subscriber = new Subscriber(viewer, {
pickResult: {
enable: true,
},
})
// const ids = data.map(item => item.id)
const addIds = difference(ids, [...polygonMap.keys()])
const removeIds = difference([...polygonMap.keys()], ids)
// 添加
if (addIds.length > 0) {
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 => {
addPolygon(zone, id)
})
} else {
addPolygon(hjData)
}
}
})
}
// 删除
if (removeIds.length > 0) {
console.log(removeIds, 'removeIds')
removeIds.forEach(id => {
removeEventSub(polygonMap.get(id))
removeHangjingPolygon(id)
})
}
}
function addPolygon(item, parentId: number | null = null) {
const { id, geom, title } = item
const feature = parseWKT(geom)
const position = feature.coordinates[0].map(pos => {
return Cesium.Cartesian3.fromDegrees(...pos)
})
const labels = addTextAlongCurve('Cesium中文垂直排列测试', position)
// 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()
// }
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
const centerEntity = addPolygonCenter(centerPoint)
addEventSub({
parentId,
id,
entity: polygon,
hjData: { ...item, centerPoint },
})
if (parentId) {
if (polygonMap.get(parentId)) {
polygonMap.get(parentId).push(polygon)
} else {
polygonMap.set(parentId, [polygon])
}
polygonMap.get(parentId).push(centerEntity)
} else {
polygonMap.set(id, polygon)
}
// console.log(polygonMap, 'polygonMap')
}
function addPolygonCenter(centerPoint: number[]) {
return viewer.entities.add({
show: false,
position: Cesium.Cartesian3.fromDegrees(...centerPoint),
point: {
pixelSize: 10,
color: Cesium.Color.RED,
},
})
}
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,
polyline: {
positions: position,
width: 1,
material: Cesium.Color.RED,
},
polygon: {
hierarchy: new Cesium.PolygonHierarchy(position),
material: Cesium.Color.fromCssColorString(color),
// material: polygonMaterial(color),
// 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,
}
}
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)
})
}