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