Skip to content

Instantly share code, notes, and snippets.

@hugolpz
Forked from mbostock/.block
Last active October 21, 2016 15:21
Show Gist options
  • Save hugolpz/4351d8f1b3da93de2c61 to your computer and use it in GitHub Desktop.
Save hugolpz/4351d8f1b3da93de2c61 to your computer and use it in GitHub Desktop.
Topography via D3js image processing

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.

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="http://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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment