Skip to content

Instantly share code, notes, and snippets.

@rveciana
Last active August 29, 2015 14:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rveciana/f46df2272b289a9ce4e7 to your computer and use it in GitHub Desktop.
Save rveciana/f46df2272b289a9ce4e7 to your computer and use it in GitHub Desktop.
Automatic colouring World Map using Canvas

This example is the same as this Jason Davies GIST, but using Canvas instead of SVG.

How is the color selected:

The place was easy to find

context.fillStyle = color(d.color = d3.max(neighbors[i], function(n) { return countries[n].color; }) + 1 | 0);

I had to think for some time to understand how does this work. Let's separate it:

color(
    d.color = 
            d3.max(
                    neighbors[i], 
                    function(n) { return countries[n].color; }
                    ) 
                    + 1 | 0
);

Going from the inside to the outside:

  • The first thing is calling a d3.max function. The docs say that the first argument is an array. In our case, the ids of all the neighbor countries. The second optional argument is an accessor equivalent to call array.map before calculating the maximum value. This means calling a function for each array element before calculating the maximum value. In our case, the function returns the color field of the country with the indicated id. The result, of course, will be an undefined value when the color is not set.
  • After calculating the maximum color value of all the neighbors, we add +1, so we will use the next color. If the value was undefined, the color is set to 0 using |0.
  • This calculated value is assigned to the country at the field color using d.color = . When another country has the current country as a neighbour, the color number will be found
  • The color function is a color scale. Each color number will be transformed into the RGB value.
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/d3.geo.projection.v0.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 500;
var canvas = d3.select("body").append("canvas")
.attr("width", width)
.attr("height", height);
var context = canvas.node().getContext("2d");
var projection = d3.geo.kavrayskiy7(),
color = d3.scale.category20(),
graticule = d3.geo.graticule();
var path = d3.geo.path()
.projection(projection)
.context(context);
context.strokeStyle = '#000';
context.beginPath();
path(graticule());
context.lineWidth = .5;
context.stroke();
context.strokeStyle = '#333';
context.beginPath();
path(graticule.outline());
context.lineWidth = 1.5;
context.stroke();
d3.json("/mbostock/raw/4090846/world-50m.json", function(error, world) {
var countries = topojson.feature(world, world.objects.countries).features,
neighbors = topojson.neighbors(world.objects.countries.geometries);
countries.forEach(function(d, i) {
context.fillStyle = color(d.color = d3.max(neighbors[i], function(n) { return countries[n].color; }) + 1 | 0);
context.beginPath();
path(d);
context.fill();
});
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment