For this recipe we are going to overlay a heat map over a map layer. This is a bit more challenging than what we did before but it should go smoothly as the concept is not that complex. Heat maps are used to represent density over a region by adding different colors over certain areas. In the case, we are going to display the number of animal specimens over the area of the United States using the Global Biodiversity Information Facility's data.
Open the files from the code downloaded from the Packt Publishing web site in the folder Chapter 6
and within that, Recipe 6
.
The following are the steps required to overlay a heat map on a map:
Main
class as shown in the following code snippet:[Embed(source = "/AnimalsUS.xml", mimeType="application/octet-stream")] private var XmlData:Class;
HeatMapData
.var byteArray:ByteArray = (new XmlData()) as ByteArray; var xml:XML = XML(String(byteArray.readUTFBytes( byteArray.length ))); var xmlList:XMLList = xml.children(); var data:Vector.<HeatMapData> = new Vector.<HeatMapData>(); var i:int; for (i = 0; i < xmlList.length(); i++) { data.push(new HeatMapData(xmlList[i].count, xmlList[i].minLongitude, xmlList[i].minLatitude)); }
heatMapData
instance to find its neighbors in all eight directions.VectorLayer
class to hold the heat map:_map = new Map(); _map.size = new Size(800, 500); _map.center = new Location(-93.98,40.77, "EPSG:4326"); _map.resolution = new Resolution(10000, "EPSG:900913"); var bing:Bing = new Bing("Ar3-LKk-acyISMevsF2bqH70h21mzr_FN9AhHfi7pS26F5hMH1DmpI7PBK1VCLBk","Aerial"); _map.addLayer(bing); var heatLayer:VectorLayer = new VectorLayer("HeatMap"); _map.addLayer(heatLayer);
createPolygon
function of the HeatMapData
class.To make a heat map we need the data to be packaged in a particular way. We need one value to correspond to one area. For this recipe, the data from the Global Biodiversity Information Facility was aggregated over an area of one degree squared.
The first step is to get the data from the XML file into our holder class. Notice how using XML is a bit less straightforward than using JSON like in the Parsing data to use as region fill recipe.
Once we got that, we will need to loop over the data multiple times in order to calculate various values. We first need to average each value in relation to its neighbors. We do so, so that it creates more rounded regions on the heat map and also brings back values that would otherwise be too high to create a smoother range. To calculate this average we must do a double loop so that each data point can look at every other data point to check if it is one of its eight possible neighbors.
Once we got the neighbors, we loop over the data once more to create the average (we can't create the average while we are looking for neighbors, because a neighbor could use an already calculated average and that would skew the data) and find the maximum value of those averages. In this particular case we don't have to find a minimum as we know it is zero.
With the maximum found we can calculate the scaleRatio
value:
var scaleRatio:Number = 495 / maximumCount;
495 here is our color range (255 for red and 240 for green).
Now we create the map so that we can add our polygons (they will be squares, much like pixels). We are ready for the last loop over the data. This is where we will create the PolygonFeature
class and assign it a color depending on its averaged count value. If the averaged count value is equal to the maximum value, its polygon will be fully red, otherwise it will go from red to yellow to green. We put all the code to generate the polygon inside the HeatMapData
class to keep the Main
class cleaner.
The Coloring a map recipe has all the explanations on creating a PolygonFeature
class and assigning it a color. Once we have all our polygons, we add them to the map. Note that since there are so many polygons on the map, it becomes a bit unresponsive when interacting with it, which is not really a problem if you want to use it as a static map.
By using more precise data and a bigger color range we could make a better heat map.
The XML file we used had data for buckets of the size of one degree squares. This made it so, that the resolution of our heat map wasn't so great. By using more precise data we could have a much smoother heat map. Everything starts from the data.
3.17.76.175