开源GIS加油站GIS相关

mapboxGL中雷电动画展示

2020-04-11  本文已影响0人  牛老师讲GIS

概述

在网站ventusky中,点开降雨量图层,你会发现在降雨上面会有一些闪烁的点(小级别)或者闪烁的线(大级别),这些点或者线按照我的分析应该是根据降水相态(取雷阵雨)或者闪电数据叠加而成的,算是一个比较不错的亮点,本文参考此效果,结合github上的代码,在mapbox GL中进行了业务封装实现类似的效果。

效果

线

说明: 因为数据是随机造的,所以效果上不如实际数据那么好;

实现

1、雷电数据

一般的雷电数据都是散点数据,会包含:经纬度、强度等信息,本文在做测试数据的时候只用了经纬度,数据格式如下:

var data = [
  [112.08111460679623, 20.81427375298513],
  [112.71805970936151, 20.09658100108571],
  [113.9003630369973, 19.673781290716917],
  [114.10018895152848, 21.004828899791406],
  [115.36575307688554, 21.605996657108136],
  [116.46479560680149, 22.01182925045798],
  [116.34406745010693, 19.75608056730792]
];

2、工具封装

class Lightning {

  constructor(c) {
    this.config = c;
  }

  Cast(context, from, to, map) {
    if (!from || !to) {
      return;
    }
    //Main vector
    var v = new Vector(from.X1, from.Y1, to.X1, to.Y1);
    //skip cas if not close enough
    if (this.config.Threshold && v.Length() > context.canvas.width * this.config.Threshold) {
      return;
    }
    var vLen = v.Length();
    var refv = from;
    var lR = (vLen / context.canvas.width)
    //count of segemnets
    var segments = Math.floor(this.config.Segments * lR);
    //lenth of each
    var l = vLen / segments;

    var zoom = map.getZoom();
    if(zoom > 6) {
      for (let i = 1; i <= segments; i++) {
        //position in the main vector
        var dv = v.Multiply((1 / segments) * i);

        //add position noise
        if (i != segments) {
          dv.Y1 += l * Math.random();
          dv.X1 += l * Math.random();
        }

        //new vector for segment
        var r = new Vector(refv.X1, refv.Y1, dv.X1, dv.Y1);

        //background blur
        this.Line(context, r, {
          Color: this.config.GlowColor,
          With: this.config.GlowWidth * lR,
          Blur: this.config.GlowBlur * lR,
          BlurColor: this.config.GlowColor,
          Alpha: this.Random(this.config.GlowAlpha, this.config.GlowAlpha * 2) / 100

        });

        //main line
        this.Line(context, r, {
          Color: this.config.Color,
          With: this.config.Width,
          Blur: this.config.Blur,
          BlurColor: this.config.BlurColor,
          Alpha: this.config.Alpha
        });
        refv = r;
      }
    } else {
      this.Circle(context, to, lR);
    }
  }

  Line(context, v, c) {
    context.beginPath();
    context.strokeStyle = c.Color;
    context.lineWidth = c.With;
    context.moveTo(v.X, v.Y);
    context.lineTo(v.X1, v.Y1);
    context.globalAlpha = c.Alpha;
    context.shadowBlur = c.Blur;
    context.shadowColor = c.BlurColor;
    context.stroke();
  }

  Circle(context, p, lR) {
    context.beginPath();
    context.arc(p.X1 + Math.random() * 10 * lR, p.Y1 + Math.random() * 10 * lR, 5, 0, 2 * Math.PI, false);
    context.fillStyle = 'white';
    context.shadowBlur = 100;
    context.shadowColor = "#2319FF";
    context.fill();
  }

  Random(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
  }
}

class Vector {
  constructor(x, y, x1, y1) {
    this.X = x;
    this.Y = y;
    this.X1 = x1;
    this.Y1 = y1;
  }

  dX() {
    return this.X1 - this.X;
  }
  dY() {
    return this.Y1 - this.Y;
  }
  Normalized() {
    var l = this.Length();
    return new Vector(this.X, this.Y, this.X + (this.dX() / l), this.Y + (this.dY() / l));
  }

  Length() {
    return Math.sqrt(Math.pow(this.dX(), 2) + Math.pow(this.dY(), 2));
  }

  Multiply(n) {
    return new Vector(this.X, this.Y, this.X + this.dX() * n, this.Y + this.dY() * n);
  }

  Clone() {
    return new Vector(this.x, this.y, this.X1, this.Y1);
  }
}

var LightnMap = {
  _map: null,
  _data: [],
  _canvas: null,
  _context: null,
  _options: {
    Alpha: "0.5",
    Blur: "5",
    BlurColor: "white",
    Color: "white",
    GlowAlpha: "30",
    GlowBlur: "10",
    GlowColor: "#000055",
    GlowWidth: "40",
    Segments: "600",
    Threshold: "0.5",
    Width: "2"
  },
  _lts: [],
  _animation: null,
  _timer: 0,
  init: function(map, data) {
    const self = this;
    self._map = map;
    self._data = data;
    let canvas = document.createElement('canvas');
    canvas.id = 'lightCanvas';
    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);
    self._context = canvas.getContext("2d");
    self._canvas = canvas;

    self._clearAndRestart();

    map.on("dragstart", function() {
      self._clear();
    });

    map.on("dragend", function() {
      self._clearAndRestart();
    });

    map.on("zoomstart", function() {
      self._clear();
    });

    map.on("zoomend", function() {
      self._clearAndRestart();
    });

    map.on("resize", function() {
      self._clear();
    });
  },
  _getPoints: function(x, y, size) {
    var self = this;
    points = [];
    var num = self._randomNum(1, 4);
    for (var i = 0; i < num; i++) {
      points.push(new Vector(
        x + self._randomNum(-size, size),
        y + self._randomNum(-size, size),
        x + self._randomNum(-size, size),
        y + self._randomNum(-size, size)));
    }
    return points;
  },
  _clearAndRestart: function() {
    var self = this;
    self._clear();
    // 初始化雷电数据
    self._lts = [];
    for (var i = 0; i < self._data.length; i++) {
      var d = self._data[i];
      var xy = self._map.project(d);
      var x = xy.x + self._randomNum(-50, 50);
      var y = xy.y + self._randomNum(-50, 50);
      var size = self._randomNum(20, 50);
      var lt = {
        lt: new Lightning(self._options),
        x: x,
        y: y,
        size: size
      };
      self._lts.push(lt);
    }
    self._animation = window.requestAnimationFrame(function() {
      self._Animate();
    });
  },
  _Animate() {
    var self = this;
    self._clear();
    for (var i = 0; i < self._data.length; i++) {
      var lt = self._lts[i];
      var x = lt.x;
      var y = lt.y;
      var size = lt.size;
      var points = self._getPoints(x, y, size);
      var target = new Vector(
        x - size,
        y - size,
        x + self._randomNum(-10, 10),
        y + self._randomNum(-10, 10)
      );
      points.forEach(p => {
        lt.lt.Cast(self._context, p, target, self._map);
      });
    }
    if (self._timer) clearTimeout(self._timer);
    self._timer = setTimeout(() => {
      self._Animate();
    }, 100);
  },
  _randomNum(minNum, maxNum) {
    switch (arguments.length) {
      case 1:
        return parseInt(Math.random() * minNum + 1, 10);
        break;
      case 2:
        return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
        break;
      default:
        return 0;
        break;
    }
  },
  _clear: function() {
    const self = this;
    self._context.clearRect(0, 0, self._canvas.width, self._canvas.height);
    if (self._animation) cancelAnimationFrame(self._animation);
    if (self._timer) clearTimeout(self._timer);
  },
  destory: function() {
    this._canvas.parentNode.removeChild(this._canvas);
  }
}

3、调用实现

LightnMap.init(map, data);

技术博客
CSDN:http://blog.csdn.NET/gisshixisheng
联系方式

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

猜你喜欢

热点阅读