/*******************************************************************************
* Description:
* This JavaScript file provides an algorithm that can be used to add a heatmap
* overlay on a Bing Maps v7 control. The intensity and temperature palette
* of the heatmap are designed to be easily customisable.
*
* Requirements:
* The heatmap layer itself is created dynamically on the client-side using
* the HTML5 <canvas> element, and therefore requires a browser that supports
* this element. It has been tested on IE9, Firefox 3.6/4 and
* Chrome 10 browsers. If you can confirm whether it works on other browsers or
* not, I'd love to hear from you!
* Usage:
* The HeatMapLayer constructor requires:
* - A reference to a map object
* - An array or Microsoft.Maps.Location items
* - Optional parameters to customise the appearance of the layer
* (Radius, Intensity, and ColourGradient), and a callback function
*
*/
var HeatMapLayer = function(map, locations, options) {
/* Private Properties */
var _map = map,
_canvas,
_temperaturemap,
_locations = [];
// Set default options
var _options = {
// Opacity at the centre of each heat point
intensity: 0.5,
// Affected radius of each heat point
radius: 20,
// Colour temperature gradient of the map
colourgradient: {
"0.00": 'rgba(255,0,255,20)', // Magenta
"0.25": 'rgba(0,0,255,40)', // Blue
"0.50": 'rgba(0,255,0,80)', // Green
"0.75": 'rgba(255,255,0,120)', // Yellow
"1.00": 'rgba(255,0,0,150)' // Red
},
// Callback function to be fired after heatmap layer has been redrawn
callback: null
};
/* Private Methods */
function _init() {
// Create the canvas element and place it in the DOM
_canvas = document.createElement('canvas');
_canvas.id = 'heatmapcanvas'
_canvas.style.position = 'relative';
var _mapDiv = _map.getRootElement();
_mapDiv.parentNode.lastChild.appendChild(_canvas);
// Override defaults with any options passed in the constructor
setOptions(options);
// Load array of location data
setPoints(locations);
// Create a colour gradient from the suppied colourstops
_temperaturemap = createColourGradient(_options.colourgradient);
// Wire up the event handler to redraw heatmap canvas
Microsoft.Maps.Events.addHandler(_map, 'viewchangestart', clearHeatMap);
Microsoft.Maps.Events.addHandler(_map, 'viewchangeend', createHeatMap);
delete _init;
}
/* Public Methods */
function clearHeatMap() {
var ctx = _canvas.getContext("2d");
ctx.clearRect(0, 0, _canvas.width, _canvas.height)
}
function createColourGradient(colourstops) {
var ctx = document.createElement('canvas').getContext('2d');
var grd = ctx.createLinearGradient(0, 0, 256, 0);
for (var c in colourstops) {
grd.addColorStop(c, colourstops[c]);
}
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 256, 1);
return ctx.getImageData(0, 0, 256, 1).data;
}
function colouriseHeatMap() {
var ctx = _canvas.getContext("2d");
// Colourise
var dat = ctx.getImageData(0, 0, _canvas.width, _canvas.height);
var pix = dat.data; // pix is a CanvasPixelArray containing height x width x 4 bytes of data (RGBA)
for (var p = 0; p < pix.length; ) {
var a = pix[p + 3] * 4; // get the alpha of this pixel
if (a != 0) { // If there is any data to plot
pix[p] = _temperaturemap[a]; // set the red value of the gradient that corresponds to this alpha
pix[p + 1] = _temperaturemap[a + 1]; //set the green value based on alpha
pix[p + 2] = _temperaturemap[a + 2]; //set the blue value based on alpha
}
p += 4; // Move on to the next pixel
}
ctx.putImageData(dat, 0, 0);
}
function setOptions(options) {
for (attrname in options) {
_options[attrname] = options[attrname];
}
}
function setPoints(locations) {
_locations = locations;
}
function createHeatMap() {
// Ensure the canvas matches the current dimensions of the map
// This also has the effect of resetting the canvas
_canvas.height = _map.getHeight();
_canvas.width = _map.getWidth();
var ctx = _canvas.getContext("2d");
// Create the Intensity Map
for (var i = 0; i < _locations.length; i++) {
var loc = _locations[i];
// Convert lat/long to pixel location
var pixloc = _map.tryLocationToPixel(loc, Microsoft.Maps.PixelReference.control);
var x = pixloc.x;
var y = pixloc.y;
// Create radial gradient centred on this point
var grd = ctx.createRadialGradient(x, y, 0, x, y, _options.radius);
grd.addColorStop(0.0, 'rgba(0, 0, 0, ' + _options.intensity + ')');
grd.addColorStop(1.0, 'transparent');
// Draw the heatpoint onto the canvas
ctx.fillStyle = grd;
ctx.fillRect(x - _options.radius, y - _options.radius, x + _options.radius, y + _options.radius);
}
// Apply the specified colour gradient to the intensity map
colouriseHeatMap();
// Call the callback function, if specified
if (_options.callback) {
_options.callback();
}
}
this.SetOptions = function(options) {
setOptions(options);
}
this.SetPoints = function(points) {
clearHeatMap();
setPoints(points);
createHeatMap();
}
this.Remove = function() {
var _mapDiv = _map.getRootElement();
_mapDiv.parentNode.lastChild.removeChild(_canvas);
}
// Call the initalisation routine
_init();
};
BingHeatMap.aspx –
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="BingHeatMap.aspx.cs" Inherits="HeatMapGenerator.BingHeatMap" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Bing Maps Client Side Heat Map</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>
<!-- Include the heatmap library -->
<script type="text/javascript" src="Scripts/heatmap.js"></script>
<script type="text/javascript">
var map;
var heatmapLayer;
var coordinatesToMap = <%= coodinateString%>;
function GetMap() {
map = new Microsoft.Maps.Map(document.getElementById("divMap"),
{ credentials: "YourBing Map Key",
center: new Microsoft.Maps.Location(36.592, -95.332),
mapTypeId: Microsoft.Maps.MapTypeId.road,
labelOverlay: Microsoft.Maps.LabelOverlay.hidden,
zoom: 4
});
heatmapLayer = new HeatMapLayer(
map,
coordinatesToMap,
{ intensity: 0.25,
radius: 25,
colourgradient:
{
0.0: '#FF0000',
0.1: '#FF0000',
0.2: '#FF0000',
0.3: '#FF0000',
0.4: '#FF0000',
0.5: '#FFFF00',
0.6: '#FFFF00',
0.7: '#90EE90',
0.8: '#9ACD32',
0.9: '#228B22',
1.0: '#008000'
// 0.0: '#FF0000',
// 0.1: '#FF0000',
// 0.2: '#FF0000',
// 0.3: '#FF0000',
// 0.4: '#FF0000',
// 0.5: '#FF0000',
// 0.6: '#FFFF00',
// 0.7: '#90EE90',
// 0.8: '#9ACD32',
// 0.9: '#228B22',
// 1.0: '#008000'
}
}
);
}
</script>
<style type="text/css">
.style1
{
width: 100%;
height: 113%;
}
</style>
</head>
<body onload="GetMap();" style="height: 100%">
<form id="form1" runat="server">
<table border="1" style="width: 100%; height: 100%;">
<tr>
<td id="k" >
<table style="height: 100%; width: 100%;">
<tr>
<td style="height: 100%; width: 70%;">
<div id="divMap" style="position:relative; width:840px; height:480px;"></div>
</td>
</tr>
</table>
</td>
</tr>
</table>
</form>
</body>
</html>
BingHeatMap.aspx.cs –
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
namespace HeatMapGenerator
{
public partial class BingHeatMap : System.Web.UI.Page
{
/// <summary>
/// variable used for creating point to plot on bing map. This variable will be accessed in javascript code after finalising points to plot.
/// </summary>
public string coodinateString = "[";
/// <summary>
/// Page load function used for downloading the input from blob and plotting points on Bing map
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Page_Load(object sender, EventArgs e)
{
try
{
string line = string.Empty;
// Read the file and display it line by line.
System.IO.StreamReader file = new System.IO.StreamReader(AppDomain.CurrentDomain.BaseDirectory + "Input/HeatMapInput.txt");
//create random number generator
Random r = new Random();
int pointCount = 1;
while ((line = file.ReadLine()) != null)//read till end of file is reached
{
//split the lne based on ',' delimiter and retrieve lattitude and longitude
string[] words = line.Split(new char[] { ',' });
double baseLat = Convert.ToDouble(words[0]);
words[1] = words[1].Substring(0, (words[1].Length - 1));
double baseLongi = Convert.ToDouble(words[1]);
double lat = baseLat;
double longi = baseLongi;
//loop to plot bing map points in the near vicinity
for (int i = 1; i <= pointCount + 6; i++)
{
coodinateString = coodinateString + "new Microsoft.Maps.Location(" + lat.ToString() + "," + longi.ToString() + "),";
#region point plotting combination 1
if (lat >= baseLat + 2)
{
lat = lat - r.Next(1, 3);
}
else
{
lat = lat + r.Next(0, 3);
}
if (longi >= baseLongi + 2)
{
longi = longi - r.Next(1, 3);
}
else
{
longi = longi + r.Next(0, 3);
}
#endregion
#region point plotting combination 2
//if (lat >= baseLat + 2)
//{
// lat = lat - r.NextDouble() - 1;
//}
//else
//{
// lat = lat + r.NextDouble() + 1;
//}
//if (longi >= baseLongi - 3)
//{
// longi = longi - r.Next(1, 3) - 1;
//}
//else
//{
// longi = longi + r.Next(0, 3) + 1;
//}
#endregion
}
pointCount = pointCount + 3;
//pointCount = pointCount + 1;
}
coodinateString = coodinateString + "]";
//close the file streamreader
file.Close();
}
catch (Exception ex)
{
}
}
}
}