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