feat: Add scaleWithZoom option to heatmaps.
MODIFY
examples/heatmap/index.pug
examples/heatmap/index.pug
@@ -46,6 +46,9 @@ block append mainContent
.form-group(title="Use either a Gaussian distribution or a solid circle with a blurred edge for each point. If a Gaussian is used, the total radius is the sume of the radius and blur radius values.")
label(for="gaussian") Gaussian Points
input#gaussian(type="checkbox", placeholder="true", checked="checked")
+ .form-group(title="If true, scale the point size with zoom. In this case, the radius is specified in pixels a zoom-level 0.")
+ label(for="scaleWithZoom") Scale With Zoom
+ input#scaleWithZoom(type="checkbox", placeholder="false")
.form-group(title="Color Gradient. Entries with intensities of 0 and 1 are needed to form a valid color gradient.")
label Color Gradient
table.gradient

MODIFY
examples/heatmap/main.js
examples/heatmap/main.js
@@ -52,6 +52,7 @@ $(function () {
ctlvalue = value ? value : 'adderall';
break;
case 'gaussian':
+ case 'scaleWithZoom':
ctlvalue = value === 'true';
heatmapOptions.style[key] = value;
break;
@@ -100,7 +101,6 @@ $(function () {
heatmapOptions[key] = ctlvalue = parseInt(value, 10);
}
break;
- // add gaussian and binning when they are added as features
}
if (ctlvalue !== undefined) {
$('#' + ctlkey).val(ctlvalue);
@@ -229,6 +229,7 @@ $(function () {
fetch_data();
break;
case 'gaussian':
+ case 'scaleWithZoom':
heatmapOptions.style[param] = processedValue;
heatmap.style(param, processedValue);
heatmap.draw();

MODIFY
src/canvas/heatmapFeature.js
src/canvas/heatmapFeature.js
@@ -73,15 +73,22 @@ var canvas_heatmapFeature = function (arg) {
};
/**
- * Create circle for each data point.
+ * Create a circle to render at each data point.
*
* @returns {this}
*/
this._createCircle = function () {
- var circle, ctx, r, r2, blur, gaussian;
+ var circle, ctx, r, r2, blur, gaussian, scale;
r = m_this.style('radius');
blur = m_this.style('blurRadius');
gaussian = m_this.style('gaussian');
+ scale = m_this.style('scaleWithZoom');
+ if (scale) {
+ let zoom = this.layer().map().zoom();
+ scale = Math.pow(2, zoom);
+ r *= scale;
+ blur *= scale;
+ }
if (!m_this._circle || m_this._circle.gaussian !== gaussian ||
m_this._circle.radius !== r || m_this._circle.blurRadius !== blur) {
circle = m_this._circle = document.createElement('canvas');
@@ -307,9 +314,12 @@ var canvas_heatmapFeature = function (arg) {
layer = m_this.layer(),
mapSize = map.size();
+ if (m_this.style('scaleWithZoom')) {
+ radius *= Math.pow(2, map.zoom());
+ }
/* Determine if we should bin the data */
if (binned === true || binned === 'auto') {
- binned = Math.max(Math.floor(radius / 8), 3);
+ binned = Math.max(Math.floor(radius / 8), Math.max(1.5, Math.min(3, radius / 2.5)));
if (m_this.binned() === 'auto') {
var numbins = (Math.ceil((mapSize.width + radius * 2) / binned) *
Math.ceil((mapSize.height + radius * 2) / binned));
@@ -327,6 +337,7 @@ var canvas_heatmapFeature = function (arg) {
context2d.setTransform(1, 0, 0, 1, 0, 0);
context2d.clearRect(0, 0, mapSize.width, mapSize.height);
m_heatMapTransform = '';
+ if (radius > 0.5 && radius < 8192) {
map.scheduleAnimationFrame(m_this._setTransform, false);
layer.canvas().css({transform: ''});
@@ -341,6 +352,7 @@ var canvas_heatmapFeature = function (arg) {
pixelArray = context2d.getImageData(0, 0, canvas.width, canvas.height);
m_this._colorize(pixelArray.data, m_this._grad);
context2d.putImageData(pixelArray, 0, 0);
+ }
m_heatMapPosition = {
zoom: map.zoom(),

MODIFY
src/heatmapFeature.js
src/heatmapFeature.js
@@ -40,6 +40,11 @@ var transform = require('./transform');
* approximation. The total weight of the gaussian area is approximately the
* `9/16 r^2`. The sum of `radius + blurRadius` is used as the radius for
* the gaussian distribution.
+ * @property {boolean} [scaleWithZoom=false] If truthy, the value for radius
+ * and blurRadius scale with zoom. In this case, the values for radius and
+ * blurRadius are the values at zoom-level zero. If the scaled radius is
+ * less than 0.5 or more than 8192 screen pixels, the heatmap will not
+ * render.
*/
/**
@@ -238,7 +243,8 @@ var heatmapFeature = function (arg) {
0.25: {r: 0, g: 0, b: 1, a: 0.5},
0.5: {r: 0, g: 1, b: 1, a: 0.6},
0.75: {r: 1, g: 1, b: 0, a: 0.7},
- 1: {r: 1, g: 0, b: 0, a: 0.8}}
+ 1: {r: 1, g: 0, b: 0, a: 0.8}},
+ scaleWithZoom: false
},
arg.style === undefined ? {} : arg.style
);

MODIFY
tests/cases/heatmap.js
tests/cases/heatmap.js
@@ -164,6 +164,20 @@ describe('canvas heatmap', function () {
stepAnimationFrame(Date.now());
expect(feature1._binned).toBe(r / 8);
});
+
+ it('scaleWithZoom', function () {
+ // animation frames are already mocked
+ var r = 0.80;
+ feature1.style({radius: r, blurRadius: 0, scaleWithZoom: true});
+ map.draw();
+ stepAnimationFrame(Date.now());
+ expect(feature1._binned).toBe(102);
+ feature1.style('scaleWithZoom', false);
+ map.draw();
+ stepAnimationFrame(Date.now());
+ expect(feature1._binned).toBe(1.5);
+ });
+
it('Remove a feature from a layer', function () {
layer.deleteFeature(feature1).draw();
expect(layer.children().length).toBe(0);

feat: Add scaleWithZoom option to heatmaps.