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:
map
.If you want to update the data, you will only need steps 2 and 4.
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: E4ytb
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.
Datawrapper provides an API-endpoint with their basemaps, which you might curl
curl --request GET \
--url https://api.datawrapper.de/plugin/basemaps
Or you can use the built-in dataframe in DatawRappr:
head(dw_basemaps)
#> # A tibble: 6 × 6
#> id level slug value label description
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 world-2019 World world DW_NAME Name The three-letter …
#> 2 world-2019 World world NAME_SHORT Name (short) The three-letter …
#> 3 world-2019 World world DW_STATE_CODE ISO-Code The written name …
#> 4 world-2019 World world GERMAN_NAME_NEW Name (German) The written name …
#> 5 world-2019 World world GERMAN_NAME Name old (German) The written name …
#> 6 africa-2020 Africa africa DW_NAME Name The three-letter …
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: 12 × 6
#> id level slug value label description
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 us-nyc-boros USA » New … usa-new-yo… BoroN… Boroug… "Name of th…
#> 2 us-nyc-censustracts USA » New … usa-new-yo… BoroC… BoroCT… ""
#> 3 us-nyc-censustracts USA » New … usa-new-yo… geoid GEOID … ""
#> 4 us-nyc-citycouncils USA » New … usa-new-yo… CounD… Counci… ""
#> 5 us-nyc-communitydistricts USA » New … usa-new-yo… BoroCD Boro C… ""
#> 6 us-nyc-hoods USA » New … usa-new-yo… NTANa… NTA Na… ""
#> 7 us-nyc-hoods USA » New … usa-new-yo… NTACo… NTA Co… ""
#> 8 us-nyc-policeprecincts USA » New … usa-new-yo… Preci… Precin… ""
#> 9 us-nyc-schooldistricts USA » New … usa-new-yo… Schoo… School… ""
#> 10 us-nyc-uhf USA » New … usa-new-yo… uhfco… Code "Name of th…
#> 11 us-nyc-uhf USA » New … usa-new-yo… uhf_n… Name "Name of th…
#> 12 us-nyc-zipcodes USA » New … usa-new-yo… ZIPCO… ZIP co… ""
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 × 6
#> id level slug value label description
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 germany-nuts3 Germany » NUTS3 (2016) germany-nuts3-2016 NUTS_NAME Name The writte…
#> 2 germany-nuts3 Germany » NUTS3 (2016) germany-nuts3-2016 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:
curl --request GET \
--url https://api.datawrapper.de/plugin/basemaps/ID/VALUE
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"
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)
#> Warning in if (format == "csv") {: Bedingung hat Länge > 1 und nur das erste
#> Element wird benutzt
#> Data in E4ytb successfully updated.
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 E4ytb 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.
This is probably the easiest part:
dw_publish_chart(new_choropleth_chart)
#> Chart E4ytb published!
#> ### Responsive iFrame-code: ###
#> <iframe title="This is a automated choropleth map" aria-label="Karte" id="datawrapper-chart-E4ytb" src="https://datawrapper.dwcdn.net/E4ytb/1/" scrolling="no" frameborder="0" style="width: 0; min-width: 100% !important; border: none;" height="580"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();
#> </script>
#>
#> ### Chart-URL:###
#> https://datawrapper.dwcdn.net/E4ytb/1/
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.
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: PGgcj
We will now upload some data to our newly created map. Beforehand, we have to decide on an basemap.
Datawrapper provides an API-endpoint with their basemaps, which you might curl
curl --request GET \
--url https://api.datawrapper.de/plugin/basemaps
Or you can use the built-in dataframe in {DatawRappr}:
head(dw_basemaps)
#> # A tibble: 6 × 6
#> id level slug value label description
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 world-2019 World world DW_NAME Name The three-letter …
#> 2 world-2019 World world NAME_SHORT Name (short) The three-letter …
#> 3 world-2019 World world DW_STATE_CODE ISO-Code The written name …
#> 4 world-2019 World world GERMAN_NAME_NEW Name (German) The written name …
#> 5 world-2019 World world GERMAN_NAME Name old (German) The written name …
#> 6 africa-2020 Africa africa DW_NAME Name The three-letter …
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: 12 × 6
#> id level slug value label description
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 us-nyc-boros USA » New … usa-new-yo… BoroN… Boroug… "Name of th…
#> 2 us-nyc-censustracts USA » New … usa-new-yo… BoroC… BoroCT… ""
#> 3 us-nyc-censustracts USA » New … usa-new-yo… geoid GEOID … ""
#> 4 us-nyc-citycouncils USA » New … usa-new-yo… CounD… Counci… ""
#> 5 us-nyc-communitydistricts USA » New … usa-new-yo… BoroCD Boro C… ""
#> 6 us-nyc-hoods USA » New … usa-new-yo… NTANa… NTA Na… ""
#> 7 us-nyc-hoods USA » New … usa-new-yo… NTACo… NTA Co… ""
#> 8 us-nyc-policeprecincts USA » New … usa-new-yo… Preci… Precin… ""
#> 9 us-nyc-schooldistricts USA » New … usa-new-yo… Schoo… School… ""
#> 10 us-nyc-uhf USA » New … usa-new-yo… uhfco… Code "Name of th…
#> 11 us-nyc-uhf USA » New … usa-new-yo… uhf_n… Name "Name of th…
#> 12 us-nyc-zipcodes USA » New … usa-new-yo… ZIPCO… ZIP co… ""
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)
#> Warning in if (format == "csv") {: Bedingung hat Länge > 1 und nur das erste
#> Element wird benutzt
#> Data in PGgcj successfully updated.
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 symbolvalue
: 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 PGgcj 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.
This is probably the easiest part:
dw_publish_chart(new_symbol_chart)
#> Chart PGgcj published!
#> ### Responsive iFrame-code: ###
#> <iframe title="This is a automated symbol map" aria-label="Karte" id="datawrapper-chart-PGgcj" src="https://datawrapper.dwcdn.net/PGgcj/1/" scrolling="no" frameborder="0" style="width: 0; min-width: 100% !important; border: none;" height="580"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();
#> </script>
#>
#> ### Chart-URL:###
#> https://datawrapper.dwcdn.net/PGgcj/1/
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"
)
)))