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 }
|
}
|