/plugins/leaflet-heatmap.js

http://github.com/pa7/heatmap.js · JavaScript · 203 lines · 151 code · 36 blank · 16 comment · 26 complexity · 633ea1575e888d3accb1ef0a4a6e33d3 MD5 · raw file

  1. /*
  2. * Leaflet Heatmap Overlay
  3. *
  4. * Copyright (c) 2014, Patrick Wied (http://www.patrick-wied.at)
  5. * Dual-licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
  6. * and the Beerware (http://en.wikipedia.org/wiki/Beerware) license.
  7. */
  8. // Leaflet < 0.8 compatibility
  9. if (typeof L.Layer === 'undefined') {
  10. L.Layer = L.Class;
  11. }
  12. var HeatmapOverlay = L.Layer.extend({
  13. initialize: function (config) {
  14. this.cfg = config;
  15. this._el = L.DomUtil.create('div', 'leaflet-zoom-hide');
  16. this._data = [];
  17. this._max = 1;
  18. this._min = 0;
  19. this.cfg.container = this._el;
  20. },
  21. onAdd: function (map) {
  22. var size = map.getSize();
  23. this._map = map;
  24. this._width = size.x;
  25. this._height = size.y;
  26. this._el.style.width = size.x + 'px';
  27. this._el.style.height = size.y + 'px';
  28. this._resetOrigin();
  29. map.getPanes().overlayPane.appendChild(this._el);
  30. if (!this._heatmap) {
  31. this._heatmap = h337.create(this.cfg);
  32. }
  33. // on zoom, reset origin
  34. map.on('viewreset', this._resetOrigin, this);
  35. // redraw whenever dragend
  36. map.on('dragend', this._draw, this);
  37. this._draw();
  38. },
  39. onRemove: function (map) {
  40. // remove layer's DOM elements and listeners
  41. map.getPanes().overlayPane.removeChild(this._el);
  42. map.off('viewreset', this._resetOrigin, this);
  43. map.off('dragend', this._draw, this);
  44. },
  45. _draw: function() {
  46. if (!this._map) { return; }
  47. var point = this._map.latLngToContainerPoint(this._origin);
  48. // reposition the layer
  49. this._el.style[HeatmapOverlay.CSS_TRANSFORM] = 'translate(' +
  50. -Math.round(point.x) + 'px,' +
  51. -Math.round(point.y) + 'px)';
  52. this._update();
  53. },
  54. _update: function() {
  55. var bounds, zoom, scale;
  56. bounds = this._map.getBounds();
  57. zoom = this._map.getZoom();
  58. scale = Math.pow(2, zoom);
  59. if (this._data.length == 0) {
  60. return;
  61. }
  62. var generatedData = { max: this._max, min: this._min };
  63. var latLngPoints = [];
  64. var radiusMultiplier = this.cfg.scaleRadius ? scale : 1;
  65. var localMax = 0;
  66. var localMin = 0;
  67. var valueField = this.cfg.valueField;
  68. var len = this._data.length;
  69. while (len--) {
  70. var entry = this._data[len];
  71. var value = entry[valueField];
  72. var latlng = entry.latlng;
  73. // we don't wanna render points that are not even on the map ;-)
  74. if (!bounds.contains(latlng)) {
  75. continue;
  76. }
  77. // local max is the maximum within current bounds
  78. localMax = Math.max(value, localMax);
  79. localMin = Math.min(value, localMin);
  80. var point = this._map.latLngToContainerPoint(latlng);
  81. var latlngPoint = { x: Math.round(point.x), y: Math.round(point.y) };
  82. latlngPoint[valueField] = value;
  83. var radius;
  84. if (entry.radius) {
  85. radius = entry.radius * radiusMultiplier;
  86. } else {
  87. radius = (this.cfg.radius || 2) * radiusMultiplier;
  88. }
  89. latlngPoint.radius = radius;
  90. latLngPoints.push(latlngPoint);
  91. }
  92. if (this.cfg.useLocalExtrema) {
  93. generatedData.max = localMax;
  94. generatedData.min = localMin;
  95. }
  96. generatedData.data = latLngPoints;
  97. this._heatmap.setData(generatedData);
  98. },
  99. setData: function(data) {
  100. this._max = data.max || this._max;
  101. this._min = data.min || this._min;
  102. var latField = this.cfg.latField || 'lat';
  103. var lngField = this.cfg.lngField || 'lng';
  104. var valueField = this.cfg.valueField || 'value';
  105. // transform data to latlngs
  106. var data = data.data;
  107. var len = data.length;
  108. var d = [];
  109. while (len--) {
  110. var entry = data[len];
  111. var latlng = new L.LatLng(entry[latField], entry[lngField]);
  112. var dataObj = { latlng: latlng };
  113. dataObj[valueField] = entry[valueField];
  114. if (entry.radius) {
  115. dataObj.radius = entry.radius;
  116. }
  117. d.push(dataObj);
  118. }
  119. this._data = d;
  120. this._draw();
  121. },
  122. // experimential... not ready.
  123. addData: function(pointOrArray) {
  124. if (pointOrArray.length > 0) {
  125. var len = pointOrArray.length;
  126. while(len--) {
  127. this.addData(pointOrArray[len]);
  128. }
  129. } else {
  130. var latField = this.cfg.latField || 'lat';
  131. var lngField = this.cfg.lngField || 'lng';
  132. var valueField = this.cfg.valueField || 'value';
  133. var entry = pointOrArray;
  134. var latlng = new L.LatLng(entry[latField], entry[lngField]);
  135. var dataObj = { latlng: latlng };
  136. dataObj[valueField] = entry[valueField];
  137. this._max = Math.max(this._max, dataObj[valueField]);
  138. this._min = Math.min(this._min, dataObj[valueField]);
  139. if (entry.radius) {
  140. dataObj.radius = entry.radius;
  141. }
  142. this._data.push(dataObj);
  143. this._draw();
  144. }
  145. },
  146. _resetOrigin: function () {
  147. this._origin = this._map.layerPointToLatLng(new L.Point(0, 0));
  148. this._draw();
  149. }
  150. });
  151. HeatmapOverlay.CSS_TRANSFORM = (function() {
  152. var div = document.createElement('div');
  153. var props = [
  154. 'transform',
  155. 'WebkitTransform',
  156. 'MozTransform',
  157. 'OTransform',
  158. 'msTransform'
  159. ];
  160. for (var i = 0; i < props.length; i++) {
  161. var prop = props[i];
  162. if (div.style[prop] !== undefined) {
  163. return prop;
  164. }
  165. }
  166. return props[0];
  167. })();