mapbox GL台风路径的播放实现

2020-03-23 17:34:19 浏览数 (2)

概述

前面的文章中写了基于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,在实现的时候就添加了两层,实现代码如下:

代码语言:javascript复制
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

0 人点赞