Empty Pipes



D3.js and Contouring

  • 22 Jul 2015
  • |
  • javascript
  • d3.js
  • |

Contouring is an important way for displying 3D data in 2D. At its core are iso-lines, along which values in the third-dimension have equal values, and iso-bands which are areas that encompasse values within some range (e.g. greater than 2 and less than 10). It is used for anything from topographical maps to show lines of equal elevation, weather maps to show regions of equal pressure to probability density plots to show regions with equal probability density. Calculating iso-lines and iso-bands is often done by interpolating values on a grid.

From a practical standpoint, there appear to be 3 javascript libraries capable of calculating iso-lines and iso-bands, MarchingSquares.js, conrec.js and Turf.js. The table below shows an overview of the three different implementations and is followed by a slightly more thorough explanation of their strengths and weaknesses.


MarchingSquares.js

Conrec.js

Turf.js
Pros: Simple. Adds contours to the edges of the data. True iso-band support.

Pros: Options for specifying x- and y- values. Pros: Interpolates the input data. Works with GeoJSON data structures.
Cons: Works only on gridded data. No option for specifying x- and y- values. Cons: Works only on gridded data. Doesn't return true iso-bands. Requires a extra values to draw a border. Cons: Interpolates the input data (can cause distortions). Works with GeoJSON data structures. Doesn't return actual iso-bands.

MarchingSquares.js uses the eponymous algorithm to calculate both iso-lines and iso-bands. Conrec.js uses a slightly different algorithm and returns iso-lines, whereas Turf.js appears to use the conrec algorithm to generate iso-lines. The examples above used the following data set. The breaks indicates where we want the iso-lines, and the iso-regions should correspond to areas between adjacent breaks.

    var breaks = [0, 4.5, 9, 13.5, 18];
    var data = [[18, 13, 10, 9, 10, 13, 18],
        [13, 8, 5, 4, 5, 8, 13],
        [10, 5, 2, 1, 2, 5, 10],
        [9, 4, 1, 12, 1, 4, 9],
        [10, 5, 2, 1, 2, 5, 10],
        [13, 8, 5, 4, 5, 8, 13],
        [18, 13, 10, 9, 10, 13, 18],
        [18, 13, 10, 9, 10, 13, 18]];

MarchingSquares.js accurately returns is iso-bands and draws the plot as expected. Each path corresponds to a particular region. Thus hovering over the middle section also highlights the outer section which corresponds to the same values (13.5 - 18).

Conrec.js correctly outlines the regions, but the level returned for the second and third sections from the middle is the same although they should correspond to different iso-bands. To create the plot, it was also necessary to add some artifically high values along the outside boundaries so that iso-lines were drawn around the edges.

Turf.js interpolated the data onto another grid and drew iso-lines in the same manner as conrec.js. The automatic interpolation is listed in the table as both a pro and a con because it can be useful when the data is not already on a grid, but deleterious when it insists on using a uniform resolution on both the x and y scales and re-interpolating already gridded data (as in the example above).

Needless to say, of the three methods, MarchingSquares.js was the easiest and most aesthetically pleasing to me so I will likely use it in the future.

The code for all three of these examples can be found in a github repository here.

If you see any errors or omissions or know of a way to make the Turf.js example look more reasonable, please let me know on Twitter or by email.