345 lines
9.6 KiB
TypeScript
345 lines
9.6 KiB
TypeScript
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)
|
||
})
|
||
}
|