| | |
| | | <img :src="layerControlIcon" alt="图层控制" /> |
| | | </div> |
| | | <div v-if="showLayerPanel" class="layer-panel"> |
| | | <div class="panel-title">图层管理</div> |
| | | <div class="panel-title">底图切换</div> |
| | | <div class="base-map-options"> |
| | | <div class="base-map-card" :class="{ active: baseLayerKey === 'base-standard' }" |
| | | @click="handleBaseLayerSelect('base-standard')"> |
| | | <img class="base-map-thumb standard" :src="dzIcon" alt=""> |
| | | <div class="base-map-label">标准地图</div> |
| | | </div> |
| | | <div class="base-map-card" :class="{ active: baseLayerKey === 'base-satellite' }" |
| | | @click="handleBaseLayerSelect('base-satellite')"> |
| | | <img class="base-map-thumb satellite" :src="yxIcon" alt=""> |
| | | <div class="base-map-label">卫星地图</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="panel-title">图层管理</div> |
| | | <div class="panel-content"> |
| | | <el-tree class="command-tree" :data="layerTree" show-checkbox default-expand-all node-key="key" |
| | | :props="layerTreeProps" :default-checked-keys="defaultCheckedKeys" /> |
| | | <el-tree ref="layerTreeRef" class="command-tree map-layer-tree" :data="layerTree" show-checkbox |
| | | default-expand-all node-key="key" :props="layerTreeProps" |
| | | :default-checked-keys="treeCheckedKeys" @check="handleLayerCheck" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <DevicePopup |
| | | :visible="popupVisible && !isDronePopup" |
| | | :position="popupPosition" |
| | | :device="selectedDevice" |
| | | @close="closePopup" |
| | | /> |
| | | <DevicePopup :visible="popupVisible && !isDronePopup" :position="popupPosition" :device="selectedDevice" |
| | | @close="closePopup" /> |
| | | |
| | | <DronePopup |
| | | :visible="popupVisible && isDronePopup" |
| | | :position="popupPosition" |
| | | :drone="selectedDevice" |
| | | :favorite="Boolean(selectedDevice?.isFavorite)" |
| | | @close="closePopup" |
| | | @toggle-favorite="toggleDroneFavorite" |
| | | @signal="handleDroneSignal" |
| | | @counter="handleDroneCounter" |
| | | /> |
| | | <DronePopup :visible="popupVisible && isDronePopup" :position="popupPosition" :drone="selectedDevice" |
| | | :favorite="Boolean(selectedDevice?.isFavorite)" @close="closePopup" @toggle-favorite="toggleDroneFavorite" |
| | | @signal="handleDroneSignal" @counter="handleDroneCounter" /> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | createRadialGradientMaterial, |
| | | getTexturedVertexFormat, |
| | | } from './device-map-materials' |
| | | import dzIcon from '@/assets/images/dataCockpit/map/dz-map-layer.png' |
| | | import yxIcon from '@/assets/images/dataCockpit/map/yx-map-layer.png' |
| | | |
| | | const CLUSTER_HEIGHT = 100000 |
| | | const DETAIL_HEIGHT = 10000 |
| | |
| | | |
| | | const mapRef = ref(null) |
| | | let viewer = null |
| | | let publicCesium = null |
| | | let cockpitPrimitiveLayer = null |
| | | const devicePickMap = new Map() |
| | | const dronePickMap = new Map() |
| | |
| | | const countyCenterMap = new Map() |
| | | const showLayerPanel = ref(false) |
| | | const layerWrapRef = ref(null) |
| | | const layerTreeRef = ref(null) |
| | | const selectedDevice = ref(null) |
| | | const selectedTargetType = ref('device') |
| | | let selectedDeviceBillboard = null |
| | |
| | | label: 'label', |
| | | children: 'children', |
| | | } |
| | | const defaultCheckedKeys = ['global', 'city-base'] |
| | | const baseLayerKeys = ['base-standard', 'base-satellite'] |
| | | const defaultCheckedKeys = ['ja-terrain', 'admin', 'city-base'] |
| | | const baseLayerKey = ref('base-satellite') |
| | | const treeCheckedKeys = ref([...defaultCheckedKeys]) |
| | | const layerTree = ref([ |
| | | { |
| | | key: 'base', |
| | | label: '地理信息图层', |
| | | children: [ |
| | | { key: 'global', label: '全球地形' }, |
| | | { key: 'ja-terrain', label: '吉安地形' }, |
| | | { key: 'admin', label: '行政区划' }, |
| | | ], |
| | | }, |
| | | { |
| | | |
| | | ]) |
| | | |
| | | /** |
| | | * { |
| | | key: 'city', |
| | | label: '城市CIM图层', |
| | | children: [ |
| | |
| | | { key: 'route', label: '飞行航路' }, |
| | | ], |
| | | }, |
| | | ]) |
| | | */ |
| | | |
| | | const adminBoundaryVisible = ref(treeCheckedKeys.value.includes('admin')) |
| | | |
| | | const getDevicePosition = item => { |
| | | const longitudeRaw = item.longitude ?? item.lng ?? item.lon |
| | |
| | | |
| | | const setClusterVisibility = visible => { |
| | | clusterVisible.value = visible |
| | | if (aggregationSource) aggregationSource.show = visible |
| | | updateAggregationVisibility() |
| | | } |
| | | |
| | | const updateAggregationVisibility = () => { |
| | | if (!aggregationSource) return |
| | | aggregationSource.show = clusterVisible.value && adminBoundaryVisible.value |
| | | } |
| | | |
| | | const setDroneVisibility = visible => { |
| | |
| | | height: 120 |
| | | }, |
| | | { |
| | | longitude:114.957308, |
| | | longitude: 114.957308, |
| | | latitude: 27.138452, |
| | | height: 120 |
| | | }, |
| | | { |
| | | longitude:114.952, |
| | | longitude: 114.952, |
| | | latitude: 27.136317, |
| | | height: 120 |
| | | }, |
| | | { |
| | | longitude:114.949293, |
| | | longitude: 114.949293, |
| | | latitude: 27.133864, |
| | | height: 120 |
| | | }, |
| | | { |
| | | longitude:114.944666, |
| | | longitude: 114.944666, |
| | | latitude: 27.130526, |
| | | height: 120 |
| | | }, |
| | | { |
| | | longitude:114.945909, |
| | | latitude:27.127845, |
| | | longitude: 114.945909, |
| | | latitude: 27.127845, |
| | | height: 120 |
| | | }, |
| | | { |
| | | longitude:114.962974, |
| | | latitude:27.136242, |
| | | longitude: 114.962974, |
| | | latitude: 27.136242, |
| | | height: 120 |
| | | }, |
| | | ] |
| | |
| | | droneTrackBillboardCollection.show = detailVisible.value |
| | | droneTrackPolylineCollection.show = detailVisible.value |
| | | droneTrackRuntime = [] |
| | | const baseCenters = [['默认区域', { longitude:114.958541, latitude: 27.121917 }]] |
| | | const baseCenters = [['默认区域', { longitude: 114.958541, latitude: 27.121917 }]] |
| | | const segmentDuration = 6 |
| | | const baseTrackColor = Cesium.Color.fromCssColorString('red') |
| | | baseCenters.forEach(([centerName, center], trackIndex) => { |
| | |
| | | const polygonInstances = [] |
| | | const lineInstances = [] |
| | | const texturedVertexFormat = getTexturedVertexFormat() |
| | | ; (zones || []).forEach((zone, index) => { |
| | | if (!zone?.geom) return |
| | | const positions = getDefenseZonePositions(zone.geom) |
| | | if (!positions.length) return |
| | | const polygon = new Cesium.PolygonGeometry({ |
| | | polygonHierarchy: new Cesium.PolygonHierarchy(positions), |
| | | vertexFormat: texturedVertexFormat, |
| | | ; (zones || []).forEach((zone, index) => { |
| | | if (!zone?.geom) return |
| | | const positions = getDefenseZonePositions(zone.geom) |
| | | if (!positions.length) return |
| | | const polygon = new Cesium.PolygonGeometry({ |
| | | polygonHierarchy: new Cesium.PolygonHierarchy(positions), |
| | | vertexFormat: texturedVertexFormat, |
| | | }) |
| | | polygonInstances.push( |
| | | new Cesium.GeometryInstance({ |
| | | geometry: polygon, |
| | | }) |
| | | ) |
| | | const linePositions = positions.length > 1 ? [...positions, positions[0]] : positions |
| | | lineInstances.push( |
| | | new Cesium.GeometryInstance({ |
| | | geometry: new Cesium.GroundPolylineGeometry({ |
| | | positions: linePositions, |
| | | width: 2, |
| | | }), |
| | | attributes: { |
| | | color: Cesium.ColorGeometryInstanceAttribute.fromColor( |
| | | Cesium.Color.fromCssColorString(lineColor) |
| | | ), |
| | | }, |
| | | }) |
| | | ) |
| | | }) |
| | | polygonInstances.push( |
| | | new Cesium.GeometryInstance({ |
| | | geometry: polygon, |
| | | }) |
| | | ) |
| | | const linePositions = positions.length > 1 ? [...positions, positions[0]] : positions |
| | | lineInstances.push( |
| | | new Cesium.GeometryInstance({ |
| | | geometry: new Cesium.GroundPolylineGeometry({ |
| | | positions: linePositions, |
| | | width: 2, |
| | | }), |
| | | attributes: { |
| | | color: Cesium.ColorGeometryInstanceAttribute.fromColor( |
| | | Cesium.Color.fromCssColorString(lineColor) |
| | | ), |
| | | }, |
| | | }) |
| | | ) |
| | | }) |
| | | let primitive = null |
| | | if (polygonInstances.length) { |
| | | const baseColor1 = Cesium.Color.fromCssColorString(fillColor1) |
| | |
| | | viewer.dataSources.add(aggregationSource) |
| | | } |
| | | clearAggregationEntities() |
| | | aggregationSource.show = clusterVisible.value |
| | | updateAggregationVisibility() |
| | | const countMap = new Map() |
| | | ; (list || []).forEach(item => { |
| | | if (!item?.type) return |
| | |
| | | ensureCommandPostCollection() |
| | | commandPostBillboardCollection.removeAll() |
| | | commandPostBillboardCollection.show = detailVisible.value |
| | | ; (list || []).forEach((item, index) => { |
| | | const position = getDevicePosition(item) |
| | | if (!position) return |
| | | commandPostBillboardCollection.add({ |
| | | position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0), |
| | | image: commandPostIcon, |
| | | width: 40, |
| | | height: 56, |
| | | verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
| | | ; (list || []).forEach((item, index) => { |
| | | const position = getDevicePosition(item) |
| | | if (!position) return |
| | | commandPostBillboardCollection.add({ |
| | | position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0), |
| | | image: commandPostIcon, |
| | | width: 40, |
| | | height: 56, |
| | | verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
| | | }) |
| | | }) |
| | | }) |
| | | reorderCockpitPrimitives() |
| | | } |
| | | |
| | |
| | | showLayerPanel.value = !showLayerPanel.value |
| | | } |
| | | |
| | | const applyLayerVisibility = checkedKeys => { |
| | | publicCesium?.switchLayers?.(baseLayerKey.value === 'base-standard' ? 0 : 4) |
| | | const showTerrain = checkedKeys.includes('ja-terrain') |
| | | publicCesium?.setTerrainVisible?.(showTerrain) |
| | | const showAdmin = checkedKeys.includes('admin') |
| | | adminBoundaryVisible.value = showAdmin |
| | | mapRef.value?.setAdminBoundaryVisible?.(showAdmin) |
| | | updateAggregationVisibility() |
| | | } |
| | | |
| | | const handleLayerCheck = (_data, state) => { |
| | | const checkedKeys = state?.checkedKeys ?? layerTreeRef.value?.getCheckedKeys?.() ?? [] |
| | | treeCheckedKeys.value = checkedKeys |
| | | applyLayerVisibility(checkedKeys) |
| | | } |
| | | |
| | | const handleBaseLayerSelect = key => { |
| | | if (!baseLayerKeys.includes(key)) return |
| | | if (baseLayerKey.value === key) return |
| | | baseLayerKey.value = key |
| | | applyLayerVisibility(treeCheckedKeys.value) |
| | | } |
| | | |
| | | const updateStageDisplay = stage => { |
| | | const showCluster = stage === 'cluster' |
| | | setClusterVisibility(showCluster) |
| | |
| | | setDroneVisibility(!showCluster) |
| | | } |
| | | |
| | | const handleMapReady = ({ viewer: mapViewer }) => { |
| | | const handleMapReady = ({ viewer: mapViewer, publicCesium: mapPublic }) => { |
| | | viewer = mapViewer |
| | | publicCesium = mapPublic |
| | | ensureCockpitPrimitiveLayer() |
| | | applyLayerVisibility(treeCheckedKeys.value) |
| | | const height = viewer?.camera?.positionCartographic?.height |
| | | const stage = getStageByHeight(height) |
| | | updateStageDisplay(stage) |
| | |
| | | } |
| | | cockpitPrimitiveLayer = null |
| | | viewer = null |
| | | publicCesium = null |
| | | }) |
| | | </script> |
| | | |
| | |
| | | flex: 1; |
| | | overflow: auto; |
| | | } |
| | | |
| | | .base-map-title { |
| | | margin-bottom: 8px; |
| | | font-size: 12px; |
| | | color: #ffffff; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .base-map-options { |
| | | padding: 16px; |
| | | display: grid; |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | gap: 8px; |
| | | } |
| | | |
| | | .base-map-card { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 6px; |
| | | border-radius: 6px; |
| | | color: #d8e6ff; |
| | | font-size: 12px; |
| | | cursor: pointer; |
| | | transition: all 0.2s ease; |
| | | |
| | | .base-map-thumb { |
| | | width: 46px; |
| | | height: 46px; |
| | | border-radius: 4px; |
| | | background-size: cover; |
| | | background-position: center; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | &.active { |
| | | .base-map-thumb { |
| | | border: 2px solid #2ea8ff; |
| | | } |
| | | |
| | | .base-map-label { |
| | | color: #2ea8ff; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | | |