Created
May 6, 2016 11:22
-
-
Save kuzvac/84145d079b7c67a7094c973007d2e6a4 to your computer and use it in GitHub Desktop.
Leaflet.heat fixed radius
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import simpleheat from 'simpleheat'; | |
/* | |
(c) 2014, Vladimir Agafonkin | |
Leaflet.heat, a tiny and fast heatmap plugin for Leaflet. | |
https://github.com/Leaflet/Leaflet.heat | |
*/ | |
L.HeatLayer = (L.Layer ? L.Layer : L.Class).extend({ | |
// options: { | |
// minOpacity: 0.05, | |
// maxOpacity: 1, | |
// maxZoom: 18, | |
// radius: 25, | |
// blur: 15, | |
// max: 1.0, | |
// fixedZoomLevel: 15 | |
// }, | |
initialize: function (latlngs, options) { | |
/* Create geometric progression with multiplier 2 | |
It's need for scale heat points based on map zoom level */ | |
var geometryMultiplier = 2; | |
var geomProgr = [geometryMultiplier]; | |
for (var i = 0; i < 17; i++) { | |
geometryMultiplier = geometryMultiplier * 2; | |
geomProgr.push(geometryMultiplier); | |
} | |
options['fixedMultiplier'] = geomProgr; | |
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._animating) { | |
this._frame = L.Util.requestAnimFrame(this._redraw, this); | |
} | |
return this; | |
}, | |
onAdd: function (map) { | |
this._map = map; | |
if (!this._canvas) { | |
this._initCanvas(); | |
} | |
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) { | |
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(); | |
/* Calculate fixed zoom level on map load */ | |
var fixedZoomLevel = this._map._zoom - this.options.fixedZoomLevel; | |
/* Calculate base fixed radius for heat map. */ | |
var basedRadius = this._metersToPixels(this.options.radius, this._map.getCenter().lat, this.options.fixedZoomLevel); | |
/* If current map zoom greather or equal fixedZoomLevel, change heat point radius */ | |
if(fixedZoomLevel >= 0) { | |
this._heat.radius(this._metersToPixels(this.options.radius, this._map.getCenter().lat, this._map.getZoom()), this.options.blur) | |
} else { | |
/* Otherwise apply calculated basedRadius */ | |
this._heat.radius(basedRadius, this.options.blur) | |
} | |
/* If current map zoom greather or equal maxZoom || _layersMaxZoom, change opacity for heatpoints */ | |
if(this._map._zoom >= (this.options.maxZoom || this._map._layersMaxZoom)) { | |
L.DomUtil.setOpacity(this._canvas, this.options.maxOpacity || 1) | |
} else { | |
/* Otherwise remove opacity */ | |
this._canvas.style.opacity = ''; | |
} | |
}, | |
_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.max) { | |
this._heat.max(this.options.max); | |
} | |
}, | |
_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 () { | |
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; | |
// console.time('process'); | |
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); // x | |
cell[1] = (cell[1] * cell[2] + p.y * k) / (cell[2] + k); // y | |
cell[2] += k; // cumulated intensity value | |
} | |
} | |
} | |
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) | |
]); | |
} | |
} | |
} | |
} | |
// console.timeEnd('process'); | |
// console.time('draw ' + data.length); | |
this._heat.data(data).draw(this.options.minOpacity); | |
// console.timeEnd('draw ' + data.length); | |
this._frame = null; | |
}, | |
_animateZoom: function (e) { | |
var fixedZoomLevel = e.zoom - this.options.fixedZoomLevel; | |
var fixedZoomRadius; | |
/* Change heat point radius when zoom is changed */ | |
if(fixedZoomLevel >= 0) { | |
this._heat.radius(this._metersToPixels(this.options.radius, e.center.lat, e.zoom), this.options.blur) | |
} | |
/* Change opacity for heat points when zoom is changed */ | |
if(this.options.maxZoom && e.zoom >= this.options.maxZoom) { | |
L.DomUtil.setOpacity(this._canvas, this.options.maxOpacity || 1) | |
} else if(!this.options.maxZoom && e.zoom >= (e.target._layersMaxZoom - 1)) { | |
L.DomUtil.setOpacity(this._canvas, this.options.maxOpacity || 1) | |
} else { | |
this._canvas.style.opacity = ''; | |
} | |
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 + ')'; | |
} | |
}, | |
/* Convert meters from options to map pixel for radius */ | |
_metersToPixels: function(meters, center, zoom) { | |
var metersPerPixel = 40075016.686 * Math.abs(Math.cos(center * 180/Math.PI)) / Math.pow(2, zoom+8); | |
//var metersToMiles = metersPerPixel * 0.00062137; | |
return meters / metersPerPixel; | |
}, | |
}); | |
L.heatLayer = function (latlngs, options) { | |
return new L.HeatLayer(latlngs, options); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var heatmapOption = { | |
minOpacity: 0.3, | |
maxOpacity: 0.5, | |
radius: 200, //in meters | |
blur: 15, | |
//maxZoom: 16, | |
fixedZoomLevel: 13 | |
} | |
var heat = L.heatLayer(heatmapPoints, heatmapOption).addTo(map); |
maybe it should be 40075016.686 * Math.abs(Math.cos(center * Math.PI / 180 ))
instead of 40075016.686 * Math.abs(Math.cos(center * 180/Math.PI))
?
L.heatLayer is not a function. Am I doing something wrong?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I need to plot heat maps using leaflet.heat with constant radius in meters. I dont want that the zoom changes the area covered by an color/intensity. It looks that your code do that, but, I could not figure out how to use it with map quest. This code is working? Do you have some running example?