A choropleth is a thematic map where areas are colored in proportion to a variable such as population density. GMT lets us plot choropleth maps but the process is not straightforward because we need to put the color information in the header of a multi-segment file. To facilitate this, some tools were added to the Julia wrapper.

(But see also the more extended example in tutorials)

Countries (Europe DCW)

Load packages needed to download data and put into a DataFrame

using GMT, DataFrames, CSV
paises = CSV.File(HTTP.get("").body, delim=';') |> DataFrame;

Extract the Europe countries from the DCW file

eu = coast(DCW="=EU+z", dump=true);

Generate two vector with country codes (2 chars codes) and the population density for European countries.

codes, vals = GMT.mk_codes_values(paises[!, 2], paises[!, 3], region="eu");

Create a Categorical CPT that allows plotting the choropleth. Note that we need to limit the CPT range to leave out the very high density states like Monaco otherwise all others would get the same color.

C = cpt4dcw(codes, vals, range=[10, 500, 10]);

Show the result

plot(eu, cmap=C, fill="+z", proj=:guess, R="-76/36/26/84", title="Population density",
	colorbar=true, show=true)
DCW choropleth

Countries (World)

While GMT come with the world countries in its DCW data base, those countries polygons have a somewhat too high resolution for world maps and hence produce files unnecessarily, which also makes them slower to create. An alternative is to use a much lower resolution file that can be found here but whose download link is here. And we can read it directly into GMT (and wait a bit while it gets downloaded).

countries = gmtread("/vsicurl/");

The country polygons have attributes like:

Attributes: Dict("ISO_A2" => "AW", "ISO_A3" => "ABW", "ADMIN" => "Aruba")

Like in the Europe case above, we load the world population into a DataFrame

pop = CSV.File(HTTP.get("").body, delim=';') |> DataFrame;

The contents of that file look like this:

julia> pop
248×3 DataFrame
 Row │ Country Name                       Country Code  2010
     │ String                             String3       Float64?
   1 │ Arab World                         ARB            26.2725
   2 │ Caribbean small states             CSS            16.9939
   3 │ East Asia & Pacific (all income …  EAS            90.5122

So we must now link the countries in countries with density in 2010 via the attribute ISO_A3 in countries and the third column in pop. To make a global choropleth map with the population density we will use first the polygonlevels function to get as the population density in the same order as the country polygons are stored in the countries GMTdataset.

zvals = polygonlevels(countries, string.(pop[!,2]), pop[!,3], att="ISO_A3");

And now we are ready to make the map

# First create the colormap. Limit the maximum range to 500 otherwise states
# like Monaco would the all the `reds` and the rest of the world would be all blues.
C = makecpt(range=(0,500,10));
plot(countries, region=(-180,180,-60,85), level=zvals, cmap=C, proj=:guess,
     title="World population density", colorbar=true, show=true)
World pop

Covid rate of infection in Portugal 2021

First, download the Portuguese district polygons shape file from this Github repo Next load it with:

using GMT, DataFrames, CSV

Pt = gmtread("C:\\programs\\compa_libs\\covid19pt\\extra\\mapas\\concelhos\\concelhos.shp");

Download and load a CSV file from same repo with rate of infection per district. Load it into a DataFrame to simplify data extraction.

incidence ="C:\\programs\\compa_libs\\covid19pt\\data_concelhos_incidencia.csv", DataFrame);

Get the rate of incidence in number of infected per 100_000 habitants for the last reported week.

r = collect(incidence[end, 2:end]);

But the damn polygon names above are all uppercase, Ghrrr. We will have to take care of that.

ids = names(incidence)[2:end];

Each of the Pt datasets have attributes (e.g., Pt[1].attrib) and the one that is common with the names in ids is the Pt[1].attrib["NAME2"] (the conselho name). But the names in dataconcelhosincidencia.csv (from which the ids are derived) and the concelhos.shp (that we read into Pt) do not use the same case (one is full upper case) so we need to use the nocase=true below. The comparison is made inside the next call to the polygonlevels() function that takes care to return the numerical vector that we need in plot's level option.

zvals = polygonlevels(Pt, ids, r, att="NAME_2", nocase=true);

Create a Colormap to paint the district polygons

C = makecpt(range=(0,1500,10), inverse=true, cmap=:oleron, hinge=240, bg=:o);

Get the date for the data being represented to use in title

date = incidence[end,1];

And finaly do the plot

plot(Pt, level=zvals, cmap=C, pen=0.5, region=(-9.75,-5.9,36.9,42.1), proj=:Mercator,
     title="Infected / 100.000 habitants " * date)

colorbar!(pos=(anchor=:MR,length=(12,0.6), offset=(-2.4,-4)), color=C,
          axes=(annot=100,), show=true)
DCW choropleth