Create Interactive World Map to Visualize Country Data image 4

Create Interactive World Map to Visualize Country Data

Data associated with countries is fairly common. Presenting such data in the form of world map that shows the countries and presents the associated data through a color is a common requirement. I faced that exact requirement a few months back. It seemed pretty straightforward to me: with so many libraries, articles, demo applications around, how hard could it be?

Well, not extremely hard as it turned out. Yet I had to do some searching, reading, picking and choosing and putting it together. Which is a lot of fun. But not when you are in a hurry, as I was. So for future me as well as any other interested reader, I decided to share some of my findings, to allow for a quick start.

Technology & Tool Selection

First of all, presenting an interactive world map visualization in a browser can be done in several ways. I decided on HTML, JavaScript, CSS in combination with SVG – scalable vector graphics, a W3C standard (as of 1999), supported in any browser. SVG is an XML-based vector image format for defining two-dimensional graphics, having support for interactivity and animation. To construct SVG objects in JavaScript, a very convenient and defacto standard library is available that I also decided to use: D3.js (“The JavaScript library for bespoke data visualization”, created by a former visual graphics editor of the New York Times).

D3 is not just good at SVG data visualization in general but also has special support for geographical visualization. For example it understands geo coordinates (longitude and latitude) and projection (turning 3D earth based geocoordinates into 2D x,y pixel  coordinates). SVG has a concept of a path – a series of points that form a shape (circle, line, arc, polynomial area). The d3 geo capabilities allow translation of geoshapes (for example: continent, country, lake, island) into SVG paths.

So the tools to use: d3.js – built on/with/for JavaScript, SVG and of course HTML and CSS.

Geographic Data

The geographic data I need that defines the countries is available in a standard data format called GeoJSON. In GeoJSON files, geographic data structures are defined for locations, areas, routes etc using structured arrays of geo coordinates pairs. Many geographic data sets are available as GeoJSON (or sometimes in a format that can easily be converted to GeoJSON – read for example this article on converting commonly encountered map shape files to GeoJSON).

There are many GeoJSON files on offer on the internet with data for the countries of the world; most of them use the same underlying source of data: Natural Earth. One convenient way of composing a GeoJSON data file with the required countries and the right level of detail (less detail == smaller data file) is through this site. You select the countries you are interested in, specify the level of detail and download the data file corresponding with your choices. I downloaded a file with lowest resolution (110 meter details) for all countries except Antartica and got a file of just under 1MB.

Note: TopoJSON offers a more compact data format: it recognizes the fact that in most cases the border of one country is also the border of another country and so the points describing that border need to be included in the datafile only once. For now I will not further discuss that optimization, but for real world applications you probably should look into it.

Creating the Web Application with the World Map Visualization

To keep things really simple, let’s put everything in a single HTML file.

The initial file loads the D3.js library, creates an SVG element and draws a circle – to show it is working. image

No world map or any level of GeoJSON in here. That comes next.

In order to draw countries on a map, I need a data set that defines [shapes of] countries. A data set that contains geocoordinates for points on the borders of each country, that we can use to draw the shape of the country on the browser’s page.

In this case, I am leveraging the GeoJSON data file at Open Data Soft | Data Hub. It contains many additional country details besides just the Geo coordinates of the points that make up the country borders, including the name, numerical and alphanumerical identifier, population size and GDP:

image

Note: in this example, the data set is loaded from its remote URL whenever the page is (re)loaded. In a real world application, that would be extremely unkind to the hosting party and probably result in poor performance. The dataset would be included in the application, stripped of all unneeded properties – and possibly included in its CDN distribution set.

The code required to turn the GeoJSON file into the picture of the world map shown next is quite compact and straightforward. We need to decide on the projection to use (how should d3 map from longitude and latitude, locations in the 3D world of the globe to X,Y pixel coordinates in the 2D world of our computer screen). There are many predefined projections in d3 – from the classic Mercator (which makes Africa and any region near the equator too small and Greenland and Canada and other very northern or very southern areas too large) to projections that focus on proper representation of shape or size. We next define a function – using the projection of choice – to produce an SVG Path (in X,Y units) from the GeoJSON collection of Long/Lat coordinates).

The real magic happens next – as we use the typical d3 approach for generating SVG nodes:

svg.selectAll(‘path’)
                .data(geojsonData.features)
                .join(‘path’)
                .attr(‘d’, geoGenerator);

Start with the svg component. Locate all its existing path children (of which there are none), bring in a data collection that is created from all elements in the geojsonData.features collection – one entry for each country. The result of the join is a set of new SVG path elements – one for each country. For each of these SVG elements, a single attribute is set: the d property hat defines the path to be drawn. This path is generated by the function geoGenerator that in turn uses the geoEquirectangular projection to convert the long/lat pairs defined in the element from the geojsonData.features collection to suitable x,y coordinates.

image

When the index.html page is loaded, the world map does indeed show up. Without colors. Without interactivity. Without anything else besides the shapes of the countries. So it is bare bones. But it is also just a few lines of code. And we will see next how easily we can add more information and some interactivity.

Add Data Visualization to the World Map

The shapes – aka countries – in the world map can be colored in a way that is not just pretty but has real meaning. The color could indicate the size or population or economical status of a country. Or the length of its name. Any attribute that we can turn into a color can be used.

Let’s first turn every country blue.

image

This gives you an idea how we assign colors to shapes: the style’s fill property is set to the result of invoking a function. That function gets passed in the data element associated with the path element and it returns a string that defines the color.

If we want to have the color used for a country shape depend on the population size of the country, we need to have the value for that property for every country (available in the data set as properties.pop_est on the data element. And we need a way to translate this value to a color. This article explains in detail how color scales can be defined in d3.

In this example, I have used d3.extent to determine the range of population size values in the dataset, in the form of a domain (essentially just an array of at least two numerical values, the first indicating the mininum value in the domain and the last one representing the other extreme):

const populationDomain = d3.extent(geojsonData.features, (d) => d.properties.pop_est) 

Using the domain, I can define the color scale – that maps any value from the minimum to the maximum value in the domain to a color. Many different ways for defining a color scale are available. One is:

const colorScale = d3.scaleLinear().domain(populationDomain).range([‘pink’, ‘blue’]).clamp(true)

Here the color scale is linear, using colors from pink on the low end of the domain / scale to blue on the high end. Note: the clamp method ensures that any values outside of the domain will be clamped to the given range.

Another way to define the colorScale:

const colorScale = d3.scaleDiverging(d3.interpolateRdYlBu).domain(populationDomain)

In both cases we have a bit of a challenge with the data: the population size ranges from 400 to 1400 Million. A logarithmic scale would probably produce better looking and easier interpretable results.

To use the colorScale to determine the fill color for every country, we redefine the function that gives the fill attribute of style its value; this function uses the pop_est property to get the color code from the colorScale:

style(‘fill’, (d) => colorScale( d.properties.pop_est))

Let’s look at what we have got:

image

A more straightforward result perhaps is achieved for a color based on the length of the name of each country:

image

Add a Small Dose of Interactivity

I promised some interactivity in the world map. In future articles I plan to add much more – such as select and zoom – but for now we will only add a title property to the each country’s SVG path element. As a result, hovering over a country will bring up the browser “hover balloon text” with whatever information we want to show for the country. Its name seems like a logical piece of data to present, perhaps with the population size.

Defining the title attribute on the path element is straightforward:

.append(‘title’)
.text((d) => `${d.properties.name} (estimated population size: ${Math.floor(d.properties.pop_est/1000000)}M)`)

Here the title contains the name of the country and the population size rounded off to the nearest million. This results in the simple popup text shown when the cursor hovers over the shape for a country.

image

Note that I have now also specified the class attribute for each shape. It is set to country. The CSS definition for this style:

<style>
.country {
stroke: lightblue;
stroke-width: 0.75px;
}
</style>

The country borders are made more distinctive – in light blue and with a width of 0.75 pixel.

Conclusion

This article hopefully made it clear how quickly and easily and elegantly we can visualize data associated with countries on a world map – using colors mapped to the underlying data value we want to present. The d3 library makes it very easy to create SVG elements that visualize each country and to set properties and behavior for each of these elements that is dynamically derived from the data values applicable for that country.

In this article we have not yet discusses several interesting features we may want to add to our maps: initialize somewhere else than on  Long/Lat 0,0, zoom in and out, select countries, show legend, .. I intend to talk some more more about this in future articles.

Resources

GitHub Repo for this article.

Working with Colour Scales for Data Visualisation in D3 – https://css-irl.info/working-with-color-scales-for-data-visualisation-in-d3/

D3 in Depth – Making maps with D3- https://www.d3indepth.com/geographic/

Command-Line Cartography, Part 1 A tour of d3-geo’s new command-line interface.– Mike Bostock – https://medium.com/@mbostock/command-line-cartography-part-1-897aa8f8ca2c

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.