概述:
本文讲述结合heatmap.js,在Openlayers中如何实现热力图。
heatmap.js简介:
Heatmap 是用来呈现一定区域内的统计度量,最常见的网站访问热力图就是以特殊高亮的形式显示访客热衷的页面区域和访客所在的地理区域的图示。Heatmap.js 这个 JavaScript 库可以实现各种动态热力图的网页,帮助您研究和可视化用户的行为。
实现效果:
实现代码:
<html> <head> <meta charset="UTF-8"> <title>heatmap.js OpenLayers Heatmap Layer</title> <link rel="stylesheet" href="../../../plugin/OpenLayers-2.13.1/theme/default/style.css" type="text/css"> <style> html, body, #map{ padding:0; margin:0; height:100%; width:100%; overflow: hidden; } </style> <script src="../../../plugin/OpenLayers-2.13.1/OpenLayers.js"></script> <script type="text/javascript" src="extend/heatmap.js"></script> <script type="text/javascript" src="extend/heatmap-openlayers.js"></script> <script type="text/javascript"> var map, layer, heatmap; function init(){ var testData={ max: 5, data: [ {name:"乌鲁木齐",lat:43.782225,lon:87.576079,count:1}, {name:"拉萨",lat:29.71056,lon:91.163218,count:1}, {name:"西宁",lat:36.593725,lon:101.797439,count:1}, {name:"兰州",lat:36.119175,lon:103.584421,count:2}, {name:"成都",lat:30.714315,lon:104.035634,count:3}, {name:"重庆",lat:29.479073,lon:106.519225,count:4}, {name:"贵阳",lat:26.457486,lon:106.668183,count:2}, {name:"昆明",lat:24.969568,lon:102.726915,count:2}, {name:"银川",lat:38.598593,lon:106.167324,count:2}, {name:"西安",lat:34.276221,lon:108.967213,count:3}, {name:"南宁",lat:22.748502,lon:108.234036,count:3}, {name:"海口",lat:19.97015,lon:110.346274,count:3}, {name:"广州",lat:23.183277,lon:113.226755,count:4}, {name:"长沙",lat:28.170082,lon:112.947996,count:4}, {name:"南昌",lat:28.652529,lon:115.893762,count:4}, {name:"福州",lat:26.070956,lon:119.246798,count:4}, {name:"台北",lat:25.008476,lon:121.503585,count:2}, {name:"杭州",lat:30.330742,lon:120.183062,count:4}, {name:"上海",lat:31.253514,lon:121.449713,count:5}, {name:"武汉",lat:30.579401,lon:114.216652,count:5}, {name:"合肥",lat:31.838495,lon:117.262334,count:3}, {name:"南京",lat:32.085164,lon:118.805714,count:4}, {name:"郑州",lat:34.746419,lon:113.651151,count:4}, {name:"济南",lat:36.608511,lon:117.048354,count:4}, {name:"石家庄",lat:38.033361,lon:114.478253,count:4}, {name:"太原",lat:37.798488,lon:112.483119,count:3}, {name:"呼和浩特",lat:40.895807,lon:111.842856,count:3}, {name:"天津",lat:38.925801,lon:117.351108,count:4}, {name:"沈阳",lat:41.801674,lon:123.29626,count:3}, {name:"长春",lat:43.982041,lon:125.261357,count:4}, {name:"哈尔滨",lat:45.693857,lon:126.567056,count:3}, {name:"北京",lat:39.892297,lon:116.068297,count:5}, {name:"香港",lat:22.428066,lon:114.093184,count:2}, {name:"澳门",lat:22.18471,lon:113.552554,count:1} ] }; var transformedTestData = { max: testData.max , data: [] }, data = testData.data, datalen = data.length, nudata = []; // in order to use the OpenLayers Heatmap Layer we have to transform our data into // { max: <max>, data: [{lonlat: <OpenLayers.LonLat>, count: <count>},...]} while(datalen--){ nudata.push({ lonlat: new OpenLayers.LonLat(data[datalen].lon, data[datalen].lat), count: data[datalen].count }); } transformedTestData.data = nudata; var format = 'image/png'; var bounds = new OpenLayers.Bounds( 73.45100463562233, 18.16324718764174, 134.97679764650596, 53.531943152223576 ); var options = { controls: [], maxExtent: bounds, maxResolution: 0.2403351289487642, projection: "EPSG:4326", units: 'degrees' }; map = new OpenLayers.Map('map', options); var tiled = new OpenLayers.Layer.WMS( "Geoserver layers - Tiled", "http://localhost:8088/geoserver/lzugis/wms", { "LAYERS": 'province', "STYLES": '', format: format }, { buffer: 0, displayOutsideMaxExtent: true, isBaseLayer: true, yx : {'EPSG:4326' : true} } ); OpenLayers.INCHES_PER_UNIT["千米"] = OpenLayers.INCHES_PER_UNIT["km"]; OpenLayers.INCHES_PER_UNIT["米"] = OpenLayers.INCHES_PER_UNIT["m"]; OpenLayers.INCHES_PER_UNIT["英里"] = OpenLayers.INCHES_PER_UNIT["mi"]; OpenLayers.INCHES_PER_UNIT["英寸"] = OpenLayers.INCHES_PER_UNIT["ft"]; //比例尺 map.addControl(new OpenLayers.Control.ScaleLine({topOutUnits:"千米",topInUnits:"米",bottomOutUnits:"英里", bottomInUnits:"英寸" })); map.addControl(new OpenLayers.Control.Zoom()); map.addControl(new OpenLayers.Control.Navigation()); map.addControl(new OpenLayers.Control.OverviewMap()); // create our heatmap layer heatmap = new OpenLayers.Layer.Heatmap( "Heatmap Layer", map, tiled, { visible: true, radius:10 }, { isBaseLayer: false, opacity: 0.3, projection: new OpenLayers.Projection("EPSG:4326") } ); map.addLayers([tiled,heatmap]); map.zoomToExtent(bounds); console.log(transformedTestData); heatmap.setDataSet(transformedTestData); } </script> </head> <body onload="init()"> <div id="map"></div> </body> </html>
附件:
heatmap-openlayers.js
/* * heatmap.js OpenLayers Heatmap Class * * Copyright (c) 2011, Patrick Wied (http://www.patrick-wied.at) * Dual-licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) * and the Beerware (http://en.wikipedia.org/wiki/Beerware) license. * * Modified on Jun,06 2011 by Antonio Santiago (http://www.acuriousanimal.com) * - Heatmaps as independent map layer. * - Points based on OpenLayers.LonLat. * - Data initialization in constructor. * - Improved 'addDataPoint' to add new lonlat based points. */ OpenLayers.Layer.Heatmap = OpenLayers.Class(OpenLayers.Layer, { // the heatmap isn't a basic layer by default - you usually want to display the heatmap over another map ;) isBaseLayer: false, heatmap: null, mapLayer: null, // we store the lon lat data, because we have to redraw with new positions on zoomend|moveend tmpData: {}, initialize: function(name, map, mLayer, hmoptions, options){ var heatdiv = document.createElement("div"), handler; OpenLayers.Layer.prototype.initialize.apply(this, [name, options]); heatdiv.style.cssText = "position:absolute;width:"+map.size.w+"px;height:"+map.size.h+"px;"; // this will be the heatmaps element this.div.appendChild(heatdiv); // add to our heatmap.js config hmoptions.element = heatdiv; this.mapLayer = mLayer; this.map = map; // create the heatmap with passed heatmap-options this.heatmap = h337.create(hmoptions); handler = function(){ if(this.tmpData.max){ this.updateLayer(); } }; // on zoomend and moveend we have to move the canvas element and redraw the datapoints with new positions map.events.register("zoomend", this, handler); map.events.register("moveend", this, handler); }, updateLayer: function(){ var pixelOffset = this.getPixelOffset(), el = this.heatmap.get('element'); // if the pixeloffset e.g. for x was positive move the canvas element to the left by setting left:-offset.y px // otherwise move it the right by setting it a positive value. same for top el.style.top = ((pixelOffset.y > 0)?('-'+pixelOffset.y):(Math.abs(pixelOffset.y)))+'px'; el.style.left = ((pixelOffset.x > 0)?('-'+pixelOffset.x):(Math.abs(pixelOffset.x)))+'px'; this.setDataSet(this.tmpData); }, getPixelOffset: function () { var o = this.mapLayer.map.layerContainerOrigin, o_lonlat = new OpenLayers.LonLat(o.lon, o.lat), o_pixel = this.mapLayer.getViewPortPxFromLonLat(o_lonlat), c = this.mapLayer.map.center, c_lonlat = new OpenLayers.LonLat(c.lon, c.lat), c_pixel = this.mapLayer.getViewPortPxFromLonLat(c_lonlat); return { x: o_pixel.x - c_pixel.x, y: o_pixel.y - c_pixel.y }; }, setDataSet: function(obj){ var set = {}, dataset = obj.data, dlen = dataset.length, entry, lonlat, pixel; set.max = obj.max; set.data = []; // get the pixels for all the lonlat entries while(dlen--){ entry = dataset[dlen], lonlat = entry.lonlat.clone().transform(this.projection, this.map.getProjectionObject()), pixel = this.roundPixels(this.getViewPortPxFromLonLat(lonlat)); if(pixel){ set.data.push({x: pixel.x, y: pixel.y, count: entry.count}); } } this.tmpData = obj; this.heatmap.store.setDataSet(set); }, // we don't want to have decimal numbers such as xxx.9813212 since they slow canvas performance down + don't look nice roundPixels: function(p){ if(p.x < 0 || p.y < 0){ return false; } p.x = (p.x >> 0); p.y = (p.y >> 0); return p; }, // same procedure as setDataSet addDataPoint: function(lonlat){ var pixel = this.roundPixels(this.mapLayer.getViewPortPxFromLonLat(lonlat)), entry = {lonlat: lonlat}, args; if(arguments.length == 2){ entry.count = arguments[1]; } this.tmpData.data.push(entry); if(pixel){ args = [pixel.x, pixel.y]; if(arguments.length == 2){ args.push(arguments[1]); } this.heatmap.store.addDataPoint.apply(this.heatmap.store, args); } }, toggle: function(){ this.heatmap.toggleDisplay(); }, destroy: function() { // for now, nothing special to do here. OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments); }, CLASS_NAME: "OpenLayers.Layer.Heatmap" });