| New file |
| | |
| | | /* |
| | | * @Author: shuishen 1109946754@qq.com |
| | | * @Date: 2025-04-15 22:41:40 |
| | | * @LastEditors: shuishen 1109946754@qq.com |
| | | * @LastEditTime: 2025-04-16 15:06:15 |
| | | * @FilePath: \command-center-dashboard\src\hooks\useSingleDroneMap\index.js |
| | | * @Description: |
| | | * |
| | | * Copyright (c) 2025 by shuishen, All Rights Reserved. |
| | | */ |
| | | import { useMapMarkers } from './useMapMarkers' |
| | | import { useMapEvents } from './useMapEvents' |
| | | import { useMapPopup } from './useMapPopup' |
| | | |
| | | /** |
| | | * Cesium地图功能组合Hook |
| | | * @param {Object} options - 配置选项 |
| | | * @param {Function} options.fetchMarkers - 获取标注的函数 |
| | | * @param {Function} options.fetchEvents - 获取事件的函数 |
| | | * @param {Component} options.popupComponent - 弹窗组件 |
| | | */ |
| | | export function useSingleDroneMap (options = { |
| | | dronePosition: {}, |
| | | eventLoadType: 'data', |
| | | eventPositions: [], |
| | | eventApi: null, |
| | | eventApiParams: {} |
| | | }) { |
| | | let viewer = null |
| | | let currentEntity = null |
| | | |
| | | // 组合各个功能Hook |
| | | const { |
| | | initLayer, |
| | | removeLayer, |
| | | } = useMapMarkers(viewer, {}) |
| | | |
| | | const { |
| | | handlerInit, |
| | | removeHandler |
| | | } = useMapEvents(viewer, { |
| | | onEventClick: (entity) => { |
| | | currentEntity = entity // 更新当前实体 |
| | | |
| | | addLabel() |
| | | } |
| | | }) |
| | | |
| | | const { |
| | | addLabel |
| | | } = useMapPopup(viewer, { |
| | | currentEntity |
| | | }) |
| | | |
| | | const init = () => { |
| | | viewer = window.$viewer |
| | | |
| | | initLayer() |
| | | handlerInit() |
| | | } |
| | | |
| | | const removeAll = () => { |
| | | removeLayer() |
| | | removeHandler() |
| | | removeLabel() |
| | | } |
| | | |
| | | // 自动清理 |
| | | onUnmounted(() => { |
| | | removeAll() |
| | | }) |
| | | |
| | | return { |
| | | init, |
| | | removeAll |
| | | } |
| | | } |
| New file |
| | |
| | | import * as Cesium from 'cesium' |
| | | /** |
| | | * 地图事件 Hook |
| | | * @param {Cesium.Viewer} viewer - Cesium 实例 |
| | | * @param {Object} options - 配置选项 |
| | | * @param {Function} options.onEventClick - 点击事件的回调 |
| | | */ |
| | | export function useMapEvents (viewer, options = {}) { |
| | | const { onEventClick } = options |
| | | let handler = null |
| | | |
| | | // 查找特定类型的实体 |
| | | const findEntityByType = (entities, type) => { |
| | | return entities.find(entity => |
| | | entity?.properties?.customData?._value?.data?.type === type |
| | | ) |
| | | } |
| | | |
| | | // 左键单机事件 |
| | | const singleMachineEvent = async click => { |
| | | let clickedEntities = viewer.scene.drillPick(click.position).map(item => item.id) |
| | | if (!clickedEntities.length) return |
| | | |
| | | const currentEntity = findEntityByType(clickedEntities, 'device') || |
| | | findEntityByType(clickedEntities, 'event') |
| | | |
| | | currentEntity = deviceAggregationFind || deviceFind || eventFind |
| | | |
| | | if (currentEntity) { |
| | | onEventClick?.(currentEntity) |
| | | } |
| | | } |
| | | |
| | | const handlerInit = () => { |
| | | if (handler) return |
| | | |
| | | handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas) |
| | | handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK) |
| | | } |
| | | |
| | | const removeHandler = () => { |
| | | handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK) |
| | | handler?.destroy() |
| | | handler = null |
| | | } |
| | | |
| | | // 自动清理 |
| | | onUnmounted(() => { |
| | | removeHandler() |
| | | }) |
| | | |
| | | return { |
| | | handlerInit, |
| | | removeHandler |
| | | } |
| | | } |
| New file |
| | |
| | | import * as Cesium from 'cesium' |
| | | import aggregationImg from '@/assets/images/home/useUavHome/aggregation.png' |
| | | |
| | | /** |
| | | * 地图标注 |
| | | * @param {*} viewer |
| | | * @param {*} options |
| | | * @returns |
| | | */ |
| | | export function useMapMarkers (viewer, options = { |
| | | dronePosition: {}, |
| | | eventLoadType: 'data', |
| | | eventPositions: [], |
| | | eventApi: null, |
| | | eventApiParams: {} |
| | | }) { |
| | | const { dronePosition, eventLoadType, eventPositions, eventApi, eventApiParams } = options |
| | | |
| | | const initDroneEntity = () => { |
| | | const featuresList = item.gJson.features.map(item1 => { |
| | | // const {lng,lat} = getCenterPoint(item1.geometry.coordinates[0][0]) |
| | | return { |
| | | name: item1.properties.name, |
| | | position: item1.properties.centroid, |
| | | data: item1.data, |
| | | id: item1.region_code, |
| | | } |
| | | }) |
| | | // 遍历特征并添加实体 |
| | | featuresList.forEach((feature, index) => { |
| | | if (!feature.position) return |
| | | const position = Cesium.Cartesian3.fromDegrees(feature.position[0], feature.position[1], 0) |
| | | viewer.entities.add({ |
| | | position: position, |
| | | id: `aggregation-name-${index}`, |
| | | label: { |
| | | text: feature.name, |
| | | font: '14pt Source Han Sans CN', |
| | | fillColor: Cesium.Color.WHITE, |
| | | outlineColor: Cesium.Color.BLACK, |
| | | outlineWidth: 2, |
| | | style: Cesium.LabelStyle.FILL_AND_OUTLINE, |
| | | verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
| | | pixelOffset: new Cesium.Cartesian2(0, -9), |
| | | }, |
| | | }) |
| | | viewer.entities.add({ |
| | | id: `aggregation-count-${index}`, |
| | | position: position, |
| | | label: { |
| | | text: (feature.data.total_device_count || 0).toString(), |
| | | font: '12pt Source Han Sans CN', |
| | | fillColor: Cesium.Color.BLACK, |
| | | outlineColor: Cesium.Color.BLACK, |
| | | style: Cesium.LabelStyle.FILL_AND_OUTLINE, |
| | | eyeOffset: new Cesium.Cartesian3(0, 0, -10), // 让label "浮" 在广告牌前面 |
| | | }, |
| | | billboard: { |
| | | image: new Cesium.ConstantProperty(aggregationImg), |
| | | width: 35, |
| | | height: 35, |
| | | }, |
| | | properties: { |
| | | id: feature.id, |
| | | customData: { |
| | | data: feature.data, |
| | | }, |
| | | }, |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | const initDroneEventEntity = () => { |
| | | // 遍历特征并添加实体 |
| | | eventPositions.forEach((feature, index) => { |
| | | if (!feature.position) return |
| | | const position = Cesium.Cartesian3.fromDegrees(feature.position[0], feature.position[1], 0) |
| | | viewer.entities.add({ |
| | | position: position, |
| | | id: `aggregation-name-${index}`, |
| | | label: { |
| | | text: feature.name, |
| | | font: '14pt Source Han Sans CN', |
| | | fillColor: Cesium.Color.WHITE, |
| | | outlineColor: Cesium.Color.BLACK, |
| | | outlineWidth: 2, |
| | | style: Cesium.LabelStyle.FILL_AND_OUTLINE, |
| | | verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
| | | pixelOffset: new Cesium.Cartesian2(0, -9), |
| | | }, |
| | | }) |
| | | viewer.entities.add({ |
| | | id: `aggregation-count-${index}`, |
| | | position: position, |
| | | label: { |
| | | text: (feature.data.total_device_count || 0).toString(), |
| | | font: '12pt Source Han Sans CN', |
| | | fillColor: Cesium.Color.BLACK, |
| | | outlineColor: Cesium.Color.BLACK, |
| | | style: Cesium.LabelStyle.FILL_AND_OUTLINE, |
| | | eyeOffset: new Cesium.Cartesian3(0, 0, -10), // 让label "浮" 在广告牌前面 |
| | | }, |
| | | billboard: { |
| | | image: new Cesium.ConstantProperty(aggregationImg), |
| | | width: 35, |
| | | height: 35, |
| | | }, |
| | | properties: { |
| | | id: feature.id, |
| | | customData: { |
| | | data: feature.data, |
| | | }, |
| | | }, |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | const initEventApiEntity = () => { |
| | | eventApi(eventApiParams).then(res => { |
| | | console.log(res) |
| | | }) |
| | | } |
| | | |
| | | // 移除 点 和 gjson 实体 |
| | | const removeEntities = () => { |
| | | // entities移除 |
| | | const entitiesIDs = viewer.entities.values.map(i => i.id) |
| | | entitiesIDs.forEach(item => { |
| | | item.includes('aggregation-') && viewer.entities.removeById(item) |
| | | }) |
| | | } |
| | | |
| | | const initLayer = () => { |
| | | initDroneEntity() |
| | | |
| | | eventLoadType === 'data' ? initDroneEventEntity() : initEventApiEntity() |
| | | } |
| | | |
| | | const removeLayer = () => { |
| | | removeEntities() |
| | | } |
| | | |
| | | return { |
| | | initLayer, |
| | | removeLayer |
| | | } |
| | | } |
| New file |
| | |
| | | import * as Cesium from 'cesium' |
| | | import EventPopUpBox from '@/hooks/components/EventPopUpBox.vue' |
| | | |
| | | /** |
| | | * 地图弹窗 Hook |
| | | * @param {Cesium.Viewer} viewer - Cesium 实例 |
| | | * @param {Object} options - 配置选项 |
| | | * @param {Component} options.popupComponent - 弹窗组件 |
| | | */ |
| | | export function useMapPopup (viewer, options = {}) { |
| | | const { currentEntity } = options |
| | | |
| | | // 获取弹框box |
| | | const getLabelDom = data => { |
| | | const vNode = h(EventPopUpBox, { data, removeLabel }) |
| | | const tooltipContainer = document.createElement('div') |
| | | tooltipContainer.id = 'mapPopUpBox' |
| | | tooltipContainer.style.position = 'absolute' |
| | | tooltipContainer.style.transform = styleTransform |
| | | tooltipContainer.style.pointerEvents = 'none' |
| | | document.querySelector('.page-index').append(tooltipContainer) |
| | | render(vNode, tooltipContainer) |
| | | return tooltipContainer |
| | | } |
| | | |
| | | // 弹框位置刷新 |
| | | const labelBoxRender = () => { |
| | | if (!currentEntity) return |
| | | let dom = document.querySelector('#mapPopUpBox') |
| | | if (!dom) { |
| | | dom = getLabelDom(currentEntity.properties.customData._value.data) |
| | | } |
| | | const screenPosition = viewer.scene.cartesianToCanvasCoordinates(currentEntity?.position?._value) |
| | | if (screenPosition) { |
| | | dom.style.left = `${screenPosition.x}px` |
| | | dom.style.top = `${screenPosition.y}px` |
| | | dom.style.display = 'block' |
| | | } |
| | | } |
| | | |
| | | const removeDom = () => { |
| | | const dom = document.querySelector('#mapPopUpBox') |
| | | if (dom && dom.parentNode) { |
| | | dom.parentNode.removeChild(dom) |
| | | } |
| | | } |
| | | |
| | | const addLabel = () => { |
| | | removeLabel() |
| | | viewer?.scene.postRender.addEventListener(labelBoxRender) |
| | | } |
| | | |
| | | // 移除弹框标签 |
| | | const removeLabel = () => { |
| | | viewer?.scene.postRender.removeEventListener(labelBoxRender) |
| | | removeDom() |
| | | } |
| | | |
| | | // 自动清理 |
| | | onUnmounted(() => { |
| | | removeLabel() |
| | | }) |
| | | |
| | | return { |
| | | addLabel |
| | | } |
| | | } |