| | |
| | | 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 { areaCodeToArr, getContourByCode, getDockPolyLine, getLnglatAltitude } from '@/utils/cesium/mapUtil' |
| | | import getBaseConfig from '@/buildConfig/config' |
| | | import { getDroneStatusImage } from '@/utils/stateToImageMap/drone' |
| | | import { |
| | | GroundCirclePrimitiveManager |
| | | } from '@/utils/mapUtils' |
| | | import { GroundCirclePrimitiveManager } from '@/utils/mapUtils' |
| | | |
| | | import lowBattery from '@/assets/images/aiNowFly/low-battery.svg' |
| | | import mediumBattery from '@/assets/images/aiNowFly/medium-battery.svg' |
| | | import fullBattery from '@/assets/images/aiNowFly/full-charge.svg' |
| | | |
| | | const { VITE_APP_BASE, VITE_APP_ENV, VITE_APP_REGION_URL } = import.meta.env |
| | | // const { singleDockSystem } = getBaseConfig() |
| | | const { singleDockSystem } = getBaseConfig() |
| | | |
| | | |
| | | |
| | | /** |
| | | * 使用通用的边界 |
| | |
| | | * @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 { |
| | | multiple = 1.4, |
| | | dockOptions = {}, |
| | | boundaryChange, |
| | | boundaryColor, |
| | | dockRangeType, |
| | | useDockHeight = false, |
| | | } = options |
| | | const { scrollShowDock = false, showDock = false, showDockNameAndBattery = false } = dockOptions |
| | | |
| | | const manager = new GroundCirclePrimitiveManager() |
| | | manager.init(viewer) |
| | |
| | | } |
| | | |
| | | // 获取设备列表 |
| | | async function getDeviceRegionFun () { |
| | | async function getDeviceRegionFun() { |
| | | const res = await getDeviceRegion({ areaCode: selectedAreaCode }) |
| | | return res?.data?.data || [] |
| | | } |
| | |
| | | return await res.json() |
| | | } |
| | | |
| | | function getBase64Image(imgUrl) { |
| | | const img = new Image() |
| | | const canvas = document.createElement('canvas') |
| | | const ctx = canvas.getContext('2d') |
| | | |
| | | return new Promise(resolve => { |
| | | img.onload = function () { |
| | | canvas.width = img.width |
| | | canvas.height = img.height |
| | | ctx.drawImage(img, 0, 0) |
| | | const base64 = canvas.toDataURL('image/png') |
| | | resolve(base64) |
| | | } |
| | | img.src = imgUrl |
| | | }) |
| | | } |
| | | |
| | | function batteryLevelSvg(batteryNum) { |
| | | if (batteryNum <= 30) { |
| | | return lowBattery |
| | | } else if (batteryNum > 30 && batteryNum <= 60) { |
| | | return mediumBattery |
| | | } else { |
| | | return fullBattery |
| | | } |
| | | } |
| | | |
| | | async function nestSVG(nickname, batteryImgUrl, batteryNum) { |
| | | let txt = nickname.length < 4 ? 130 : nickname.length * 38 |
| | | let tranx = nickname.length < 4 ? 86 : nickname.length * 26 + 3 |
| | | const batteryBase64Image = await getBase64Image(batteryImgUrl) |
| | | const svg = ` |
| | | <svg width="${txt}" height="100" xmlns="http://www.w3.org/2000/svg"> |
| | | <g transform="translate(${tranx}, 10)"> |
| | | <!-- 电池图标 --> |
| | | <image href="${batteryBase64Image}" x="0" y="2" width="12" height="13"/> |
| | | <!-- 电量值 --> |
| | | <text x="12" y="10" font-size="14" fill="${ |
| | | batteryNum <= 30 ? '#FF604B' : batteryNum >= 60 ? '#40FF5C' : '#00FFF2' |
| | | }" text-anchor="start" dominant-baseline="middle" font-weight="bolder" font-family="Source Han Sans CN" text-rendering="geometricPrecision"> |
| | | ${batteryNum}% |
| | | </text> |
| | | </g> |
| | | </svg> |
| | | ` |
| | | return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}` |
| | | } |
| | | |
| | | // 无人机散点渲染 |
| | | const droneSplashed = () => { |
| | | let showDockList = showDockSnList === null |
| | | ? _.cloneDeep(initDockList) |
| | | : initDockList.filter(item => showDockSnList.includes(item.device_sn)) |
| | | manager.removeAll() |
| | | let showDockList = |
| | | showDockSnList === null |
| | | ? _.cloneDeep(initDockList) |
| | | : initDockList.filter(item => showDockSnList.includes(item.device_sn)) |
| | | |
| | | showDockList.forEach((item, index) => { |
| | | showDockList.forEach(async (item, index) => { |
| | | if (item.status === 'OFFLINE') return |
| | | let polyline = {} |
| | | |
| | | if (dockRangeType === 2) { |
| | | manager.addCircleOutline({ |
| | | data: { |
| | | lng: item.longitude, |
| | | lat: item.latitude |
| | | lat: item.latitude, |
| | | }, |
| | | frameColor: Cesium.Color.fromCssColorString('#7FFFD4') |
| | | frameColor: Cesium.Color.fromCssColorString('#7FFFD4'), |
| | | }) |
| | | } else { |
| | | manager.addCircle({ |
| | | data: { |
| | | lng: item.longitude, |
| | | lat: item.latitude |
| | | lat: item.latitude, |
| | | }, |
| | | materialColor: Cesium.Color.CORNFLOWERBLUE.withAlpha(0.3) |
| | | materialColor: item.color ? item.color : 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, |
| | | } |
| | | polyline = getDockPolyLine(item, viewer) |
| | | } |
| | | |
| | | const batteryImgUrl = batteryLevelSvg(item.capacity_percent) |
| | | const combinedImage = await nestSVG(item.nickname, batteryImgUrl, item.capacity_percent) |
| | | let pointObj = {} |
| | | try { |
| | | pointObj = await getLnglatAltitude(item.longitude, item.latitude, viewer) |
| | | } catch (e) {} |
| | | const position = Cesium.Cartesian3.fromDegrees( |
| | | +item.longitude, |
| | | +item.latitude, |
| | | useDockHeight ? +item.height : pointObj?.height || 0 |
| | | ) |
| | | if (showDockNameAndBattery) { |
| | | // 带电量 |
| | | dockSource.entities.add({ |
| | | position, |
| | | billboard: { |
| | | // new Cesium.ConstantProperty(pointData.status === 'OFFLINE' ? endingImg : unSelectedOnline), |
| | | image: combinedImage, |
| | | }, |
| | | }) |
| | | } |
| | | dockSource.entities.add({ |
| | | position: Cesium.Cartesian3.fromDegrees(+item.longitude, +item.latitude, !useDockHeight ? 0 : +item.height), |
| | | label: { |
| | | text: item.nickname, |
| | | font: 'bold 16px 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, -24), |
| | | }, |
| | | position, |
| | | polyline, |
| | | billboard: { |
| | | image: getDroneStatusImage(item.status), |
| | | width: 60, |
| | | height: 60, |
| | | eyeOffset: new Cesium.Cartesian3(0, 0, -5) |
| | | disableDepthTestDistance: Number.POSITIVE_INFINITY, |
| | | eyeOffset: new Cesium.Cartesian3(0, 0, -5), |
| | | }, |
| | | }) |
| | | }) |
| | |
| | | positions: positions, |
| | | width: 5, // 直接设置宽度 |
| | | clampToGround: true, |
| | | material: Cesium.Color.fromCssColorString(boundaryColor) |
| | | material: Cesium.Color.fromCssColorString(boundaryColor), |
| | | }, |
| | | polygon, |
| | | }) |
| | |
| | | positions: positions, |
| | | width: 1, // 直接设置宽度 |
| | | clampToGround: true, |
| | | material: Cesium.Color.fromCssColorString(boundaryColor) |
| | | material: Cesium.Color.fromCssColorString(boundaryColor), |
| | | }, |
| | | }) |
| | | }) |
| | |
| | | // 打开边界 |
| | | const openContour = async () => { |
| | | const areaCode = selectedAreaCode || userAreaCode |
| | | |
| | | viewer.scene.postRender.removeEventListener(determineScaling) |
| | | if (!areaCode) return |
| | | const hierarchy = areaCodeToArr(areaCode.slice(0, 6)) |
| | |
| | | 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) |
| | | } catch (e) {} |
| | | try { |
| | | viewer?.scene?.postRender?.addEventListener(determineScaling) |
| | | }catch (e) { |
| | | |
| | | } |
| | | } |
| | | |
| | | // 关闭边界 |
| | | function closeContour () { |
| | | function closeContour() { |
| | | removeAllEntities() |
| | | viewer.scene.postRender.removeEventListener(determineScaling) |
| | | } |
| | | |
| | | // 飞向边界 |
| | | async function flyToBoundary () { |
| | | 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), |
| | | destination: Cesium.Cartesian3.fromDegrees(initDockList[0].longitude, initDockList[0].latitude, 22000), |
| | | duration: 0, |
| | | }) |
| | | return |
| | |
| | | const dataSource = await Cesium.GeoJsonDataSource.load(gJson) |
| | | if (!dataSource) return |
| | | viewer.dataSources.add(dataSource) |
| | | |
| | | // 获取多边形边界所有点 |
| | | let positionList = [] |
| | | dataSource.entities.values.forEach(function (entity) { |
| | | if (entity.polygon) { |
| | | // 获取多边形的边界球 |
| | |
| | | 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, |
| | | }) |
| | | positionList = positionList.concat(curPolygonPosition) |
| | | } |
| | | }) |
| | | const newBox = boxTransformScale(positionList, 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 => { |
| | |
| | | * 设置要显示的机巢列表 |
| | | * @param dockSns - 要显示的机巢设备序列号数组。如果为 undefined,则显示所有已初始化的机巢;如果为空数组,则不显示任何机巢。 |
| | | */ |
| | | function setShowDock (dockSns) { |
| | | function setShowDock(dockSns) { |
| | | if (dockSns === undefined) { |
| | | showDockSnList = null |
| | | } else if (dockSns.length === 0) { |
| | |
| | | } else { |
| | | showDockSnList = dockSns |
| | | } |
| | | |
| | | removeDockCover() |
| | | |
| | | if (active === '县') droneSplashed() |
| | | if (!showDock) return |
| | | if (scrollShowDock ? active === '县' : true) { |
| | | droneSplashed() |
| | | } |
| | | } |
| | | |
| | | function setDockCoverColor(arr) { |
| | | initDockList.forEach(item => { |
| | | arr.forEach(dock => { |
| | | item.color = dock.device_sn === item.device_sn ? dock.color : null |
| | | }) |
| | | }) |
| | | if (scrollShowDock ? active === '县' : true) { |
| | | droneSplashed() |
| | | } |
| | | } |
| | | onBeforeUnmount(() => { |
| | | manager.destroy() |
| | | }) |
| | | |
| | | |
| | | return { |
| | | openContour, |
| | | closeContour, |
| | | flyToBoundary, |
| | | setShowDock |
| | | setShowDock, |
| | | setDockCoverColor, |
| | | } |
| | | } |