├── .jshintrc ├── MIT-LICENSE.txt ├── README.md ├── index.html ├── license.txt └── src ├── HeatmapLayer.js └── heatmap.js /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "undef": true, 3 | "browser":true, 4 | "devel":true, 5 | "globals": { "require": false, "define":false } 6 | } -------------------------------------------------------------------------------- /MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2012 Esri and other contributors 2 | http://esri.com/ 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # heatmap-layer-js 2 | 3 | This is a custom DynamicMapServiceLayer for Heatmap.js to work with the ArcGIS Javascript API. 4 | 5 | [View it live](http://esri.github.com/heatmap-layer-js/) 6 | 7 | ## Features 8 | [http://www.patrick-wied.at/static/heatmapjs/](http://www.patrick-wied.at/static/heatmapjs/) 9 | heatmap.js is a JavaScript library that can be used to generate web heatmaps with the html5canvas element based on your data. Heatmap instances contain a store in order to colorize the heatmap based on relative data, which means if you're adding only a single datapoint to the store it will be displayed as the hottest(red) spot, then adding another point with a higher count, it will dynamically recalculate. The heatmaps are fully customizable - you're welcome to choose your own color gradient, change its opacity, datapoint radius and many more. 10 | 11 | ## Instructions 12 | See example HTML file included in download. 13 | 14 | [New to Github? Get started here.](https://github.com/) 15 | 16 | ## Requirements 17 | 18 | * Notepad or HTML editor 19 | * A little background with Javascript 20 | * Experience with the [ArcGIS Javascript API](http://www.esri.com/) would help. 21 | 22 | ## Resources 23 | 24 | * [ArcGIS for JavaScript API Resource Center](http://help.arcgis.com/en/webapi/javascript/arcgis/index.html) 25 | * [ArcGIS Blog](http://blogs.esri.com/esri/arcgis/) 26 | * [twitter@esri](http://twitter.com/esri) 27 | 28 | ## Issues 29 | 30 | Find a bug or want to request a new feature? Please let us know by submitting an issue. 31 | 32 | ## Contributing 33 | 34 | Anyone and everyone is welcome to contribute. 35 | 36 | ## Licensing 37 | 38 | Copyright 2017 Esri 39 | 40 | Licensed under the Apache License, Version 2.0 (the "License"); 41 | you may not use this file except in compliance with the License. 42 | You may obtain a copy of the License at 43 | 44 | http://www.apache.org/licenses/LICENSE-2.0 45 | 46 | Unless required by applicable law or agreed to in writing, software 47 | distributed under the License is distributed on an "AS IS" BASIS, 48 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 49 | See the License for the specific language governing permissions and 50 | limitations under the License. 51 | 52 | A copy of the license is available in the repository's [license.txt](https://raw.github.com/Esri/heatmap-layer-js/master/license.txt) file. 53 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | heatmap.js ArcGIS JavaScript API Heatmap Layer 11 | 13 | 14 | 15 | 82 | 83 | 84 | 85 |
86 |

ArcGIS JavaScript API Heatmap Overlay

87 | Download code. 88 |
90 |
91 |
92 |
93 |
San Francisco 311 incidents
94 |
95 |
96 |
97 |

Sidenotes

98 | This is a demonstration of a canvas heatmap overlay with the ArcGIS API for JavaScript. 100 |
101 |
View the Feature layer. 103 |
Toggle HeatmapOverlay
104 |
Toggle Points
105 |
Local Max Off
106 |
107 |
heatmap.js by 108 | Patrick Wied 110 |
111 |
112 |
113 |
114 |

Description

115 | 116 |

This is a custom DynamicMapServiceLayer for Heatmap.js to work with the 117 | ArcGIS Javascript API.

118 |

Instructions

119 | 120 |
    121 |
  1. Include the heatmap.js in your HTML document and require "application/HeatmapLayer"
    122 |         <script type="text/javascript" src="src/heatmap.js"></script>
    123 |         
    124 | 125 |
  2. 126 |
  3. Add a div with an ID to hold he canvas element
    <div id="heatLayer"></div>
    127 | 128 |
  4. 129 |
  5. Create the heatmap with it's settings and assign it to a global variable.
    130 |         // create heat layer
    131 |             heatLayer = new HeatmapLayer({
    132 |                 config: {
    133 |                     "useLocalMaximum": false,
    134 |                     "radius": 40,
    135 |                     "gradient": {
    136 |                         0.45: "rgb(000,000,255)",
    137 |                         0.55: "rgb(000,255,255)",
    138 |                         0.65: "rgb(000,255,000)",
    139 |                         0.95: "rgb(255,255,000)",
    140 |                         1.00: "rgb(255,000,000)"
    141 |                     }
    142 |                 },
    143 |                 "map": map,
    144 |                 "opacity": 0.85
    145 |             }, "heatLayer");
    146 |         
    147 | 148 |
  6. 149 |
  7. Add the heatLayer to the map.
    150 |         map.addLayer(heatLayer);
    151 |         
    152 | 153 |
  8. 154 |
  9. Now you just need to add data points to the heatmap. In the example above, 155 | I am adding features from a feature layer of 311 incidents. Here's an example with 156 | two features.
    157 |         var data = [
    158 |             {
    159 |                 attributes: {},
    160 |                 geometry: {
    161 |                     spatialReference: {wkid: 102100}
    162 |                     type: "point"
    163 |                     x: -13625078.0408
    164 |                     y: 4548494.332400002
    165 |                 }
    166 |             },
    167 |             {
    168 |                 attributes: {},
    169 |                 geometry: {
    170 |                     spatialReference: {wkid: 102100}
    171 |                     type: "point"
    172 |                     x: -13625078.0408
    173 |                     y: 4548494.332400002
    174 |                 }
    175 |             }
    176 |         ];
    177 |         
    178 |
    179 |         heatLayer.setData(data);
    180 |         
    181 | 182 |
  10. 183 |
  11. I created a function that executes on map pan that gets all the features 184 | from a featureLayer within the map's extent. 185 |
    186 | function getFeatures() {
    187 |     // set up query
    188 |     var q = new Query();
    189 |     // only within extent
    190 |     q.geometry = map.extent;
    191 |     // give me all of them!
    192 |     q.where = "1=1";
    193 |     // make sure I get them back in my spatial reference
    194 |     q.outSpatialReference = map.spatialReference;
    195 |     // get em!
    196 |     featureLayer.queryFeatures(q, function (featureSet) {
    197 |         var data = [];
    198 |         // if we get results back
    199 |         if (featureSet && featureSet.features && featureSet.features.length > 0) {
    200 |             // set data to features
    201 |             data = featureSet.features;
    202 |         }
    203 |         // set heatmap data
    204 |         heatLayer.setData(data);
    205 |     });
    206 | }
    207 | 
    208 | 209 |
  12. 210 |
211 |

Dependencies

212 | 213 | 222 |
223 | 224 | 238 | 239 | 240 | 364 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Apache License - 2.0 2 | 3 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 4 | 5 | 1. Definitions. 6 | 7 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 8 | 9 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 10 | 11 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control 12 | with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management 13 | of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial 14 | ownership of such entity. 15 | 16 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 17 | 18 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, 19 | and configuration files. 20 | 21 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to 22 | compiled object code, generated documentation, and conversions to other media types. 23 | 24 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice 25 | that is included in or attached to the work (an example is provided in the Appendix below). 26 | 27 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the 28 | editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes 29 | of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, 30 | the Work and Derivative Works thereof. 31 | 32 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work 33 | or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual 34 | or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of 35 | electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on 36 | electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for 37 | the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing 38 | by the copyright owner as "Not a Contribution." 39 | 40 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and 41 | subsequently incorporated within the Work. 42 | 43 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, 44 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, 45 | publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 46 | 47 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, 48 | non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, 49 | sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are 50 | necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was 51 | submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work 52 | or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You 53 | under this License for that Work shall terminate as of the date such litigation is filed. 54 | 55 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, 56 | and in Source or Object form, provided that You meet the following conditions: 57 | 58 | 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and 59 | 60 | 2. You must cause any modified files to carry prominent notices stating that You changed the files; and 61 | 62 | 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices 63 | from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 64 | 65 | 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a 66 | readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the 67 | Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the 68 | Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever 69 | such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. 70 | You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, 71 | provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to 72 | Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your 73 | modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with 74 | the conditions stated in this License. 75 | 76 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You 77 | to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, 78 | nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 79 | 80 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except 81 | as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 82 | 83 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides 84 | its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, 85 | any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for 86 | determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under 87 | this License. 88 | 89 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required 90 | by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, 91 | including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the 92 | use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or 93 | any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 94 | 95 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a 96 | fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting 97 | such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree 98 | to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your 99 | accepting any such warranty or additional liability. 100 | 101 | END OF TERMS AND CONDITIONS 102 | -------------------------------------------------------------------------------- /src/HeatmapLayer.js: -------------------------------------------------------------------------------- 1 | /* global h337 */ 2 | define([ 3 | "dojo/_base/declare", 4 | "dojo/_base/lang", 5 | "dijit/_WidgetBase", 6 | "dojo/dom", 7 | "dojo/query", 8 | "dojo/dom-style", 9 | "dojo/on", 10 | "esri/layers/DynamicMapServiceLayer", 11 | "esri/geometry/screenUtils", 12 | "esri/geometry/Point" 13 | ], function( 14 | declare, lang, 15 | _WidgetBase, 16 | dom, 17 | query, 18 | domStyle, 19 | on, 20 | DynamicMapServiceLayer, 21 | screenUtils, 22 | Point 23 | ) { 24 | return declare([_WidgetBase, DynamicMapServiceLayer], { 25 | options: { 26 | useLocalMaximum: false, 27 | map: null, 28 | config:{ 29 | radius: 40, 30 | debug: false, 31 | visible: true, 32 | gradient: { 33 | 0.45: "rgb(000,000,255)", 34 | 0.55: "rgb(000,255,255)", 35 | 0.65: "rgb(000,255,000)", 36 | 0.95: "rgb(255,255,000)", 37 | 1.00: "rgb(255,000,000)" 38 | } 39 | } 40 | }, 41 | // constructor 42 | constructor: function(options, srcNode) { 43 | // last data storage 44 | this.set("data", []); 45 | // map node 46 | this.domNode = dom.byId(srcNode); 47 | // defaults 48 | var defaults = lang.mixin({}, this.options, options); 49 | // map var 50 | this.set("map", defaults.map); 51 | this.set("useLocalMaximum", defaults.useLocalMaximum); 52 | defaults.config.height = this.get("map").height; 53 | defaults.config.width = this.get("map").width; 54 | defaults.config.container = this.domNode; 55 | this.set("config", defaults.config); 56 | // create heatmap 57 | this.heatMap = h337.create(this.get("config")); 58 | // global maximum value 59 | this.set("globalMax", 0); 60 | // connect on resize 61 | this.own(on(this.get("map"), "resize", lang.hitch(this, function(evt) { 62 | this.resizeHeatmap(evt.width, evt.height); 63 | }))); 64 | // heatlayer div styling 65 | domStyle.set(this.domNode, { 66 | position: "absolute", 67 | display: "none" 68 | }); 69 | // loaded 70 | this.set("loaded",true); 71 | this.inherited(arguments); 72 | }, 73 | resizeHeatmap: function(width, height) { 74 | // set heatmap data size 75 | 76 | this.heatMap._renderer.setDimensions(width,height); 77 | //this.heatMap.set("width", width); 78 | //this.heatMap.set("height", height); 79 | // set width and height of container 80 | domStyle.set(this.domNode, { 81 | "width": width + 'px', 82 | "height": height + 'px' 83 | }); 84 | // set width and height of canvas element inside of container 85 | var child = query(':first-child', this.domNode); 86 | if (child) { 87 | child.attr('width', width); 88 | child.attr('height', height); 89 | } 90 | // set atx canvas width and height fix 91 | /* var actx = this.heatMap._renderer.shadowCtx; 92 | actx.canvas.height = height; 93 | actx.canvas.width = width; 94 | this.heatMap._renderer.shadowCtx = actx;*/ 95 | // refresh image and heat map size 96 | this.refresh(); 97 | }, 98 | // stores heatmap converted data into the plugin which renders it 99 | storeHeatmapData: function(heatPluginData) { 100 | // set heatmap data 101 | this.heatMap.setData(heatPluginData); 102 | }, 103 | // converts parsed data into heatmap format 104 | convertHeatmapData: function(parsedData) { 105 | // variables 106 | var xParsed, yParsed, heatPluginData, screenGeometry; 107 | // set heat plugin data object 108 | heatPluginData = { 109 | max: parsedData.max, 110 | data: [] // empty data 111 | }; 112 | // if data 113 | if (parsedData.data) { 114 | // for all x values 115 | for (xParsed in parsedData.data) { 116 | // if data[x] 117 | if (parsedData.data.hasOwnProperty(xParsed)) { 118 | // for all y values and count 119 | for (yParsed in parsedData.data[xParsed]) { 120 | if (parsedData.data[xParsed].hasOwnProperty(yParsed)) { 121 | // make sure extent is normalized 122 | var normalizedExtent = this.map.extent._normalize(); 123 | // convert data point into screen geometry 124 | screenGeometry = screenUtils.toScreenGeometry(normalizedExtent, this.get("map").width, this.get("map").height, parsedData.data[xParsed][yParsed].dataPoint); 125 | // push to heatmap plugin data array 126 | heatPluginData.data.push({ 127 | x: screenGeometry.x, 128 | y: screenGeometry.y, 129 | count: parsedData.data[xParsed][yParsed].count // count value of x,y 130 | }); 131 | } 132 | } 133 | } 134 | } 135 | } 136 | // store in heatmap plugin which will render it 137 | this.storeHeatmapData(heatPluginData); 138 | }, 139 | // runs through data and calulates weights and max 140 | parseHeatmapData: function(features) { 141 | // variables 142 | var i, parsedData, dataPoint, attributes; 143 | // if data points exist 144 | if (features) { 145 | // create parsed data object 146 | parsedData = { 147 | max: 0, 148 | data: [] 149 | }; 150 | if (!this.get("useLocalMaximum")) { 151 | parsedData.max = this.get("globalMax"); 152 | } 153 | // for each data point 154 | for (i = 0; i < features.length; i++) { 155 | // get geometry and normalize it 156 | var geo = features[i].geometry.normalize(); 157 | // create geometry point 158 | dataPoint = new Point(geo.x, geo.y, this.get("map").spatialReference); 159 | // get extent and normalize it. 160 | var normalizedExtent = this.get("map").extent._normalize(); 161 | // check point 162 | var validPoint = false; 163 | // if not using local max, point is valid 164 | if (!this.get("useLocalMaximum")) { 165 | validPoint = true; 166 | } 167 | // using local max, make sure point is within extent 168 | else if (normalizedExtent.contains(dataPoint)) { 169 | validPoint = true; 170 | } 171 | if (validPoint) { 172 | // attributes 173 | attributes = features[i].attributes; 174 | // if array value is undefined 175 | if (!parsedData.data[dataPoint.x]) { 176 | // create empty array value 177 | parsedData.data[dataPoint.x] = []; 178 | } 179 | // array value array is undefined 180 | if (!parsedData.data[dataPoint.x][dataPoint.y]) { 181 | // create object in array 182 | parsedData.data[dataPoint.x][dataPoint.y] = {}; 183 | // if count is defined in datapoint 184 | if (attributes && attributes.hasOwnProperty('count')) { 185 | // create array value with count of count set in datapoint 186 | parsedData.data[dataPoint.x][dataPoint.y].count = attributes.count; 187 | } else { 188 | // create array value with count of 0 189 | parsedData.data[dataPoint.x][dataPoint.y].count = 0; 190 | } 191 | } 192 | // add 1 to the count 193 | parsedData.data[dataPoint.x][dataPoint.y].count += 1; 194 | // store dataPoint var 195 | parsedData.data[dataPoint.x][dataPoint.y].dataPoint = dataPoint; 196 | // if count is greater than current max 197 | if (parsedData.max < parsedData.data[dataPoint.x][dataPoint.y].count) { 198 | // set max to this count 199 | parsedData.max = parsedData.data[dataPoint.x][dataPoint.y].count; 200 | if (!this.get("useLocalMaximum")) { 201 | this.set("globalMax", parsedData.data[dataPoint.x][dataPoint.y].count); 202 | } 203 | } 204 | } 205 | } 206 | // convert parsed data into heatmap plugin formatted data 207 | this.convertHeatmapData(parsedData); 208 | } 209 | }, 210 | // set data function call 211 | setData: function(features) { 212 | // set width/height 213 | this.resizeHeatmap(this.get("map").width, this.get("map").height); 214 | // store points 215 | this.set("data", features); 216 | // create data and then store it 217 | this.parseHeatmapData(features); 218 | // redraws the heatmap 219 | this.refresh(); 220 | }, 221 | // add one feature to the heatmap 222 | addDataPoint: function(feature) { 223 | if (feature) { 224 | // push to data 225 | var data = this.get("data"); 226 | data.push(feature); 227 | // set data 228 | this.setData(data); 229 | } 230 | }, 231 | // return data set of features 232 | exportDataSet: function() { 233 | return this.get("data"); 234 | }, 235 | // clear data function 236 | clearData: function() { 237 | // empty heat map 238 | this.heatMap.clear(); 239 | // empty array 240 | var empty = []; 241 | // set data to empty array 242 | this.setData(empty); 243 | }, 244 | // get image 245 | getImageUrl: function(extent, width, height, callback) { 246 | // create heatmap data using last data 247 | this.parseHeatmapData(this.get("data")); 248 | // image data 249 | var imageUrl = this.heatMap.getDataURL(); 250 | // callback 251 | callback(imageUrl); 252 | } 253 | }); 254 | }); -------------------------------------------------------------------------------- /src/heatmap.js: -------------------------------------------------------------------------------- 1 | /* 2 | * heatmap.js v2.0.0 | JavaScript Heatmap Library 3 | * 4 | * Copyright 2008-2014 Patrick Wied - All rights reserved. 5 | * Dual licensed under MIT and Beerware license 6 | * 7 | * :: 2014-08-05 01:42 8 | */ 9 | (function(a){var b={defaultRadius:40,defaultRenderer:"canvas2d",defaultGradient:{.25:"rgb(0,0,255)",.55:"rgb(0,255,0)",.85:"yellow",1:"rgb(255,0,0)"},defaultMaxOpacity:1,defaultMinOpacity:0,defaultBlur:.85,defaultXField:"x",defaultYField:"y",defaultValueField:"value",plugins:{}};var c=function i(){var a=function d(a){this._coordinator={};this._data=[];this._radi=[];this._min=0;this._max=1;this._xField=a["xField"]||a.defaultXField;this._yField=a["yField"]||a.defaultYField;this._valueField=a["valueField"]||a.defaultValueField;if(a["radius"]){this._cfgRadius=a["radius"]}};var c=b.defaultRadius;a.prototype={_organiseData:function(a,b){var d=a[this._xField];var e=a[this._yField];var f=this._radi;var g=this._data;var h=this._max;var i=this._min;var j=a[this._valueField]||1;var k=a.radius||this._cfgRadius||c;if(!g[d]){g[d]=[];f[d]=[]}if(!g[d][e]){g[d][e]=j;f[d][e]=k}else{g[d][e]+=j}if(g[d][e]>h){if(!b){this._max=g[d][e]}else{this.setDataMax(g[d][e])}return false}else{return{x:d,y:e,value:j,radius:k,min:i,max:h}}},_unOrganizeData:function(){var a=[];var b=this._data;var c=this._radi;for(var d in b){for(var e in b[d]){a.push({x:d,y:e,radius:c[d][e],value:b[d][e]})}}return{min:this._min,max:this._max,data:a}},_onExtremaChange:function(){this._coordinator.emit("extremachange",{min:this._min,max:this._max})},addData:function(){if(arguments[0].length>0){var a=arguments[0];var b=a.length;while(b--){this.addData.call(this,a[b])}}else{var c=this._organiseData(arguments[0],true);if(c){this._coordinator.emit("renderpartial",{min:this._min,max:this._max,data:[c]})}}return this},setData:function(a){var b=a.data;var c=b.length;this._max=a.max;this._min=a.min||0;this._data=[];this._radi=[];for(var d=0;dthis._renderBoundaries[2]){this._renderBoundaries[2]=l+2*j}if(m+2*j>this._renderBoundaries[3]){this._renderBoundaries[3]=m+2*j}}},_colorize:function(){var a=this._renderBoundaries[0];var b=this._renderBoundaries[1];var c=this._renderBoundaries[2]-a;var d=this._renderBoundaries[3]-b;var e=this._width;var f=this._height;var g=this._opacity;var h=this._maxOpacity;var i=this._minOpacity;if(a<0){a=0}if(b<0){b=0}if(a+c>e){c=e-a}if(b+d>f){d=f-b}var j=this.shadowCtx.getImageData(a,b,c,d);var k=j.data;var l=k.length;var m=this._palette;for(var n=3;n0){q=g}else{if(o>0;return b},getDataURL:function(){return this.canvas.toDataURL()}};return d}();var e=function k(){var a=false;if(b["defaultRenderer"]==="canvas2d"){a=d}return a}();var f={merge:function(){var a={};var b=arguments.length;for(var c=0;c