Tuesday, 18 February 2014

Generate a heatmap with Leaflet.heat and leaflet.js

The following post is a portion of the Leaflet Tips and Tricks book which is free to download. To use this post in context, consider it with the others in this blog or just download the the book as a pdf / epub or mobi .
----------------------------------------------------------

Generate a heatmap with Leaflet.heat

A heatmap is a two dimensional representation of values encodes as colours. In our case these colours are superimposed over a map generated using leaflet.js.
The example here represents seismic activity in July / August of 2013 in central New Zealand, when they were unfortunate enough to experience a series of earthquakes.
Leaflet.heat representation of earthquakes in central New Zealand
In areas of greater intensity and density, the heatmap produces a gradient that varies from blue to red. This example shows a definite region of increased activity over the time in question.
There are several variations on heatmap plugins for Leaflet. The one we are going to examine is called Leaflet.heat. It is fairly new (at time of writing), and it builds on a separate JavaScript library called simpleheat. Both of these have been developed by Vladimir Agafonkin (yes, the developer of Leaflet), so they come with a considerable pedigree.

Leaflet.heat code description

The following is a code listing that we will use to describe the required changes from our simple-map.html example to enable Leaflet.heat. There is also an online version on bl.ocks.orgGitHub and can be downloaded along with the data file (in a zip file) when you download the book from Leanpub.
<!DOCTYPE html>
<html>
<head>
    <title>Simple Leaflet Map with Heatmap </title>
    <meta charset="utf-8" />
    <link 
        rel="stylesheet" 
        href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css"
    />
</head>
<body>
    <div id="map" style="width: 600px; height: 400px"></div>

    <script
       src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js">
    </script>
    
    <script
       src="http://leaflet.github.io/Leaflet.heat/dist/leaflet-heat.js">
    </script>
  <script src="2013-earthquake.js"></script>
    <script>

        var map = L.map('map').setView([-41.5546,174.146], 10);
        mapLink = 
            '<a href="http://openstreetmap.org">OpenStreetMap</a>';
        L.tileLayer(
            'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: '&copy; ' + mapLink + ' Contributors',
            maxZoom: 18,
        }).addTo(map);
        
        var heat = L.heatLayer(quakePoints,{
            radius: 20,
            blur: 15, 
            maxZoom: 17,
        }).addTo(map);
    
    </script>
</body>
</html>
We will be loading our data from a separate JavaScript file called 2013-earthquake.js. The data has been sourced from New Zealand’s Geonet site over a date range that covers a period of reasonable seismic activity in July / August 2013.
Loading the file from a separate JavaScript file is purely for conveniences sake and the format of the data is as follows;
var quakePoints = [
[-41.5396,174.1242,1.7345],
[-38.8725,175.9561,2.6901],
[-41.8992,174.3117,4.6968],
[-41.7495,174.02,1.8642],
[-41.7008,174.0876,2.1629],
[-41.7371,174.0682,2.0408],
[-41.372,173.3502,2.7565],
[-41.7511,174.0623,2.4531],
[-41.7557,174.3391,2.1871],
[-41.6881,174.2726,3.1336],
[-41.7463,174.1194,2.7113],
[-41.6966,174.1238,2.4168],
...
];
There are only two ‘blocks’ that have changed in the code from our simple map example.
The first is an additional two links to external JavaScript files;
    <script
       src="http://leaflet.github.io/Leaflet.heat/dist/leaflet-heat.js">
    </script>
    <script src="2013-earthquake.js"></script>
The first link loads the Leaflet.heat plugin and the second loads our data from 2013-earthquake.js.
The second change from our simple map example adds and configures our heatmap layer;
        var heat = L.heatLayer(quakePoints,{
            radius: 20,
            blur: 15, 
            maxZoom: 17,
        }).addTo(map);
In this block we load the data as the variable quakePoints then set our radius, blur and maxZoom values for the plugin.

radius configuration option

The radius option sets the radius (duh) of the circles that correspond to a single data point on the map. If we remove the blur effect we can clearly see the effect on the map.
Radii of points
As we zoom in and out of the map, the radii of the points remains constant on the screen, but the representation that they create on the screen changes as there are subsequently more or less points to generate an effect.
Radii of points zoomed in

blur configuration option

Blurring the points results in a smoothing and dispersing of the data. The previous section demonstrated the effect of removing the blurring (set to 1), but it can also be increased to the point where they can run the risk of fading away a bit too much. For example this is the effect when blurring is set to 50;
Points with blur set to 50

maxZoom configuration option

The maxZoom configuration option designates the level where the points reach their maximum intensity. Therefore if we were to set the maxZoom value to the initial zoom level (10) of our map, we should expect to see each point represented by a full spectrum of blue to green;
Zoom level and maxZoom both on 10
Obviously this is something that you will want to consider as you plan how to set the configuration options on your maps :-).
A copy of this file and a copy of all the files that appear in the book can be downloaded (in a zip file) when you download the book from Leanpub


The description above (and heaps of other stuff) is in the Leaflet Tips and Tricks book that can be downloaded for free (or donate if you really want to :-)).

10 comments:

  1. Wow, Heat map is so spectacular. It makes any "boring data" look like a Piet Mondrian art piece !

    ReplyDelete
  2. Thanks for your post!

    Is there any method to clear existing data inside of a map? For instance, I would simply like to show and clear the data every time I click a button.
    Thanks

    ReplyDelete
    Replies
    1. Sorry for the delay in replying. Thinking off the top of my head I would associate the javascript with an html button such that on a click it loaded a null set of data. Similar to the process here https://leanpub.com/D3-Tips-and-Tricks/read#leanpub-auto-update-data-dynamically---on-click

      Delete
  3. Hi, any idea how to update the L.heatLayer (e.g. i want to add a slider for radius size)...? Many thanks for a great page!

    ReplyDelete
    Replies
    1. Sorry for the late reply. I've never experimented with that myself, but I recommend that you try a couple of things that might get you closer and if you're still struggling use these experiments to ask a question on Stack Overflow. Best of luck.

      Delete
  4. Hi, All heatmap and leaflet examples do have a map in them where they plot data accordingly. I have a data where there are x,y,z position co ordinates given, how do I create a heat map for that data(because I do not have a definite map defining the total area, just by the x,y,z and the time spent, I have to visualize a heatmap for a person

    ReplyDelete
    Replies
    1. Interesting question. I'm not sure that I know the answer, but try looking for examples using d3 hexagons http://www.visualcinnamon.com/2013/07/self-organizing-maps-creating-hexagonal.html type of thing

      Delete
    2. thank you for the reference. Still looking for a detailed example where they read data from file(say ".csv") and visualize a heat map. Couldnt fine one though.

      Delete
    3. Hmm... could you possible consider that the hexagons (if small enough) could form the heatmap?

      Delete