import _ from 'lodash' import { boxTransformScale } from '@/utils/turfFunc' import { MAP_LEVEL } from '@/const/drc' import * as Cesium from 'cesium' import { PolyGradientMaterial } from '@/utils/cesium/Material' import { useStore } from 'vuex' import { getDeviceRegion, getDeviceRegionCount } from '@/api/home/aggregation' import userStore from '@/store/modules/user' import { areaCodeToArr, getContourByCode } from '@/utils/cesium/mapUtil' // import { getEllipse } from '@/hooks/useSingleDroneMap/useSingleDroneMap' import getBaseConfig from '@/buildConfig/config' import { getDroneStatusImage } from '@/utils/stateToImageMap/drone' import { GroundCirclePrimitiveManager } from '@/utils/mapUtils' const { VITE_APP_BASE, VITE_APP_ENV, VITE_APP_REGION_URL } = import.meta.env // const { singleDockSystem } = getBaseConfig() const { singleDockSystem } = getBaseConfig() /** * 使用通用的边界 * @param viewer * @param options */ export const useBoundary = (viewer, options = {}) => { const { multiple = 1.4, dockOptions = {}, boundaryChange, boundaryColor, dockRangeType, useDockHeight = false } = options const { scrollShowDock = false, showDock = false } = dockOptions const manager = new GroundCirclePrimitiveManager() manager.init(viewer) // 缩放判断list(包含省市县的轮廓边界散点数据) let scalingJudgment = _.cloneDeep(MAP_LEVEL).map(i => ({ ...i, gJson: null, show: false, outline: {} })) let active = null const defaultDir = `${VITE_APP_REGION_URL}/100000/` const userAreaCode = userStore.state.userInfo.detail.areaCode const selectedAreaCode = userStore.state.selectedAreaCode const dockSource = new Cesium.CustomDataSource('dockSource') const outlineSource = new Cesium.CustomDataSource('outlineSource') const minOutlineSource = new Cesium.CustomDataSource('minOutlineSource') viewer.dataSources.add(dockSource) viewer.dataSources.add(outlineSource) viewer.dataSources.add(minOutlineSource) let initDockList = [] let showDockSnList = null if (scrollShowDock || showDock) { getDeviceRegionFun().then(list => { initDockList = list if (showDock && !scrollShowDock) droneSplashed() }) } // 获取设备列表 async function getDeviceRegionFun () { const res = await getDeviceRegion({ areaCode: selectedAreaCode }) return res?.data?.data || [] } // 确定缩放比例 const determineScaling = () => { if (!viewer) return let height = viewer.camera.positionCartographic.height // 根据高度展示对应的 gJson for (let [index, item] of scalingJudgment.entries()) { if (!item.show) return if (height > item.heightRange[0] && height <= item.heightRange[1]) { if (active === item.name) return boundaryChange?.(item?.gJson?.features) active = item.name removeBoundary() renderOutline(item) scrollShowDock && removeDockCover() item.gJson || (scrollShowDock && droneSplashed(item)) break } } } const removeBoundary = () => { minOutlineSource?.entities.removeAll() outlineSource?.entities?.removeAll() } const removeAllEntities = () => { removeBoundary() removeDockCover() } const removeDockCover = () => { dockSource?.entities?.removeAll() manager?.removeAll() } const getFiler = async url => { const res = await fetch(url) return await res.json() } // 无人机散点渲染 const droneSplashed = () => { let showDockList = showDockSnList === null ? _.cloneDeep(initDockList) : initDockList.filter(item => showDockSnList.includes(item.device_sn)) showDockList.forEach((item, index) => { let polyline = {} if (dockRangeType === 2) { manager.addCircleOutline({ data: { lng: item.longitude, lat: item.latitude }, frameColor: Cesium.Color.fromCssColorString('#7FFFD4') }) } else { manager.addCircle({ data: { lng: item.longitude, lat: item.latitude }, materialColor: Cesium.Color.CORNFLOWERBLUE.withAlpha(0.3) }) } if (useDockHeight) { polyline = { positions: new Cesium.CallbackProperty(() => { const pointPosition = Cesium.Cartesian3.fromDegrees(+item.longitude, +item.latitude, +item.height) if (!pointPosition) return [] // 获取点的位置 const cartographic = Cesium.Cartographic.fromCartesian(pointPosition) // 获取地形高度 const terrainHeight = viewer.scene.globe.getHeight(cartographic) || 0 // 创建地面点位置 const groundPosition = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, terrainHeight) return [pointPosition, groundPosition] }, false), width: 0.5, material: Cesium.Color.WHITE, } } dockSource.entities.add({ position: Cesium.Cartesian3.fromDegrees(+item.longitude, +item.latitude, !useDockHeight ? 0 : +item.height), polyline, billboard: { image: getDroneStatusImage(item.status), width: 60, height: 60, eyeOffset: new Cesium.Cartesian3(0, 0, -5) }, }) }) } // 渲染各个独立的轮廓 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 === '县' && !scalingJudgment[0].outline) { 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 // 显示边框 } outlineSource.entities.add({ polyline: { positions: positions, width: 5, // 直接设置宽度 clampToGround: true, material: Cesium.Color.fromCssColorString(boundaryColor) }, polygon, }) }) }) if (!item.gJson) return // 加载小边界 Cesium.GeoJsonDataSource.load(item.gJson).then(dataSource => { // 获取数据源中的实体 const entities = dataSource.entities.values entities.forEach(entity => { const positions = entity.polygon.hierarchy.getValue().positions minOutlineSource.entities.add({ polyline: { positions: positions, width: 1, // 直接设置宽度 clampToGround: true, material: Cesium.Color.fromCssColorString(boundaryColor) }, }) }) }) } const findFun = (featItem, numItem) => Number(featItem.region_code.slice(0, 6)) === numItem.properties.adcode // 打开边界 const openContour = async () => { const areaCode = selectedAreaCode || userAreaCode viewer.scene.postRender.removeEventListener(determineScaling) if (!areaCode) return const hierarchy = areaCodeToArr(areaCode.slice(0, 6)) const jsonPath = hierarchy.join('/') scalingJudgment = scalingJudgment.map(item => ({ ...item, show: true })) scalingJudgment[0].gJson = null active = null const res = await getDeviceRegionCount({ areaCode }) const list = res?.data?.data || [] try { // 省市县三级 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 } } }), } scalingJudgment[2].gJson = gJson2 } // 市县两级 if (hierarchy.length === 2) { scalingJudgment[2].gJson = null scalingJudgment[2].show = false scalingJudgment[1].gJson = await getFiler(`${defaultDir}${jsonPath}/index.json`) } // 区县一级 if (hierarchy.length === 3) { scalingJudgment[1].gJson = null scalingJudgment[1].show = false scalingJudgment[2].gJson = null scalingJudgment[2].show = false } // 轮廓 const outlineGJson = await getContourByCode(areaCode.slice(0, 6)) scalingJudgment.forEach(item => item.show && (item.outline = outlineGJson)) renderOutline(scalingJudgment[(hierarchy.length - 3) * -1]) } catch (e) { } viewer.scene.postRender.addEventListener(determineScaling) } // 关闭边界 function closeContour () { removeAllEntities() viewer.scene.postRender.removeEventListener(determineScaling) } // 飞向边界 async function flyToBoundary () { // 单机巢系统 if (singleDockSystem) { if (!initDockList.length) { initDockList = await getDeviceRegionFun() } viewer?.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(initDockList[0].longitude, initDockList[0].latitude, 24000), duration: 0, }) return } const areaCode = selectedAreaCode || userAreaCode const gJson = await getContourByCode(areaCode.slice(0, 6)) const dataSource = await Cesium.GeoJsonDataSource.load(gJson) if (!dataSource) return viewer.dataSources.add(dataSource) dataSource.entities.values.forEach(function (entity) { if (entity.polygon) { // 获取多边形的边界球 // const boundingSphere = entity.polygon.hierarchy.getValue(viewer.clock.currentTime).boundingSphere let curPolygonPosition = entity.polygon.hierarchy._value.positions.map(item => { let cartographic = Cesium.Cartographic.fromCartesian(item) let lng = Cesium.Math.toDegrees(cartographic.longitude) // 经度 let lat = Cesium.Math.toDegrees(cartographic.latitude) // 纬度 return [_.round(lng, 6), _.round(lat, 6)] }) const newBox = boxTransformScale(curPolygonPosition, multiple) viewer.camera.flyTo({ destination: Cesium.Rectangle.fromDegrees(...newBox), offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-90), 0), duration: 0.5, }) } }) dataSource.entities.values.forEach(entity => { entity.polygon.material = new Cesium.ColorMaterialProperty(Cesium.Color.YELLOW.withAlpha(0)) entity.polygon.outline = new Cesium.ConstantProperty(false) // 显示边框 entity.polygon.outlineColor = new Cesium.ConstantProperty(Cesium.Color.AQUAMARINE.withAlpha(0)) }) } /** * 设置要显示的机巢列表 * @param dockSns - 要显示的机巢设备序列号数组。如果为 undefined,则显示所有已初始化的机巢;如果为空数组,则不显示任何机巢。 */ function setShowDock (dockSns) { if (dockSns === undefined) { showDockSnList = null } else if (dockSns.length === 0) { showDockSnList = [] } else { showDockSnList = dockSns } removeDockCover() if (active === '县') droneSplashed() } onBeforeUnmount(() => { manager.destroy() }) return { openContour, closeContour, flyToBoundary, setShowDock } }