468 lines
14 KiB
Vue
468 lines
14 KiB
Vue
![]() |
<template>
|
||
|
<div id="map">
|
||
|
<slot name="content"></slot>
|
||
|
</div>
|
||
|
</template>
|
||
|
|
||
|
<script>
|
||
|
import mapboxgl from 'mapbox-gl'
|
||
|
import planeIcon from '@/assets/svg/plane.svg'
|
||
|
|
||
|
export default {
|
||
|
name: 'MapBox',
|
||
|
data () {
|
||
|
return {
|
||
|
map: null,
|
||
|
planes: [], // 飞机对象
|
||
|
lngLats: [], // 航线 所有航点
|
||
|
wayLngLats: [], // 航线 不包括起点航点
|
||
|
takeoffLngLats: [], // 航线 第一个航点 起点 最后一个航点
|
||
|
GoogleRasterStyle: {
|
||
|
name: 'Mapbox Streets',
|
||
|
sprite: this.$store.state.settings.host + '/map/sprite',
|
||
|
glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
|
||
|
version: 8,
|
||
|
sources: {
|
||
|
google: {
|
||
|
type: 'raster',
|
||
|
tileSize: 256,
|
||
|
tiles: ['https://sb.im/google-maps/vt?lyrs=s&x={x}&y={y}&z={z}']
|
||
|
}
|
||
|
},
|
||
|
layers: [{ id: 'GoogleRasterLayer', type: 'raster', source: 'google' }]
|
||
|
},
|
||
|
MapBoxglRasterStyle: 'mapbox://styles/mapbox/outdoors-v12',
|
||
|
MapBoxglSatellite: 'mapbox://styles/mapbox/satellite-streets-v12'
|
||
|
}
|
||
|
},
|
||
|
computed: {
|
||
|
defaultLnglat () {
|
||
|
return this.$store.getters['app/getDefaultLngLat']
|
||
|
},
|
||
|
defaultZoom () {
|
||
|
return this.$store.getters['app/getDefaultZoom']
|
||
|
}
|
||
|
},
|
||
|
mounted () {
|
||
|
this.init().then(() => { // 地图初始化之后
|
||
|
this.map.on('load', () => {
|
||
|
// 更新样式,添加自定义 sprite
|
||
|
|
||
|
// 星空背景 大气层
|
||
|
this.map.setFog({
|
||
|
range: [1.0, 8.0],
|
||
|
color: 'white',
|
||
|
'horizon-blend': 0.01
|
||
|
})
|
||
|
// 添加等高线图层
|
||
|
// this.map.addSource('contours', {
|
||
|
// type: 'vector',
|
||
|
// url: 'mapbox://mapbox.mapbox-terrain-v2'
|
||
|
// })
|
||
|
// this.map.addLayer({
|
||
|
// id: 'contours',
|
||
|
// type: 'line',
|
||
|
// source: 'contours',
|
||
|
// 'source-layer': 'contour',
|
||
|
// layout: {
|
||
|
// 'line-join': 'round',
|
||
|
// 'line-cap': 'round'
|
||
|
// },
|
||
|
// paint: {
|
||
|
// 'line-color': '#ff69b4', // 设置等高线颜色
|
||
|
// 'line-width': 1 // 设置等高线宽度
|
||
|
// }
|
||
|
// })
|
||
|
// 3纬度
|
||
|
// this.map.addSource('mapbox-dem', {
|
||
|
// type: 'raster-dem',
|
||
|
// url: 'mapbox://mapbox.mapbox-terrain-dem-v1',
|
||
|
// tileSize: 512,
|
||
|
// maxzoom: 14
|
||
|
// })
|
||
|
// this.map.setTerrain({ source: 'mapbox-dem', exaggeration: 1.5 })
|
||
|
})
|
||
|
})
|
||
|
},
|
||
|
methods: {
|
||
|
/**
|
||
|
* @description: 地图初始化
|
||
|
*/
|
||
|
async init () {
|
||
|
// token
|
||
|
mapboxgl.accessToken = 'pk.eyJ1Ijoic3pkb3QiLCJhIjoiY2xhNXpjd3IxMG9leTNubjg3dnFhZm84ZyJ9.ubUTyh6ZR87qyndE5adgvw'
|
||
|
// 实例化map
|
||
|
this.map = new mapboxgl.Map({
|
||
|
container: 'map',
|
||
|
style: this.GoogleRasterStyle,
|
||
|
center: this.defaultLnglat,
|
||
|
zoom: this.defaultZoom,
|
||
|
pitch: 45,
|
||
|
bearing: 0,
|
||
|
projection: 'globe'
|
||
|
})
|
||
|
// 地图控件
|
||
|
this.map.addControl(new mapboxgl.ScaleControl(), 'bottom-right')
|
||
|
this.map.addControl(new mapboxgl.NavigationControl(), 'bottom-right')
|
||
|
},
|
||
|
/**
|
||
|
* @description: 清除地图上的航线
|
||
|
*/
|
||
|
clearRoute () {
|
||
|
// 判断地图上是否已经存在 航线信息 存在先将其清除
|
||
|
if (this.map.getSource('way_route')) {
|
||
|
this.map.removeLayer('takeoff_layer')
|
||
|
this.map.removeSource('takeoff_route')
|
||
|
this.map.removeLayer('way_route_layer')
|
||
|
this.map.removeLayer('way_route_layer1')
|
||
|
this.map.removeSource('way_route')
|
||
|
this.map.removeLayer('home_point_layer')
|
||
|
this.map.removeSource('home_point')
|
||
|
this.map.removeLayer('takeoff_point_layer')
|
||
|
this.map.removeSource('takeoff_point')
|
||
|
for (let i = 2; i < this.lngLats.length - 1; i++) {
|
||
|
this.map.removeLayer('way_point_layer' + i)
|
||
|
this.map.removeSource('way_point' + i)
|
||
|
}
|
||
|
this.lngLats = []
|
||
|
this.wayLngLats = []
|
||
|
this.takeoffLngLats = []
|
||
|
}
|
||
|
},
|
||
|
/**
|
||
|
* @description: 绘画航线
|
||
|
* @param {*} routeObj 航点文件对象
|
||
|
*/
|
||
|
makeRoute (routeObj) {
|
||
|
// 初始化清空航线
|
||
|
this.clearRoute()
|
||
|
// 获取所有航点 及 非起点所有航点
|
||
|
routeObj.tasks.forEach((element, index) => {
|
||
|
const lngLat = [element.y, element.x, 100]
|
||
|
this.lngLats.push(lngLat)
|
||
|
if (index > 1 && index < routeObj.taskcount - 1) {
|
||
|
this.wayLngLats.push(lngLat)
|
||
|
}
|
||
|
})
|
||
|
// 跳转home点位置
|
||
|
this.goto(this.lngLats[0])
|
||
|
// 起飞 降落虚线
|
||
|
if (routeObj.tasks[this.lngLats.length - 1].command === 20) {
|
||
|
this.takeoffLngLats.push(this.lngLats[2])
|
||
|
this.takeoffLngLats.push(this.lngLats[1])
|
||
|
this.takeoffLngLats.push(this.lngLats[this.lngLats.length - 2])
|
||
|
} else if (routeObj.tasks[this.lngLats.length - 1].command === 21) {
|
||
|
this.takeoffLngLats.push(this.lngLats[2])
|
||
|
this.takeoffLngLats.push(this.lngLats[1])
|
||
|
}
|
||
|
|
||
|
// 起飞降落虚线
|
||
|
this.map.addSource('takeoff_route', {
|
||
|
type: 'geojson',
|
||
|
data: {
|
||
|
type: 'Feature',
|
||
|
properties: {},
|
||
|
geometry: {
|
||
|
type: 'LineString',
|
||
|
coordinates: this.takeoffLngLats
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
this.map.addLayer({
|
||
|
id: 'takeoff_layer',
|
||
|
type: 'line',
|
||
|
source: 'takeoff_route',
|
||
|
layout: {
|
||
|
'line-join': 'round', // 线连接处的样式
|
||
|
'line-cap': 'round'// 线结束处的样式
|
||
|
},
|
||
|
paint: {
|
||
|
'line-color': '#fff', // 线的颜色
|
||
|
'line-width': 2, // 线的宽度
|
||
|
'line-opacity': 1.0, // 线的透明度
|
||
|
'line-dasharray': [3, 2]// 虚线
|
||
|
}
|
||
|
})
|
||
|
// 航线 叠加描边线
|
||
|
this.map.addSource('way_route', {
|
||
|
type: 'geojson',
|
||
|
data: {
|
||
|
type: 'Feature',
|
||
|
properties: {},
|
||
|
geometry: {
|
||
|
type: 'LineString',
|
||
|
coordinates: this.wayLngLats
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
this.map.addLayer({
|
||
|
id: 'way_route_layer1',
|
||
|
type: 'line',
|
||
|
source: 'way_route',
|
||
|
layout: {
|
||
|
'line-join': 'round', // 线连接处的样式
|
||
|
'line-cap': 'round'// 线结束处的样式
|
||
|
},
|
||
|
paint: {
|
||
|
'line-color': '#fff', // 线的颜色
|
||
|
'line-width': 4, // 线的宽度
|
||
|
'line-opacity': 1.0// 线的透明度
|
||
|
}
|
||
|
})
|
||
|
this.map.addLayer({
|
||
|
id: 'way_route_layer',
|
||
|
type: 'line',
|
||
|
source: 'way_route',
|
||
|
layout: {
|
||
|
'line-join': 'round', // 线连接处的样式
|
||
|
'line-cap': 'round'// 线结束处的样式
|
||
|
},
|
||
|
paint: {
|
||
|
'line-color': '#f00', // 线的颜色
|
||
|
'line-width': 2, // 线的宽度
|
||
|
'line-opacity': 1.0// 线的透明度
|
||
|
}
|
||
|
})
|
||
|
// 航点 图标 PS:home点 起飞点 航点 送餐点等
|
||
|
this.lngLats.forEach((item, index) => {
|
||
|
// home点
|
||
|
if (index === 0) { // 第一点home点
|
||
|
this.map.addSource('home_point', {
|
||
|
type: 'geojson',
|
||
|
data: {
|
||
|
type: 'FeatureCollection',
|
||
|
features: [{
|
||
|
type: 'Feature',
|
||
|
geometry: {
|
||
|
type: 'Point',
|
||
|
coordinates: item
|
||
|
},
|
||
|
properties: {
|
||
|
'marker-symbol': 'homePoint'// home点图标
|
||
|
}
|
||
|
}]
|
||
|
}
|
||
|
})
|
||
|
this.map.addLayer({
|
||
|
id: 'home_point_layer',
|
||
|
type: 'symbol',
|
||
|
source: 'home_point',
|
||
|
layout: {
|
||
|
'icon-image': '{marker-symbol}',
|
||
|
'icon-size': 1.5, // 图标的大小
|
||
|
'icon-allow-overlap': true
|
||
|
}
|
||
|
})
|
||
|
} else if (index === 1) { // 起飞点
|
||
|
let takeoffPoint
|
||
|
// 起点图标设定 根据最后一个航点为RETURN_TO_LAUNCH 20 返回起点降落 或者 最后一个航点为LAND 21 指定点降落
|
||
|
if (routeObj.tasks[this.lngLats.length - 1].command === 20) {
|
||
|
takeoffPoint = 'takeoffLandPoint'
|
||
|
} else if (routeObj.tasks[this.lngLats.length - 1].command === 21) {
|
||
|
takeoffPoint = 'takeoffPoint'
|
||
|
}
|
||
|
this.map.addSource('takeoff_point', {
|
||
|
type: 'geojson',
|
||
|
data: {
|
||
|
type: 'FeatureCollection',
|
||
|
features: [{
|
||
|
type: 'Feature',
|
||
|
geometry: {
|
||
|
type: 'Point',
|
||
|
coordinates: item
|
||
|
},
|
||
|
properties: {
|
||
|
'marker-symbol': takeoffPoint// 起飞点图标
|
||
|
}
|
||
|
}]
|
||
|
}
|
||
|
})
|
||
|
this.map.addLayer({
|
||
|
id: 'takeoff_point_layer',
|
||
|
type: 'symbol',
|
||
|
source: 'takeoff_point',
|
||
|
layout: {
|
||
|
'icon-image': '{marker-symbol}',
|
||
|
'icon-size': 1.5, // 图标的大小
|
||
|
'icon-allow-overlap': true
|
||
|
}
|
||
|
})
|
||
|
} else { // waypoint点
|
||
|
if (index !== this.lngLats.length - 1) { // 最后一个点不显示 要不和最后一个点结合 要不和起点结合
|
||
|
let wayPoint = 'wayPoint'
|
||
|
if (index === this.lngLats.length - 2) { // 降落点 如果是LAND 21 和最后一个waypoint点结合
|
||
|
if (routeObj.tasks[this.lngLats.length - 1].command === 21) {
|
||
|
wayPoint = 'wayLandPoint'
|
||
|
}
|
||
|
}
|
||
|
if (routeObj.tasks[index].command === 94) { // command字段为94既然 投放放勾航点 放勾图标
|
||
|
wayPoint = 'hookPoint'
|
||
|
}
|
||
|
this.map.addSource('way_point' + index, {
|
||
|
type: 'geojson',
|
||
|
data: {
|
||
|
type: 'FeatureCollection',
|
||
|
features: [{
|
||
|
type: 'Feature',
|
||
|
geometry: {
|
||
|
type: 'Point',
|
||
|
coordinates: item
|
||
|
},
|
||
|
properties: {
|
||
|
'marker-symbol': wayPoint
|
||
|
}
|
||
|
}]
|
||
|
}
|
||
|
})
|
||
|
this.map.addLayer({
|
||
|
id: 'way_point_layer' + index,
|
||
|
type: 'symbol',
|
||
|
source: 'way_point' + index,
|
||
|
layout: {
|
||
|
'icon-image': '{marker-symbol}',
|
||
|
'icon-size': 1.5, // 图标的大小
|
||
|
'icon-allow-overlap': true
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
},
|
||
|
/**
|
||
|
* @description: 创建飞机轨迹 ps:原理删除之前的轨迹 重新绘制
|
||
|
* @param {arr} coordinatesArray 飞机经纬高度数组
|
||
|
*/
|
||
|
createPathWithArray (coordinatesArray) {
|
||
|
// 创建一个包含纬度、经度和高度信息的 GeoJSON LineString
|
||
|
const geojson = {
|
||
|
type: 'Feature',
|
||
|
properties: {},
|
||
|
geometry: {
|
||
|
type: 'LineString',
|
||
|
coordinates: coordinatesArray.map(coord => [coord[0], coord[1], coord[2]])
|
||
|
}
|
||
|
}
|
||
|
// 不是第一次 则改变路径图层里面得数据
|
||
|
if (this.map.getLayer('path')) {
|
||
|
this.map.getSource('path').setData(geojson)
|
||
|
} else { // 第一次在地图里添加路径图层
|
||
|
// 如果坐标数组不为空,创建新路径
|
||
|
if (coordinatesArray.length > 0) {
|
||
|
// 添加3D图层
|
||
|
this.map.addLayer({
|
||
|
id: 'path',
|
||
|
type: 'line',
|
||
|
source: {
|
||
|
type: 'geojson',
|
||
|
data: geojson
|
||
|
},
|
||
|
layout: {
|
||
|
'line-cap': 'round',
|
||
|
'line-join': 'round'
|
||
|
},
|
||
|
paint: {
|
||
|
'line-color': 'purple',
|
||
|
'line-width': 1
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
/**
|
||
|
* @description: 创建一个飞机
|
||
|
* @param {*} plane 飞机对象
|
||
|
*/
|
||
|
makePlane (plane, index = 0) {
|
||
|
const customIcon = document.createElement('div')
|
||
|
customIcon.className = 'custom-marker' // 添加自定义样式类名
|
||
|
customIcon.style.backgroundImage = `url(${planeIcon})` // 使用引入的 SVG 图标
|
||
|
customIcon.style.width = '64px' // 图标宽度
|
||
|
customIcon.style.height = '64px' // 图标高度
|
||
|
// 创建一个marker对象
|
||
|
this.planes[index] = new mapboxgl.Marker(customIcon)
|
||
|
.setLngLat([plane.lng, plane.lat])
|
||
|
.setPopup(new mapboxgl.Popup({ offset: 25 }).setHTML('<h3>' + plane.name + '</h3><hr><p>macID:' + plane.macadd + '</p>')) // 添加弹出窗口
|
||
|
.addTo(this.map)
|
||
|
},
|
||
|
/**
|
||
|
* @description: 移除页面上的所有飞机
|
||
|
*/
|
||
|
removePlanes () {
|
||
|
this.planes.forEach(plane => {
|
||
|
plane.remove()
|
||
|
})
|
||
|
this.planes = []
|
||
|
},
|
||
|
/**
|
||
|
* @description: 实时更新经纬度
|
||
|
* @param {obj} lnglat lng经度 lat纬度
|
||
|
* @param {*} index 飞机序号
|
||
|
* @param {*} pathArr 是否创建轨迹
|
||
|
*/
|
||
|
setPlaneLngLat (lnglat, index, pathArr, isflow) {
|
||
|
// 设置新的经纬度
|
||
|
const plane = this.planes[index]
|
||
|
if (plane != null) {
|
||
|
plane.setLngLat([lnglat.lng, lnglat.lat])
|
||
|
}
|
||
|
// 创建轨迹
|
||
|
this.createPathWithArray(pathArr)// 创建轨迹
|
||
|
// 镜头跟随飞机
|
||
|
if (isflow) {
|
||
|
this.map.flyTo({
|
||
|
center: lnglat,
|
||
|
speed: 2,
|
||
|
curve: 1,
|
||
|
easing (t) {
|
||
|
return t
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
},
|
||
|
/**
|
||
|
* @description: 镜头跳转
|
||
|
* @param {obj} lnglat {lng:lng,lat:lat} 经纬度
|
||
|
*/
|
||
|
goto (lnglat) {
|
||
|
this.map.flyTo({
|
||
|
center: lnglat,
|
||
|
speed: 2,
|
||
|
curve: 1,
|
||
|
easing (t) {
|
||
|
return t
|
||
|
}
|
||
|
})
|
||
|
},
|
||
|
/**
|
||
|
* @description: 屏幕横移
|
||
|
* @param {*} val 正数向左移动 负数向右移动
|
||
|
*/
|
||
|
mapXOffset (val) {
|
||
|
this.map.panBy([val, 0], {
|
||
|
duration: 333 // 过渡持续时间,以毫秒为单位
|
||
|
})
|
||
|
}
|
||
|
},
|
||
|
beforeDestroy () {
|
||
|
if (this.map) {
|
||
|
this.$store.commit('app/setDefaultLngLat', this.map.getCenter())// 记录当前经纬度 缺省
|
||
|
this.$store.commit('app/setDefaultZoom', this.map.getZoom())// 记录当前经纬度 缺省
|
||
|
this.map.remove()// 清除地图资源
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
</script>
|
||
|
|
||
|
<style lang="scss" scoped>
|
||
|
#map {
|
||
|
width: 100%;
|
||
|
height: 100%;
|
||
|
overflow: hidden;
|
||
|
}
|
||
|
|
||
|
.custom-marker {
|
||
|
background-size: cover;
|
||
|
cursor: pointer;
|
||
|
}
|
||
|
</style>
|