Countries of the world

This article does not reveal much new things that haven't been covered before in Populated places maps and Theme maps of Germany. What is new though is where to get these data and how to use a shape file in order to get a GeoJSON and finally an SVG. Also, this app lets you create an SVG from an outline of the borders of a country.

Find and download country data

The country data comes from Natural Earth. I went for the Admin 0 Countries. You get a zip file that needs to be unpacked. Inside the archive is a list of files that can be used in a GIS system. For our needs we will rely on the GDAL library that exists also in a linux package ready to install on Debian/Ubuntu via sudo apt install gdal-bin.

From a shape file to a country data set

Once you have the GDAL binaries installed we may have a short glimpse into the shape file:

ogrinfo -al ne_10m_admin_0_countries.shp | less

At that point we also are able to transform the shape file into a GeoJSON:

ogr2ogr -f GeoJSON world.json ne_10m_admin_0_countries.shp

A GeoJSON file contains an ordinary json string, so any json parser can be used to extract information from that file. This world.json contains a list of Features. Each Feature consists of a geometry object and a properties object. The first contains a list of coordinates that describes a polygon, the latter contains information about the object. In our case this is the country name, iso codes etc.

Again we may run ogrinfo that reveals us the name of the FeatureCollection:

ogrinfo world.json -so
INFO: Open of `world.json'
      using driver `GeoJSON' successful.
1: ne_10m_admin_0_countries

Together with the properties that you were able to figure out by the output from the shape file above or by looking directly into the json we may select one country by doing:

ogr2ogr -f GeoJSON de.json world.json -sql "SELECT * FROM ne_10m_admin_0_countries WHERE ISO_A2='DE'"

In this example we were extracting Germany from the list. The output is written into de.json. Again you may look into this file. This should now be the same as in the big world.json file, except that it contains the entry for Germany only.

You may also get a list from the countries that the world.json contains:

ogr2ogr -f CSV countries.csv world.json -sql "SELECT NAME,ADM0_A3 FROM ne_10m_admin_0_countries"

Looking into the countries.csv you see a list of the three-letter iso code and the country name. Before, when I used the 2-letter iso code, entries contained -99. This seems to be the case if the territory is not a sovereign state.

Extract data for all countries

Now that we know how to work with the data we want to get a json file for each country. The world.json needs to be split depending on it's content to have a list of files that we later can use one by one to display the outline of a single country.

One way (for me the easiest) was to use some php to read the whole content of the world.json and write out json files for each country.

select_countries.php DownloadView all
<?php

$data = json_decode(file_get_contents('world.json'), true);

foreach ($data['features'] as $feature) {
    $country = [
        "type" => "FeatureCollection",
        "name" => $feature['properties']['ADM0_A3'],
        "features" => [ $feature ],
    ];
    file_put_contents($feature['properties']['ADM0_A3'] . '.json', json_encode($country));
}

PHP has the necessary built-in functions to read and parse a json string. We read the whole content of the file, transform it into an array and then loop over the features in that array. Each feature is an entry for one country or territory. At first, I was using the 2letter iso code (like above when selecting Germany). At the end I used the 3letter country code from the field ADM0_A3 (see below why). Every single feature element goes into a single json file. This is done by wrapping the item into a feature list that a GeoJSON consists of, just with a single item. The file name is the 3letter ISO country code.

The same can be done as well with the bash and the gdal binaries as described above.

select_countries.sh DownloadView all
#!/bin/bash

cat countries.csv | \
cut -d, -f 2 | \
while read iso ; do
  ogr2ogr -f GeoJSON ${iso}.json world.json -sql "SELECT * FROM ne_10m_admin_0_countries WHERE ADM0_A3='$iso'"
done

However, the run time of this script is much longer. I was puzzled at first that the script didn't terminate and was afraid that I run into an endless loop or something else only to look from a second terminal into the directory that the files showed up slowly one by one. That's because there's a lot of overhead in this approach. In each loop step the world.json needs to be read from scratch only to find the data that need to be extracted.

Also, the shell version leaves you with an "empty" file ADM0_A3.json. This originates from the column header line in the countries.csv. You may skip this line with an additional grep -v ADM0_A3 before or after the cut command.

The webpage

In order to display the data of the countries, we need to make them visible. This is basically done by drawing the paths from the geometry object. I am using the same approach as in other articles before, building a small webpage that includes d3.js with the topoJson extension to read the GeoJSON and draw a svg from it.

The file must also include a selection list, where to choose a country from. This list can be easily built from the cointries.csv by transforming it into an <option> list ready to be used inside an <select> element.

cat countries.csv | \
  grep -v ADM0_A3 | \
  sed  -E "s/(.*?),(.*?)/<option value=\"\2\">\1<\/option>/"

If you want to have the list sorted alphabetical then you need to add a sort after the grep commands (to order by country name) or after the sed command to order by the ISO code. Whenever you add sort this must be prefixed by the pipe symbol so that the output piping is working.

France is missing

When looking at the webpage to check the results, you will realize that the map is not centered sometimes and the scale of 1700 is not optimal chosen. Also, when looking at countries that have a mainland and islands somewhere a bit farther away, the center might be optimized to fit both, which doesn't make much sense sometimes. One example is France, which has oversea territories. However, France is missing on my country list. This is because the field ISO_A2 has got the value -99 a couple of times. I noticed this before and was filtering these because on a small glimpse I discovered that non-independent or disputed territories had this value. But because of the missing entry for France in my country list I discovered that France also contains this weird -99 value in the ISO_A2 field. Therefore, I decided to extract the country list once more, this time using the field ADM0_A3 which seems to be filled always. The scripts above are already corrected using the 3-letter code field.

The web application

Putting the steps from above all together gives a small and simple web application that contains a list of countries and territories and whenever selecting one, shows the outline of the borders of that country or territory.

To display the whole country within the window, the center of the polygons must be determined and the scale must be adjusted accordingly. Both, didn't work for me automatically using the information that I had. Therefore, I run through the list manually and put in a center and a scale factor in a list called presets. The effort was a bit time-consuming but was worth it. Again France (as already suspected) was off the way. When you use a reasonable large map (1200x1200) and a small scale (400), you will notice some land in the south-west of the mainland which is French Guiana, some smaller islands north of Guiana, the French West Indies and if you look closely in the south you should recognize Reunion. These overseas territories belong to the French republic and thus are included in the outline of France. However, the calculation of the center is not anywhere close to the mainland of France, thus not usable for our case to display a centered map.

By the way, a nice quiz question about the longest neighbouring country of France reveals Brazil to be the correct answer.

The presets that I used, are fine to start with. You can always adapt them by changing the values for the Latitude and Longitude as well as the map scale and hit the "update" button to redraw the map.