There will probably be a function to create a map in one run in the next major version of {DatawRappr}. In the meantime, I wrote this tutorial:

There are three map types in Datawrapper: locator, symbol and choropleth. I’m trying to explain in short, how you can use DatawRappr to create a map programmatically for symbols and choropleths. And as an add-on: How to create a tooltip using {DatawRappr}.

Why exclude Locator-maps? They are a little tricky and probably not so heavily automated compared to choropleths and symbols. That’s why I decided place them a little lower on my priority list and keep them out here. There is a tutorial on Locator-maps with the API by Datawrapper using curl.

The basic steps are always the same:

  • Create a new chart with chart-type map.
  • Upload your data to this chart/map with the required columns (IDs or Lat/Long-coordinates).
  • Edit the chart to select the matching for the columns.
  • Publish the chart.

If you want to update the data, you will only need steps 2 and 4.


Create a Choropleth map

Create a new chart

You can use this code to create a new chart and save the chart to your current environment. You don’t have to save it. You will just need the chart-id later.

new_choropleth_chart <- dw_create_chart(
  title = "This is a automated choropleth map",
  type = "d3-maps-choropleth"
  )
#> New chart's id: ZO9I5

Upload data to the chart

We will now upload some data to our newly created map. Beforehand, we have to decide on an basemap and a key that maps the data to the chart.

Select the right map

Datawrapper provides an API-endpoint with their basemaps, which you might curl

Or you can use the built-in dataframe in DatawRappr:

head(dw_basemaps)
#> # A tibble: 6 x 6
#>   id         level  slug   value      label      description                    
#>   <chr>      <chr>  <chr>  <chr>      <chr>      <chr>                          
#> 1 world-2019 World  world  DW_NAME    Name       The three-letter ISO-code of t…
#> 2 world-2019 World  world  DW_STATE_… ISO-Code   The written name of the countr…
#> 3 world-2019 World  world  GERMAN_NA… Name (Ger… The written name of the countr…
#> 4 africa-20… Africa africa DW_NAME    Name       The three-letter ISO-code of t…
#> 5 africa-20… Africa africa DW_STATE_… ISO-Code   The written name of the countr…
#> 6 europe     Europe europe ISO_A3     ISO 3-dig… DE21, FR24, TR42, ...

First, you have to decide, which map to use. It is identified by the id-column.

You can use dplyr’s View() to get an interactive table. Or you might want to filter using grepl()

dw_basemaps %>%
  filter(grepl("us-nyc", id))
#> # A tibble: 10 x 6
#>    id          level             slug            value  label     description   
#>    <chr>       <chr>             <chr>           <chr>  <chr>     <chr>         
#>  1 us-nyc-bor… USA » New York C… usa-new-york-c… BoroN… Borough … "Name of the …
#>  2 us-nyc-cen… USA » New York C… usa-new-york-c… BoroC… BoroCT20… ""            
#>  3 us-nyc-cen… USA » New York C… usa-new-york-c… geoid  GEOID co… ""            
#>  4 us-nyc-cit… USA » New York C… usa-new-york-c… CounD… Council … ""            
#>  5 us-nyc-com… USA » New York C… usa-new-york-c… BoroCD Boro CD … ""            
#>  6 us-nyc-hoo… USA » New York C… usa-new-york-c… NTANa… NTA Name  ""            
#>  7 us-nyc-hoo… USA » New York C… usa-new-york-c… NTACo… NTA Code  ""            
#>  8 us-nyc-pol… USA » New York C… usa-new-york-c… Preci… Precinct… ""            
#>  9 us-nyc-sch… USA » New York C… usa-new-york-c… Schoo… School D… ""            
#> 10 us-nyc-zip… USA » New York C… usa-new-york-c… ZIPCO… ZIP code  ""

Select the right key

In a second step, you have to choose a key for the map, which is identified in value. label and description provide some more details about it.

The key is the variable that binds the data to the map.

dw_basemaps %>%
  filter(grepl("germany-nuts3", id))
#> # A tibble: 2 x 6
#>   id         level            slug          value   label description           
#>   <chr>      <chr>            <chr>         <chr>   <chr> <chr>                 
#> 1 germany-n… Germany » NUTS3… germany-nuts… NUTS_N… Name  The written name of t…
#> 2 germany-n… Germany » NUTS3… germany-nuts… NUTS_ID Code  code / desc

In this example the basemap germany-nuts3 offers two key-options: NUTS_ID and NUTS_NAME. If you already have one of these options in your dataset, select the right one.

Otherwise, you will have to wrangle the data anyway.


Getting Country IDs

You can easily transform country names in various formats and languages with the {countrycode}-package. In most cases you won’t need a manual translation table. The package is available via CRAN or Github.

After installation, simply add a column with something like countrycode::countrycode(sourcevar = SOURCE VARIABLE, origin = "country.name.de", destination = "iso3c") to your dataframe.


Just in case you need it: You can retrieve the expected keys for a basemap by calling this API-endpoint. Simply enter the id of the basemap and the value of the key:

you could also use the {jsonlite}-package for this task:

library(jsonlite)

r <- fromJSON("https://api.datawrapper.de/plugin/basemaps/germany-states/name")
r$data$values
#>  [1] "Nordrhein-Westfalen"    "Baden-Württemberg"      "Hessen"                
#>  [4] "Bremen"                 "Niedersachsen"          "Thüringen"             
#>  [7] "Hamburg"                "Schleswig-Holstein"     "Rheinland-Pfalz"       
#> [10] "Saarland"               "Bayern"                 "Berlin"                
#> [13] "Sachsen-Anhalt"         "Sachsen"                "Brandenburg"           
#> [16] "Mecklenburg-Vorpommern"

Upload the data

We are using a simple dataset for German States here as an example: It contains the share of foreign population per German federal state (source: Federal Statistical Office Germany (Destatis)).

df_foreign_pop <- read.csv("data/foreign_pop.csv", stringsAsFactors = FALSE)

str(df_foreign_pop)
#> 'data.frame':    16 obs. of  2 variables:
#>  $ state                   : chr  "Baden-Württemberg" "Bayern" "Berlin" "Brandenburg" ...
#>  $ share_foreign_population: num  15.5 13.2 18.5 4.7 18.1 16.4 16.2 4.5 9.4 13.3 ...

You can simply upload the data as you would do with any other {DatawRappr}-chart.

dw_data_to_chart(df_foreign_pop, chart_id = new_choropleth_chart)
#> Data in ZO9I5 successfully updated.

Set the keys in the metadata

The metadata of a chart gives us the full flexibility to change what we want to change. In this case, we have to set the axes. A chart in Datawrapper returned as a JSON-Object, which contains differents sublevels. One of those is called metadata and contains the relevant keys:

"metadata": {
      "axes": {
          "keys": "KEY FROM DATA",
          "values": "VALUE FROM DATA"
      },
      "visualize": {
          "basemap": "ID of the basemap",
          "map-key-attr": "VALUE of the map key"
      }
}    

In our example from above, we are using the names of the German federal states as the IDs. We will therefore use the basemap germany-states with the value name. name consists of the keys that are already present in our dataset in the state-column. So we don’t need any transformation here. We just need to tell Datawrapper which column to use.

dw_edit_chart(new_choropleth_chart,
              axes = list(
                keys = "state",
                values = "share_foreign_population"
                ),
              visualize = list(
                basemap = "germany-states",
                "map-key-attr" = "name"
                )
              )
#> Chart ZO9I5 succesfully updated.

At that point, you might want to switch to Datawrapper’s UI to make the final adjustments to the legend before publishing. But: You could do them via dw_edit_chart as well. It’s probably just not that much fun.

Publish the chart

This is probably the easiest part:

dw_publish_chart(new_choropleth_chart)
#> Chart ZO9I5 published!### Responsive iFrame-code: ###
#> <iframe title="This is a automated choropleth map" aria-label="Karte" id="datawrapper-chart-ZO9I5" src="https://datawrapper.dwcdn.net/ZO9I5/1/" scrolling="no" frameborder="0" style="width: 0; min-width: 100% !important; border: none;" height="undefined"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(a){if(void 0!==a.data["datawrapper-height"])for(var e in a.data["datawrapper-height"]){var t=document.getElementById("datawrapper-chart-"+e)||document.querySelector("iframe[src*='"+e+"']");t&&(t.style.height=a.data["datawrapper-height"][e]+"px")}}))}();
#> </script>
#> 
#> ### Chart-URL:###
#> https://datawrapper.dwcdn.net/ZO9I5/1/
Screenshot of the choropleth map

Screenshot of the choropleth map


Create a Symbol map

A symbol map maps a certain value to a geographic location. We will use predefined coordinates here. Datawrapper’s user interface allows geocoding of adresses.

The workflow is pretty similar to the choropleth map. The main difference: We will use Latitude and Longitude coordinates, instead of the key.

Create a new chart

You can use this code to create a new chart and save the chart to your current environment. You don’t have to save it. You will just need the chart-id later.

new_symbol_chart <- dw_create_chart(
  title = "This is a automated symbol map",
  type = "d3-maps-symbols"
  )
#> New chart's id: X8IcM

Upload data to the chart

We will now upload some data to our newly created map. Beforehand, we have to decide on an basemap.

Select the right map

Datawrapper provides an API-endpoint with their basemaps, which you might curl

Or you can use the built-in dataframe in {DatawRappr}:

head(dw_basemaps)
#> # A tibble: 6 x 6
#>   id         level  slug   value      label      description                    
#>   <chr>      <chr>  <chr>  <chr>      <chr>      <chr>                          
#> 1 world-2019 World  world  DW_NAME    Name       The three-letter ISO-code of t…
#> 2 world-2019 World  world  DW_STATE_… ISO-Code   The written name of the countr…
#> 3 world-2019 World  world  GERMAN_NA… Name (Ger… The written name of the countr…
#> 4 africa-20… Africa africa DW_NAME    Name       The three-letter ISO-code of t…
#> 5 africa-20… Africa africa DW_STATE_… ISO-Code   The written name of the countr…
#> 6 europe     Europe europe ISO_A3     ISO 3-dig… DE21, FR24, TR42, ...

First, you have to decide, which map to use. It is identified by the id-column.

You can use dplyr’s View() to get an interactive table. Or you might want to filter using grepl()

dw_basemaps %>%
  filter(grepl("us-nyc", id))
#> # A tibble: 10 x 6
#>    id          level             slug            value  label     description   
#>    <chr>       <chr>             <chr>           <chr>  <chr>     <chr>         
#>  1 us-nyc-bor… USA » New York C… usa-new-york-c… BoroN… Borough … "Name of the …
#>  2 us-nyc-cen… USA » New York C… usa-new-york-c… BoroC… BoroCT20… ""            
#>  3 us-nyc-cen… USA » New York C… usa-new-york-c… geoid  GEOID co… ""            
#>  4 us-nyc-cit… USA » New York C… usa-new-york-c… CounD… Council … ""            
#>  5 us-nyc-com… USA » New York C… usa-new-york-c… BoroCD Boro CD … ""            
#>  6 us-nyc-hoo… USA » New York C… usa-new-york-c… NTANa… NTA Name  ""            
#>  7 us-nyc-hoo… USA » New York C… usa-new-york-c… NTACo… NTA Code  ""            
#>  8 us-nyc-pol… USA » New York C… usa-new-york-c… Preci… Precinct… ""            
#>  9 us-nyc-sch… USA » New York C… usa-new-york-c… Schoo… School D… ""            
#> 10 us-nyc-zip… USA » New York C… usa-new-york-c… ZIPCO… ZIP code  ""

Upload the data

We are using a simple dataset for German States here as an example: It contains the share of foreign population per German federal state (source: Federal Statistical Office Germany (Destatis)).

df_foreign_pop_symbol <- read.csv("data/foreign_pop_symbol.csv", stringsAsFactors = FALSE)

str(df_foreign_pop_symbol)
#> 'data.frame':    16 obs. of  4 variables:
#>  $ Lat                     : num  48.6 48.9 52.5 52.8 53.1 ...
#>  $ Lon                     : num  9.19 11.4 13.39 13.25 8.81 ...
#>  $ name                    : chr  "Baden-Württemberg" "Bayern" "Berlin" "Brandenburg" ...
#>  $ share_foreign_population: num  15.5 13.2 18.5 4.7 18.1 16.4 16.2 4.5 9.4 13.3 ...

The data is already geocoded into the Lat and Lon-columns. They are required for the symbol map.


Geocoding Adresses

There are loads of resources on how to geocode places with R. For example, you might want to check out the {ggmap}-package. Unfortunately geocoding with Google does require an API-Key.

For open alternatives you might have a look at hrbrmstr’s {nominatim} or the geocode_OSM()-function in {tmaptools}.


With Lat and Lon-columns set, you can simply upload the data as you would do with any other {DatawRappr}-chart.

dw_data_to_chart(df_foreign_pop_symbol, chart_id = new_symbol_chart)
#> Data in X8IcM successfully updated.

Set the keys in the metadata

The metadata of a chart gives us the full flexibility to change what we want to change. In this case, we have to set the axes. A chart in Datawrapper returned as a JSON-Object, which contains differents sublevels. One of those is called metadata and contains the relevant keys:

"metadata": {
        "axes": {
            "lat": "Lat-Col",
            "lon": "Lon-Col",
            "area": "Value-Col",
            "values": "Value-Col"
        },
      "visualize": {
          "basemap": "world-2019"
      }
}

In our example from above, we are using the names of the German federal states as the IDs. We will therefore use the basemap germany-states. We just need to tell Datawrapper which basemap and columns to use for the mapping. Further options in axes would be:

  • area: for the area/size of the symbol
  • value: for the color of the symbol
dw_edit_chart(new_symbol_chart,
              axes = list(
                lat = "Lat",
                lon = "Lon",
                area = "share_foreign_population"
                ),
              visualize = list(
                basemap = "germany-states"
                )
              )
#> Chart X8IcM succesfully updated.

At that point, you might want to switch to Datawrapper’s UI to make the final adjustments to the legend before publishing. But: You could do them via dw_edit_chart as well. It’s probably just not that much fun.

Publish the chart

This is probably the easiest part:

dw_publish_chart(new_symbol_chart)
#> Chart X8IcM published!### Responsive iFrame-code: ###
#> <iframe title="This is a automated symbol map" aria-label="Karte" id="datawrapper-chart-X8IcM" src="https://datawrapper.dwcdn.net/X8IcM/1/" scrolling="no" frameborder="0" style="width: 0; min-width: 100% !important; border: none;" height="undefined"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(a){if(void 0!==a.data["datawrapper-height"])for(var e in a.data["datawrapper-height"]){var t=document.getElementById("datawrapper-chart-"+e)||document.querySelector("iframe[src*='"+e+"']");t&&(t.style.height=a.data["datawrapper-height"][e]+"px")}}))}();
#> </script>
#> 
#> ### Chart-URL:###
#> https://datawrapper.dwcdn.net/X8IcM/1/
Screenshot of the symbol map

Screenshot of the symbol map


Including tooltips

Including a tooltip is a task that is not only useful for maps, but also for scatterplots. The way to change them via tha API are pretty easy. Just use the visualize-argument in dw_edit_chart() and add a list which includes another list called tooltip.

tooltip has three different keys:

  • body: which is the body text of the tooltip. Add double brackets ton include a variable: {{ variable }}.
  • title: is the title of the tooltip. It may also contain a variable with double brackets.
  • fields: is a list that contains the mapping between the chart data and the variables in the tooltips. You have to include each variable here that is used in the tooltip double brackets in the format: "variable in tooltip" = "variable in data". You may (although it makes things more complicated) map variables to different names, as shown below where "name" = "b".
dw_edit_chart(CHART_ID, visualize = list(
  tooltip = list(
    body = "{{ name }} has value {{ a }}.",
    title = "{{ name }}",
    fields = list(
      "name" = "b",
      "a" = "a"
    )
    )))