block by hugolpz 4351d8f1b3da93de2c61

Topography via D3js image processing

Full Screen

Proof of concept of grayscale image recolored directly via D3js color scale. Wikimaps colors used.

Source: The source image is a global heightmap from the Shuttle Radar Topography Mission, released as part of NASA’s Blue Marble collection at 8km resolution. The topography data is stored in a simple 130KB black&white, 8-bit PNG. In it, darker values represent lower elevations (sea floor), lighter values represent higher elevations (mountains).

Colors are read out of the image using the Canvas API.

Data properties:

Formerly, Mike Bostock used the 5th, 50th and 95th percentiles for land elevation as 15, 35 and 132 respectively; quantiles being an effective way to maximize contrast while remapping colors, similar to auto-tone features popular in image editors.

The percentiles are used as the domain of a diverging linear scale; red values are below the median elevation, and blue values are above. Interpolating in HCL colorspace improves perception.

Stylesheet: this dataviz follow the Wikipedia Maps Conventions for Topographic maps.

var color = d3.scale.linear()
   .domain([0,14,15,40,100,200])
   .range([
           "#71ABD8", //-10000m dark blue
           "#D8F2FE", //     0m light-blue
        "#94BF8B", //     1m green
        "#EFEBC0", //   300m yellow
        "#AA8753", //  3000m brown
        "#FFFFFF"]) //~6000m white
   .interpolate(d3.interpolateHcl);

Binary images can store lots of data efficiently. Also, see Mike Bostock’s :

Note: Semantically, traditional rainbow color scale are to avoid since rainbow color scales are harmful. To do better, let’s use perceptually-optimized scales.

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="../js/d3.v3.min.js"></script>
<script>
var width = 960,
    height = 500; 

var color = d3.scale.linear()
   .domain([0,14,15,40,100,200])
   .range(["#71ABD8", "#D8F2FE", "#94BF8B", "#EFEBC0", "#AA8753", "#FFFFFF"])
   .interpolate(d3.interpolateHcl);

var canvas = d3.select("body").append("canvas")
    .attr("width", width)
    .attr("height", height);

var context = canvas.node().getContext("2d");

getImage("readme.png", function(image) {
  context.drawImage(image, 0, 0, width, height);
  image = context.getImageData(0, 0, width, height);

  // Rescale the colors.
  for (var c, i = 0, n = width * height * 4, d = image.data; i < n; i += 4) {
    c = d3.rgb(color(d[i]));
    d[i + 0] = c.r;
    d[i + 1] = c.g;
    d[i + 2] = c.b;
  }

  context.putImageData(image, 0, 0);
});

function getImage(path, callback) {
  var image = new Image;
  image.onload = function() { callback(image); };
  image.src = path;
}
</script>