目录
1.1 数据准备,这里可以从中央气象台台风网抓取数据http://typhoon.nmc.cn/web.html
1、台风路径信息准备与添加
1.1 数据准备,这里可以从中央气象台台风网抓取数据http://typhoon.nmc.cn/web.html
1.2 添加地图信息
添加台风预报路径
function addTyLine(map, typData) {
// 最后的点位添加编号名称
typData.points[typData.points.length - 1].forecast.forEach((element, index) => {
let coordList = [];
element.forecastpoints.map(item => {
coordList.push([Number(item.lng), Number(item.lat)]);
});
if (!map.getSource(`${typData.tfid}_typhon_route_line_${element.tm}`)) {
map.addSource(`${typData.tfid}_typhon_route_line_${element.tm}`, {
type: 'geojson',
data: {
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: coordList,
},
},
});
}
if (!map.getLayer(`typhon_route_${typData.tfid}_${index}`)) {
map.addLayer({
id: `typhon_route_${typData.tfid}_${index}`,
type: 'line',
source: `${typData.tfid}_typhon_route_line_${element.tm}`,
layout: {
'line-join': 'round',
'line-cap': 'round',
},
paint: {
'line-color': '设定颜色',
'line-width': 2,
'line-dasharray': [0.005, 3],
},
});
}
});
}
根据数据的格式来整理成geojson添加
预报站点信息、已测路径信息、已测站点信息同理添加
2、添加台风风圈
关于台风风圈,已知的数据有台风中心点以及台风半径,而台风半径又分为西北、西南、东南、东北四个方位角,这里在网上搜集了两种方法:
1、使用坐标转换计算
已知的中心点坐标为wgs84投影类型,也就是经纬度,需将其转换为投影坐标系来计算距离,再根据距离将其转换为经纬度来添加
let coords_7 = getCircleCoords(coordList[time], radiusData);
map.addSource(`currentFill_${typData.tfid}`, {
type: 'geojson',
data: {
type: 'Feature',
properties: {
name: typData.name,
radius: '7',
},
geometry: {
type: 'Polygon',
coordinates: coords_7,
},
},
});
map.addLayer({
id: `currentFill_${typData.tfid}`,
source: `currentFill_${typData.tfid}`,
type: 'fill',
paint: {
'fill-color': ['match', ['get', 'radius'], '7', '#ffff00', '10', '#ffa911', '#ff5341'],
'fill-opacity': 0.2,
'fill-outline-color': ['match', ['get', 'radius'], '7', '#ffff00', '10', '#ffa911', '#ff5341'],
},
});
getCircleCoords(center, radiusData) {
//radiusData = {
// ne: 100,
//nw: 120,
//sw: 120,
//se: 10,
//};
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];
}
传入的radiusData 需包含四个半径,格式如上;
proj4.js是坐标系转换通用的库,需单独引入;
以上方法可行,但是算出来的台风风圈经过验证存在一点误差,所以这里介绍另外一种使用turf.js计算的方法,算出来的风圈范围跟官网一致;
2、使用turf.js计算台风风圈范围
drawTyphoonCircle(p, radiusData) {
var center = turf.point(p);
let quadrant = {
// 逆时针算角度
0: 'ne',
1: 'sw',
2: 'se',
3: 'nw',
};
if (radiusData[quadrant[0]]) {
var options = { number: 2048 };
var arc_ne = turf.lineArc(center, radiusData[quadrant[0]], 0, 89.9, options);
var arc_se = turf.lineArc(center, radiusData[quadrant[3]], 90, 179.9, options);
var arc_sw = turf.lineArc(center, radiusData[quadrant[2]], 180, 269.9, options);
var arc_nw = turf.lineArc(center, radiusData[quadrant[1]], 270, 360.1, options);
var arcs = [];
arcs.push(arc_ne, arc_se, arc_sw, arc_nw);
var typhoonCircleCoords = [];
for (var i = 0; i < arcs.length; i++) {
var rawCoords1 = arcs[i].geometry.coordinates;
for (var j = 0; j < rawCoords1.length; j++) {
typhoonCircleCoords.push(rawCoords1[j]);
}
}
var lineAll = turf.lineString(typhoonCircleCoords);
var typhoonCirclePolygon = turf.lineToPolygon(lineAll);
console.log('typhoonCirclePolygon', typhoonCirclePolygon);
return typhoonCirclePolygon;
} else {
return;
}
}
这里返回直接是geojson,
3、台风路径的播放效果
设置定时器,对geojson数据源定时修改,用到的核心方法是 map.getSource(`currentPoint`).setData(geojson);记得动画播放结束后要clearInterval()哦
let time = 0
updateSource[typData.tfid] = setInterval(async () => {
time++;
if (!typData.points[time]) {
clearInterval(updateSource[typData.tfid]);
updateSource[typData.tfid] = null;
return;
}
radiusSeven = typData.points[time]?.radius7.split('|');
radiusData = {
ne: radiusSeven[0],
nw: radiusSeven[1],
sw: radiusSeven[2],
se: radiusSeven[3],
};
// let coords_7 = getCircleCoords(coordList[time], radiusData);
let coords_7 = drawTyphoonCircle(coordList[time], radiusData);
const geojson_7 = await {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: coords_7?.geometry?.coordinates,
},
properties: {
tfid: typData.tfid,
radius: '7',
},
},
],
};
radiusTen = typData.points[time].radius10.split('|');
radiusTenData = {
ne: radiusTen[0],
nw: radiusTen[1],
sw: radiusTen[2],
se: radiusTen[3],
};
let coords_10 = drawTyphoonCircle(coordList[time], radiusTenData);
const geojson_10 = await {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: coords_10?.geometry?.coordinates,
},
properties: {
name: typData.name,
radius: '10',
},
},
],
};
let radiusTwelve = typData.points[time].radius12.split('|');
let radiusTwelveData = {
ne: radiusTwelve[0],
nw: radiusTwelve[1],
sw: radiusTwelve[2],
se: radiusTwelve[3],
};
let coords_12 = drawTyphoonCircle(coordList[time], radiusTwelveData);
const geojson_12 = await {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: coords_12?.geometry?.coordinates,
},
properties: {
name: typData.name,
radius: '12',
},
},
],
};
const geojson = await {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: coordList[time],
},
},
],
};
try {
if (!coordList[time] || !typData.points[time]) {
clearInterval(updateSource[typData.tfid]);
updateSource[typData.tfid] = null;
return;
}
map.getSource(`currentPoint_${typData.tfid}`).setData(geojson);
map.getSource(`currentFill_${typData.tfid}`).setData(geojson_7);
map.getSource(`currentFill_10_${typData.tfid}`).setData(geojson_10);
map.getSource(`currentFill_12_${typData.tfid}`).setData(geojson_12);
} catch (err) {
// If the updateSource interval is defined, clear the interval to stop updating the source.
if (updateSource) {
clearInterval(updateSource[typData.tfid]);
updateSource[typData.tfid] = null;
}
throw new Error(err);
}
}, 100);
类似台风路径的播放效果官网上也有所介绍,https://docs.mapbox.com/mapbox-gl-js/example/animate-point-along-route/其里面的核心内容就是设置定时器更新数据源 ,当然数据源的要求是geojson, map.getSource('pointSourceName').setData(pointGeojson);