/*
|
* @Author : yuan
|
* @Date : 2025-07-03 14:58:03
|
* @LastEditors : yuan
|
* @LastEditTime : 2025-08-11 16:20:46
|
* @FilePath : \src\utils\mapUtils.js
|
* @Description :
|
* Copyright 2025 OBKoro1, All Rights Reserved.
|
* 2025-07-03 14:58:03
|
*/
|
import * as Cesium from 'cesium'
|
import * as turf from '@turf/turf'
|
|
/**
|
* 计算dataSource的包围盒
|
* @param {*} entities dataSource加载完成后的entities
|
* @returns
|
*/
|
export function computeEntitiesBoundingSphere (entities) {
|
let positions = []
|
entities.forEach(function (entity) {
|
if (entity.position) {
|
positions.push(entity.position.getValue(Cesium.JulianDate.now()))
|
}
|
// 如果是多边形或线,需要提取所有顶点
|
if (entity.polygon) {
|
let hierarchy = entity.polygon.hierarchy.getValue(Cesium.JulianDate.now())
|
positions = positions.concat(hierarchy.positions)
|
}
|
})
|
return Cesium.BoundingSphere.fromPoints(positions)
|
}
|
|
|
export function getGeoJsonBoundingSphere (geojson) {
|
// 提取所有坐标点
|
let positions = []
|
geojson.features.forEach(function (feature) {
|
if (feature.geometry.type === 'Point') {
|
positions.push(Cesium.Cartesian3.fromDegrees(
|
feature.geometry.coordinates[0],
|
feature.geometry.coordinates[1]
|
))
|
} else if (feature.geometry.type === 'Polygon') {
|
feature.geometry.coordinates[0].forEach(function (coord) {
|
positions.push(Cesium.Cartesian3.fromDegrees(coord[0], coord[1]))
|
})
|
} else if (feature.geometry.type === 'MultiPolygon') {
|
feature.geometry.coordinates[0][0].forEach(function (coord) {
|
positions.push(Cesium.Cartesian3.fromDegrees(coord[0], coord[1]))
|
})
|
}
|
// 可以添加对其他几何类型的处理
|
})
|
|
// 计算边界球
|
return Cesium.BoundingSphere.fromPoints(positions)
|
}
|
|
/**
|
* 三维白膜的shader设置
|
*/
|
export const customShader = new Cesium.CustomShader({
|
lightingModel: Cesium.LightingModel.UNLIT,
|
fragmentShaderText: `
|
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)
|
{
|
|
|
vec4 position = czm_inverseModelView * vec4(fsInput.attributes.positionEC,1); // 位置
|
|
// 注意shader中写浮点数是,一定要带小数点,否则会报错,比如0需要写成0.0,1要写成1.0
|
float _baseHeight = 0.0; // 物体的基础高度,需要修改成一个合适的建筑基础高度
|
float _heightRange = 60.0; // 高亮的范围(_baseHeight ~ _baseHeight + _heightRange)
|
float _glowRange = 80.0; // 光环的移动范围(高度)
|
|
// 建筑基础色
|
float mars_height = position.z - _baseHeight;
|
|
// ========== 地面以下透明处理 ==========
|
// if(position.z < _baseHeight) {
|
// // 地面以下部分完全透明
|
// material.alpha = 0.0;
|
// return; // 提前返回,不执行后续着色计算
|
// }
|
|
float mars_a11 = fract(czm_frameNumber / 60.0) * 3.14159265 * 2.0;
|
float mars_a12 = mars_height / _heightRange;
|
|
// + sin(mars_a11) * 0.1;
|
|
// 基础颜色
|
material.diffuse = vec3(0.3176, 0.6706, 1.0); // 颜色
|
material.diffuse *= vec3(mars_a12);// 渐变
|
|
// 动态光环
|
// float maxFrame = 500.0; // 设置最大生效帧数(例如 1000 帧后停止)
|
// if (czm_frameNumber <= maxFrame) {
|
// // 动态光环逻辑
|
// float time = fract(czm_frameNumber / 360.0);
|
// time = abs(time - 0.5) * 2.0;
|
// float mars_h = clamp(mars_height / _glowRange, 0.0, 1.0);
|
// float mars_diff = step(0.005, abs(mars_h - time));
|
// material.diffuse += material.diffuse * (1.0 - mars_diff);
|
// }
|
} `
|
})
|
|
/**
|
* 用于在Cesium场景中创建和管理贴地圆形及圆形边框的Primitive
|
*/
|
export class GroundCirclePrimitiveManager {
|
constructor() {
|
this._viewer = null
|
this._primitiveCollection = null
|
this._entityCircleDataSource = null
|
}
|
|
init (viewer) {
|
if (this._primitiveCollection && this._entityCircleDataSource && this._viewer) {
|
this.removeAll()
|
|
return
|
}
|
|
this._viewer = viewer
|
|
this._entityCircleDataSource = new Cesium.CustomDataSource('entityCircleDataSource')
|
this._viewer.dataSources.add(this._entityCircleDataSource)
|
this._primitiveCollection = new Cesium.PrimitiveCollection()
|
this._viewer.scene.primitives.add(this._primitiveCollection)
|
}
|
|
/**
|
* 创建贴地实心圆形及圆环
|
* @param {object} options - 配置选项对象
|
* @param {object} options.data - 位置信息
|
* @param {number} [options.eventCount=0] - 事件数量,默认为 0
|
* @param {boolean} [options.useEventMaterial=false] - 是否使用事件的材质颜色,默认为 false
|
* @param {number} [options.addAll=1] - 是否覆盖圆与光环都加载,1都加,2光加载覆盖圆,3光加载覆盖圆环
|
* @param {*} [options.materialColor] - 覆盖圆的填充材质
|
* @param {*} [options.frameColor] - 覆盖圆边界的填充材质
|
*/
|
addCircleOrOutline (options) {
|
let {
|
data,
|
eventCount = 0,
|
useEventMaterial = false,
|
addAll = 1,
|
materialColor = Cesium.Color.fromBytes(100, 149, 237, 40),
|
frameColor = Cesium.Color.fromBytes(100, 149, 237, 255),
|
} = options
|
|
if (useEventMaterial) {
|
if (eventCount > 100) {
|
materialColor = Cesium.Color.fromBytes(255, 37, 9, 40)
|
frameColor = Cesium.Color.fromBytes(255, 37, 9, 255)
|
} else if (eventCount > 50) {
|
materialColor = Cesium.Color.fromBytes(255, 171, 54, 40)
|
frameColor = Cesium.Color.fromBytes(255, 171, 54, 255)
|
} else {
|
materialColor = Cesium.Color.fromBytes(27, 215, 89, 40)
|
frameColor = Cesium.Color.fromBytes(27, 215, 89, 255)
|
}
|
}
|
|
if (addAll === 1) {
|
this.addCircle({
|
data, materialColor
|
})
|
this.addCircleOutline({
|
data, frameColor
|
})
|
} else if (addAll === 2) {
|
this.addCircle({
|
data, materialColor
|
})
|
} else if (addAll === 3) {
|
this.addCircleOutline({
|
data, frameColor
|
})
|
}
|
|
}
|
|
/**
|
* 创建贴地实心圆形---覆盖圆形
|
* @param {object} options - 配置选项对象
|
* @param {object} options.data - 位置信息
|
* @param {*} [options.materialColor] - 覆盖圆的填充材质
|
*/
|
addCircle (options) {
|
const {
|
data,
|
materialColor = Cesium.Color.fromBytes(100, 149, 237, 40),
|
radius = 5000
|
} = options
|
|
const center = Cesium.Cartesian3.fromDegrees(+data.lng, +data.lat)
|
|
const entity = {
|
position: center, // 北京坐标(示例)
|
ellipse: {
|
semiMajorAxis: radius, // 半长轴(米)
|
semiMinorAxis: radius, // 半短轴(米)
|
HeightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
classificationType: Cesium.ClassificationType.TERRAIN,
|
material: new Cesium.ColorMaterialProperty(materialColor),
|
}
|
}
|
|
this._entityCircleDataSource.entities.add(entity)
|
}
|
|
/**
|
* 创建贴地实心圆形---覆盖圆形
|
* @param {object} options - 配置选项对象
|
* @param {object} options.data - 位置信息
|
* @param {*} [options.materialColor] - 覆盖圆的填充材质
|
*/
|
addCirclePrimitive (options) {
|
const {
|
data,
|
materialColor = Cesium.Color.fromBytes(100, 149, 237, 40),
|
radius = 5000
|
} = options
|
|
const center = Cesium.Cartesian3.fromDegrees(+data.lng, +data.lat)
|
|
const circleGeometry = new Cesium.CircleGeometry({
|
center: center,
|
radius: radius,
|
vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
|
height: 0, // 贴地
|
})
|
|
const primitive = new Cesium.GroundPrimitive({
|
geometryInstances: new Cesium.GeometryInstance({
|
geometry: circleGeometry,
|
}),
|
appearance: new Cesium.EllipsoidSurfaceAppearance({
|
material: new Cesium.Material({
|
fabric: {
|
type: 'Color',
|
uniforms: {
|
color: materialColor,
|
},
|
},
|
}),
|
}),
|
classificationType: Cesium.ClassificationType.TERRAIN
|
})
|
|
// this._primitiveCollection.add(primitive)
|
}
|
|
/**
|
* 创建贴地实心圆形---覆盖圆环
|
* @param {object} options - 配置选项对象
|
* @param {object} options.data - 位置信息
|
* @param {*} [options.frameColor] - 覆盖圆边界的填充材质
|
*/
|
addCircleOutline (options) {
|
const {
|
data,
|
frameColor = Cesium.Color.fromBytes(100, 149, 237, 255),
|
radius = 5000
|
} = options
|
|
const turfCircle = turf.circle([data.lng, data.lat], radius, { steps: 360, units: 'meters' })
|
|
// 1. 获取Turf生成的圆形坐标点
|
const coordinates = turfCircle.geometry.coordinates[0]
|
|
// 2. 转换为Cesium Cartesian3数组
|
const positions = coordinates.map(coord => Cesium.Cartesian3.fromDegrees(coord[0], coord[1]))
|
|
// 3. 创建贴地线
|
const polyline = new Cesium.GroundPolylineGeometry({
|
positions: positions,
|
width: 2, // 线宽
|
vertexFormat: Cesium.VertexFormat.POSITION_AND_ST,
|
})
|
|
const primitive = new Cesium.GroundPolylinePrimitive({
|
geometryInstances: new Cesium.GeometryInstance({
|
geometry: polyline,
|
}),
|
|
appearance: new Cesium.PolylineMaterialAppearance({
|
material: new Cesium.Material({
|
fabric: {
|
type: 'Color', // 使用纯色材质
|
uniforms: {
|
color: frameColor, // 设置颜色
|
},
|
},
|
}),
|
}),
|
})
|
|
this._primitiveCollection.add(primitive)
|
}
|
|
// 清空当前primitive组
|
removeAll () {
|
if (this._primitiveCollection) {
|
this._primitiveCollection.removeAll()
|
}
|
|
if (this._entityCircleDataSource) {
|
this._entityCircleDataSource?.entities.removeAll()
|
}
|
}
|
|
// 销毁处理
|
destroy () {
|
if (this._primitiveCollection) {
|
this._primitiveCollection.removeAll()
|
this._viewer.scene.primitives.remove(this._primitiveCollection)
|
this._primitiveCollection = null
|
}
|
|
if (this._entityCircleDataSource) {
|
this._entityCircleDataSource?.entities.removeAll()
|
this._viewer.dataSources.remove(this._entityCircleDataSource, true)
|
this._entityCircleDataSource = null
|
}
|
|
this._viewer = null
|
}
|
}
|
|
|
/**
|
* 计算多点距离
|
* @param {*} data 笛卡尔坐标系数组
|
* @returns {object} {length: 线长度, xRepeatCount: x重复数量}
|
*/
|
export function getCurPolylineLength (data) {
|
let polylineLength = 0
|
|
data.forEach((item, index) => {
|
if (index == data.length - 1) return
|
|
let point1 = item
|
let point2 = data[index + 1]
|
|
polylineLength += Cesium.Cartesian3.distance(point1, point2)
|
})
|
|
return {
|
length: polylineLength,
|
xRepeatCount: Math.ceil(polylineLength / 1000),
|
}
|
}
|