import * as Cesium from 'cesium' import aggregationImg from '@/assets/images/home/useUavHome/aggregation.png' import eventAggregationImg from '@/assets/images/home/useUavHome/eventAggregationImg.png' import uavImg from '@/assets/images/home/useUavHome/uavImg.png' import { getEventImage } from '@/utils/stateToImageMap/event' import { getDroneStatusImage } from '@/utils/stateToImageMap/drone' import { useStore } from 'vuex' import { getCenterPoint } from '@/utils/cesium/mapUtil' import cesiumOperation from '@/utils/cesium-tsa' import { getDeviceRegion, getDeviceRegionCount, getEventDetails, getMapEvents } from '@/api/home/aggregation' import { PolyGradientMaterial } from '@/utils/cesium/Material' import { start } from 'nprogress' // hook import { useMapHandlerClick } from '@/hooks/components/useMapHandlerClick' /** * 机巢聚合功能 */ let arrColor = [ 'rgb(15,176,255)', 'rgb(18,76,154)', '#40C4E4', '#42B2BE', 'rgb(51,176,204)', '#8CB7E5', 'rgb(0,244,188)', '#139FF0', ] let index = 0 function getColor () { return arrColor[++index % arrColor.length] } export const useMapAggregation = (type, status) => { const { flyTo } = cesiumOperation() const mergeImg = type === 'device' ? aggregationImg : eventAggregationImg const styleTransform = type === 'device' ? 'translateY(-50%)' : 'translate(-50%,-110%)' const eventType = type === 'device' ? ['deviceAggregation', 'device'] : 'event' const { handlerInit: mapHandlerInit, removeAll: removeMapHandlerAll, removeLabel: removeMapLabel } = useMapHandlerClick({ popupType: `${type}-popup`, eventType: eventType, styleTransform, getViewer () { return viewer } }) let scalingJudgment = [ { name: '县', splashedList: [], gJson: null, show: false, outline: {}, value: [0, 48651], height: 31753 }, { name: '市', splashedList: [], gJson: null, show: false, outline: {}, value: [48651, 314863], height: 257731 }, { name: '省', splashedList: [], gJson: null, show: false, outline: {}, value: [314863, 3796280000], height: 1987280, }, ] let viewer = null let active = null const store = useStore() const userAreaCode = computed(() => store.state.user.userInfo.detail.areaCode) const selectedAreaCode = computed(() => store.state.user.selectedAreaCode) const eventTimeType = computed(() => store.state.home.eventTimeType) const eventTimeRang = computed(() => store.state.home.eventTimeRang) const singleUavHome = computed(() => store.state.home.singleUavHome) let needFly = true const combinedValues = computed(() => ({ selectedAreaCode: selectedAreaCode.value, eventTimeType: eventTimeType.value, eventTimeRang: eventTimeRang.value, singleUavHome: singleUavHome.value, })) let saveParams = { area_code: '', date_enum: 'CURRENT_WEEK' } // 确定缩放比例 const determineScaling = () => { // console.log('确定缩放比例'); if (!viewer) return let height = viewer.camera.positionCartographic.height // 根据高度展示对应的 gJson for (let [index, item] of scalingJudgment.entries()) { if (!item.show) return if (height > item.value[0] && height <= item.value[1]) { if (active === item.name) return active = item.name removeEntities() removeMapLabel() renderOutline(item) if (!item.gJson && !item.splashedList?.length) return item.gJson ? aggregation(item) : splashed(item) break } } } // 转换为目录结构, 返回数组, function convertToHierarchy (code) { const codeStr = code.toString() const provinceCode = codeStr.slice(0, 2) + '0000' const cityCode = codeStr.slice(0, 4) + '00' if (codeStr.slice(2, 4) === '00' && codeStr.slice(4, 6) === '00') { return [provinceCode] } else if (codeStr.slice(4, 6) === '00') { return [provinceCode, cityCode] } return [provinceCode, cityCode, code] } // 获取设备聚合 function getDeviceCount (areaCode) { return getDeviceRegionCount({ areaCode }).then(res => { return res?.data?.data || [] }) } // 获取设备散点 function getDeviceList (areaCode) { return getDeviceRegion({ areaCode }).then(res => { return (res?.data?.data || []).map(i => ({ ...i, type })) }) } // 事件散点 let eventList = [] function processChildren (childrens) { return (childrens || []).map(item => { const arr = processChildren(item.childrens) if (item.data) { eventList = eventList.concat(item.data) } const returnObj = { total_device_count: item.number, region_code: item.id, region_name: item.name, } if (arr.length !== 0) { returnObj.childrens = arr } return returnObj }) } // 获取事件聚合 function getMapEventCount (params) { return getMapEvents(params).then(res => { const resData = res?.data?.data if (resData?.data) { eventList = resData?.data return [] } return processChildren(resData?.childrens) }) } const findFun = (featItem, numItem) => Number(featItem.region_code.slice(0, 6)) === numItem.properties.adcode const { VITE_APP_BASE, VITE_APP_ENV } = import.meta.env const defaultDir = `${VITE_APP_BASE}${VITE_APP_ENV === 'development' ? 'public/' : ''}geoJson/100000/` const getFiler = async url => { const res = await fetch(url) return await res.json() } const getOutLine = async (jsonPathPre, hierarchy) => { const parentGJson = await getFiler(`${defaultDir}${jsonPathPre}/index.json`) let features = parentGJson.features.find(item => item.properties.adcode === Number(hierarchy[hierarchy.length - 1])) return { type: 'FeatureCollection', features: [features] } } const injectData = (gJson, dataList) => { return { ...gJson, features: gJson.features.map(item => { const findData = dataList.find(item1 => findFun(item1, item)) return { ...item, data: { ...findData, type: type + 'Aggregation' } } }), } } const initMapData = async areaCode => { eventList = [] if (!areaCode) return saveParams.area_code = areaCode const list = type === 'device' ? await getDeviceCount(areaCode) : await getMapEventCount(saveParams) const splashedList = type === 'device' ? await getDeviceList(areaCode) : eventList.map(i => ({ eventId: i.id, latitude: Number(i.latitude), longitude: Number(i.longitude), type: 'event', status: i.status })) const hierarchy = convertToHierarchy(areaCode.slice(0, 6)) const jsonPath = hierarchy.join('/') const jsonPathPre = hierarchy.slice(0, hierarchy.length - 1).join('/') scalingJudgment = scalingJudgment.map(item => ({ ...item, show: true })) scalingJudgment[0].gJson = null scalingJudgment[0].splashedList = splashedList active = null // 省 if (hierarchy.length === 1) { const gJson1 = await getFiler(`${defaultDir}${jsonPath}/indexDistrict.json`) const gJson2 = await getFiler(`${defaultDir}${jsonPath}/index.json`) scalingJudgment[1].gJson = { ...gJson1, features: gJson1.features.map(item => { const findData = list.flatMap(item => item.childrens || []).find(item1 => findFun(item1, item)) return { ...item, data: { ...findData, type: type + 'Aggregation' } } }), } scalingJudgment[2].gJson = injectData(gJson2, list) } // 市 if (hierarchy.length === 2) { scalingJudgment[2].gJson = null scalingJudgment[2].show = false const gJson1 = await getFiler(`${defaultDir}${jsonPath}/index.json`) scalingJudgment[1].gJson = injectData(gJson1, list) } // 区县 if (hierarchy.length === 3) { scalingJudgment[1].gJson = null scalingJudgment[1].show = false scalingJudgment[2].gJson = null scalingJudgment[2].show = false } // 轮廓 const outlineGJson = await getOutLine(jsonPathPre, hierarchy) scalingJudgment.forEach(item => item.show && (item.outline = outlineGJson)) const [longitude, latitude] = outlineGJson.features[0].properties.centroid const height = scalingJudgment[(hierarchy.length - 3) * -1].height setCenterPosition({ longitude, latitude, height }) flyTo({ longitude, latitude }, 0, height) } const userAreaPosition = computed(() => store.state.home.userAreaPosition) const setCenterPosition = position => { store.commit('setCurrentAreaPosition', position) if (!userAreaPosition.value.longitude) { store.commit('setUserAreaPosition', position) } } watch( combinedValues, async (newValue, oldValue) => { if (newValue.singleUavHome?.device_sn) { clearMapEntity() return } if (newValue.eventTimeType) { saveParams = { area_code: newValue.selectedAreaCode, date_enum: store.state.home.eventTimeParams } } if (newValue.eventTimeRang) { saveParams = { area_code: newValue.selectedAreaCode, start_date: newValue.eventTimeRang[0], end_date: newValue.eventTimeRang[1], } } needFly = true if (!viewer) return mapHandlerInit() viewer.scene.postRender.removeEventListener(determineScaling) initMapData(newValue.selectedAreaCode).then(() => { viewer.scene.postRender.addEventListener(determineScaling) }) }, { deep: true } ) //散点机巢 function splashed (row) { row.splashedList.forEach((item, index) => { const image = type === 'device' ? getDroneStatusImage(item.status) : getEventImage(item.status) viewer.entities.add({ id: `aggregation-splashed-${index}`, position: Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude), label: { text: item.nickname, font: '12pt 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), }, billboard: { // singleImg image: new Cesium.ConstantProperty(image), width: 24, height: 24, }, properties: { customData: { data: item, }, }, }) }) } // 渲染轮廓 const renderOutline = item => { item.outline && Cesium.GeoJsonDataSource.load(item.outline).then(dataSource => { const entities = dataSource.entities.values entities.forEach((entity, index) => { // 创建独立折线作为轮廓 const positions = entity.polygon.hierarchy.getValue().positions const randomInt = Math.floor(Math.random() * 5) + 1 let polygon = {} if (item.name === '县') { let material = new PolyGradientMaterial({ color: Cesium.Color.fromCssColorString(arrColor[randomInt]), opacity: 0.7, alphaPower: 1.3, }) entity.polygon.extrudedHeight = (entity.properties.childrenNum._value || 1) * 500 entity.polygon.material = material polygon = entity.polygon entity.polygon.outline = false // 显示边框 } viewer.entities.add({ id: `aggregation-outline-${index}`, polyline: { positions: positions, width: 5, // 直接设置宽度 material: new Cesium.PolylineGlowMaterialProperty({ glowPower: 0.5, color: Cesium.Color.AQUA, }), }, polygon, }) }) }) } // 聚合机巢 const aggregation = item => { if (!item.gJson) return 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(mergeImg), width: 35, height: 35, }, properties: { id: feature.id, customData: { data: feature.data, }, }, }) }) // 加载边界 Cesium.GeoJsonDataSource.load(item.gJson).then(dataSource => { viewer.dataSources.add(dataSource) item.BJDataSource = dataSource // 保存数据源以便后续删除 // 获取数据源中的实体 const entities = dataSource.entities.values entities.forEach(entity => { let material = new PolyGradientMaterial({ color: Cesium.Color.fromCssColorString(getColor()), opacity: 0.7, alphaPower: 1.3, }) const randomInt = Math.floor(Math.random() * 8) + 1 entity.polygon.extrudedHeight = (entity.properties.childrenNum._value || randomInt) * 500 entity.polygon.material = material entity.polygon.outline = false // 显示边框 }) needFly && viewer.flyTo(dataSource, { offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-60), 0), duration: 0.5, }) needFly = false }) } /** * 根据条件获取项 * @param {Array} arr 数据源 * @param {Function} condition 条件 * @returns */ const findTypeItem = (arr, condition) => { return arr.find(item => condition(item)) } // 移除 点 和 gjson 实体 const removeEntities = () => { // dataSources移除 scalingJudgment.forEach(item => { item.BJDataSource && viewer.dataSources.remove(item.BJDataSource) item.BJDataSource = null }) // entities移除 const entitiesIDs = viewer.entities.values.map(i => i.id) entitiesIDs.forEach(item => { item.includes('aggregation-') && viewer.entities.removeById(item) }) } // 移除所有监听事件,变量置空 const removeAll = () => { clearMapEntity() viewer = null } const clearMapEntity = () => { if (!viewer) return removeEntities() removeMapHandlerAll() // viewer.camera.moveEnd.removeEventListener(determineScaling); viewer.scene.postRender.removeEventListener(determineScaling) active = null } const init = () => { viewer = window.$viewer viewer.scene.postRender.removeEventListener(determineScaling) initMapData(selectedAreaCode.value || userAreaCode.value).then(() => { viewer.scene.postRender.addEventListener(determineScaling) }) mapHandlerInit() } onBeforeUnmount(() => { }) // onMounted(() => { // nextTick(() => { // viewer = window.$viewer; // handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); // handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK); // }); // }); return { init, removeAll } }