美文网首页开源GIS相关
mapbox GL台风路径的播放实现

mapbox GL台风路径的播放实现

作者: 牛老师讲webgis | 来源:发表于2020-03-22 22:45 被阅读0次

    概述

    前面的文章中写了基于openlayers4的台风路径播放,最近用到mapbox GL,也要实现相似的功能,网上找了好久都没有找到,于是就放弃了“拿来主义”的想法,只能自己动手了。经过一下午的努力,终于有了一个雏形,在此分享出来,希望对你有用!

    效果

    实现后效果

    实现

    1、数据获取

    测试数据是从温州台风网,抓取了201929号台风数据作为测试数据。

    2、添加台风编号和名称到地图

    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;
    }
    

    对应的样式为:

    $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、添加风圈

    说明:添加的顺序分别为风圈、路径和实况点,目的是为了让三者按照顺序叠加展示。

    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'
          ]
        }
      });
    }
    

    里面用到了一个生成风圈的方法,方法如下:

    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、添加点

    点包括:实况点和预报点。由于涉及到后面播放的控制,此处将两者分别添加了。

    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、播放与播放控制

    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
    联系方式

    类型 内容
    qq 1004740957
    公众号 lzugis15
    e-mail niujp08@qq.com
    webgis群 452117357
    LZUGIS

    相关文章

      网友评论

        本文标题:mapbox GL台风路径的播放实现

        本文链接:https://www.haomeiwen.com/subject/jzebyhtx.html