mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 22:59:59 -07:00
298 lines
11 KiB
JavaScript
298 lines
11 KiB
JavaScript
/*
|
|
* Leaflet.heat — a tiny, fast Leaflet heatmap plugin
|
|
* https://github.com/Leaflet/Leaflet.heat
|
|
* (c) 2014, Vladimir Agafonkin
|
|
* MIT License
|
|
*
|
|
* Bundled local copy for INTERCEPT — avoids CDN dependency.
|
|
* Includes simpleheat (https://github.com/mourner/simpleheat), MIT License.
|
|
*/
|
|
|
|
// ---- simpleheat ----
|
|
(function (global, factory) {
|
|
typeof define === 'function' && define.amd ? define(factory) :
|
|
typeof exports !== 'undefined' ? module.exports = factory() :
|
|
global.simpleheat = factory();
|
|
}(this, function () {
|
|
'use strict';
|
|
|
|
function simpleheat(canvas) {
|
|
if (!(this instanceof simpleheat)) return new simpleheat(canvas);
|
|
this._canvas = canvas = typeof canvas === 'string' ? document.getElementById(canvas) : canvas;
|
|
this._ctx = canvas.getContext('2d');
|
|
this._width = canvas.width;
|
|
this._height = canvas.height;
|
|
this._max = 1;
|
|
this._data = [];
|
|
}
|
|
|
|
simpleheat.prototype = {
|
|
defaultRadius: 25,
|
|
defaultGradient: {
|
|
0.4: 'blue',
|
|
0.6: 'cyan',
|
|
0.7: 'lime',
|
|
0.8: 'yellow',
|
|
1.0: 'red'
|
|
},
|
|
|
|
data: function (data) {
|
|
this._data = data;
|
|
return this;
|
|
},
|
|
|
|
max: function (max) {
|
|
this._max = max;
|
|
return this;
|
|
},
|
|
|
|
add: function (point) {
|
|
this._data.push(point);
|
|
return this;
|
|
},
|
|
|
|
clear: function () {
|
|
this._data = [];
|
|
return this;
|
|
},
|
|
|
|
radius: function (r, blur) {
|
|
blur = blur === undefined ? 15 : blur;
|
|
var circle = this._circle = this._createCanvas(),
|
|
ctx = circle.getContext('2d'),
|
|
r2 = this._r = r + blur;
|
|
circle.width = circle.height = r2 * 2;
|
|
ctx.shadowOffsetX = ctx.shadowOffsetY = r2 * 2;
|
|
ctx.shadowBlur = blur;
|
|
ctx.shadowColor = 'black';
|
|
ctx.beginPath();
|
|
ctx.arc(-r2, -r2, r, 0, Math.PI * 2, true);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
return this;
|
|
},
|
|
|
|
resize: function () {
|
|
this._width = this._canvas.width;
|
|
this._height = this._canvas.height;
|
|
},
|
|
|
|
gradient: function (grad) {
|
|
var canvas = this._createCanvas(),
|
|
ctx = canvas.getContext('2d'),
|
|
gradient = ctx.createLinearGradient(0, 0, 0, 256);
|
|
canvas.width = 1;
|
|
canvas.height = 256;
|
|
for (var i in grad) {
|
|
gradient.addColorStop(+i, grad[i]);
|
|
}
|
|
ctx.fillStyle = gradient;
|
|
ctx.fillRect(0, 0, 1, 256);
|
|
this._grad = ctx.getImageData(0, 0, 1, 256).data;
|
|
return this;
|
|
},
|
|
|
|
draw: function (minOpacity) {
|
|
if (!this._circle) this.radius(this.defaultRadius);
|
|
if (!this._grad) this.gradient(this.defaultGradient);
|
|
|
|
var ctx = this._ctx;
|
|
ctx.clearRect(0, 0, this._width, this._height);
|
|
|
|
for (var i = 0, len = this._data.length, p; i < len; i++) {
|
|
p = this._data[i];
|
|
ctx.globalAlpha = Math.min(Math.max(p[2] / this._max, minOpacity === undefined ? 0.05 : minOpacity), 1);
|
|
ctx.drawImage(this._circle, p[0] - this._r, p[1] - this._r);
|
|
}
|
|
|
|
var colored = ctx.getImageData(0, 0, this._width, this._height);
|
|
this._colorize(colored.data, this._grad);
|
|
ctx.putImageData(colored, 0, 0);
|
|
|
|
return this;
|
|
},
|
|
|
|
_colorize: function (pixels, gradient) {
|
|
for (var i = 3, len = pixels.length, j; i < len; i += 4) {
|
|
j = pixels[i] * 4;
|
|
if (j) {
|
|
pixels[i - 3] = gradient[j];
|
|
pixels[i - 2] = gradient[j + 1];
|
|
pixels[i - 1] = gradient[j + 2];
|
|
}
|
|
}
|
|
},
|
|
|
|
_createCanvas: function () {
|
|
if (typeof document !== 'undefined') {
|
|
return document.createElement('canvas');
|
|
}
|
|
return { getContext: function () {} };
|
|
}
|
|
};
|
|
|
|
return simpleheat;
|
|
}));
|
|
|
|
// ---- Leaflet.heat plugin ----
|
|
(function () {
|
|
if (typeof L === 'undefined') return;
|
|
|
|
L.HeatLayer = (L.Layer ? L.Layer : L.Class).extend({
|
|
initialize: function (latlngs, options) {
|
|
this._latlngs = latlngs;
|
|
L.setOptions(this, options);
|
|
},
|
|
|
|
setLatLngs: function (latlngs) {
|
|
this._latlngs = latlngs;
|
|
return this.redraw();
|
|
},
|
|
|
|
addLatLng: function (latlng) {
|
|
this._latlngs.push(latlng);
|
|
return this.redraw();
|
|
},
|
|
|
|
setOptions: function (options) {
|
|
L.setOptions(this, options);
|
|
if (this._heat) this._updateOptions();
|
|
return this.redraw();
|
|
},
|
|
|
|
redraw: function () {
|
|
if (this._heat && !this._frame && this._map && !this._map._animating) {
|
|
this._frame = L.Util.requestAnimFrame(this._redraw, this);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
onAdd: function (map) {
|
|
this._map = map;
|
|
if (!this._canvas) this._initCanvas();
|
|
if (this.options.pane) this.getPane().appendChild(this._canvas);
|
|
else map._panes.overlayPane.appendChild(this._canvas);
|
|
map.on('moveend', this._reset, this);
|
|
if (map.options.zoomAnimation && L.Browser.any3d) {
|
|
map.on('zoomanim', this._animateZoom, this);
|
|
}
|
|
this._reset();
|
|
},
|
|
|
|
onRemove: function (map) {
|
|
if (this.options.pane) this.getPane().removeChild(this._canvas);
|
|
else map.getPanes().overlayPane.removeChild(this._canvas);
|
|
map.off('moveend', this._reset, this);
|
|
if (map.options.zoomAnimation) {
|
|
map.off('zoomanim', this._animateZoom, this);
|
|
}
|
|
},
|
|
|
|
addTo: function (map) {
|
|
map.addLayer(this);
|
|
return this;
|
|
},
|
|
|
|
_initCanvas: function () {
|
|
var canvas = this._canvas = L.DomUtil.create('canvas', 'leaflet-heatmap-layer leaflet-layer');
|
|
var originProp = L.DomUtil.testProp(['transformOrigin', 'WebkitTransformOrigin', 'msTransformOrigin']);
|
|
canvas.style[originProp] = '50% 50%';
|
|
var size = this._map.getSize();
|
|
canvas.width = size.x;
|
|
canvas.height = size.y;
|
|
var animated = this._map.options.zoomAnimation && L.Browser.any3d;
|
|
L.DomUtil.addClass(canvas, 'leaflet-zoom-' + (animated ? 'animated' : 'hide'));
|
|
this._heat = simpleheat(canvas);
|
|
this._updateOptions();
|
|
},
|
|
|
|
_updateOptions: function () {
|
|
this._heat.radius(this.options.radius || this._heat.defaultRadius, this.options.blur);
|
|
if (this.options.gradient) this._heat.gradient(this.options.gradient);
|
|
if (this.options.minOpacity) this._heat.minOpacity = this.options.minOpacity;
|
|
},
|
|
|
|
_reset: function () {
|
|
var topLeft = this._map.containerPointToLayerPoint([0, 0]);
|
|
L.DomUtil.setPosition(this._canvas, topLeft);
|
|
var size = this._map.getSize();
|
|
if (this._heat._width !== size.x) {
|
|
this._canvas.width = this._heat._width = size.x;
|
|
}
|
|
if (this._heat._height !== size.y) {
|
|
this._canvas.height = this._heat._height = size.y;
|
|
}
|
|
this._redraw();
|
|
},
|
|
|
|
_redraw: function () {
|
|
this._frame = null;
|
|
if (!this._map) return;
|
|
var data = [],
|
|
r = this._heat._r,
|
|
size = this._map.getSize(),
|
|
bounds = new L.Bounds(L.point([-r, -r]), size.add([r, r])),
|
|
max = this.options.max === undefined ? 1 : this.options.max,
|
|
maxZoom = this.options.maxZoom === undefined ? this._map.getMaxZoom() : this.options.maxZoom,
|
|
v = 1 / Math.pow(2, Math.max(0, Math.min(maxZoom - this._map.getZoom(), 12))),
|
|
cellSize = r / 2,
|
|
grid = [],
|
|
panePos = this._map._getMapPanePos(),
|
|
offsetX = panePos.x % cellSize,
|
|
offsetY = panePos.y % cellSize,
|
|
i, len, p, cell, x, y, j, len2, k;
|
|
|
|
for (i = 0, len = this._latlngs.length; i < len; i++) {
|
|
p = this._map.latLngToContainerPoint(this._latlngs[i]);
|
|
if (bounds.contains(p)) {
|
|
x = Math.floor((p.x - offsetX) / cellSize) + 2;
|
|
y = Math.floor((p.y - offsetY) / cellSize) + 2;
|
|
var alt = this._latlngs[i].alt !== undefined ? this._latlngs[i].alt :
|
|
this._latlngs[i][2] !== undefined ? +this._latlngs[i][2] : 1;
|
|
k = alt * v;
|
|
grid[y] = grid[y] || [];
|
|
cell = grid[y][x];
|
|
if (!cell) {
|
|
grid[y][x] = [p.x, p.y, k];
|
|
} else {
|
|
cell[0] = (cell[0] * cell[2] + p.x * k) / (cell[2] + k);
|
|
cell[1] = (cell[1] * cell[2] + p.y * k) / (cell[2] + k);
|
|
cell[2] += k;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0, len = grid.length; i < len; i++) {
|
|
if (grid[i]) {
|
|
for (j = 0, len2 = grid[i].length; j < len2; j++) {
|
|
cell = grid[i][j];
|
|
if (cell) {
|
|
data.push([
|
|
Math.round(cell[0]),
|
|
Math.round(cell[1]),
|
|
Math.min(cell[2], max)
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this._heat.data(data).draw(this.options.minOpacity);
|
|
},
|
|
|
|
_animateZoom: function (e) {
|
|
var scale = this._map.getZoomScale(e.zoom),
|
|
offset = this._map._getCenterOffset(e.center)._multiplyBy(-scale).subtract(this._map._getMapPanePos());
|
|
if (L.DomUtil.setTransform) {
|
|
L.DomUtil.setTransform(this._canvas, offset, scale);
|
|
} else {
|
|
this._canvas.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(offset) + ' scale(' + scale + ')';
|
|
}
|
|
}
|
|
});
|
|
|
|
L.heatLayer = function (latlngs, options) {
|
|
return new L.HeatLayer(latlngs, options);
|
|
};
|
|
}());
|