3 files modified
1 files added
| | |
| | | import DevicePopup from './components/DevicePopup.vue' |
| | | import DronePopup from './components/DronePopup.vue' |
| | | import { |
| | | RING_STYLES, |
| | | addDeviceRings, |
| | | createDroneTrackMaterial, |
| | | createRadialGradientMaterial, |
| | | getTexturedVertexFormat, |
| | | } from './device-map-materials' |
| | | import { getPointPositionsHeight } from '@/utils/cesium/mapUtil' |
| | | import { createDeviceRangePrimitiveWithHeight } from '@/utils/cesium/deviceRange' |
| | | import dzIcon from '@/assets/images/dataCockpit/map/dz-map-layer.png' |
| | | import yxIcon from '@/assets/images/dataCockpit/map/yx-map-layer.png' |
| | | |
| | |
| | | if (!Number.isFinite(range) || range <= 0) return null |
| | | return range |
| | | } |
| | | |
| | | |
| | | const ensureCockpitPrimitiveLayer = () => { |
| | | if (!viewer) return |
| | |
| | | reorderCockpitPrimitives() |
| | | } |
| | | |
| | | const renderDeviceEntities = devices => { |
| | | const renderDeviceEntities = async devices => { |
| | | if (!viewer) return |
| | | ensureCockpitPrimitiveLayer() |
| | | ensureDeviceCollections() |
| | |
| | | } |
| | | devicePickMap.clear() |
| | | deviceBillboardCollection.show = detailVisible.value |
| | | const ringFillInstancesByStyle = RING_STYLES.map(() => []) |
| | | const ringOutlineInstancesByStyle = RING_STYLES.map(() => []) |
| | | const deviceEntries = [] |
| | | const devicePositions = [] |
| | | devices.forEach((item, index) => { |
| | | const position = getDevicePosition(item) |
| | | if (!position) return |
| | | const entityId = `online-device-${item.id ?? index}-${index}` |
| | | const rangeMeters = getDeviceRange(item) |
| | | addDeviceRings(position, ringFillInstancesByStyle, ringOutlineInstancesByStyle, rangeMeters) |
| | | devicePositions.push({ lng: position.longitude, lat: position.latitude }) |
| | | const billboard = deviceBillboardCollection.add({ |
| | | position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0), |
| | | image: equipmentIcon, |
| | |
| | | }) |
| | | billboard.id = entityId |
| | | devicePickMap.set(entityId, { data: item, billboard }) |
| | | deviceEntries.push({ position, rangeMeters, billboard }) |
| | | }) |
| | | if (selectedTargetType.value === 'device' && selectedDeviceBillboard && !devicePickMap.has(selectedDeviceBillboard.id)) { |
| | | closePopup() |
| | | } |
| | | ringFillInstancesByStyle.forEach((instances, index) => { |
| | | if (!instances.length) return |
| | | const ring = RING_STYLES[index] |
| | | const alpha = ring.alpha ?? 0.64 |
| | | const innerRatio = |
| | | typeof ring.innerRatio === 'number' |
| | | ? ring.innerRatio |
| | | : ring.inner && ring.outer |
| | | ? Math.min(Math.max(ring.inner / ring.outer, 0), 0.9) |
| | | : 0 |
| | | const material = createRadialGradientMaterial( |
| | | Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(alpha), |
| | | Cesium.Color.fromCssColorString(ring.gradient[1]).withAlpha(alpha), |
| | | { |
| | | gamma: 1.7, |
| | | innerCutoff: innerRatio, |
| | | if (deviceEntries.length) { |
| | | const heights = await getPointPositionsHeight(devicePositions, viewer) |
| | | deviceEntries.forEach((entry, index) => { |
| | | const height = Number(heights?.[index]?.ASL) |
| | | const centerHeight = Number.isFinite(height) ? height : 0 |
| | | if (entry.billboard) { |
| | | entry.billboard.position = Cesium.Cartesian3.fromDegrees( |
| | | entry.position.longitude, |
| | | entry.position.latitude, |
| | | centerHeight |
| | | ) |
| | | } |
| | | ) |
| | | const primitive = new Cesium.GroundPrimitive({ |
| | | geometryInstances: instances, |
| | | appearance: new Cesium.MaterialAppearance({ |
| | | material, |
| | | translucent: true, |
| | | }), |
| | | if (!Number.isFinite(entry.rangeMeters) || entry.rangeMeters <= 0) return |
| | | const primitive = createDeviceRangePrimitiveWithHeight(entry.position, entry.rangeMeters, centerHeight) |
| | | if (!primitive) return |
| | | primitive.show = detailVisible.value |
| | | addCockpitPrimitive(primitive) |
| | | deviceRingFillPrimitives.push(primitive) |
| | | }) |
| | | primitive.show = detailVisible.value |
| | | addCockpitPrimitive(primitive) |
| | | deviceRingFillPrimitives.push(primitive) |
| | | }) |
| | | ringOutlineInstancesByStyle.forEach(instances => { |
| | | if (!instances.length) return |
| | | const primitive = new Cesium.GroundPolylinePrimitive({ |
| | | geometryInstances: instances, |
| | | appearance: new Cesium.PolylineColorAppearance(), |
| | | }) |
| | | primitive.show = detailVisible.value |
| | | addCockpitPrimitive(primitive) |
| | | deviceRingOutlinePrimitives.push(primitive) |
| | | }) |
| | | } |
| | | reorderCockpitPrimitives() |
| | | } |
| | | |
| New file |
| | |
| | | import * as Cesium from 'cesium' |
| | | import { getPointPositionsHeight } from './mapUtil' |
| | | |
| | | const DEVICE_RANGE_MATERIAL_TYPE = 'DeviceRangeEllipsoid' |
| | | let deviceRangeMaterialRegistered = false |
| | | |
| | | export const registerDeviceRangeMaterial = () => { |
| | | if (deviceRangeMaterialRegistered || !Cesium?.Material) return |
| | | deviceRangeMaterialRegistered = true |
| | | Cesium.Material._materialCache.addMaterial(DEVICE_RANGE_MATERIAL_TYPE, { |
| | | fabric: { |
| | | type: DEVICE_RANGE_MATERIAL_TYPE, |
| | | uniforms: { |
| | | color: new Cesium.Color(1.0, 0.0, 0.0, 0.7), |
| | | speed: 3.0, |
| | | globalAlpha: 0.9 |
| | | }, |
| | | source: ` |
| | | uniform vec4 color; |
| | | uniform float speed; |
| | | uniform float globalAlpha; |
| | | |
| | | czm_material czm_getMaterial(czm_materialInput materialInput) |
| | | { |
| | | czm_material material = czm_getDefaultMaterial(materialInput); |
| | | vec2 st = materialInput.st; |
| | | |
| | | float time = fract(czm_frameNumber * speed / 1000.0); |
| | | |
| | | if (st.t < 0.5) { |
| | | discard; |
| | | } |
| | | |
| | | float alphaMars3D = abs(smoothstep(0.5, 1.0, fract(-st.t - time))); |
| | | alphaMars3D += 0.1; |
| | | |
| | | material.alpha = alphaMars3D * globalAlpha; |
| | | material.diffuse = color.rgb; |
| | | |
| | | return material; |
| | | } |
| | | `, |
| | | }, |
| | | translucent: () => true, |
| | | }) |
| | | } |
| | | |
| | | export const createDeviceRangeMaterial = () => { |
| | | registerDeviceRangeMaterial() |
| | | return Cesium.Material.fromType(DEVICE_RANGE_MATERIAL_TYPE, { |
| | | color: Cesium.Color.fromBytes(0, 255, 255, 155), |
| | | }) |
| | | } |
| | | |
| | | export const getDeviceLngLat = device => { |
| | | const lngRaw = device?.longitude ?? device?.lng ?? device?.lon |
| | | const latRaw = device?.latitude ?? device?.lat |
| | | const lng = Number(lngRaw) |
| | | const lat = Number(latRaw) |
| | | if (!Number.isFinite(lng) || !Number.isFinite(lat)) return null |
| | | return { lng, lat } |
| | | } |
| | | |
| | | export const normalizeDeviceRange = value => { |
| | | const range = Number(value) |
| | | if (!Number.isFinite(range) || range <= 0) return 2000 |
| | | return range |
| | | } |
| | | |
| | | export const resolveDeviceTerrainHeight = async (viewer, position) => { |
| | | if (!viewer || !position) return 0 |
| | | const lng = position.lng ?? position.longitude |
| | | const lat = position.lat ?? position.latitude |
| | | if (!Number.isFinite(Number(lng)) || !Number.isFinite(Number(lat))) return 0 |
| | | try { |
| | | const results = await getPointPositionsHeight([{ lng: Number(lng), lat: Number(lat) }], viewer) |
| | | const height = Number(results?.[0]?.ASL) |
| | | return Number.isFinite(height) ? height : 0 |
| | | } catch (error) { |
| | | return 0 |
| | | } |
| | | } |
| | | |
| | | export const createDeviceRangePrimitiveWithHeight = (position, rangeMeters, height, material = null) => { |
| | | if (!position || !Number.isFinite(rangeMeters)) return null |
| | | const lng = position.lng ?? position.longitude |
| | | const lat = position.lat ?? position.latitude |
| | | if (!Number.isFinite(Number(lng)) || !Number.isFinite(Number(lat))) return null |
| | | const center = Cesium.Cartesian3.fromDegrees(Number(lng), Number(lat), Number(height) || 0) |
| | | const radii = new Cesium.Cartesian3(rangeMeters, rangeMeters, rangeMeters) |
| | | const geometry = new Cesium.EllipsoidGeometry({ |
| | | radii, |
| | | vertexFormat: Cesium.MaterialAppearance.MaterialSupport.TEXTURED?.vertexFormat |
| | | || Cesium.VertexFormat.POSITION_AND_NORMAL, |
| | | }) |
| | | const instance = new Cesium.GeometryInstance({ |
| | | geometry, |
| | | modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(center), |
| | | }) |
| | | return new Cesium.Primitive({ |
| | | geometryInstances: instance, |
| | | appearance: new Cesium.MaterialAppearance({ |
| | | material: material || createDeviceRangeMaterial(), |
| | | closed: true, |
| | | translucent: true, |
| | | flat: true, |
| | | faceForward: true, |
| | | }), |
| | | asynchronous: false |
| | | }) |
| | | } |
| | | |
| | | export const createDeviceRangePrimitive = async (viewer, position, rangeMeters, material = null) => { |
| | | if (!viewer) return null |
| | | const height = await resolveDeviceTerrainHeight(viewer, position) |
| | | return createDeviceRangePrimitiveWithHeight(position, rangeMeters, height, material) |
| | | } |
| | |
| | | import { fieldRules, geomAnalysis, getDictLabel } from '@ztzf/utils' |
| | | import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue' |
| | | import { DrawManager, EditManager, buildEllipsePositions } from '@/utils/cesium/shapeTools' |
| | | import { cartesian3Convert, getPointPositionsHeight } from '@/utils/cesium/mapUtil' |
| | | import { cartesian3Convert } from '@/utils/cesium/mapUtil' |
| | | import { |
| | | createDeviceRangePrimitive, |
| | | getDeviceLngLat, |
| | | normalizeDeviceRange, |
| | | resolveDeviceTerrainHeight, |
| | | } from '@/utils/cesium/deviceRange' |
| | | import * as Cesium from 'cesium' |
| | | import { AREA_TYPE_STYLE_MAP, BUFFER_LEVEL_STYLES, DEFAULT_AREA_STYLE } from '@ztzf/constants' |
| | | import { fwPoliceStationListApi } from '@/views/areaManage/precinctInfo/precinctInfoApi' |
| | |
| | | let viewEntity |
| | | let shapeDisplaySource |
| | | const deviceRangePrimitiveMap = new Map() |
| | | let deviceRangeMaterialRegistered = false |
| | | const activeToolMode = ref('') |
| | | |
| | | const rules = { |
| | |
| | | return shapeDisplaySource |
| | | } |
| | | |
| | | function registerDeviceRangeMaterial () { |
| | | if (deviceRangeMaterialRegistered || !Cesium?.Material) return |
| | | deviceRangeMaterialRegistered = true |
| | | Cesium.Material._materialCache.addMaterial('DeviceRangeEllipsoid', { |
| | | fabric: { |
| | | type: 'DeviceRangeEllipsoid', |
| | | uniforms: { |
| | | color: new Cesium.Color(1.0, 0.0, 0.0, 0.7), |
| | | speed: 3.0, |
| | | }, |
| | | source: ` |
| | | uniform vec4 color; |
| | | uniform float speed; |
| | | czm_material czm_getMaterial(czm_materialInput materialInput){ |
| | | czm_material material = czm_getDefaultMaterial(materialInput); |
| | | vec2 st = materialInput.st; |
| | | float time = fract(czm_frameNumber * speed / 1000.0); |
| | | float alpha = abs(smoothstep(0.5,1.,fract( -st.t - time))); |
| | | alpha += .1; |
| | | material.alpha = alpha; |
| | | material.diffuse = color.rgb; |
| | | return material; |
| | | } |
| | | `, |
| | | }, |
| | | translucent: () => true, |
| | | }) |
| | | } |
| | | |
| | | function createDeviceRangeMaterial () { |
| | | registerDeviceRangeMaterial() |
| | | return Cesium.Material.fromType('DeviceRangeEllipsoid', { |
| | | color: Cesium.Color.fromBytes(0, 255, 255, 155), |
| | | }) |
| | | } |
| | | |
| | | function clearDeviceRangePrimitives () { |
| | | if (!viewer || !deviceRangePrimitiveMap.size) return |
| | | for (const primitive of deviceRangePrimitiveMap.values()) { |
| | |
| | | deviceRangePrimitiveMap.clear() |
| | | } |
| | | |
| | | function normalizeDeviceRange (value) { |
| | | const range = Number(value) |
| | | if (!Number.isFinite(range) || range <= 0) return 2000 |
| | | return range |
| | | } |
| | | |
| | | function getDeviceLngLat (device) { |
| | | const lngRaw = device?.longitude ?? device?.lng ?? device?.lon |
| | | const latRaw = device?.latitude ?? device?.lat |
| | | const lng = Number(lngRaw) |
| | | const lat = Number(latRaw) |
| | | if (!Number.isFinite(lng) || !Number.isFinite(lat)) return null |
| | | return { lng, lat } |
| | | } |
| | | |
| | | async function resolveDeviceTerrainHeight (position) { |
| | | if (!viewer) return 0 |
| | | try { |
| | | const results = await getPointPositionsHeight([{ lng: position.lng, lat: position.lat }], viewer) |
| | | const height = Number(results?.[0]?.ASL) |
| | | return Number.isFinite(height) ? height : 0 |
| | | } catch (error) { |
| | | return 0 |
| | | } |
| | | } |
| | | |
| | | async function createDeviceRangePrimitive (device) { |
| | | if (!viewer) return null |
| | | if (!device) return null |
| | | const position = getDeviceLngLat(device) |
| | | if (!position) return null |
| | | const rangeMeters = normalizeDeviceRange(device.effectiveRangeKm) |
| | | const centerHeight = await resolveDeviceTerrainHeight(position) |
| | | const center = Cesium.Cartesian3.fromDegrees(position.lng, position.lat, centerHeight) |
| | | const radii = new Cesium.Cartesian3(rangeMeters, rangeMeters, rangeMeters) |
| | | const geometry = new Cesium.EllipsoidGeometry({ |
| | | radii, |
| | | vertexFormat: Cesium.MaterialAppearance.MaterialSupport.TEXTURED?.vertexFormat |
| | | || Cesium.VertexFormat.POSITION_AND_NORMAL, |
| | | }) |
| | | const instance = new Cesium.GeometryInstance({ |
| | | geometry, |
| | | modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(center), |
| | | }) |
| | | return new Cesium.Primitive({ |
| | | geometryInstances: instance, |
| | | appearance: new Cesium.MaterialAppearance({ |
| | | material: createDeviceRangeMaterial(), |
| | | closed: true, |
| | | translucent: true, |
| | | flat: true, |
| | | faceForward: true, |
| | | }), |
| | | asynchronous: false |
| | | }) |
| | | } |
| | | |
| | | async function renderDeviceRangeSingle (device) { |
| | | if (!viewer) return |
| | | clearDeviceRangePrimitives() |
| | | const primitive = await createDeviceRangePrimitive(device) |
| | | const position = getDeviceLngLat(device) |
| | | if (!position) return |
| | | const rangeMeters = normalizeDeviceRange(device.effectiveRangeKm) |
| | | const primitive = await createDeviceRangePrimitive(viewer, position, rangeMeters) |
| | | if (!primitive) return |
| | | deviceRangePrimitiveMap.set(String(device?.id ?? Date.now()), primitive) |
| | | viewer.scene.primitives.add(primitive) |
| | |
| | | if (!pendingRows.length) return |
| | | const primitives = await Promise.all( |
| | | pendingRows.map(async row => { |
| | | const primitive = await createDeviceRangePrimitive(row) |
| | | const position = getDeviceLngLat(row) |
| | | if (!position) return { row, primitive: null } |
| | | const rangeMeters = normalizeDeviceRange(row.effectiveRangeKm) |
| | | const primitive = await createDeviceRangePrimitive(viewer, position, rangeMeters) |
| | | return { row, primitive } |
| | | }) |
| | | ) |
| | |
| | | if (!viewer) return |
| | | const position = getDeviceLngLat(device) |
| | | if (!position) return |
| | | const height = await resolveDeviceTerrainHeight(position) |
| | | const destination = Cesium.Cartesian3.fromDegrees(position.lng, position.lat, height + device.effectiveRangeKm * 3) |
| | | const height = await resolveDeviceTerrainHeight(viewer, position) |
| | | const rangeMeters = normalizeDeviceRange(device?.effectiveRangeKm) |
| | | const destination = Cesium.Cartesian3.fromDegrees(position.lng, position.lat, height + rangeMeters * 3) |
| | | viewer.camera.flyTo({ |
| | | destination, |
| | | orientation: { |
| | |
| | | <el-table-column type="selection" width="46" /> |
| | | <el-table-column type="index" show-overflow-tooltip width="64" label="序号" /> |
| | | <el-table-column prop="areaName" show-overflow-tooltip width="150" label="区域名称" /> |
| | | <el-table-column prop="areaTypeKeys" show-overflow-tooltip width="120" label="区域类型"> |
| | | <el-table-column prop="areaTypeKeys" show-overflow-tooltip width="196" label="区域类型"> |
| | | <template v-slot="{ row }"> |
| | | {{ getDictLabel(row.areaTypeKeys, dictObj.areaType) }} |
| | | </template> |
| | |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="policeStationName" show-overflow-tooltip label="关联派出所" /> |
| | | <el-table-column show-overflow-tooltip label="可飞行时间段"> |
| | | <el-table-column show-overflow-tooltip width="300" label="可飞行时间段"> |
| | | <template v-slot="{ row }"> |
| | | {{ formatFlyDate(row) }} |
| | | </template> |