Code the World Part 2 …

Up to this point, you should have something that looks like this:

Primordial Earth

It may not seem like a lot, but it’s actually pretty amazing considering how little code has been written. The next step is to add the land with the outline of the different countries. For this, two things are needed: a topojson script and the json containing the country geometry data.

First add the topojson script. Either add a script to your html page:

<script src="https://d3js.org/topojson.v2.min.js"></script>

or paste https://d3js.org/topojson.v2.min.js into the Codepen javascript settings. If you want to look at the repo for this script look here.This script is used to parse json file we are about to import.

Next, add a new variable called worldCoords or something similar and assign it to this json.

var worldCoords = 'https://raw.githubusercontent.com/fable-compiler/Fable/master/samples/browser/d3map/data/world-110m.json';

Up to this point, your code should look like this:

var width = 600,
height = 500;
var worldCoords = 'https://raw.githubusercontent.com/fable-compiler/Fable/master/samples/browser/d3map/data/world-110m.json';
//define projection type (ie orthographic, equaldistant, etc.)
var projection = d3.geoOrthographic()
.scale(245)
.rotate([0,0])
.translate([width / 2, height / 2])
.clipAngle(90);
//assign the projection type to the path
var path = d3.geoPath()
.projection(projection);
//create base svg and appending it to the container div in the html
var svg = d3.select('.container').append('svg')
.attr('width', width)
.attr('height', height);
//create basic shape of svg
var water = svg.append('path')
.datum({type: 'Sphere'})
.attr('class', 'water')
.attr('d', path)

We have our data, now it’s time to build the function that will help us use the data. But first, we need to add d3.queue(). We don’t need it for part 2 but it will come in handy for part 3 when we actually map some data. Below the water var add the following:

d3.queue()
.defer(d3.json, worldCoords)
.await(ready)

Then we create our function:

function ready(err, data){
}

Inside the function we first want to use the topojson script to parse the data.

//make sure topojson is refrenced in the js resources
var countries = topojson.feature(data, data.objects.countries).features;

Next we need to create the land by appending a path to the main svg. Inside the data property place the “countries” variable we created above.

var world = svg.selectAll('path.land')
.data(countries)
.enter().append('path')
.attr('class', 'land')
.attr('d', path)

The function so far should look like this:

function ready(err, data){
//make sure topojson is refrenced in the js resources
var countries = topojson.feature(data, data.objects.countries).features;
    var world = svg.selectAll('path.land')
.data(countries)
.enter().append('path')
.attr('class', 'land')
.attr('d', path)
}

At this point, something magical should have happened. Your earth should look like this:

Add a bit of css:

.land {
fill: #d2b48c;
stroke: #FFF;
stroke-width: 0.7px;
cursor: pointer;
}

and now we have this:

This looks nice and all, but wouldn’t it be great if we could rotate it with our cursor so that we could see the entire globe? This will involve using the d3.grab() function. Add to the world path:

.call(d3.drag()
.subject(function() { var r = projection.rotate(); return {x: r[0] / speed, y: -r[1] / speed}; })
.on("drag", function() {
var rotate = projection.rotate();
projection.rotate([d3.event.x * speed, -d3.event.y * speed, rotate[2]]);//this tells d3 to rotate on the x, y and z axis.
    svg.selectAll("path.land").attr("d", path);
svg.selectAll(".focused").classed("focused", focused = false);
}));

At this point, it won’t work because speed is not defined yet. Go to the top and set a variable speed to 0.25:

var speed = 0.25;

Now you’ll find that it works but only if you grab the land with your mouse. Let’s fix that by adding the same exact thing to the water:

.call(d3.drag()
.subject(function() { var r = projection.rotate(); return {x: r[0] / speed, y: -r[1] / speed}; })
.on("drag", function() {
var rotate = projection.rotate();
projection.rotate([d3.event.x * speed, -d3.event.y * speed, rotate[2]]);//this tells d3 to rotate on the x, y and z axis.
svg.selectAll("path.land").attr("d", path);
svg.selectAll(".focused").classed("focused", focused = false);
}));

Now everything should be rotatable.

Rotating Earth

Here’s the full javascript:

var width = 600,
height = 500,
speed = 0.25,
focused;
var worldCoords = 'https://raw.githubusercontent.com/fable-compiler/Fable/master/samples/browser/d3map/data/world-110m.json';
//define projection type (ie orthographic, equaldistant, etc.)
var projection = d3.geoOrthographic()
.scale(245)
.rotate([0,0])
.translate([width / 2, height / 2])
.clipAngle(90);
//an alternate projection: EqualArea.
var altProjection = d3.geoConicEqualArea()
.scale(245)
.rotate([0,0])
.translate([width / 2, height / 2])
.clipAngle(90);
//assign the projection type to the path
var path = d3.geoPath()
.projection(projection);
//create base svg and appending it to the container div in the html
var svg = d3.select('.container').append('svg')
.attr('width', width)
.attr('height', height);
//create basic shape of svg
var water = svg.append('path')
.datum({type: 'Sphere'})
.attr('class', 'water')
.attr('d', path)
.call(d3.drag()
.subject(function() { var r = projection.rotate(); return {x: r[0] / speed, y: -r[1] / speed}; })
.on("drag", function() {
var rotate = projection.rotate();
projection.rotate([d3.event.x * speed, -d3.event.y * speed, rotate[2]]);
svg.selectAll("path.land").attr("d", path);
svg.selectAll(".focused").classed("focused", focused = false);
svg.selectAll("path.gt").attr("d", path);
}));
d3.queue()
.defer(d3.json, worldCoords)
.await(ready)
//adding the map using the data from the json object
function ready(error, data){
//make sure topojson is refrenced in the js resources
var countries = topojson.feature(data, data.objects.countries).features;
var world = svg.selectAll('path.land')
.data(countries)
.enter().append('path')
.attr('class', 'land')
.attr('d', path)
.call(d3.drag()
.subject(function() { var r = projection.rotate(); return {x: r[0] / speed, y: -r[1] / speed}; })
.on("drag", function() {
var rotate = projection.rotate();
projection.rotate([d3.event.x * speed, -d3.event.y * speed, rotate[2]]);
svg.selectAll("path.land").attr("d", path);
svg.selectAll(".focused").classed("focused", focused = false);
}));


};

Here’s the css:

body {
padding-top: 30px;
padding-left: 550px;
padding-top: 100px;
}
.water {
fill: #00248F;
cursor: pointer;
}
.land {
fill: #d2b48c;
stroke: #FFF;
stroke-width: 0.7px;
cursor: pointer;
}

And we’re done with part 2. For part 3 we’ll take a look at how to visualize data points (such as capitals cities, etc.) on the world map. Here’s the Codepen:

See you in part 3.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.