import * as Cesium from 'cesium'
|
import aggregationImg from '@/assets/images/home/useUavHome/aggregation.png'
|
import uavImg from '@/assets/images/home/useUavHome/uavImg.png'
|
import MapPopUpBox from '@/views/Home/useUavHome/MapPopUpBox.vue'
|
import { render } from 'vue'
|
import { useStore } from 'vuex'
|
import { getCenterPoint } from '@/utils/cesium/mapUtil'
|
import cesiumOperation from '@/utils/cesium-tsa'
|
import { HeadingPitchRange } from 'cesium'
|
import { getDeviceRegion, getDeviceRegionCount } from '@/api/home/aggregation'
|
|
/**
|
* 机巢聚合功能
|
*/
|
export const useUavHome = () => {
|
const { flyTo } = cesiumOperation()
|
|
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
|
let handler = null
|
let positionC3 = null
|
let currentEntity = null
|
const listenerHeight = () => {
|
determineScaling()
|
viewer.camera.moveEnd.addEventListener(determineScaling)
|
}
|
|
// 确定缩放比例
|
const determineScaling = index => {
|
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()
|
removeLabel()
|
item.gJson ? aggregation(item) : splashed(item)
|
renderOutline(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]
|
}
|
|
const store = useStore()
|
const selectedAreaCode = computed(() => store.state.user.selectedAreaCode)
|
|
function getDeviceCount(areaCode) {
|
return getDeviceRegionCount({ areaCode }).then(res => {
|
return res?.data?.data || []
|
})
|
}
|
|
function getDeviceList(areaCode) {
|
return getDeviceRegion({ areaCode }).then(res => {
|
return res?.data?.data || []
|
})
|
}
|
const findFun = (featItem, numItem) =>
|
Number(featItem.region_code.slice(0, 6)) === numItem.properties.adcode
|
const { VITE_APP_BASE } = import.meta.env
|
const defaultDir = `${VITE_APP_BASE}public/geoJson/100000/`
|
|
const getFiler = async url => {
|
const gJson = await import(/* @vite-ignore */ url)
|
return JSON.parse(JSON.stringify(gJson))
|
}
|
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 => ({
|
...item,
|
data: dataList.find(item1 => findFun(item1, item)),
|
})),
|
}
|
}
|
|
watch(
|
selectedAreaCode,
|
async (newValue, oldValue) => {
|
if (newValue) {
|
const list = await getDeviceCount(newValue)
|
const splashedList = await getDeviceList(newValue)
|
console.log(list)
|
console.log(splashedList)
|
const hierarchy = convertToHierarchy(newValue.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 => ({
|
...item,
|
data: list.flatMap(item => item.childrens || []).find(item1 => findFun(item1, item)),
|
})),
|
}
|
scalingJudgment[2].gJson = injectData(gJson2, list)
|
const { lng, lat } = getCenterPoint(gJson2.features.map(item => item.properties.center))
|
flyTo({ longitude: lng, latitude: lat }, 0, scalingJudgment[2].height)
|
}
|
// 市
|
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)
|
const center = getCenterPoint(gJson1.features.map(item => item.properties.center))
|
flyTo({ longitude: center.lng, latitude: center.lat }, 0, scalingJudgment[1].height)
|
}
|
// 区县
|
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)
|
const center = outlineGJson.features[0].properties.center
|
flyTo({ longitude: center[0], latitude: center[1] }, 0, scalingJudgment[0].height)
|
}
|
// 轮廓
|
const outlineGJson = await getOutLine(jsonPathPre, hierarchy)
|
scalingJudgment.forEach(item => item.show && (item.outline = outlineGJson))
|
}
|
},
|
{ immediate: true, deep: true }
|
)
|
|
//散点机巢
|
function splashed(row) {
|
row.splashedList.forEach(item => {
|
viewer.entities.add({
|
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: {
|
image: new Cesium.ConstantProperty(uavImg),
|
width: 24,
|
height: 24,
|
},
|
properties: {
|
customData: {
|
data: item,
|
},
|
},
|
})
|
})
|
}
|
|
// 渲染轮廓
|
const renderOutline = item => {
|
item.outline &&
|
Cesium.GeoJsonDataSource.load(item.outline).then(dataSource => {
|
viewer.dataSources.add(dataSource)
|
const entities = dataSource.entities.values
|
entities.forEach(entity => {
|
// 隐藏多边形填充
|
entity.polygon.material = Cesium.Color.TRANSPARENT
|
entity.polygon.outline = false // 关闭原生轮廓
|
// 创建独立折线作为轮廓
|
const positions = entity.polygon.hierarchy.getValue().positions
|
viewer.entities.add({
|
polyline: {
|
positions: positions,
|
width: 5, // 直接设置宽度
|
material: new Cesium.PolylineGlowMaterialProperty({
|
glowPower: 0.5,
|
color: Cesium.Color.AQUA,
|
}),
|
clampToGround: true, // 贴地显示
|
},
|
})
|
})
|
})
|
}
|
|
// 聚合机巢
|
const aggregation = (item, flyTo) => {
|
if (!item.gJson) return
|
const featuresList = item.gJson.features.map(item => ({
|
name: item.properties.name,
|
position: item.properties.center,
|
data: item.data,
|
id: item.region_code,
|
}))
|
// 遍历特征并添加实体
|
featuresList.forEach(feature => {
|
if (!feature.position) return
|
const position = Cesium.Cartesian3.fromDegrees(feature.position[0], feature.position[1])
|
viewer.entities.add({
|
position: position,
|
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: feature.id,
|
position: position,
|
label: {
|
text: feature.data.total_device_count,
|
font: '12pt Source Han Sans CN',
|
fillColor: 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,
|
},
|
},
|
})
|
})
|
|
// 加载新的 GeoJSON 数据
|
Cesium.GeoJsonDataSource.load(item.gJson).then(dataSource => {
|
viewer.dataSources.add(dataSource)
|
item.dataSource = dataSource // 保存数据源以便后续删除
|
// 获取数据源中的实体
|
const entities = dataSource.entities.values
|
entities.forEach(entity => {
|
entity.polygon.material = new Cesium.ColorMaterialProperty(
|
Cesium.Color.YELLOW.withAlpha(0) // 透明填充
|
)
|
entity.polygon.outline = new Cesium.ConstantProperty(true) // 显示边框
|
entity.polygon.outlineColor = new Cesium.ConstantProperty(Cesium.Color.AQUAMARINE)
|
})
|
flyTo &&
|
viewer.flyTo(dataSource, {
|
offset: new Cesium.HeadingPitchRange(
|
0, // heading: 0 (朝向不变)
|
Cesium.Math.toRadians(-90), // pitch: -90° (垂直向下)
|
0 // range: 0 (默认距离)
|
),
|
duration: 0.5,
|
})
|
})
|
}
|
|
// 获取弹框box
|
const getLabelDom = data => {
|
const vNode = h(MapPopUpBox, { data, removeLabel })
|
const tooltipContainer = document.createElement('div')
|
tooltipContainer.id = 'mapPopUpBox'
|
tooltipContainer.style.position = 'absolute'
|
tooltipContainer.style.transform = 'translateY(-50%)'
|
tooltipContainer.style.pointerEvents = 'none'
|
document.querySelector('.page-index').append(tooltipContainer)
|
render(vNode, tooltipContainer)
|
return tooltipContainer
|
}
|
|
// 弹框位置刷新
|
const labelBoxRender = () => {
|
let dom = document.querySelector('#mapPopUpBox')
|
if (!dom) {
|
dom = getLabelDom(currentEntity.properties.customData._value.data)
|
}
|
const screenPosition = viewer.scene.cartesianToCanvasCoordinates(positionC3)
|
if (screenPosition) {
|
dom.style.left = `${screenPosition.x}px`
|
dom.style.top = `${screenPosition.y}px`
|
dom.style.display = 'block'
|
}
|
}
|
|
// 左键单机事件
|
const singleMachineEvent = async click => {
|
const pickedObject = viewer.scene.pick(click.position)
|
if (Cesium.defined(pickedObject) && pickedObject.id) {
|
const entity = pickedObject.id
|
currentEntity = entity
|
positionC3 = entity?.position?._value
|
if (!positionC3) return
|
removeLabel()
|
const customData = entity.properties.customData._value.data
|
console.log(customData)
|
if (customData.device_sn) {
|
store.commit('setSingleUavHome', { id: '123' })
|
return
|
}
|
viewer.scene.postRender.addEventListener(labelBoxRender)
|
}
|
}
|
|
const removeDom = () => {
|
const dom = document.querySelector('#mapPopUpBox')
|
if (dom && dom.parentNode) {
|
dom.parentNode.removeChild(dom)
|
}
|
}
|
|
// 移除 点 和 gjson 实体
|
const removeEntities = () => {
|
viewer.dataSources?.removeAll(true)
|
viewer.entities?.removeAll()
|
}
|
// 移除弹框标签
|
const removeLabel = () => {
|
viewer.scene.postRender.removeEventListener(labelBoxRender)
|
removeDom()
|
}
|
|
// 移除所有监听事件,变量置空
|
const removeAll = () => {
|
removeEntities()
|
removeLabel()
|
viewer.camera.moveEnd.removeEventListener(determineScaling)
|
handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
handler?.destroy()
|
viewer = null
|
active = null
|
handler = null
|
positionC3 = null
|
currentEntity = null
|
}
|
const init = () => {
|
viewer = window.$viewer
|
handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
|
handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
listenerHeight()
|
}
|
// onMounted(() => {
|
// nextTick(() => {
|
// viewer = window.$viewer;
|
// handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
|
// handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
// listenerHeight();
|
// });
|
// });
|
|
return { init, removeAll }
|
}
|