近期项目中涉及使用openlayer 绘制台风轨迹的效果,事后坐下分享与整理,效果就像这样:
功能描述
- 台风轨迹点实时绘制,根据不同点的类型绘制不同的轨迹点颜色
- 轨迹线绘制,涉及实时轨迹线段与预报轨迹线,根据台风类型绘制成不同颜色
- 当前正在发生的台风还需增加当前台风所风圈位置
- 台风轨迹点点击弹框显示轨迹点信息
openlayers(简称ol)这里不做介绍,刚开始写此类文章,直接上代码
创建一个地图容器
引入地图相关对象
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';
复制代码
创建地图对象
都是一些基本活
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()
复制代码
监听地图点击事件
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
// 监听地图点击事件
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')
// 台风点点击
// && 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">风速: ${Properties.MOVE_SPEED || '--'} km/h</div>
<div class="con">中心气压: ${Properties.PRESS || '--'}</div>
<div class="con">时间: ${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'
}
}
}
)
})
}
复制代码
开始绘制
准备台风数据和图层
台风数据我是用的JSON。这里就简单描述一下数据中只要用到的字段信息
[
{
CODE: "202122",//台风编号
DB7: null,//七级东北
DB10: null,//十级东北
DB12: null,//十二级东北
DN7: null,//七级东南
DN10: null,//十级东南
DN12: null,//十二级东南
LAT: 5.5,//维度
LON: 140.9,//经度
MOVE_DIR: null,//风向
MOVE_SPEED: null,//风向速度
OBJECTID: 27848,//id
PRESS: 998,//中心气压
SHIJIAN: null,
STRENGTH: "台风(热带风暴级)",//强度
TH: null,
TIME: "2021-12-13-14",//日期
WIND: 18,//最大风速
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)
复制代码
绘制台风名称
// 利用第一个点 创建名称Overlay层 显示台风名称
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)
复制代码
绘制台风轨迹点和轨迹线
//point 为数组对象中每一个点数据
// 点颜色 根据强度区分不同点的颜色
let pointColor = this.setPointFillColor(point.STRENGTH)
// 添加点
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为我提前定义到的各种类型的点样式
feature.setStyle(this.tfStyle[pointColor.index])
tflayer.getSource().addFeature(feature)
}
// 添加线
let startPoint = [point.LON, point.LAT] 开始点
let endPonit = [points[index - 1].LON, points[index - 1].LAT] 结束点
let coords = [startPoint, endPonit]
let lineDash 线段样式 实线或者虚线
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 为提前定义好的点和线的样式,这么做的目的是为了提高性能,减少oplayer中new 一堆重复性的样式堆栈,消耗内存
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函数判别点类型
setPointFillColor (type) {
let pointFillColor = '#eed139'
let index = 0
switch (type) {
case '台风(热带低压)':
case '热带低压':
pointFillColor = '#eed139'
index = 0
break
case '热带风暴':
case '热带风暴级':
case '台风(热带风暴)':
case '台风(热带风暴级)':
pointFillColor = '#0000ff'
index = 1
break
case '台风(强热带风暴级)':
case '强热带风暴级':
case '强热带风暴':
pointFillColor = '#0f8000'
index = 2
break
case '台风':
pointFillColor = '#fe9c45'
index = 3
break
case '强台风':
case '台风(强台风级)':
case '台风(强台风)':
pointFillColor = '#fe00fe'
index = 4
break
case '超强台风':
case '台风(超强台风级)':
case '台风(超强台风)':
pointFillColor = '#fe0000'
index = 5
break
}
return {pointFillColor, index}
}
复制代码
以上代码很完整,我加了注释,整体思路总结如下:
- 先获取台风数据
- 构造台风轨迹图层并添加到地图容器
- 循环构造点、线及名称要素对象 添加到台风图层数据源中
- 添加风圈动画
添加台风风圈动画
根据判断台风类型是否是实时台风还是历史台风进行添加台风风圈,在数据中获取到最后一个实时点位数据,利用Overlay添加一个动态图标,我是利用的gif图片
let key = LX + '-' + tfbh
const points = this._typhoonData[key].point
let fqindex = points.findIndex(item => !item.predict)
// 添加风圈
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
}
复制代码
让台风轨迹动起来
加载的时候利用定时器,一个点位一个点位的绘制,这样看起来就有动画效果啦
this._typhoonPlayFlag[key] = setInterval(() => {
去干些事请
}, 50)
复制代码
结尾
这里我只是大致的描述了我绘制台风轨迹的一些思路和大致方法,具体项目中,我把整个过场都封装成一个class 类,毕竟台风可能是有多条的,而且还需要做一些显示隐藏等的一些操作,我这里不做过多描述,仅做为一些思路分享,谢谢