In my previous blog I described how to create thematic maps using Virtual Earth, SQL Server 2008 and the UMN MapServer. While thematic maps are a powerful visualization when you analyze by fixed boundaries it is also often helpful to create heat maps based on geographic density. Heat maps are not only useful in a geographic context, they are also used for many other sorts of density analysis. According to Wikipedia a heat map in general is a graphical representation of data where the values taken by a variable in a two-dimensional map are represented as colours. Aside from geographic analysis heat maps are used for example as:
- Web heat maps for displaying areas of a Web page most frequently scanned by visitors
- Biology heat maps in molecular biology to represent the level of expression of many genes across a number of comparable samples
In the context of this blog I will use the heat map algorithm described by Dylan Vester to visualize the results of a geographic cluster analysis. Dylan has explained the creation of heat-points and the colorizing very well so I won’t go into the details there but rather describe the clustering that allows us to set the intensity for the heat-points.
Generating the Heat-Map
As Dylan explains, creating the heat-map is a 2 step process. First we have to create an intensity mask as a 256 grayscale image and then we translate each shade of gray into a colour. For more details have a look at Dylan’s blog. The intensity is determined by the density of points and to determine this density we create a grid and count how many points are in each cell of the grid.
In order to do so we will pan and zoom the map to the area that we want to analyze. Next we determine some required parameters of the current map view.
- The coordinates of the upper left and lower right corner,
- the zoom-level and
- the height and width of our map
Virtual Earth provides the necessary methods. The pixel-coordinates of the upper left corner in the map view are always (0, 0) and of the lower right corner they are (MapWidth, MapHeight). The method to determine the latitude and longitude for a pixel in the MapView is VEMap.PixelToLatLong. The width and height are the dimensions of the HTML DIV-element which hosts our VEMapControl. To determine the zoom-level we use the method VEMap.GetZoomLevel.
With these information we will now create an AJAX-call to a Generic WebHandler.
First we set up the grid. In my sample I have set the size of a grid-cell to 40 pixels but you can change this value of course. Since we are now using a generic web handler we can’t use the VEMap.LatLongToPixel method to determine the pixel-coordinates. However, the math is very well explained in this MSDN article on the Virtual Earth Tile System. In my sample code it is implemented in the function LatLongToPixel. We express all points as absolute pixels in the Virtual Earth pixel-coordinate system who’s origin – pixel (0,0) – corresponds to Latitude 85.05112878 and Longitude –180 in the WGS84 coordinate system. For more details see the article mentioned above. We start by determining the absolute pixel-coordinates of the upper left corner in the current map view and then set up a database connection to our source data which we want to analyze. While we loop through our data reader we convert each pair of latitude and longitude to a pixel-coordinate and assign it to a grid cell. Once we read all data from our database connection we have a grid and each cell has information about the pixel coordinates and the number of points in it.
Now we will loop through the grid cells and create a heat point for each one and add them to an empty bitmap. This is the part where I re-use the code from Dylan Vester. As a result we will have a heat-map-image which matches the current map view in its size:
Creating a Virtual Earth Tile Layer from our Heat Map using MapCruncher
The most simple way to create a Virtual Earth Tile Layer is to use the MapCruncher. We already generated our image and we have discussed 2 that we calculated the latitudes and longitudes of the upper left and lower right corner. To align the image in MapCruncher and project it for Virtual Earth we need at least 3 points. Therefore I extended the sample application and retrieve all 4 points of the bounding box of our map view (see 01_Generate_HeatMap.htm in the attached sample code).
Now we load the image into MapCruncher and move the corners 1 by one underneath the crosshair in the left window. In the right window we paste the latitudes and longitudes for each map-correspondence point as determined above in the input-boxes for the Virtual Earth Position.
Once we have at least 3 points we can lock the view and render the tiles (see 02_Overlay_HeatMap.htm in the attached sample code).
Dynamically Creating and Tiling the Heat Map
As mentioned above the generation of the heat map uses the current zoom-level as 1 parameter which means that we are not working on a optimum level of detail when we zoom in if we follow the approach mentioned above. In order to be more dynamic it would be ideal to generate the heat map on the fly whenever we pan or zoom the map. A while ago I posted a blog which describes how you could generate dynamic tile layers from images through an ad-hoc tile-cutting service. I have recycled parts of that code in the 03_Dynamic_HeatMap.htm of the attached sample code.
In order to reduce the number of AJAX-calls to our web handler we will introduce a threshold and only generate a new heat map if we pan for more pixels than the result is or if we zoom the map.
Well, that’s it. you will find the sample code here.