As with the SVG process, we start by loading the data in:
d3.json('data/countries.topo.json', function(error, world) {
if (error) throw error;
d3.select('div#controls').style('top', height + 'px');
countries = topojson.feature(world, world.objects.countries); // GeoJSON;
drawMap(countries);
We then do a bit of housekeeping and move the buttons in the div#controls below the canvases. You recode the TopoJSON to GeoJSON features and save the data as a global variable before you draw the map:
function drawMap(world) {
countries.features.forEach(function(el, i) {
contextWorld.beginPath();
pathCanvas(el);
contextWorld.fillStyle = '#ccc';
contextWorld.fill();
contextWorld.beginPath();
pathCanvas(el);
contextWorld.strokeStyle = '#fff';
contextWorld.lineWidth = 1;
contextWorld.stroke();
});
}
Thanks to D3's versatile path generator, this is all it needs to draw the world. Easy!
Back in our asynchronous d3.json() data load function, you'll handle the button events next. Remember, nothing has happened yet, but as soon as the user hits a button, the animation should kick off.
You attach a mouse-down listener to all buttons:
d3.selectAll('button.flight-select').on('mousedown', handleFlights);
Proceed with writing the handler:
function handleFlights() {
d3.selectAll('button').style('background-color', '#f7f7f7');
d3.select(this).style('background-color', '#ddd');
if (requestID) cancelAnimationFrame(requestID);
var flights = this.dataset.flights;
d3.queue()
.defer(d3.csv, 'data/routes_' + flights + '.csv')
.defer(d3.csv, 'data/airports_' + flights + '.csv')
.await(ready);
}
The button colors are handled in the first two lines. The next line will stop the current loop. We haven't even got a loop yet, so let's get back to this as soon as we have.
Finally, we retrieve the number of flights the button represents and load the respective route and airport location data from the server. That's it for the d3.json() callback, as the ready() function will take over as soon as the data is loaded.