【类型】:feat
【原因】:要把 ADS-B 的飞行器数据实时显示在地图上 【过程】:监听后台推送的 ADS-B 数据,拿到经纬度后在地图上添加飞行器图标,并定时更新图标位置;支持多个飞行器同时显示,还加了去重和更新逻辑,避免重复创建标记 【影响】:
This commit is contained in:
parent
fc4a3d3029
commit
f55ec12808
@ -5,8 +5,11 @@
|
||||
@open="openCallback">
|
||||
<!-- 起飞设置弹出框 -->
|
||||
<template v-if="dialogItem == 'takeoffBox'">
|
||||
<el-slider class="w-100" v-model="takeoffValue" :show-tooltip="false" show-input :min="1" :max="100">
|
||||
</el-slider>
|
||||
<div class="flex mse mac">
|
||||
<el-slider class="w-100" v-model="takeoffValue" :show-tooltip="false" show-input :min="1" :max="100">
|
||||
</el-slider>
|
||||
<font class="m-l-5">米</font>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button size="medium" @click="dialogVisible = false">关闭</el-button>
|
||||
<el-button size="medium" type="primary"
|
||||
@ -214,7 +217,7 @@
|
||||
<div class="m-t-5">加锁</div>
|
||||
</el-button>
|
||||
<el-button size="medium" type="primary" class="flex1 butIcon"
|
||||
@click="dialogVisible = true; dialogTitle = '高度设置(米)'; dialogItem = 'takeoffBox'; speakText('设置起飞高度')">
|
||||
@click="dialogVisible = true; dialogTitle = '高度设置'; dialogItem = 'takeoffBox'; speakText('设置起飞高度')">
|
||||
<i class="iconfont icon-yangshi_icon_tongyong_departure f-s-24"></i>
|
||||
<div class="m-t-5">起飞</div>
|
||||
</el-button>
|
||||
|
@ -8,6 +8,7 @@
|
||||
import mapboxgl from 'mapbox-gl'
|
||||
import { MapboxStyleSwitcherControl, FollowControl, CustomFullscreenControl, NoFlyControl, RestrictflyControl, SaveToFileControl, PolygonToggleControl } from '@/utils/mapboxgl_plugs'
|
||||
import planeIcon from '@/assets/svg/plane.svg'
|
||||
import civilIcon from '@/assets/svg/civil.svg'
|
||||
|
||||
export default {
|
||||
name: 'MapBox',
|
||||
@ -15,6 +16,7 @@ export default {
|
||||
return {
|
||||
token: 'pk.eyJ1Ijoic3pkb3QiLCJhIjoiY2xhN2pkMWFoMHJ4eTN3cXp6bmlzaHZ0NCJ9.3hH-EAUr0wQCaLvIM2lBMQ',
|
||||
map: null,
|
||||
ADSBMarkers: [], // ADSB民航飞机对象
|
||||
planes: [], // 飞机对象
|
||||
lonLats: [], // 航线 所有航点
|
||||
wayLonLats: [], // 航线 不包括起点航点
|
||||
@ -252,8 +254,8 @@ export default {
|
||||
const dy = currentPoint.y - startPoint.y
|
||||
if (Math.sqrt(dx * dx + dy * dy) <= 5 && isLongPress) { // 没有发生拖拽地图
|
||||
const lonLat = {
|
||||
lon: this.map.unproject(currentPoint).lng,
|
||||
lat: this.map.unproject(currentPoint).lat
|
||||
lon: Number(this.map.unproject(currentPoint).lng.toFixed(7)),
|
||||
lat: Number(this.map.unproject(currentPoint).lat.toFixed(7))
|
||||
}
|
||||
this.$emit('longPress', lonLat) // 将经纬度信息传递到组件外部
|
||||
this.makeGuidedMarker(lonLat) // 创建标记
|
||||
@ -864,6 +866,54 @@ export default {
|
||||
})
|
||||
this.planes = []
|
||||
},
|
||||
/**
|
||||
* @description: 创建 ADSB 飞机图标并添加到地
|
||||
* @param {Array} adsbList - ADSB 飞机数据列表
|
||||
*/
|
||||
makeADSBPlanes (adsbList = []) {
|
||||
// 清除旧图标
|
||||
if (this.ADSBMarkers && this.ADSBMarkers.length) {
|
||||
this.ADSBMarkers.forEach(marker => marker.remove())
|
||||
}
|
||||
this.ADSBMarkers = []
|
||||
|
||||
adsbList.forEach((plane) => {
|
||||
// 创建图标容器
|
||||
const iconWrapper = document.createElement('div')
|
||||
iconWrapper.className = 'adsb-wrapper'
|
||||
iconWrapper.style.width = '36px'
|
||||
iconWrapper.style.height = '36px'
|
||||
|
||||
// 创建真正的飞机图标元素(子元素旋转!父级无法控制transiton)
|
||||
const planeIcon = document.createElement('div')
|
||||
planeIcon.className = 'adsb-icon'
|
||||
planeIcon.style.backgroundImage = `url(${civilIcon})`
|
||||
planeIcon.style.width = '100%'
|
||||
planeIcon.style.height = '100%'
|
||||
planeIcon.style.backgroundSize = 'contain'
|
||||
planeIcon.style.backgroundRepeat = 'no-repeat'
|
||||
planeIcon.style.transform = `rotate(${plane.heading}deg)`
|
||||
planeIcon.style.transition = 'transform 0.3s linear'
|
||||
|
||||
// 加入子元素
|
||||
iconWrapper.appendChild(planeIcon)
|
||||
|
||||
const popupContent = `
|
||||
<h3>ADSB 飞机</h3>
|
||||
<hr>
|
||||
<p>ICAO: ${plane.icao}</p>
|
||||
<p>高度: ${plane.alt} m</p>
|
||||
<p>航向: ${plane.heading}°</p>
|
||||
`
|
||||
|
||||
const marker = new mapboxgl.Marker(iconWrapper)
|
||||
.setLngLat([plane.lon, plane.lat])
|
||||
.setPopup(new mapboxgl.Popup({ offset: 25 }).setHTML(popupContent))
|
||||
.addTo(this.map)
|
||||
|
||||
this.ADSBMarkers.push(marker)
|
||||
})
|
||||
},
|
||||
/**
|
||||
* @description: 实时更新经纬度
|
||||
* @param {obj} lonLat lon经度 lat纬度
|
||||
@ -950,4 +1000,14 @@ export default {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.adsb-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.adsb-icon {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -25,7 +25,8 @@ const store = new Vuex.Store({
|
||||
paidOrderList: [], // 已付款 和已退款但是发货状态为 已发货 订单列表
|
||||
logList: [], // 操作日志列表
|
||||
messageList: [], // 管理员公告列表
|
||||
crosFrequency: null// 对频macadd
|
||||
crosFrequency: null, // 对频macadd
|
||||
ADSBList: [] // 存放当前活跃的 ADSB 飞机数据
|
||||
},
|
||||
mutations: {
|
||||
/**
|
||||
@ -130,6 +131,27 @@ const store = new Vuex.Store({
|
||||
*/
|
||||
setCrosFrequency (state, macAdd) {
|
||||
state.crosFrequency = macAdd
|
||||
},
|
||||
/**
|
||||
* @description: 设置当前活跃的 ADSB 飞机数据
|
||||
* @param {*} ADSB
|
||||
*/
|
||||
setADSBList (state, ADSB) {
|
||||
const index = state.ADSBList.findIndex(i => i.icao === ADSB.icao)
|
||||
if (index > -1) {
|
||||
// 更新已有
|
||||
state.ADSBList[index] = { ...ADSB, timestamp: Date.now() }
|
||||
} else {
|
||||
// 新增
|
||||
state.ADSBList.push({ ...ADSB, timestamp: Date.now() })
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @description: 清除过期的 ADSB 数据
|
||||
*/
|
||||
cleanExpiredADSBList (state) {
|
||||
const now = Date.now()
|
||||
state.ADSBList = state.ADSBList.filter(i => now - i.timestamp < 20000)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
@ -15,10 +15,17 @@
|
||||
<!-- 点飞设置弹出框 -->
|
||||
<template v-if="dialogItem == 'guidedBox'">
|
||||
<el-form label-position="left">
|
||||
<el-form-item label="纬度" label-width="80px">
|
||||
<el-input v-model="guidedLonLat.lat" placeholder="请输维度" label="纬度"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="经度" label-width="80px">
|
||||
<el-input v-model="guidedLonLat.lon" placeholder="请输经度" label="经度"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="高度设置" label-width="80px">
|
||||
<el-input-number v-model="guidedAlt" label="高度设置"></el-input-number>
|
||||
<font class="m-l-5">米</font>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button size="medium" @click="dialogVisible = false">关闭</el-button>
|
||||
@ -47,8 +54,6 @@ export default {
|
||||
guidedLonLat: {}, // 点飞 的经纬度
|
||||
guidedAlt: '', // 点飞的高度
|
||||
isReserveGuidedMaker: false, // 关闭指点飞行操作窗口时 标记是否删除图标
|
||||
showMapbox: true, // 数据更新的时候 用于刷新地图组件
|
||||
mapboxKey: 0, // 初始化一个变量用于控制map-box组件的重新渲染
|
||||
planesId: this.$route.params.id,
|
||||
localCount: 0 // 本地存储计数器
|
||||
}
|
||||
@ -80,6 +85,9 @@ export default {
|
||||
noflyData () {
|
||||
return this.$store.state.noflyData
|
||||
},
|
||||
ADSBList () {
|
||||
return this.$store.state.ADSBList
|
||||
},
|
||||
/**
|
||||
* @description: 侧边栏显隐
|
||||
*/
|
||||
@ -185,6 +193,15 @@ export default {
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
ADSBList: {
|
||||
handler (newList) {
|
||||
if (this.$refs.mapbox && typeof this.$refs.mapbox.makeADSBPlanes === 'function') {
|
||||
this.$refs.mapbox.makeADSBPlanes(newList)
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
},
|
||||
/**
|
||||
* @description: 侧边栏缩进有变化时 地图重新自适应
|
||||
*/
|
||||
|
@ -101,22 +101,31 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// 定时清理过期的ADSB数据
|
||||
setInterval(() => {
|
||||
if (this.$store.state.ADSBList.length > 0) {
|
||||
this.$store.commit('cleanExpiredADSBList')
|
||||
}
|
||||
}, 5000)
|
||||
},
|
||||
created () {
|
||||
/* init */
|
||||
this.$store.commit('app/setIsMobile')// 获取客户端平台类型
|
||||
this.$store.dispatch('fetchPlaneClassList')// 获取机型列表
|
||||
this.$store.dispatch('fetchAirList')// 获取飞机列表
|
||||
this.$store.dispatch('fetchShopList')// 获取商铺列表
|
||||
this.$store.dispatch('fetchAdminList')// 获取管理员列表
|
||||
this.$store.dispatch('fetchSiteList')// 获取站点列表
|
||||
this.$store.dispatch('fetchRouteList')// 获取航线列表
|
||||
this.$store.dispatch('fetchNoflyData', this.shop_id)// 获取禁飞区数据 注意这里要有shopid 总管理员再操作其他商铺飞机时 要刷新此缓存 需要对应商铺的禁飞区数据
|
||||
this.$store.dispatch('fetchCategoryList')// 获取分类列表(小程序)
|
||||
this.$store.dispatch('fetchSpuList')// 获取商品spu列表
|
||||
this.$store.dispatch('fetchSkuList')// 获取商品sku列表
|
||||
this.$store.dispatch('fetchPaidOrderList')// 获取订单列表
|
||||
this.$store.dispatch('fetchMessageList')// 获取管理员公告列表 并弹出公告框
|
||||
this.$store.commit('app/setIsMobile')
|
||||
Promise.all([
|
||||
this.$store.dispatch('fetchPlaneClassList'),
|
||||
this.$store.dispatch('fetchAirList'),
|
||||
this.$store.dispatch('fetchShopList'),
|
||||
this.$store.dispatch('fetchAdminList'),
|
||||
this.$store.dispatch('fetchSiteList'),
|
||||
this.$store.dispatch('fetchRouteList'),
|
||||
this.$store.dispatch('fetchCategoryList'),
|
||||
this.$store.dispatch('fetchSpuList'),
|
||||
this.$store.dispatch('fetchSkuList'),
|
||||
this.$store.dispatch('fetchPaidOrderList'),
|
||||
this.$store.dispatch('fetchMessageList')
|
||||
]).then(() => {
|
||||
return this.$store.dispatch('fetchNoflyData', this.shop_id)
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
/**
|
||||
@ -211,7 +220,8 @@ export default {
|
||||
// 订阅ADSB信息
|
||||
mqtt.doSubscribe('ADSB/+', (res) => {
|
||||
if (res.topic.indexOf('ADSB') > -1) {
|
||||
|
||||
const data = JSON.parse(res.msg)
|
||||
this.$store.commit('setADSBList', data)
|
||||
}
|
||||
})
|
||||
// 订阅对频
|
||||
|
Loading…
Reference in New Issue
Block a user