leaflet-heatmap.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /*
  2. * Leaflet Heatmap Overlay
  3. *
  4. * Copyright (c) 2008-2016, Patrick Wied (https://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. ;(function (name, context, factory) {
  9. // Supports UMD. AMD, CommonJS/Node.js and browser context
  10. if (typeof module !== "undefined" && module.exports) {
  11. module.exports = factory(
  12. require('heatmap.js'),
  13. require('leaflet')
  14. );
  15. } else if (typeof define === "function" && define.amd) {
  16. define(['heatmap.js', 'leaflet'], factory);
  17. } else {
  18. // browser globals
  19. if (typeof window.h337 === 'undefined') {
  20. throw new Error('heatmap.js must be loaded before the leaflet heatmap plugin');
  21. }
  22. if (typeof window.L === 'undefined') {
  23. throw new Error('Leaflet must be loaded before the leaflet heatmap plugin');
  24. }
  25. context[name] = factory(window.h337, window.L);
  26. }
  27. })("HeatmapOverlay", this, function (h337, L) {
  28. 'use strict';
  29. // Leaflet < 0.8 compatibility
  30. if (typeof L.Layer === 'undefined') {
  31. L.Layer = L.Class;
  32. }
  33. var HeatmapOverlay = L.Layer.extend({
  34. initialize: function (config) {
  35. this.cfg = config;
  36. this._el = L.DomUtil.create('div', 'leaflet-zoom-hide');
  37. this._data = [];
  38. this._max = 1;
  39. this._min = 0;
  40. this.cfg.container = this._el;
  41. },
  42. onAdd: function (map) {
  43. var size = map.getSize();
  44. this._map = map;
  45. this._width = size.x;
  46. this._height = size.y;
  47. this._el.style.width = size.x + 'px';
  48. this._el.style.height = size.y + 'px';
  49. this._el.style.position = 'absolute';
  50. this._origin = this._map.layerPointToLatLng(new L.Point(0, 0));
  51. map.getPanes().overlayPane.appendChild(this._el);
  52. if (!this._heatmap) {
  53. this._heatmap = h337.create(this.cfg);
  54. }
  55. // this resets the origin and redraws whenever
  56. // the zoom changed or the map has been moved
  57. map.on('moveend', this._reset, this);
  58. this._draw();
  59. },
  60. addTo: function (map) {
  61. map.addLayer(this);
  62. return this;
  63. },
  64. onRemove: function (map) {
  65. // remove layer's DOM elements and listeners
  66. map.getPanes().overlayPane.removeChild(this._el);
  67. map.off('moveend', this._reset, this);
  68. },
  69. _draw: function() {
  70. if (!this._map) { return; }
  71. var mapPane = this._map.getPanes().mapPane;
  72. var point = mapPane._leaflet_pos;
  73. // reposition the layer
  74. this._el.style[HeatmapOverlay.CSS_TRANSFORM] = 'translate(' +
  75. -Math.round(point.x) + 'px,' +
  76. -Math.round(point.y) + 'px)';
  77. this._update();
  78. },
  79. _update: function() {
  80. var bounds, zoom, scale;
  81. var generatedData = { max: this._max, min: this._min, data: [] };
  82. bounds = this._map.getBounds();
  83. zoom = this._map.getZoom();
  84. scale = Math.pow(2, zoom);
  85. if (this._data.length == 0) {
  86. if (this._heatmap) {
  87. this._heatmap.setData(generatedData);
  88. }
  89. return;
  90. }
  91. var latLngPoints = [];
  92. var radiusMultiplier = this.cfg.scaleRadius ? scale : 1;
  93. var localMax = 0;
  94. var localMin = 0;
  95. var valueField = this.cfg.valueField;
  96. var len = this._data.length;
  97. while (len--) {
  98. var entry = this._data[len];
  99. var value = entry[valueField];
  100. var latlng = entry.latlng;
  101. // we don't wanna render points that are not even on the map ;-)
  102. if (!bounds.contains(latlng)) {
  103. continue;
  104. }
  105. // local max is the maximum within current bounds
  106. localMax = Math.max(value, localMax);
  107. localMin = Math.min(value, localMin);
  108. var point = this._map.latLngToContainerPoint(latlng);
  109. var latlngPoint = { x: Math.round(point.x), y: Math.round(point.y) };
  110. latlngPoint[valueField] = value;
  111. var radius;
  112. if (entry.radius) {
  113. radius = entry.radius * radiusMultiplier;
  114. } else {
  115. radius = (this.cfg.radius || 2) * radiusMultiplier;
  116. }
  117. latlngPoint.radius = radius;
  118. latLngPoints.push(latlngPoint);
  119. }
  120. if (this.cfg.useLocalExtrema) {
  121. generatedData.max = localMax;
  122. generatedData.min = localMin;
  123. }
  124. generatedData.data = latLngPoints;
  125. this._heatmap.setData(generatedData);
  126. },
  127. setData: function(data) {
  128. this._max = data.max || this._max;
  129. this._min = data.min || this._min;
  130. var latField = this.cfg.latField || 'lat';
  131. var lngField = this.cfg.lngField || 'lng';
  132. var valueField = this.cfg.valueField || 'value';
  133. // transform data to latlngs
  134. var data = data.data;
  135. var len = data.length;
  136. var d = [];
  137. while (len--) {
  138. var entry = data[len];
  139. var latlng = new L.LatLng(entry[latField], entry[lngField]);
  140. var dataObj = { latlng: latlng };
  141. dataObj[valueField] = entry[valueField];
  142. if (entry.radius) {
  143. dataObj.radius = entry.radius;
  144. }
  145. d.push(dataObj);
  146. }
  147. this._data = d;
  148. this._draw();
  149. },
  150. // experimential... not ready.
  151. addData: function(pointOrArray) {
  152. if (pointOrArray.length > 0) {
  153. var len = pointOrArray.length;
  154. while(len--) {
  155. this.addData(pointOrArray[len]);
  156. }
  157. } else {
  158. var latField = this.cfg.latField || 'lat';
  159. var lngField = this.cfg.lngField || 'lng';
  160. var valueField = this.cfg.valueField || 'value';
  161. var entry = pointOrArray;
  162. var latlng = new L.LatLng(entry[latField], entry[lngField]);
  163. var dataObj = { latlng: latlng };
  164. dataObj[valueField] = entry[valueField];
  165. this._max = Math.max(this._max, dataObj[valueField]);
  166. this._min = Math.min(this._min, dataObj[valueField]);
  167. if (entry.radius) {
  168. dataObj.radius = entry.radius;
  169. }
  170. this._data.push(dataObj);
  171. this._draw();
  172. }
  173. },
  174. _reset: function () {
  175. this._origin = this._map.layerPointToLatLng(new L.Point(0, 0));
  176. var size = this._map.getSize();
  177. if (this._width !== size.x || this._height !== size.y) {
  178. this._width = size.x;
  179. this._height = size.y;
  180. this._el.style.width = this._width + 'px';
  181. this._el.style.height = this._height + 'px';
  182. this._heatmap._renderer.setDimensions(this._width, this._height);
  183. }
  184. this._draw();
  185. }
  186. });
  187. HeatmapOverlay.CSS_TRANSFORM = (function() {
  188. var div = document.createElement('div');
  189. var props = [
  190. 'transform',
  191. 'WebkitTransform',
  192. 'MozTransform',
  193. 'OTransform',
  194. 'msTransform'
  195. ];
  196. for (var i = 0; i < props.length; i++) {
  197. var prop = props[i];
  198. if (div.style[prop] !== undefined) {
  199. return prop;
  200. }
  201. }
  202. return props[0];
  203. })();
  204. return HeatmapOverlay;
  205. });