How to generate a heatmap in Cesium?

Intro

By default, Cesium cannot generate heatmaps, so that third-party solutions must be used to implement this functionality in the viewer.

There is a library called heatmap.js that calculates a represents heatmaps from different input types, such as mouse position on the screen, where you click or even coordinates in the browser's own canvas.

From this library, and from the idea of representing points in the canvas, a different library called CesiumHeatmap.js was developed. This one allows you to generate heatmaps in the viewer itself, from point coordinates and a value representing a point's weight.

At the end of the tutorial, using an ontology of points, you can generate a heatmap like this one:

Eh! This is no photo, but an interactive map.  Try to browse through it.


Library installation

The first step will be download and uncompressing the library, or cloning the repository (https://github.com/manuelnas/CesiumHeatmap.git) in the project folder containing the viewer.

The downloaded/cloned file has the following files:

  • CesiumHeatmap.js: the library to aggregate heatmaps using heatmap.js (included) and the functions Cesium.Entity.Rectangle or Cesium.SingleTileImageryProvider, the later will be used in this tutorial.

  • HeatmapImageryProvider.js: another library to aggregate heatmaps, also using heatmap.js (also including it) through customization of the Cesium.ImageryProvider function, which will not be studied for the moment.

  • LICENSE: a generic document with the libraries's copyright.

  • package.json: file with the repository's address.

  • README.md: file with the libraries0 description, along with an example of use of CesiumHeatmap.js.

 

To use the library in the Cesium viewer, you must insert the CesiumHeatmap.js library after the Cesium.js library call, as shown next:

Insert CesiumHeatmap.js library
<script src="https://www.onesaitplatform.online/web/Cesium160/Cesium.js"></script> <script src="js/heatmap/CesiumHeatmap.js"></script> <style> @import url(https://www.onesaitplatform.online/web/Cesium160/Widgets/widgets.css);

Having done this, you can start generating heatmaps.

Functioning philosophy

To generate a heatmap, you need a series of point-type entities (It does not work with lines or polygons), with defined geospace coordinates (usually longitude and latitude), and a field including each point's value or weight respect to the other points.

For this tutorial, we created a series of points, each of them with a value. These points, represented on a map along with their values, are seen in the following way:

The coordinates and values of all these points, which total 30, are stored in an ontology already loaded in the platform and served as a REST API. Each entity has the following structure:

Ontology entity example structure
{ "type": "Feature", "properties": { "id": 2, "value": 22 }, "geometry": { "type": "Point", coordinates": [ -15.432677385995223, 28.138219879738312 ] } }

In this structure, we have defined fields such as longitude (geometry.coordinates[0]), latitude (geometry.coordinates[1]) and the value assigned to the point (properties.value).

 

heatmapSampleData

What? You don't know yet how you can generate an ontology and serve it as a REST API service? You're lucky, we explain it right here. And, if you want to try and create your own REST API for this example, here you have the JSON file: heatmapSampleData.json

 

Besides the points with values, the heatmap generation also requires you to define the study area, understanding it as a rectangle delimiting the points to be considered. Thus, for the point series in the tutorial, a possible bounding rectangle can be this one:

 

Bounding rectangle

This is the rectangle referenced when we say that this library uses the Cesium.Entity.Rectangle function.

 

We are not interested in the rectangle's geometries, but in the upper, lower, left and right limit coordinates. This, while easy to get if you use a GIS program, can be harder if you have to do it manually. We suggest you to take the "higher" point, the increase the value of its latitude coordinate (geometry.coordinates[1]), so the limit is a bit to its north. Repeat it for each of the sides to get the coordinates.


Another possibility, somewhat simpler but more technical, is drawing two points: One corresponding to the higher left corner, and the other to the lower right. These two point give you four coordinates, corresponding to the four corners in the diagram. See it in the following example:


And that's it. With this idea in mind, generating heatmaps in Cesium is quite easy.

Library use (or how I can, at last, create a heatmap in Cesium)

Having seen the theory, we can start generating a heatmap like the one in the previous example.


Firstly we need to get the data of the point entities. These are in an ontology served by a REST service. To do this, we need to use AJAX, so in the webpage's <head>, you must include a call to a jQuery library:

Include the jQuery library
<script src="https://www.onesaitplatform.online/web/Cesium160/Cesium.js"></script> <script src="js/heatmap/CesiumHeatmap.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous" ></script>

Next, in the Cesium viewer's Javascript code, you must call the REST service  using the following code:

Call the REST service using AJAX

 

 

 

 

We will now define a variable containing an object with the previously-defined rectangle's corners:

Defining the rectangle's corners

We will also create another variable defining the heatmap's configuration properties. For now, we will specify the most basic ones; we will later give details on how to customize the heatmap.

Configuring the heatmap's properties

 

Next, generate an instance of CesiumHeatmap using CesiumHeatmap.create(). This instance will be configured with three members:

  • The map's viewer: understood as the 'viewer' object that defines the viewer.

  • The interpolation limits: corresponding with the previously defined rectangle's limits.

  • A number of configuration properties: that we have just created.

So, a basic instance can be defined as:

Creating CesiumHeatmap instance

 

Now, you only need to add the data to the viewer, specifying the viewer's coordinate's format. There are two options:

  • setData(): for data in X & Y coordinates (UTM style).

  • setWGS84Data(): for data in longitude and latitude coordinates.

 

In both cases, the function gets input parameters:

  • Maximum and minimum interpolation value: for a range of given values, the more the maximum and minimum values are near the points' maximum and minimum values, the more the differentiation visual effect will be in the interpolation (the colors will be more extreme).

  • List of point entities: with their weight values.

 

For the example data, as they are defined with longitude and latitude coordinates, it will be something like:

Adding heatmap to viewer


In this case, 15 is the minimum value to be considered in the interpolation, 26 is the maximum value, and data are the values of the ontology points, following the format{ x: longitude, y: latitude, value: weight }.

 

To get those values, you can generate a function that iterates each element in the ontology, returning a list with the coordinates and weight values, and then gives this list to the heatmap generating function. In the case of the example ontology, the used function is this:

Function iterating the JSON and extracting the needed data

 

Then, the function to add data looks like this, understanding as jsonData the object returning the function where the code is:

Add heatmap to viewer

Having done that, when we view the map, the heatmap should look like this:

Heatmap configuration

The heatmap customization options are the following ones:

  • useEntitiesIfAvailable: specify whether entities will be used to generate the heatmap(true) or an image provider will always be used (false). The default value is true.

  • minCanvasSize: minimum size, in pixels, for the color map canvas. The default value is 700.

  • maxCanvasSize: maximum size, in pixels, for the color map canvas. The default value is 2000.

  • radius: each point's interpolation size. If the radius is not enough and does not touch another point, it gives a series of concentric circles.

  • radiusFactor: size factor used if no radius is specified. The value is the higher height and width, divided by this value to generate the radius value to be used. The default value is 60.

  • spacingFactor: additional space around the analysis area's borders. The extra space is calculated by multiplying the points' radius by this value. The default value is 1.5.

  • maxOpacity: maximum used opacity value. The default value is 0.8.

  • minOpacity: minimum used opacity value. The default value is 0.1.

  • blur: blur value to be represented. The default value is 0.85.

  • gradient: a set of colors and limit conforming the map's color gradient. The default value is:

    • '.3': 'blue',

    • '.65': 'yellow',

    • '.8': 'orange',

    • '.95': 'red'


Unless specifically required, it's best to interact only with radius, maximum and minimum opacity, blur and gradient, because the other properties do not give such a direct visual result and modifying them can generate abominations depending on the values to be shown in the heatmap (or alternatively, to not detecting any obvious change).

 

About the heatmap's symbology, this is achieved by modifying the gradient's options. Each gradient range is defined by the gradient's cut-off value (under a color, over another color; .65 means, for instance, that yellow is under this color and orange over it), and the value of the color to be used. This color is based on CSS, meaning that it accepts both the color's name (blue) and the color's hexadecimal value (#0000FF).

We will now see the heatmap with different gradient values:






Viewer code

This is the code used in the example viewer.

Código del visor al completo