mapboxGL之风流图

Posted 牛老师讲GIS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mapboxGL之风流图相关的知识,希望对你有一定的参考价值。

概述

前面的文章说到了Openlayers4中风场的实现,本文将讲述如何在mapbox GL实现类似的效果。

效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZkUGd75p-1585748579661)(https://upload-images.jianshu.io/upload_images/6826673-a72b98f01e6917fa.gif?imageMogr2/auto-orient/strip)]

实现

实现是在windy-esri的基础上做了和mapboxGL的结合,结合代码如下:

var windyMap = 
  windy: null,
  map: null,
  visible: true,
  context: null,
  timer: 0,
  initWindy(data, map) 
    const self = this;
    self.visible = true;
    self.map = map;

    // 删除dom
    self.hideWind();

    let canvas = document.createElement('canvas');
    canvas.id = 'windCanvas';
    canvas.width = map.getCanvas().width;
    canvas.height = map.getCanvas().height;
    canvas.style.position = 'absolute';
    canvas.style.top = 0;
    canvas.style.left = 0;
    map.getCanvasContainer().appendChild(canvas);
    this.context = canvas.getContext("2d");

    self.windy = new Windy(
      canvas: canvas,
      data: data,
      map: map
    );

    if (self.timer) clearTimeout(self.timer);
    this.timer = setTimeout(function () 
      self._refreshWindy();
    , 750);

    map.on("dragstart", function()
      if(self.context) self.context.clearRect(0, 0, 3000, 3000);
      self.windy.stop();
    );

    map.on("dragend", function() 
      self._refreshWindy();
    );

    map.on("zoomstart", function()
      if(self.context) self.context.clearRect(0, 0, 3000, 3000);
      self.windy.stop();
    );

    map.on("zoomend", function() 
      self._refreshWindy();
    );

    map.on("resize", function() 
      self.clearWind();
    );
  ,
  _refreshWindy: function() 
    const self = this;
    const _canvas = self.windy.params.canvas;
    if (!self.windy) return;
    let bounds = self.map.getBounds();
    let extent = [
      bounds._sw.lng,
      bounds._sw.lat,
      bounds._ne.lng,
      bounds._ne.lat
    ];

    _canvas.width = map.getCanvas().width;
    _canvas.height = map.getCanvas().height;

    self.windy.start(
      [[0, 0], [_canvas.width, _canvas.height]],
      _canvas.width,
      _canvas.height,
      [[extent[0], extent[1]], [extent[2], extent[3]]]
    );
  ,

  hideWind: function() 
    if(this.context) this.context.clearRect(0, 0, 3000, 3000);
    let dom = document.getElementById('windCanvas');
    if (dom) dom.parentNode.removeChild(dom);
  ,

  clearWind: function() 
    if (this.windy) this.windy.stop();
    if(this.context) this.context.clearRect(0, 0, 3000, 3000);
  ,

  setVisible: function(flag) 
    const self = this;
    self.visible = flag;
    let dom = document.getElementById('windCanvas');
    if (!dom) return;
    if (flag) 
      dom.style.display = 'block';
      self._refreshWindy();
     else 
      if (self.windy) self.windy.stop();
      dom.style.display = 'none';
    
  
;

说明:这里面核心用到了mapboxGL的接口有:map.getCanvasContainer()map.getBounds()map.getCanvas()

windy.js的代码如下:

var Windy = function Windy(params) 
   var MIN_VELOCITY_INTENSITY = params.minVelocity || 0; // velocity at which particle intensity is minimum (m/s)

   var MAX_VELOCITY_INTENSITY = params.maxVelocity || 10; // velocity at which particle intensity is maximum (m/s)

   var VELOCITY_SCALE = (params.velocityScale || 0.005) * (Math.pow(window.devicePixelRatio, 1 / 3) || 1); // scale for wind velocity (completely arbitrary--this value looks nice)

   var MAX_PARTICLE_AGE = params.particleAge || 90; // max number of frames a particle is drawn before regeneration

   var PARTICLE_LINE_WIDTH = params.lineWidth || 1; // line width of a drawn particle

   var PARTICLE_MULTIPLIER = params.particleMultiplier || 1 / 300; // particle count scalar (completely arbitrary--this values looks nice)

   var PARTICLE_REDUCTION = Math.pow(window.devicePixelRatio, 1 / 3) || 1.6; // multiply particle count for mobiles by this amount

   var FRAME_RATE = params.frameRate || 15;
   var FRAME_TIME = 1000 / FRAME_RATE; // desired frames per second

   var OPACITY = 0.97;
   var defaulColorScale = ["rgb(36,104, 180)", "rgb(60,157, 194)", "rgb(128,205,193 )", "rgb(151,218,168 )", "rgb(198,231,181)", "rgb(238,247,217)", "rgb(255,238,159)", "rgb(252,217,125)", "rgb(255,182,100)", "rgb(252,150,75)", "rgb(250,112,52)", "rgb(245,64,32)", "rgb(237,45,28)", "rgb(220,24,32)", "rgb(180,0,35)"];
   var colorScale = params.colorScale || defaulColorScale;
   var NULL_WIND_VECTOR = [NaN, NaN, null]; // singleton for no wind in the form: [u, v, magnitude]

   var builder;
   var grid;
   var gridData = params.data;
   var date;
   var λ0, φ0, Δλ, Δφ, ni, nj;

   var setData = function setData(data) 
     gridData = data;
   ;

   var setOptions = function setOptions(options) 
     if (options.hasOwnProperty("minVelocity")) MIN_VELOCITY_INTENSITY = options.minVelocity;
     if (options.hasOwnProperty("maxVelocity")) MAX_VELOCITY_INTENSITY = options.maxVelocity;
     if (options.hasOwnProperty("velocityScale")) VELOCITY_SCALE = (options.velocityScale || 0.005) * (Math.pow(window.devicePixelRatio, 1 / 3) || 1);
     if (options.hasOwnProperty("particleAge")) MAX_PARTICLE_AGE = options.particleAge;
     if (options.hasOwnProperty("lineWidth")) PARTICLE_LINE_WIDTH = options.lineWidth;
     if (options.hasOwnProperty("particleMultiplier")) PARTICLE_MULTIPLIER = options.particleMultiplier;
     if (options.hasOwnProperty("opacity")) OPACITY = +options.opacity;
     if (options.hasOwnProperty("frameRate")) FRAME_RATE = options.frameRate;
     FRAME_TIME = 1000 / FRAME_RATE;
   ; // interpolation for vectors like wind (u,v,m)


   var bilinearInterpolateVector = function bilinearInterpolateVector(x, y, g00, g10, g01, g11) 
     var rx = 1 - x;
     var ry = 1 - y;
     var a = rx * ry,
         b = x * ry,
         c = rx * y,
         d = x * y;
     var u = g00[0] * a + g10[0] * b + g01[0] * c + g11[0] * d;
     var v = g00[1] * a + g10[1] * b + g01[1] * c + g11[1] * d;
     return [u, v, Math.sqrt(u * u + v * v)];
   ;

   var createWindBuilder = function createWindBuilder(uComp, vComp) 
     var uData = uComp.data,
         vData = vComp.data;
     return 
       header: uComp.header,
       //recipe: recipeFor("wind-" + uComp.header.surface1Value),
       data: function data(i) 
         return [uData[i], vData[i]];
       ,
       interpolate: bilinearInterpolateVector
     ;
   ;

   var createBuilder = function createBuilder(data) 
     var uComp = null,
         vComp = null,
         scalar = null;
     data.forEach(function (record) 
       switch (record.header.parameterCategory + "," + record.header.parameterNumber) 
         case "1,2":
         case "2,2":
           uComp = record;
           break;

         case "1,3":
         case "2,3":
           vComp = record;
           break;

         default:
           scalar = record;
       
     );
     return createWindBuilder(uComp, vComp);
   ;

   var buildGrid = function buildGrid(data, callback) 
     var supported = true;
     if (data.length < 2) supported = false;
     if (!supported) console.log("Windy Error: data must have at least two components (u,v)");
     builder = createBuilder(data);
     var header = builder.header;
     if (header.hasOwnProperty("gridDefinitionTemplate") && header.gridDefinitionTemplate != 0) supported = false;

     if (!supported) 
       console.log("Windy Error: Only data with Latitude_Longitude coordinates is supported");
     

     supported = true; // reset for futher checks

     λ0 = header.lo1;
     φ0 = header.la1; // the grid's origin (e.g., 0.0E, 90.0N)

     Δλ = header.dx;
     Δφ = header.dy; // distance between grid points (e.g., 2.5 deg lon, 2.5 deg lat)

     ni = header.nx;
     nj = header.ny; // number of grid points W-E and N-S (e.g., 144 x 73)

     if (header.hasOwnProperty("scanMode")) 
       var scanModeMask = header.scanMode.toString(2);
       scanModeMask = ('0' + scanModeMask).slice(-8);
       var scanModeMaskArray = scanModeMask.split('').map(Number).map(Boolean);
       if (scanModeMaskArray[0]) Δλ = -Δλ;
       if (scanModeMaskArray[1]) Δφ = -Δφ;
       if (scanModeMaskArray[2]) supported = false;
       if (scanModeMaskArray[3]) supported = false;
       if (scanModeMaskArray[4]) supported = false;
       if (scanModeMaskArray[5]) supported = false;
       if (scanModeMaskArray[6]) supported = false;
       if (scanModeMaskArray[7]) supported = false;
       if (!supported) console.log("Windy Error: Data with scanMode: " + header.scanMode + " is not supported.");
     

     date = new Date(header.refTime);
     date.setHours(date.getHours() + header.forecastTime); // Scan modes 0, 64 allowed.
     // http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_table3-4.shtml

     grid = [];
     var p = 0;
     var isContinuous = Math.floor(ni * Δλ) >= 360;

     for (var j = 0; j < nj; j++) 
       var row = [];

       for (var i = 0; i < ni; i++, p++) 
         row[i] = builder.data(p);
       

       if (isContinuous) 
         // For wrapped grids, duplicate first column as last column to simplify interpolation logic
         row.push(row[0]);
       

       grid[j] = row;
     

     callback(
       date: date,
       interpolate: interpolate
     );
   ;
   /**
    * Get interpolated grid value from Lon/Lat position
    * @param λ Float Longitude
    * @param φ Float Latitude
    * @returns Object
    */


   var interpolate = function interpolate(λ, φ) 
     if (!grid) return null;
     var i = floorMod(λ - λ0, 360) / Δλ; // calculate longitude index in wrapped range [0, 360)

     var j = (φ0 - φ) / Δφ; // calculate latitude index in direction +90 to -90

     var fi = Math.floor(i),
         ci = fi + 1;
     var fj = Mathmapboxgl实时修改图标透明度

如何获取 mapboxgl.GeoJSONSource 对象的边界框?

mapboxGL2离线化应用

mapboxGL2离线化应用

Mapboxgl JS不会删除标记

mapboxGL实现旋转的地球