vue?openlayers實(shí)現(xiàn)臺(tái)風(fēng)軌跡示例詳解

功能描述
- 臺(tái)風(fēng)軌跡點(diǎn)實(shí)時(shí)繪制,根據(jù)不同點(diǎn)的類型繪制不同的軌跡點(diǎn)顏色
- 軌跡線繪制,涉及實(shí)時(shí)軌跡線段與預(yù)報(bào)軌跡線,根據(jù)臺(tái)風(fēng)類型繪制成不同顏色
- 當(dāng)前正在發(fā)生的臺(tái)風(fēng)還需增加當(dāng)前臺(tái)風(fēng)所風(fēng)圈位置
- 臺(tái)風(fēng)軌跡點(diǎn)點(diǎn)擊彈框顯示軌跡點(diǎn)信息
openlayers(簡稱ol)這里不做介紹,剛開始寫此類文章,直接上代碼
創(chuàng)建一個(gè)地圖容器
引入地圖相關(guān)對象
import Map from 'ol/Map';
import View from 'ol/View';
import XYZ from 'ol/source/XYZ';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
創(chuàng)建地圖對象
都是一些基本活
const center = [-5639523.95, -3501274.52];
const map = new Map({
target: document.getElementById('map'),
view: new View({
center: center,
zoom: 10,
minZoom: 2,
maxZoom: 19,
}),
layers: [ ],
});
this.addEventMapClick()
監(jiān)聽地圖點(diǎn)擊事件
addEventMapClick () {
const nameDom = document.createElement('div')
nameDom.setAttribute('class', 'typhoon-popup')
const nameOverlay = new ol.Overlay({
element: nameDom,
position: [0, 0],
positioning: 'right-center',
stopEvent: false,
insertFirst: false,
autoPanAnimation: {
duration: 250
}
})
this.viewer.addOverlay(nameOverlay)
this._popup = nameOverlay
// 監(jiān)聽地圖點(diǎn)擊事件
this.viewer.on('singleclick', e => {
this._popup.getElement().parentElement.style.display = 'none'
this.viewer.forEachFeatureAtPixel(
e.pixel,
(result) => {
if (result) {
let Properties = result.get('properties')
let layerType = result.get('layerType')
// 臺(tái)風(fēng)點(diǎn)點(diǎn)擊
// && layerType === 'typhoonpoint'
if (layerType === 'typhoonLyer') {
let html = `<div class="typhoonLyer"><div class="con">名稱: ${Properties.CODE || ''} ${Properties.NAME_CN || ''} ${Properties.NAME_EN || ''}</div>
<div class="con">風(fēng)速: ${Properties.MOVE_SPEED || '--'} km/h</div>
<div class="con">中心氣壓: ${Properties.PRESS || '--'}</div>
<div class="con">時(shí)間: ${Properties.time || '--'}</div>
<div class="con">中心位置: ${Properties.LON}/${Properties.LAT}</div></div>`
this._popup.getElement().innerHTML = html
this._popup.setPosition([Properties.LON, Properties.LAT])
// this._popup.setOffset([25, 0])
this._popup.getElement().parentElement.style.display = 'block'
} else {
this._popup.getElement().parentElement.style.display = 'none'
}
}
}
)
})
}
開始繪制
準(zhǔn)備臺(tái)風(fēng)數(shù)據(jù)和圖層
臺(tái)風(fēng)數(shù)據(jù)我是用的JSON。這里就簡單描述一下數(shù)據(jù)中只要用到的字段信息
[
{
CODE: "202122",//臺(tái)風(fēng)編號(hào)
DB7: null,//七級東北
DB10: null,//十級東北
DB12: null,//十二級東北
DN7: null,//七級東南
DN10: null,//十級東南
DN12: null,//十二級東南
LAT: 5.5,//維度
LON: 140.9,//經(jīng)度
MOVE_DIR: null,//風(fēng)向
MOVE_SPEED: null,//風(fēng)向速度
OBJECTID: 27848,//id
PRESS: 998,//中心氣壓
SHIJIAN: null,
STRENGTH: "臺(tái)風(fēng)(熱帶風(fēng)暴級)",//強(qiáng)度
TH: null,
TIME: "2021-12-13-14",//日期
WIND: 18,//最大風(fēng)速
XB7: null,//七級西北
XB10: null,//十級西北
XB12: null,//十二級西北
XN7: null,//七級西南
XN10: null,//十級西南
XN12: null,//十二級西南
},
]
let tfsource = new ol.source.Vector({
crossOrigin: 'anonymous',
features: []
})
let tflayer = new ol.layer.Vector({
source: tfsource
})
map.addLayer(tflayer)
繪制臺(tái)風(fēng)名稱
// 利用第一個(gè)點(diǎn) 創(chuàng)建名稱Overlay層 顯示臺(tái)風(fēng)名稱
const nameDom = document.createElement('div')
nameDom.setAttribute('class', 'typhoon-name-panel')
nameDom.classList.add(lx)
nameDom.innerHTML = label
const position = [point.LON, point.LAT]
const nameOverlay = new ol.Overlay({
element: nameDom,
position: position,
wz: position,
positioning: 'center-left',
offset: [15, 0]
})
map.addOverlay(nameOverlay)
map.getView().setCenter(position)
繪制臺(tái)風(fēng)軌跡點(diǎn)和軌跡線
//point 為數(shù)組對象中每一個(gè)點(diǎn)數(shù)據(jù)
// 點(diǎn)顏色 根據(jù)強(qiáng)度區(qū)分不同點(diǎn)的顏色
let pointColor = this.setPointFillColor(point.STRENGTH)
// 添加點(diǎn)
if (point.LON && point.LAT) {
const feature = new ol.Feature({
geometry: new ol.geom.Point([point.LON, point.LAT]),
layerType: 'typhoonLyer',
properties: point
})
// this.tfStyle為我提前定義到的各種類型的點(diǎn)樣式
feature.setStyle(this.tfStyle[pointColor.index])
tflayer.getSource().addFeature(feature)
}
// 添加線
let startPoint = [point.LON, point.LAT] 開始點(diǎn)
let endPonit = [points[index - 1].LON, points[index - 1].LAT] 結(jié)束點(diǎn)
let coords = [startPoint, endPonit]
let lineDash 線段樣式 實(shí)線或者虛線
if (lx !== 'ss') {
lineDash = [0]
} else {
lineDash = (!point.predict && !points[index - 1].predict) ? [0] : [10]
}
// this.tfLinStyle 為提前定義好的線段樣式
let lineStyle = lineDash[0] === 0 ? this.tfLinStyle[pointColor.index] : this.tfLinStyle[6]
let feature = new ol.Feature({
geometry: new ol.geom.LineString(coords),
layerType: 'typhoonLyer',
properties: point
})
feature.setStyle(lineStyle)
tflayer.getSource().addFeature(feature)
代碼解析 文中提到的this.tfLinStyle 和 this.tfStyle 為提前定義好的點(diǎn)和線的樣式,這么做的目的是為了提高性能,減少oplayer中new 一堆重復(fù)性的樣式堆棧,消耗內(nèi)存
this.tfStyle = [
new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
fill: new ol.style.Fill({
color: '#eed139'
}),
stroke: new ol.style.Stroke({
color: 'rgba(0, 0, 0, 0.6)',
width: 1
})
})
}),
new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
fill: new ol.style.Fill({
color: '#0000ff'
}),
stroke: new ol.style.Stroke({
color: 'rgba(0, 0, 0, 0.6)',
width: 1
})
})
}),
new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
fill: new ol.style.Fill({
color: '#0f8000'
}),
stroke: new ol.style.Stroke({
color: 'rgba(0, 0, 0, 0.6)',
width: 1
})
})
}),
new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
fill: new ol.style.Fill({
color: '#fe9c45'
}),
stroke: new ol.style.Stroke({
color: 'rgba(0, 0, 0, 0.6)',
width: 1
})
})
}),
new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
fill: new ol.style.Fill({
color: '#fe00fe'
}),
stroke: new ol.style.Stroke({
color: 'rgba(0, 0, 0, 0.6)',
width: 1
})
})
}),
new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
fill: new ol.style.Fill({
color: '#fe0000'
}),
stroke: new ol.style.Stroke({
color: 'rgba(0, 0, 0, 0.6)',
width: 1
})
})
})
]
this.tfLinStyle = [
new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#eed139',
width: 2,
lineDash: [0]
})
}),
new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#0000ff',
width: 2,
lineDash: [0]
})
}),
new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#0f8000',
width: 2,
lineDash: [0]
})
}),
new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#fe9c45',
width: 2,
lineDash: [0]
})
}),
new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#fe00fe',
width: 2,
lineDash: [0]
})
}),
new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#fe0000',
width: 2,
lineDash: [0]
})
}),
new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#fe0000',
width: 2,
lineDash: [10]
})
})]
setPointFillColor函數(shù)判別點(diǎn)類型
setPointFillColor (type) {
let pointFillColor = '#eed139'
let index = 0
switch (type) {
case '臺(tái)風(fēng)(熱帶低壓)':
case '熱帶低壓':
pointFillColor = '#eed139'
index = 0
break
case '熱帶風(fēng)暴':
case '熱帶風(fēng)暴級':
case '臺(tái)風(fēng)(熱帶風(fēng)暴)':
case '臺(tái)風(fēng)(熱帶風(fēng)暴級)':
pointFillColor = '#0000ff'
index = 1
break
case '臺(tái)風(fēng)(強(qiáng)熱帶風(fēng)暴級)':
case '強(qiáng)熱帶風(fēng)暴級':
case '強(qiáng)熱帶風(fēng)暴':
pointFillColor = '#0f8000'
index = 2
break
case '臺(tái)風(fēng)':
pointFillColor = '#fe9c45'
index = 3
break
case '強(qiáng)臺(tái)風(fēng)':
case '臺(tái)風(fēng)(強(qiáng)臺(tái)風(fēng)級)':
case '臺(tái)風(fēng)(強(qiáng)臺(tái)風(fēng))':
pointFillColor = '#fe00fe'
index = 4
break
case '超強(qiáng)臺(tái)風(fēng)':
case '臺(tái)風(fēng)(超強(qiáng)臺(tái)風(fēng)級)':
case '臺(tái)風(fēng)(超強(qiáng)臺(tái)風(fēng))':
pointFillColor = '#fe0000'
index = 5
break
}
return {pointFillColor, index}
}
以上代碼很完整,我加了注釋,整體思路總結(jié)如下:
- 先獲取臺(tái)風(fēng)數(shù)據(jù)
- 構(gòu)造臺(tái)風(fēng)軌跡圖層并添加到地圖容器
- 循環(huán)構(gòu)造點(diǎn)、線及名稱要素對象 添加到臺(tái)風(fēng)圖層數(shù)據(jù)源中
- 添加風(fēng)圈動(dòng)畫
添加臺(tái)風(fēng)風(fēng)圈動(dòng)畫
根據(jù)判斷臺(tái)風(fēng)類型是否是實(shí)時(shí)臺(tái)風(fēng)還是歷史臺(tái)風(fēng)進(jìn)行添加臺(tái)風(fēng)風(fēng)圈,在數(shù)據(jù)中獲取到最后一個(gè)實(shí)時(shí)點(diǎn)位數(shù)據(jù),利用Overlay添加一個(gè)動(dòng)態(tài)圖標(biāo),我是利用的gif圖片
let key = LX + '-' + tfbh
const points = this._typhoonData[key].point
let fqindex = points.findIndex(item => !item.predict)
// 添加風(fēng)圈
if (fqindex) {
const nameDom = document.createElement('div')
nameDom.setAttribute('class', 'typhoon-area')
let nameOverlay = new ol.Overlay({
position: [points[fqindex].LON, points[fqindex].LAT],
positioning: 'center-bottom',
element: nameDom, // 綁定上面添加的元素
stopEvent: false,
offset: [-15, -15]// 圖片偏移量
})
this.viewer.addOverlay(nameOverlay)
this._tfenterCollection[key]['areaoverlay'] = nameOverlay
}
讓臺(tái)風(fēng)軌跡動(dòng)起來
加載的時(shí)候利用定時(shí)器,一個(gè)點(diǎn)位一個(gè)點(diǎn)位的繪制,這樣看起來就有動(dòng)畫效果啦
this._typhoonPlayFlag[key] = setInterval(() => {
去干些事請
}, 50)
結(jié)尾
這里我只是大致的描述了我繪制臺(tái)風(fēng)軌跡的一些思路和大致方法,具體項(xiàng)目中,我把整個(gè)過場都封裝成一個(gè)class 類,畢竟臺(tái)風(fēng)可能是有多條的,而且還需要做一些顯示隱藏等的一些操作,我這里不做過多描述,僅做為一些思路分享,更多關(guān)于vue openlayers臺(tái)風(fēng)軌跡的資料請關(guān)注腳本之家其它相關(guān)文章!
- 在Vue?3中使用OpenLayers加載GPX數(shù)據(jù)并顯示圖形效果
- vue+openlayers+nodejs+postgis實(shí)現(xiàn)軌跡運(yùn)動(dòng)效果
- Vue使用openlayers加載天地圖
- Vue+OpenLayers?創(chuàng)建地圖并顯示鼠標(biāo)所在經(jīng)緯度(完整代碼)
- vue利用openlayers實(shí)現(xiàn)動(dòng)態(tài)軌跡
- Vue結(jié)合openlayers按照經(jīng)緯度坐標(biāo)實(shí)現(xiàn)錨地標(biāo)記及繪制多邊形區(qū)域
- Vue openLayers實(shí)現(xiàn)圖層數(shù)據(jù)切換與加載流程詳解
- Vue利用openlayers實(shí)現(xiàn)點(diǎn)擊彈窗的方法詳解
- Vue使用openlayers實(shí)現(xiàn)繪制圓形和多邊形
- 在Vue 3中使用OpenLayers讀取WKB數(shù)據(jù)并顯示圖形效果
相關(guān)文章
vue3原始值響應(yīng)方案及響應(yīng)丟失問題解讀
這篇文章主要介紹了vue3原始值響應(yīng)方案及響應(yīng)丟失問題解讀,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
詳解axios 全攻略之基本介紹與使用(GET 與 POST)
本篇文章主要介紹了axios 全攻略之基本介紹與使用(GET 與 POST),詳細(xì)的介紹了axios的安裝和使用,還有 GET 與 POST方法,有興趣的可以了解一下2017-09-09
超詳細(xì)動(dòng)手搭建一個(gè)VuePress 站點(diǎn)及開啟PWA與自動(dòng)部署的方法
這篇文章主要介紹了超詳細(xì)動(dòng)手搭建一個(gè)VuePress 站點(diǎn)及開啟PWA與自動(dòng)部署的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-01-01
Vue3使用transition組件改變DOM屬性的方式小結(jié)
這篇文章主要為大家詳細(xì)介紹了Vue3中使用transition組件改變DOM屬性的常用方式,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01
vue實(shí)現(xiàn)動(dòng)態(tài)表單動(dòng)態(tài)渲染組件的方式(2)
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)動(dòng)態(tài)表單動(dòng)態(tài)渲染組件的方式第二篇,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04

