Openlayers熱力圖層
阿新 • • 發佈:2019-01-07
無意中找到的一個Openlayers生成熱力圖層的實現
原文地址:http://www.websitedev.de/temp/openlayers-heatmap-layer.html
HeatmapLayer.js:
示例Html:/* * Copyright (c) 2010 Bjoern Hoehrmann <http://bjoern.hoehrmann.de/>. * This module is licensed under the same terms as OpenLayers itself. * */ Heatmap = {}; /** * Class: Heatmap.Source */ Heatmap.Source = OpenLayers.Class({ /** * APIProperty: lonlat * {OpenLayers.LonLat} location of the heat source */ lonlat: null, /** * APIProperty: radius * {Number} Heat source radius */ radius: null, /** * APIProperty: intensity * {Number} Heat source intensity */ intensity: null, /** * Constructor: Heatmap.Source * Create a heat source. * * Parameters: * lonlat - {OpenLayers.LonLat} Coordinates of the heat source * radius - {Number} Optional radius * intensity - {Number} Optional intensity */ initialize: function(lonlat, radius, intensity) { this.lonlat = lonlat; this.radius = radius; this.intensity = intensity; }, CLASS_NAME: 'Heatmap.Source' }); /** * Class: Heatmap.Layer * * Inherits from: * - <OpenLayers.Layer> */ Heatmap.Layer = OpenLayers.Class(OpenLayers.Layer, { /** * APIProperty: isBaseLayer * {Boolean} Heatmap layer is never a base layer. */ isBaseLayer: false, /** * Property: points * {Array(<Heatmap.Source>)} internal coordinate list */ points: null, /** * Property: cache * {Object} Hashtable with CanvasGradient objects */ cache: null, /** * Property: gradient * {Array(Number)} RGBA gradient map used to colorize the intensity map. */ gradient: null, /** * Property: canvas * {DOMElement} Canvas element. */ canvas: null, /** * APIProperty: defaultRadius * {Number} Heat source default radius */ defaultRadius: null, /** * APIProperty: defaultIntensity * {Number} Heat source default intensity */ defaultIntensity: null, /** * Constructor: Heatmap.Layer * Create a heatmap layer. * * Parameters: * name - {String} Name of the Layer * options - {Object} Hashtable of extra options to tag onto the layer */ initialize: function(name, options) { OpenLayers.Layer.prototype.initialize.apply(this, arguments); this.points = []; this.cache = {}; this.canvas = document.createElement('canvas'); this.canvas.style.position = 'absolute'; this.defaultRadius = 20; this.defaultIntensity = 0.2; this.setGradientStops({ 0.00: 0xffffff00, 0.10: 0x99e9fdff, 0.20: 0x00c9fcff, 0.30: 0x00e9fdff, 0.30: 0x00a5fcff, 0.40: 0x0078f2ff, 0.50: 0x0e53e9ff, 0.60: 0x4a2cd9ff, 0.70: 0x890bbfff, 0.80: 0x99019aff, 0.90: 0x990664ff, 0.99: 0x660000ff, 1.00: 0x000000ff }); // For some reason OpenLayers.Layer.setOpacity assumes there is // an additional div between the layer's div and its contents. var sub = document.createElement('div'); sub.appendChild(this.canvas); this.div.appendChild(sub); }, /** * APIMethod: setGradientStops * ... * * Parameters: * stops - {Object} Hashtable with stop position as keys and colors * as values. Stop positions are numbers between 0 * and 1, color values numbers in 0xRRGGBBAA form. */ setGradientStops: function(stops) { // There is no need to perform the linear interpolation manually, // it is sufficient to let the canvas implementation do that. var ctx = document.createElement('canvas').getContext('2d'); var grd = ctx.createLinearGradient(0, 0, 256, 0); for (var i in stops) { grd.addColorStop(i, 'rgba(' + ((stops[i] >> 24) & 0xFF) + ',' + ((stops[i] >> 16) & 0xFF) + ',' + ((stops[i] >> 8) & 0xFF) + ',' + ((stops[i] >> 0) & 0xFF) + ')'); } ctx.fillStyle = grd; ctx.fillRect(0, 0, 256, 1); this.gradient = ctx.getImageData(0, 0, 256, 1).data; }, /** * APIMethod: addSource * Adds a heat source to the layer. * * Parameters: * source - {<Heatmap.Source>} */ addSource: function(source) { this.points.push(source); }, /** * APIMethod: removeSource * Removes a heat source from the layer. * * Parameters: * source - {<Heatmap.Source>} */ removeSource: function(source) { if (this.points && this.points.length) { OpenLayers.Util.removeItem(this.points, source); } }, /** * Method: moveTo * * Parameters: * bounds - {<OpenLayers.Bounds>} * zoomChanged - {Boolean} * dragging - {Boolean} */ moveTo: function(bounds, zoomChanged, dragging) { OpenLayers.Layer.prototype.moveTo.apply(this, arguments); // The code is too slow to update the rendering during dragging. if (dragging) return; // Pick some point on the map and use it to determine the offset // between the map's 0,0 coordinate and the layer's 0,0 position. var someLoc = new OpenLayers.LonLat(0,0); var offsetX = this.map.getViewPortPxFromLonLat(someLoc).x - this.map.getLayerPxFromLonLat(someLoc).x; var offsetY = this.map.getViewPortPxFromLonLat(someLoc).y - this.map.getLayerPxFromLonLat(someLoc).y; this.canvas.width = this.map.getSize().w; this.canvas.height = this.map.getSize().h; var ctx = this.canvas.getContext('2d'); ctx.save(); // Workaround for a bug in Google Chrome ctx.fillStyle = 'transparent'; ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); ctx.restore(); for (var i in this.points) { var src = this.points[i]; var rad = src.radius || this.defaultRadius; var int = src.intensity || this.defaultIntensity; var pos = this.map.getLayerPxFromLonLat(src.lonlat); var x = pos.x - rad + offsetX; var y = pos.y - rad + offsetY; if (!this.cache[int]) { this.cache[int] = {}; } if (!this.cache[int][rad]) { var grd = ctx.createRadialGradient(rad, rad, 0, rad, rad, rad); grd.addColorStop(0.0, 'rgba(0, 0, 0, ' + int + ')'); grd.addColorStop(1.0, 'transparent'); this.cache[int][rad] = grd; } ctx.fillStyle = this.cache[int][rad]; ctx.translate(x, y); ctx.fillRect(0, 0, 2 * rad, 2 * rad); ctx.translate(-x, -y); } var dat = ctx.getImageData(0, 0, this.canvas.width, this.canvas.height); var dim = this.canvas.width * this.canvas.height * 4; var pix = dat.data; for (var p = 0; p < dim;) { var a = pix[ p + 3 ] * 4; pix[ p++ ] = this.gradient[ a++ ]; pix[ p++ ] = this.gradient[ a++ ]; pix[ p++ ] = this.gradient[ a++ ]; pix[ p++ ] = this.gradient[ a++ ]; } ctx.putImageData(dat, 0, 0); // Unfortunately OpenLayers does not currently support layers that // remain in a fixed position with respect to the screen location // of the base layer, so this puts this layer manually back into // that position using one point's offset as determined earlier. this.canvas.style.left = (-offsetX) + 'px'; this.canvas.style.top = (-offsetY) + 'px'; }, /** * APIMethod: getDataExtent * Calculates the max extent which includes all of the heat sources. * * Returns: * {<OpenLayers.Bounds>} */ getDataExtent: function () { var maxExtent = null; if (this.points && (this.points.length > 0)) { var maxExtent = new OpenLayers.Bounds(); for(var i = 0, len = this.points.length; i < len; ++i) { var point = this.points[i]; maxExtent.extend(point.lonlat); } } return maxExtent; }, CLASS_NAME: 'Heatmap.Layer' });
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>OpenLayers Heatmap Layer (using <canvas>)</title> <script src="OpenLayers.js" type="text/javascript"></script> <script src="HeatmapLayer.js" type="text/javascript"></script> <script type="text/javascript"> //<![CDATA[ var map; var points=[]; function MapMoveend(event){ // //alert("moveend"); var level = map.getZoom(); var heat = map.getLayersByName("Heatmap")[0]; if(level>3){ for(var i=0;i<points.length;i++){ heat.removeSource(points[i]); } var coordinates = [ [ /* 47273 */ 54.5261111111, 9.91555555556 ], [ /* 58696 */ 54.4563888889, 11.2125 ], [ /* 39069 */ 54.8122777778, 9.46488888889 ], [ /* 41256 */ 54.3265, 10.14 ], [ /* 53322 */ 54.5280555556, 8.5125 ], [ /* 53326 */ 54.4652777778, 8.72638888889 ], [ /* 53327 */ 54.4633333333, 8.55305555556 ], [ /* 64553 */ 54.4683333333, 11.1438888889 ], [ /* 53876 */ 54.48, 11.0677777778 ], [ /* 310169 */ 54.46185, 11.2433666667 ], [ /* 344838 */ 54.42472, 10.28423 ], [ /* 285492 */ 54.4, 10.35 ], [ /* 288560 */ 54.4, 9.85 ] ]; for (var latlng in coordinates) { var point = new Heatmap.Source(new OpenLayers.LonLat(coordinates[latlng][1], coordinates[latlng][0]),20,(coordinates[latlng][1]-8)); points.push(point); heat.addSource(point); } heat.redraw(true); } else{ for(var i=0;i<points.length;i++){ heat.removeSource(points[i]); } for (var i=0;i<5000;i++) { var lon = Math.random()*180; var lat = Math.random()*90; var point = new Heatmap.Source(new OpenLayers.LonLat(lon,lat),20,Math.random()); points.push(point); heat.addSource(point); } heat.redraw(true); } }; function init() { map = new OpenLayers.Map('map', { controls: [ new OpenLayers.Control.Navigation(), new OpenLayers.Control.PanZoomBar(), new OpenLayers.Control.LayerSwitcher({'ascending':false}), new OpenLayers.Control.MousePosition(), // new OpenLayers.Control.KeyboardDefaults() ], eventListeners: { "moveend": MapMoveend, } }); var heat = new Heatmap.Layer("Heatmap"); //heat.defaultIntensity = 1; heat.setOpacity(0.75); heat.setGradientStops({ 0.00: 0xffffff00, 0.10: 0x00C7FFff, 0.50: 0xFFFF00ff, 1.00: 0xFF0000ff }); var wms = new OpenLayers.Layer.WMS("OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'}); map.addLayers([wms, heat]); //map.zoomToExtent(heat.getDataExtent()); } //]]> </script> </head> <body onload="init()"> <h1 style='text-align:center'>OpenLayers Heatmap Layer (using <canvas>)</h1> <div id="map" style='height:600px'></div> <h2>Usage</h2> <pre> var heat = new Heatmap.Layer("Heatmap"); heat.addSource(new Heatmap.Source(new OpenLayers.LonLat(9.434, 54.740))); heat.addSource(new Heatmap.Source(new OpenLayers.LonLat(9.833, 54.219))); ... map.addLayer(heat); map.zoomToExtent(heat.getDataExtent()); </pre> </body> </html>