applications/drone-command/src/components/map-container/common-cesium-map.vue
@@ -3,6 +3,8 @@ </template> <script setup> import * as Cesium from 'cesium' import { onBeforeUnmount, onMounted, watch } from 'vue' import { PublicCesium } from '@/utils/cesium/publicCesium' import { loadJaAdminBoundary, removeJaAdminBoundary } from '@/utils/cesium/adminBoundary' @@ -172,11 +174,36 @@ }) } const zoomToAdminBoundary = async () => { if (!viewer) return const resolveSources = () => { if (adminBoundarySources) return adminBoundarySources const boundarySource = viewer.dataSources.getByName(ADMIN_BOUNDARY_NAMES.boundary)?.[0] ?? null const lineSource = viewer.dataSources.getByName(ADMIN_BOUNDARY_NAMES.line)?.[0] ?? null const labelSource = viewer.dataSources.getByName(ADMIN_BOUNDARY_NAMES.label)?.[0] ?? null if (boundarySource || lineSource || labelSource) { adminBoundarySources = { boundarySource, lineSource, labelSource } } return adminBoundarySources } let sources = resolveSources() if (!sources?.boundarySource) { sources = await loadJaAdminBoundary(viewer, { zoomTo: false }) adminBoundarySources = sources } const target = sources?.boundarySource if (!target) return await viewer.flyTo(target, { duration: 0, offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-75), 0), }) } const getMap = () => ({ viewer, publicCesium: viewInstance }) const getViewer = () => viewer const getPublicCesium = () => viewInstance defineExpose({ getMap, getViewer, getPublicCesium, setAdminBoundaryVisible }) defineExpose({ getMap, getViewer, getPublicCesium, setAdminBoundaryVisible, zoomToAdminBoundary }) </script> <style scoped lang="scss"> applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
@@ -266,7 +266,7 @@ const formRef = ref(null) // 表单实例 const formData = ref(initForm()) // 表单数据 const visible = defineModel() // 弹框显隐 const dialogMode = ref('add') // 弹框模式 const dialogMode = ref() // 弹框模式 const submitting = ref(false) // 提交中 const readonly = computed(() => dialogMode.value === 'view') const route = useRoute() applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue
@@ -10,6 +10,7 @@ :terrain="true" :layer-mode="4" :boundary="false" :zoom-to-boundary="dialogMode === 'add'" /> </div> <div class="right-container"> @@ -160,6 +161,7 @@ import * as Cesium from 'cesium' import { fwAreaDivideDetailApi, fwAreaDivideListApi } from '../partition/partitionApi' import { buildEllipsePositions } from '@/utils/cesium/shapeTools' import { cartesian3Convert } from '@/utils/cesium/mapUtil' import { saveOperationLog } from '@ztzf/apis' import { useRoute } from 'vue-router' import { AREA_TYPE_STYLE_MAP, BUFFER_LEVEL_STYLES, DEFAULT_AREA_STYLE } from '@ztzf/constants' @@ -176,7 +178,7 @@ const formRef = ref(null) // 表单实例 const formData = ref(initForm()) // 表单数据 const visible = defineModel() // 弹框显隐 const dialogMode = ref('add') // 弹框模式 const dialogMode = ref() // 弹框模式 const submitting = ref(false) // 提交中 const readonly = computed(() => dialogMode.value === 'view') const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' }) @@ -272,6 +274,28 @@ return { lng: Number(lng), lat: Number(lat), height } } function resolveLngLatPoint(point) { if (!point) return null const lng = point?.lng ?? point?.longitude const lat = point?.lat ?? point?.latitude if (Number.isFinite(Number(lng)) && Number.isFinite(Number(lat))) { return { lng: Number(lng), lat: Number(lat), height: Number.isFinite(Number(point?.height)) ? Number(point.height) : 0, } } if (!viewer || !Number.isFinite(point?.x) || !Number.isFinite(point?.y) || !Number.isFinite(point?.z)) { return null } const val = cartesian3Convert(point, viewer) return { lng: val.longitude, lat: val.latitude, height: Number.isFinite(val.height) ? val.height : 0, } } function buildShapePositions(points = []) { const normalized = points.map(normalizeShapePoint).filter(Boolean) return normalized.map(point => Cesium.Cartesian3.fromDegrees(point.lng, point.lat, point.height)) @@ -286,6 +310,20 @@ function getAreaTypeStyle(areaType) { return AREA_TYPE_STYLE_MAP?.[String(areaType)] || DEFAULT_AREA_STYLE } function resolveControlPoints(meta, drawType) { if (!meta) return [] if (Array.isArray(meta.controlPoints) && meta.controlPoints.length) { return meta.controlPoints.map(resolveLngLatPoint).filter(Boolean) } if (drawType === 'buffer') { return [resolveLngLatPoint(meta.center)].filter(Boolean) } if (drawType === 'ellipse') { return [meta.center, meta.majorPoint, meta.minorPoint].map(resolveLngLatPoint).filter(Boolean) } return [] } function parseGeomJson(geomJson) { @@ -400,6 +438,74 @@ }) } function pushRadiusBoundingPoints(positions, center, radius) { if (!center || !Number.isFinite(Number(radius)) || radius <= 0) return const resolved = resolveLngLatPoint(center) || {} const lng = Number(resolved.lng ?? resolved.longitude) const lat = Number(resolved.lat ?? resolved.latitude) if (!Number.isFinite(lng) || !Number.isFinite(lat)) return const baseLat = Cesium.Math.toRadians(lat) const earthRadius = Cesium.Ellipsoid.WGS84.maximumRadius const deltaLat = radius / earthRadius const deltaLng = radius / (earthRadius * Math.max(Math.cos(baseLat), 0.000001)) const points = [ { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) + deltaLng), lat }, { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) - deltaLng), lat }, { lng, lat: Cesium.Math.toDegrees(baseLat + deltaLat) }, { lng, lat: Cesium.Math.toDegrees(baseLat - deltaLat) }, ] points.forEach(point => { positions.push(Cesium.Cartesian3.fromDegrees(point.lng, point.lat, resolved.height || 0)) }) } function collectSelectedAreaPositions() { const positions = [] if (!viewer || !selectedAreaRows.value.length) return positions selectedAreaRows.value.forEach(area => { const shapes = resolveAreaShapes(area) shapes.forEach(shape => { let points = getShapeDisplayPoints(shape) if (!Array.isArray(points) || !points.length) { points = resolveControlPoints(shape?.meta, shape?.drawType) } if (Array.isArray(points) && points.length) { points.forEach(point => { const resolved = resolveLngLatPoint(point) const normalized = normalizeShapePoint(resolved) if (!normalized) return positions.push( Cesium.Cartesian3.fromDegrees(normalized.lng, normalized.lat, normalized.height) ) }) } if (shape?.drawType === 'buffer' && shape?.meta?.center && Array.isArray(shape?.meta?.bufferRadii)) { const maxRadius = Math.max(...shape.meta.bufferRadii.filter(item => Number.isFinite(item))) pushRadiusBoundingPoints(positions, shape.meta.center, maxRadius) } if (shape?.drawType === 'ellipse' && shape?.meta?.center) { const major = Number(shape?.meta?.semiMajor) const minor = Number(shape?.meta?.semiMinor) const radius = Number.isFinite(major) && Number.isFinite(minor) ? Math.max(major, minor) : null pushRadiusBoundingPoints(positions, shape.meta.center, radius) } }) }) return positions } function fitViewToSelectedAreas() { if (!viewer || !selectedAreaRows.value.length) return const positions = collectSelectedAreaPositions() if (positions.length < 2) return const boundingSphere = Cesium.BoundingSphere.fromPoints(positions) const range = Math.max(boundingSphere.radius * 2.4, 200) viewer.camera.flyToBoundingSphere(boundingSphere, { duration: 0, offset: new Cesium.HeadingPitchRange(viewer.camera.heading, Cesium.Math.toRadians(-90), range), }) } watch(() => selectedAreaRows.value, renderSelectedAreas) watch( () => visible.value, @@ -468,11 +574,17 @@ await nextTick() initMap() await getAreaList() if (dialogMode.value !== 'add') { if (dialogMode.value === 'add') { console.log(111) mapRef.value?.zoomToAdminBoundary?.() } else { await loadDetail() } await nextTick() await syncAreaSelection() await nextTick() fitViewToSelectedAreas() } defineExpose({ open, applications/drone-command/src/views/areaManage/sceneManage/FormDiaLog.vue
@@ -10,6 +10,7 @@ :terrain="true" :layer-mode="4" :boundary="false" :zoom-to-boundary="dialogMode === 'add'" /> </div> <div class="right-container"> @@ -148,6 +149,7 @@ import { useRoute } from 'vue-router' import { fwAreaDivideDetailApi, fwAreaDivideListApi } from '../partition/partitionApi' import { buildEllipsePositions } from '@/utils/cesium/shapeTools' import { cartesian3Convert } from '@/utils/cesium/mapUtil' import { AREA_TYPE_STYLE_MAP, BUFFER_LEVEL_STYLES, DEFAULT_AREA_STYLE } from '@ztzf/constants' // 初始化表单数据 @@ -166,7 +168,7 @@ const formRef = ref(null) // 表单实例 const formData = ref(initForm()) // 表单数据 const visible = defineModel() // 弹框显隐 const dialogMode = ref('add') // 弹框模式 const dialogMode = ref() // 弹框模式 const submitting = ref(false) // 提交中 const readonly = computed(() => dialogMode.value === 'view') const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' }) @@ -263,6 +265,28 @@ return normalized.map(point => Cesium.Cartesian3.fromDegrees(point.lng, point.lat, point.height)) } function resolveLngLatPoint(point) { if (!point) return null const lng = point?.lng ?? point?.longitude const lat = point?.lat ?? point?.latitude if (Number.isFinite(Number(lng)) && Number.isFinite(Number(lat))) { return { lng: Number(lng), lat: Number(lat), height: Number.isFinite(Number(point?.height)) ? Number(point.height) : 0, } } if (!viewer || !Number.isFinite(point?.x) || !Number.isFinite(point?.y) || !Number.isFinite(point?.z)) { return null } const val = cartesian3Convert(point, viewer) return { lng: val.longitude, lat: val.latitude, height: Number.isFinite(val.height) ? val.height : 0, } } function getShapeDisplayPoints(shape) { if (Array.isArray(shape?.displayPoints) && shape.displayPoints.length) { return shape.displayPoints @@ -315,6 +339,71 @@ } }) .filter(Boolean) } function pushRadiusBoundingPoints(positions, center, radius) { if (!center || !Number.isFinite(Number(radius)) || radius <= 0) return const resolved = resolveLngLatPoint(center) || {} const lng = Number(resolved.lng ?? resolved.longitude) const lat = Number(resolved.lat ?? resolved.latitude) if (!Number.isFinite(lng) || !Number.isFinite(lat)) return const baseLat = Cesium.Math.toRadians(lat) const earthRadius = Cesium.Ellipsoid.WGS84.maximumRadius const deltaLat = radius / earthRadius const deltaLng = radius / (earthRadius * Math.max(Math.cos(baseLat), 0.000001)) const points = [ { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) + deltaLng), lat }, { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) - deltaLng), lat }, { lng, lat: Cesium.Math.toDegrees(baseLat + deltaLat) }, { lng, lat: Cesium.Math.toDegrees(baseLat - deltaLat) }, ] points.forEach(point => { positions.push(Cesium.Cartesian3.fromDegrees(point.lng, point.lat, resolved.height || 0)) }) } function collectSceneAreaPositions() { const positions = [] if (!viewer || !areaList.value.length) return positions areaList.value.forEach(area => { const shapes = resolveAreaShapes(area) shapes.forEach(shape => { const points = getShapeDisplayPoints(shape) if (Array.isArray(points) && points.length) { points.forEach(point => { const resolved = resolveLngLatPoint(point) const normalized = normalizeShapePoint(resolved) if (!normalized) return positions.push( Cesium.Cartesian3.fromDegrees(normalized.lng, normalized.lat, normalized.height) ) }) } if (shape?.drawType === 'buffer' && shape?.meta?.center && Array.isArray(shape?.meta?.bufferRadii)) { const maxRadius = Math.max(...shape.meta.bufferRadii.filter(item => Number.isFinite(item))) pushRadiusBoundingPoints(positions, shape.meta.center, maxRadius) } if (shape?.drawType === 'ellipse' && shape?.meta?.center) { const major = Number(shape?.meta?.semiMajor) const minor = Number(shape?.meta?.semiMinor) const radius = Number.isFinite(major) && Number.isFinite(minor) ? Math.max(major, minor) : null pushRadiusBoundingPoints(positions, shape.meta.center, radius) } }) }) return positions } function fitViewToSceneAreas() { if (!viewer || !areaList.value.length) return const positions = collectSceneAreaPositions() if (positions.length < 2) return const boundingSphere = Cesium.BoundingSphere.fromPoints(positions) const range = Math.max(boundingSphere.radius * 2.4, 200) viewer.camera.flyToBoundingSphere(boundingSphere, { duration: 0, offset: new Cesium.HeadingPitchRange(viewer.camera.heading, Cesium.Math.toRadians(-90), range), }) } function renderSceneAreas() { @@ -521,12 +610,15 @@ await nextTick() initMap() await getSceneConfigList() if (dialogMode.value !== 'add') { if (dialogMode.value === 'add') { mapRef.value?.zoomToAdminBoundary?.() } else { await loadDetail() } await nextTick() setMapPoint(formData.value.longitude, formData.value.latitude) await loadAreaList(formData.value.defenseSceneId) fitViewToSceneAreas() } onMounted(() => {