概述
前面的文章中写了基于openlayers4的台风路径播放,最近用到mapbox GL,也要实现相似的功能,网上找了好久都没有找到,于是就放弃了“拿来主义”的想法,只能自己动手了。经过一下午的努力,终于有了一个雏形,在此分享出来,希望对你有用!
效果
实现
1、数据获取
测试数据是从温州台风网,抓取了201929号台风数据作为测试数据。
2、添加台风编号和名称到地图
代码语言:javascript复制addTyphoonLabel(data) {
const ele = document.createElement('div');
ele.setAttribute('class', 'typhoon-label');
ele.innerHTML = data.tfbh data.name;
var r = data.points[0];
const option = {
element: ele,
anchor: 'left',
offset: [10, 0]
}
var marker = new mapboxgl.Marker(option).setLngLat([r.longitude, r.latitude]).addTo(map);
that.typhoonData[data.tfbh]['label'] = marker;
}
对应的样式为:
代码语言:javascript复制$white65: rgba(255, 255, 255, 0.65);
.typhoon-label {
background-color: $white65;
border-radius: 5px;
padding: 2px 5px;
font-size: 12px;
color: black;
&:after {
top: 6px;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
&:after {
border-right-color: $white65;
border-width: 5px;
left: -10px;
}
}
3、添加风圈
说明:添加的顺序分别为风圈、路径和实况点,目的是为了让三者按照顺序叠加展示。
代码语言:javascript复制addTyphoonCircle(data) {
var points = data.points;
var geojson = {
'type': 'FeatureCollection',
'features': []
};
for (var i = 0; i < points.length; i ) {
var p = points[i];
var center = [p.longitude, p.latitude];
// 7级风圈
if(p.radius7 > 0) {
var coords = that.getCircleCoords(center, p.radius7_quad);
geojson.features.push({
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: coords
},
properties: {
index: i,
radius: '7'
}
});
}
// 10级风圈
if(p.radius10 > 0) {
var coords = that.getCircleCoords(center, p.radius10_quad);
geojson.features.push({
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: coords
},
properties: {
index: i,
radius: '10'
}
});
}
// 12级风圈
if(p.radius12 > 0) {
var coords = that.getCircleCoords(center, p.radius12_quad);
geojson.features.push({
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: coords
},
properties: {
index: i,
radius: '12'
}
});
}
}
map.addSource('circle-source-' data.tfbh , {
type: 'geojson',
data: geojson
});
map.addLayer({
id: 'circle-layer-' data.tfbh,
type: 'fill',
source: 'circle-source-' data.tfbh,
paint: {
'fill-color': [
'match',
['get', 'radius'],
'7',
'#00bab2',
'10',
'#ffff00',
'#da7341'
],
'fill-opacity': 0.2,
'fill-outline-color': [
'match',
['get', 'radius'],
'7',
'#00bab2',
'10',
'#ffff00',
'#da7341'
]
}
});
}
里面用到了一个生成风圈的方法,方法如下:
代码语言:javascript复制getCircleCoords(center, radiusData) {
center = proj4(proj4('EPSG:4326'), proj4('EPSG:3857'), center);
let _coords = [];
let _angInterval = 6;
let _pointNums = 360 / (_angInterval * 4);
let quadrant = {
// 逆时针算角度
'0': 'ne',
'1': 'nw',
'2': 'sw',
'3': 'se'
};
for (let i = 0; i < 4; i ) {
let _r = parseFloat(radiusData[quadrant[i]]) * 1000; // 单位是km
if (!_r) _r = 0;
for (let j = i * _pointNums; j <= (i 1) * _pointNums; j ) {
let _ang = _angInterval * j;
let x = center[0] _r * Math.cos((_ang * Math.PI) / 180);
let y = center[1] _r * Math.sin((_ang * Math.PI) / 180);
var coord = proj4(proj4('EPSG:3857'), proj4('EPSG:4326'), [x, y]);
_coords.push(coord);
}
}
return [_coords];
}
说明:由于没有找到坐标转换的方法,所以就引用的proj4js做了投影的转换。
4、添加路径
路径的添加包括实况和预报路径的添加,由于line-dasharray
自身的BUG,在实现的时候就添加了两层,实现代码如下:
addTyphoonPath(data) {
var points = data.points;
var geojsonLive = {
'type': 'FeatureCollection',
'features': []
};
var geojsonForc = {
'type': 'FeatureCollection',
'features': []
};
var pts =[[points[0].longitude, points[0].latitude]];
for (var i = 1; i < points.length; i ) {
var p = points[i];
pts.push([p.longitude, p.latitude]);
geojsonLive.features.push({
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: pts.concat([])
},
properties: {
index: i,
type: 'live'
}
});
// 预报路径
var _p = points[i - 1];
var _pts = [[_p.longitude, _p.latitude]];
var _points = _p.forecast[0]['points'];
for(var j = 0;j < _points.length; j ) {
var _fp = _points[j];
_pts.push([_fp.longitude, _fp.latitude]);
}
geojsonForc.features.push({
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: _pts
},
properties: {
index: i - 1,
type: 'forc'
}
});
}
// 实况线
map.addSource('path-source-live-' data.tfbh , {
type: 'geojson',
data: geojsonLive
});
map.addLayer({
id: 'path-layer-live-' data.tfbh,
type: 'line',
source: 'path-source-live-' data.tfbh,
paint: {
'line-color': '#ffffff',
'line-width': 3
}
});
// 预报线
map.addSource('path-source-forc-' data.tfbh , {
type: 'geojson',
data: geojsonForc
});
map.addLayer({
id: 'path-layer-forc-' data.tfbh,
type: 'line',
source: 'path-source-forc-' data.tfbh,
paint: {
'line-color': '#ec5d72',
'line-width': 1,
'line-dasharray': [5, 3]
}
});
}
5、添加点
点包括:实况点和预报点。由于涉及到后面播放的控制,此处将两者分别添加了。
代码语言:javascript复制addTyphoonPoints(data) {
var points = data.points;
var geojsonLive = {
'type': 'FeatureCollection',
'features': []
};
var geojsonForc = {
'type': 'FeatureCollection',
'features': []
};
for (var i = 0; i < points.length; i ) {
var p = points[i];
p.index = i;
geojsonLive.features.push({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [p.longitude, p.latitude]
},
properties: p
});
// 预报点
var forcPoints = p.forecast[0]['points'];
for(var j = 0; j < forcPoints.length; j ) {
var _p = forcPoints[j];
_p.index = i;
geojsonForc.features.push({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [_p.longitude, _p.latitude]
},
properties: _p
});
}
}
var paint = {
'circle-color': [
'step',
['get', 'speed'],
'rgba(153, 255, 153, .9)',
17.2,
'rgba(102, 204, 255, .9)',
24.5,
'rgba(255, 255, 102, .9)',
32.7,
'rgba(253, 139, 0, .9)',
41.5,
'rgba(255, 51, 0, .9)',
50.1,
'rgba(255, 0, 255, .9)'
],
'circle-radius': 6,
'circle-stroke-width': 0
}
// 实况点
map.addSource('points-source-live-' data.tfbh , {
type: 'geojson',
data: geojsonLive
});
map.addLayer({
id: 'points-layer-live-' data.tfbh,
type: 'circle',
source: 'points-source-live-' data.tfbh,
paint: paint
});
// 预报点
map.addSource('points-source-forc-' data.tfbh , {
type: 'geojson',
data: geojsonForc
});
map.addLayer({
id: 'points-layer-forc-' data.tfbh,
type: 'circle',
source: 'points-source-forc-' data.tfbh,
paint: paint
});
}
6、播放与播放控制
代码语言:javascript复制playTyphoon() {
var tfbh = that.typhoonPlay;
var index = that.typhoonData[tfbh]['playIndex'];
// 台风风圈
map.setPaintProperty(
'circle-layer-' tfbh,
'fill-opacity',
[
'match',
['get', 'index'],
index,
0.2,
0
]
);
// 实况线
map.setPaintProperty(
'path-layer-live-' tfbh,
'line-opacity',
[
'match',
['get', 'index'],
index,
0.65,
0
]
);
// 预报线
map.setPaintProperty(
'path-layer-forc-' tfbh,
'line-opacity',
[
'match',
['get', 'index'],
index,
1,
0
]
);
// 实况点
map.setPaintProperty(
'points-layer-live-' tfbh,
'circle-opacity',
[
'step',
['get', 'index'],
1,
index 0.1,
0
]
);
// 预报点
map.setPaintProperty(
'points-layer-forc-' tfbh,
'circle-opacity',
[
'match',
['get', 'index'],
index,
1,
0
]
);
},
play() {
var tfbh = that.typhoonPlay;
that.typhoonData[tfbh]['playFlag'] = setInterval(function() {
that.typhoonData[tfbh]['playIndex'] ;
var len = that.typhoonData[tfbh]['data']['points'].length;
if(that.typhoonData[tfbh]['playIndex'] === len) {
that.stop();
} else {
that.playTyphoon();
}
}, 1000)
},
stop() {
var tfbh = that.typhoonPlay;
window.clearInterval(that.typhoonData[tfbh]['playFlag']);
}
技术博客 CSDN:http://blog.csdn.NET/gisshixisheng