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实时修改图标透明度