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/mapDem/' 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 // 选中模型 selectedModel = null // 右鍵菜單 contextMenu = { visible: false, left: 0, top: 0, } // 未完成的在地图上移动类的操作 operations = [] 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, }) const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas) handler.setInputAction(({ position }) => { if (this.selectedModel) { this.deselectModel(this.selectedModel) this.selectedModel = null } const pickedObject = viewer.scene.pick(position) if (!Cesium.defined(pickedObject) || !pickedObject.id) { window.Emit('pickedObject', null) return } const entity = pickedObject.id if ( !entity.properties || (entity.properties.type != MyCesium.ENTITY_TYPES.MODEL && entity.properties.type != MyCesium.ENTITY_TYPES.IMAGE) ) { window.Emit('pickedObject', null) return } this.selectedModel = entity this.selectModel(entity) window.Emit('selectedModel', { id: entity.id, }) }, Cesium.ScreenSpaceEventType.LEFT_CLICK) // 鼠标右键事件 handler.setInputAction(({ position }) => { const pickedObject = viewer.scene.pick(position) if (!Cesium.defined(pickedObject) || !pickedObject.id) return const entity = pickedObject.id const properties = entity.properties if ( !properties || !properties.type || (properties.type != MyCesium.ENTITY_TYPES.MODEL && properties.type != MyCesium.ENTITY_TYPES.IMAGE) ) { return } const nomove = properties.nomove if (selectedModel && entity.id == selectedModel.id && !(nomove && nomove.valueOf())) { this.showContextMenu(entity) this.cancelPreviousOperation() } }, Cesium.ScreenSpaceEventType.RIGHT_CLICK) // shift + 左键事件 handler.setInputAction( ({ position }) => { const pickedObject = viewer.scene.pick(position) if (!Cesium.defined(pickedObject) || !pickedObject.id) return const entity = pickedObject.id if (!entity.properties || entity.properties.type != MyCesium.ENTITY_TYPES.MODEL) return if (this.selectedModel) { // 找到已有的properties中的from和to是选中的两个模型的id的线 const find = viewer.entities.values.find((item) => { const props = item.properties if (!props || !props.type || !props.from || !props.to) return false return ( (props.from == this.selectedModel.id && props.to == entity.id) || (props.from == entity.id && props.to == this.selectedModel.id) ) }) // 如果已经有了,移除 if (find) { viewer.entities.remove(find) const { from, to } = find.properties // 发送删除线的事件 window.Emit('removeLineBetwenModel', { id: find.id, from: from.valueOf(), to: to.valueOf(), }) } else { const id = Cesium.createGuid() const currentTime = viewer.clock.currentTime const positions = [this.selectedModel.position.getValue(currentTime), entity.position.getValue(currentTime)] if (positions.some((item) => !item)) { console.log('缺少位置信息') return } this.addLineBetwenModel(id, positions, selectedModel.id, entity.id) // 发送添加线的事件 window.Emit('addLineBetwenModel', { positions, id, from: selectedModel.id, to: entity.id, }) } } }, Cesium.ScreenSpaceEventType.LEFT_CLICK, Cesium.KeyboardEventModifier.SHIFT ) } /** * 选中模型 * @param {Cesium.Entity} entity */ selectModel(entity) { if (!entity.model) return const { properties: { color: _color }, } = entity const color = _color.valueOf() // 设置模型轮廓效果 entity.model.silhouetteSize = 2 // 轮廓线宽度 entity.model.silhouetteColor = Cesium.Color.fromCssColorString(color) entity.label.show = true } /** * 取消选中模型 * @param {Cesium.Entity} entity */ deselectModel(entity) { if (!entity.model) return entity.model.silhouetteSize = 0 // 设置为0取消轮廓 entity.label.show = false } /** * 显示右键菜单 * @param {Cesium.Entity} entity */ showContextMenu(entity) { const currentTime = viewer.clock.currentTime // 获取entity中心点的屏幕坐标 const position = entity.position.getValue(currentTime) const screenPosition = Cesium.SceneTransforms.wgs84ToWindowCoordinates(viewer.scene, position) this.contextMenu.left = screenPosition.x + 20 this.contextMenu.top = screenPosition.y this.contextMenu.visible = true } // 取消未完成的在地图上移动类的操作(划线之类的) cancelPreviousOperation() { this.operations.forEach((operation) => operation.clear()) this.operations = [] } /** * 在模型之间添加线 * @param id * @param { Cesium.Cartesian3[] } positions * @param fromId * @param toId */ addLineBetwenModel(id, positions, fromId, toId) { this.viewer.entities.add({ id, polyline: { positions, width: 2, material: new Cesium.PolylineDashMaterialProperty({ color: Cesium.Color.YELLOW, dashLength: 20, //短划线长度 }), clampToGround: true, }, properties: { type: MyCesium.ENTITY_TYPES.LINE, from: fromId, to: toId, }, }) } setViewerByAllCorner(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, }, }) } /** * 添加军标(base64图片) * @param base64 * @param x * @param y */ addPlot(base64, { x, y }) { this.cancelPreviousOperation() const id = Cesium.createGuid() const position = getCatesian3FromPX(this.viewer, { x, y }) if (!position) return const isEnemy = false const color = 'red' const radius = 150000 this.viewer.entities.add({ id, position, billboard: { image: base64, width: 50, height: 50, eyeOffset: new Cesium.Cartesian3(0.0, 0.0, -100000.0), // 使标记在远处看起来更大 }, ellipse: { semiMajorAxis: radius, semiMinorAxis: radius, material: Cesium.Color.fromCssColorString('transparent'), outline: true, outlineWidth: 1, outlineColor: Cesium.Color.fromCssColorString(color), height: 0, }, properties: { type: MyCesium.ENTITY_TYPES.IMAGE, color, isEnemy, radius, collisions: new Set(), }, }) } }