LSSE-front/src/components/Common/Cesium/index.js
2025-09-18 20:31:52 +08:00

357 lines
13 KiB
JavaScript
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 * as Cesium from 'cesium'
import CesiumNavigation from 'cesium-navigation-es6'
import 'cesium/Build/Cesium/Widgets/widgets.css'
const getCatesian3FromPX = (viewer, px) => {
const picks = viewer.scene.drillPick(px)
viewer.scene.render()
let cartesian
let isOn3dtiles = false
for (var i = 0; i < picks.length; i++) {
if (picks[i] && picks[i].primitive && picks[i].primitive instanceof Cesium.Cesium3DTileset) {
//模型上拾取
isOn3dtiles = true
break
}
}
if (isOn3dtiles) {
cartesian = viewer.scene.pickPosition(px)
} else {
var ray = viewer.camera.getPickRay(px)
if (!ray) return null
cartesian = viewer.scene.globe.pick(ray, viewer.scene)
}
return cartesian
}
export default class MyCesium {
static ImageryProviderUrl = '/map/mapWX/{z}/{x}/{y}.jpg'
static RoadProviderUrl = ''
static TerrainProviderUrl = '/map/mapTerrain/'
static center = [116.39742, 39.90906, 2000000]
static ENTITY_TYPES = {
WARZONE: 1,
SAFEZONE: 2,
MODEL: 3,
IMAGE: 4,
ROUTE: 5,
LINE: 6, // 两个模型之间的连线
POINT_PLANNING: 7, // 规划点
GRAPHICS: 8, // 折线
AIR_ROUTE: 9, // 航路
ROAMING_PLANE: 10,
}
viewer = null
// 未完成的在地图上移动类的操作
operations = []
// 已添加的军标
plots = []
// 已添加的路线
routes = []
constructor(dom, options = {}) {
const imageryProvider = new Cesium.UrlTemplateImageryProvider({
url: window._CONFIG.ImageryProviderUrl || MyCesium.ImageryProviderUrl,
tilingScheme: new Cesium.WebMercatorTilingScheme(),
maximumLevel: 15,
})
const roadProvider = new Cesium.UrlTemplateImageryProvider({
url: window._CONFIG.RoadProviderUrl || MyCesium.RoadProviderUrl,
tilingScheme: new Cesium.WebMercatorTilingScheme(),
maximumLevel: 15,
format: 'image/png',
})
const terrainProvider = new Cesium.CesiumTerrainProvider({
url: window._CONFIG.TerrainProviderUrl || MyCesium.TerrainProviderUrl,
})
const viewer = new Cesium.Viewer(dom, {
shouldAnimate: true, // 粒子效果
geocoder: false, // 是否显示位置查找工具true表示是false表示否
homeButton: false, // 是否显示首页位置工具
sceneModePicker: false, // 是否显示视角模式切换工具
baseLayerPicker: false, // 是杏显示默认图层选择工具
navigationHelpButton: false, // 是否显示导航帮助工具
animation: false, // 是杏显示动画工具
timeline: false, // 是否显示时间轴工具
fullscreenButton: false, // 是否显示全屏按钮工具
imageryProvider,
terrainProvider,
infoBox: false, // 是否显示信息框
selectionIndicator: false, // 是否显示选中实体时的绿框
})
this.viewer = viewer
const { center = MyCesium.center } = options
// viewer.scene.globe.depthTestAgainstTerrain = true
viewer.imageryLayers.addImageryProvider(roadProvider)
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(...center),
})
// 不显示Cesium的Logo
viewer._cesiumWidget._creditContainer.style.display = 'none'
viewer.scene.sun.show = true //太阳和月亮
viewer.scene.moon.show = true
viewer.scene.skyAtmosphere.show = true //大气层,显示天空颜色
// 设置外天空盒
viewer.scene.skyBox = new Cesium.SkyBox({
sources: {
positiveX: '/images/Standard-Cube-Map/px1.png',
negativeX: '/images/Standard-Cube-Map/nx1.png',
positiveY: '/images/Standard-Cube-Map/pz.png',
negativeY: '/images/Standard-Cube-Map/nz1.png',
positiveZ: '/images/Standard-Cube-Map/py.png',
negativeZ: '/images/Standard-Cube-Map/ny1.png',
},
})
viewer.scene.skyBox.show = true //天空盒
// 创建指北针小部件并将其添加到地图
new CesiumNavigation(viewer, {
// 用于在使用重置导航重置地图视图时设置默认视图控制。接受的值是Cesium.Cartographic 和Cesium.Rectangle.
defaultResetView: Cesium.Cartographic.fromDegrees(...center),
// 用于启用或禁用罗盘。true是启用罗盘false是禁用罗盘。默认值为true。如果将选项设置为false则罗盘将不会添加到地图中。
enableCompass: true,
// 用于启用或禁用缩放控件。true是启用false是禁用。默认值为true。如果将选项设置为false则缩放控件 将不会添加到地图中。
enableZoomControls: true,
// 用于启用或禁用距离图例。true是启用false是禁用。默认值为true。如果将选项设置为false距离图例将不会添加到地图中。
enableDistanceLegend: true,
// 用于启用或禁用指南针外环。true是启用false是禁用。默认值为true。如果将选项设置为false则该环将可见但无效。
enableCompassOuterRing: true,
})
}
// 取消未完成的在地图上移动类的操作(划线之类的)
cancelPreviousOperation() {
this.operations.forEach((operation) => operation.clear())
this.operations = []
}
/**
* 根据四个角落坐标确定视口位置
* @param leftUp
* @param rightUp
* @param rightDown
* @param leftDown
*/
setClientByAllCorner(leftUp, rightUp, rightDown, leftDown) {
const [leftUpLon, leftUpLat] = leftUp
const [rightUpLon, rightUpLat] = rightUp
const [rightDownLon, rightDownLat] = rightDown
const [leftDownLon, leftDownLat] = leftDown
// 计算矩形区域的边界
const west = Math.min(leftDownLon, leftUpLon, rightDownLon, rightUpLon)
const east = Math.max(leftDownLon, leftUpLon, rightDownLon, rightUpLon)
const south = Math.min(leftDownLat, leftUpLat, rightDownLat, rightUpLat)
const north = Math.max(leftDownLat, leftUpLat, rightDownLat, rightUpLat)
// 创建矩形范围
const rectangle = Cesium.Rectangle.fromDegrees(west, south, east, north)
this.viewer.camera.setView({
destination: rectangle,
orientation: {
heading: 0.0,
pitch: -Cesium.Math.PI_OVER_TWO,
roll: 0.0,
},
})
}
/**
* 根据中心坐标确定视口位置
* @param position.longitude
* @param position.latitude
*/
setClientByCenter(position) {
this.viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 500000), // 目标点坐标
orientation: {
heading: 0, // 航向角弧度0表示正北
pitch: Cesium.Math.toRadians(-90), // 俯仰角(弧度),-90度表示垂直向下看
roll: 0.0, // 翻滚角弧度0表示水平
},
})
}
/**
* 移除军标
* @param id
*/
removePlotById(id) {
const targetIndex = this.plots.findIndex((item) => item.id === id)
if (targetIndex === -1) return
this.viewer.entities.remove(this.plots[targetIndex])
this.plots.splice(targetIndex, 1)
}
/**
* 添加军标(base64图片)
* @param base64
* @param screenPosition.x
* @param screenPosition.y
*/
addPlotByOffset(base64, screenPosition, customId) {
this.cancelPreviousOperation()
const position = getCatesian3FromPX(this.viewer, screenPosition)
if (!position) return false
const id = customId || Cesium.createGuid()
const isEnemy = false
const color = 'red'
const radius = 150000
const plot = {
id,
position,
billboard: {
image: base64,
width: 50,
height: 50,
scaleByDistance: new Cesium.NearFarScalar(1000000.0, 1.0, 10000000.0, 0.2), // 重点:设置随距离缩放
},
properties: {
type: MyCesium.ENTITY_TYPES.IMAGE,
color,
isEnemy,
radius,
collisions: new Set(),
},
}
this.viewer.entities.add(plot)
this.plots.push(plot)
// 1. 将屏幕坐标转换为世界坐标(在椭球体表面)
const cartesian = this.viewer.camera.pickEllipsoid(screenPosition, this.viewer.scene.globe.ellipsoid)
if (Cesium.defined(cartesian)) {
// 2. 将世界坐标 (Cartesian3) 转换为地理坐标 Cartographic (弧度)
const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
// 3. 将弧度转换为度数
const longitude = Cesium.Math.toDegrees(cartographic.longitude)
const latitude = Cesium.Math.toDegrees(cartographic.latitude)
return { plotId: id, longitude, latitude }
}
}
/**
* 添加军标(base64图片)
* @param base64
* @param location.lon
* @param location.lat
*/
addPlotByLonLat(base64, location, customId) {
this.cancelPreviousOperation()
const position = Cesium.Cartesian3.fromDegrees(location.lon, location.lat, 0)
if (!position) return false
const id = customId || Cesium.createGuid()
const isEnemy = false
const color = 'red'
const radius = 150000
const plot = {
id,
position,
billboard: {
image: base64,
width: 50,
height: 50,
scaleByDistance: new Cesium.NearFarScalar(1000000.0, 1.0, 10000000.0, 0.2), // 重点:设置随距离缩放
},
properties: {
type: MyCesium.ENTITY_TYPES.IMAGE,
color,
isEnemy,
radius,
collisions: new Set(),
},
}
this.viewer.entities.add(plot)
this.plots.push(plot)
}
/**
* 连续坐标绘制路线
* @param coordinates [[longitude, latitude], ...]
*/
drawRouteByCoordinates(coordinates) {
const route = {
id: Cesium.createGuid(),
polyline: {
positions: Cesium.Cartesian3.fromDegreesArray(coordinates.flat()),
width: 2,
material: Cesium.Color.fromCssColorString('red'),
show: true,
clampToGround: true,
},
properties: {},
}
this.viewer.entities.add(route)
this.routes.push(route)
}
/**
* 连续坐标指定军标移动
* @param plotId 军标id
* @param coordinates [[longitude, latitude], ...]
*/
initPlotMoving(plot, coordinates) {
// viewer.clock 控制了场景中时间的变化,从而驱动动画。
const startTime = Cesium.JulianDate.now()
const stopTime = Cesium.JulianDate.addSeconds(startTime, 1, new Cesium.JulianDate()) // 动画持续1秒
this.viewer.clock.startTime = startTime.clone()
this.viewer.clock.stopTime = stopTime.clone()
this.viewer.clock.currentTime = startTime.clone()
this.viewer.clock.clockRange = Cesium.ClockRange.CLAMPED // 动画播放一次后停止:cite[2]
this.viewer.clock.multiplier = 1 // 时间流逝速度默认为1
const positionProperty = new Cesium.SampledPositionProperty()
// 假设你获取到的路线坐标点数组是 positionsCartesian3数组
// 并且这些点应该在1秒内完成移动
const positions = Cesium.Cartesian3.fromDegreesArray(coordinates.flat()) // 你的坐标路线
const sampleTimeStep = 1 / (positions.length - 1) // 计算每个样本点之间的时间间隔总时间为1秒
for (let i = 0; i < positions.length; i++) {
const time = Cesium.JulianDate.addSeconds(startTime, i * sampleTimeStep, new Cesium.JulianDate())
positionProperty.addSample(time, positions[i])
}
// 将实体的位置关联到 SampledPositionProperty
plot.position = positionProperty
// 可选:让图标朝向运动方向
// plot.orientation = new Cesium.VelocityOrientationProperty(positionProperty) // cite[8]
plot._isMoving = true
}
movePlotByCoordinates(plotId, coordinates) {
// 实体
const targetPlot = this.viewer.entities.getById(plotId)
if (targetPlot._isMoving === true) {
// 1. 获取新的路线坐标点(假设是同步获取的,或者是在回调中)
const positions = Cesium.Cartesian3.fromDegreesArray(coordinates.flat()) // 你的坐标路线
// 2. 清除旧的位置样本
targetPlot.position._property._values = []
targetPlot.position._property._times = []
// 3. 计算新的开始时间和结束时间
const newStartTime = Cesium.JulianDate.now()
const newStopTime = Cesium.JulianDate.addSeconds(newStartTime, 1, new Cesium.JulianDate())
// 4. 添加新的位置样本
const newSampleTimeStep = 1 / (positions.length - 1)
for (let i = 0; i < positions.length; i++) {
const time = Cesium.JulianDate.addSeconds(newStartTime, i * newSampleTimeStep, new Cesium.JulianDate())
targetPlot.position.addSample(time, positions[i])
}
// 5. 重置时钟以立即开始新的动画
this.viewer.clock.startTime = newStartTime.clone()
this.viewer.clock.stopTime = newStopTime.clone()
this.viewer.clock.currentTime = newStartTime.clone()
this.viewer.clock.shouldAnimate = true // 确保动画开始播放:cite[2]
} else {
this.initPlotMoving(targetPlot, coordinates)
}
}
}