How to pass Thunderforest API key to the R ggspatial package to create a map

1.5k Views Asked by At

I am creating maps using the rosm and ggspatial R package. The ggosm function is easy to use to extract a base map layer based on the spatial object provided. Here is an example.

library(ggplot2)
library(sp)
library(rosm)
library(ggspatial)

ggosm() + 
  geom_spatial(longlake_waterdf, fill = NA, color = "black")

enter image description here

This works great. I can change the base map layer to other types (See osm.types() for available types. Here is an example to use cartolight as the base map layer.

ggosm(type = "cartolight") + 
  geom_spatial(longlake_waterdf, fill = NA, color = "black")

enter image description here

This also works. Now my question is how to pass Thunderforest API key? If I used the type as thunderforestoutdoors, I got the following output.

ggosm(type = "thunderforestoutdoors") + 
  geom_spatial(longlake_waterdf, fill = NA, color = "black")

enter image description here

Clearly, I need the Thunderforest API key, so I have registered an API key from https://www.thunderforest.com/. This page (https://www.thunderforest.com/docs/apikeys/) shows how to use the API key. The documentation of rosm also shows that users can define map tiles by providing the URL (See ?as.tile_source). Nevertheless, it seems like the general structure of URL is like: https://{s}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png?apikey=<insert-your-apikey-here>. I will need to know the z, x, and y (zoom level and tile number) to specify the tile. It is not possible because I have a lot of spatial objects to plot and I need ggosm to determine the right zoom level and tiles for me. It would be great if anyone can shed some light on this.

2

There are 2 best solutions below

4
On BEST ANSWER

Solution:

Chuckle - There is a way to do this do this with rosm without modifying the library.

  • Use the rosm:: source_from_url_format() function. This is what it was designed to do, it is documented in the rosm package. [Admittedly somewhat briefly]

Note - I cover how to hide your public API key when using shared code under a separate follow-on bullet for completeness.

I hope this helps, it should at the very least make it easier for those who want to do what you propose without requiring library modifications.

Take Care T.

Example Usage: source_from_url_format()

Note: Replace <insert key> with your private key from the Thunderforest website.

if (!require(ggplot2)) install.packages("ggplot2")
if (!require(rosm)) install.packages("rosm")
if (!require(ggspatial)) install.packages("ggspatial")
if (!require(sp)) install.packages("sp")

thunderforest = source_from_url_format(
  url_format = c('http://a.tile.thunderforest.com/landscape/${z}/${x}/${y}.png?apikey=<insert key>',
                 'http://b.tile.thunderforest.com/landscape/${z}/${x}/${y}.png?apikey=<insert key>',
                 'http://c.tile.thunderforest.com/landscape/${z}/${x}/${y}.png?apikey=<insert key>'),
  attribution = "More on Thunderforest at http://www.thunderforest.com/"
)

ggosm(type = thunderforest) + 
  geom_spatial(longlake_waterdf, fill = NA, color = "black")

Example Runtime output:

> if (!require(ggplot2)) install.packages("ggplot2")
> if (!require(rosm)) install.packages("rosm")
> if (!require(ggspatial)) install.packages("ggspatial")
> if (!require(sp)) install.packages("sp")
> thunderforest = source_from_url_format(

+   url_format = c('http://a.tile.thunderforest.com/landscape/${z}/${x}/${y}.png?apikey=<secret>',
+                  'http://b.tile.thunderforest.com/landscape/${z}/${x}/${y}.png?apikey=<secret>',
+                  'http://c.tile.thunderforest.com/landscape/${z}/${x}/${y}.png?apikey=<secret>'),
+   attribution = "More on Thunderforest at http://www.thunderforest.com/"
+ )
> ggosm(type = thunderforest) + 
+   geom_spatial(longlake_waterdf, fill = NA, color = "black")
Converting coordinates to lat/lon (epsg:4326)
Zoom: 15
Fetching 4 missing tiles
  |===================================================================================================================================| 100%
...complete!

Output Plot:

enter image description here

Hide your Thunderforest API Key from public Code

A separate but related problem is how you should hide your API key from your public code. The recommended approach is to add your API key to your ~/.Renviron file.

THUNDERFOREST_API_KEY="<insert api key"`

we retrieve the environment variable via a call too:

Sys.getenv("THUNDERFOREST_API_KEY")

now we can invoke this from within an R program as follows:

if (!require(ggplot2)) install.packages("ggplot2")
if (!require(rosm)) install.packages("rosm")
if (!require(ggspatial)) install.packages("ggspatial")
if (!require(sp)) install.packages("sp")

thunderforest = source_from_url_format(
  url_format = c(paste0('http://a.tile.thunderforest.com/landscape/${z}/${x}/${y}.png?apikey=',Sys.getenv("THUNDERFOREST_API_KEY")),
                 paste0('http://b.tile.thunderforest.com/landscape/${z}/${x}/${y}.png?apikey=',Sys.getenv("THUNDERFOREST_API_KEY")),
                 paste0('http://c.tile.thunderforest.com/landscape/${z}/${x}/${y}.png?apikey=',Sys.getenv("THUNDERFOREST_API_KEY"))),
  attribution = "More on Thunderforest at http://www.thunderforest.com/"
)

ggosm(type = thunderforest) +
  geom_spatial(longlake_waterdf, fill = NA, color = "black")

Executed Code

Using this example it is much easier to share your code. No keys need to be included in the published code ;-)

> if (!require(ggplot2)) install.packages("ggplot2")
> if (!require(rosm)) install.packages("rosm")
> if (!require(ggspatial)) install.packages("ggspatial")
> if (!require(sp)) install.packages("sp")    
> thunderforest = source_from_url_format(

+   url_format = c(paste0('http://a.tile.thunderforest.com/landscape/${z}/${x}/${y}.png?apikey=',Sys.getenv("THUNDERFOREST_API_KEY")),
+                  paste0('http://b.tile.thunderforest.com/landscape/${z}/${x}/${y}.png?apikey=',Sys.getenv("THUNDERFOREST_API_KEY")),
+                  paste0('http://c.tile.thunderforest.com/landscape/${z}/${x}/${y}.png?apikey=',Sys.getenv("THUNDERFOREST_API_KEY"))),
+   attribution = "More on Thunderforest at http://www.thunderforest.com/"
+ )
> ggosm(type = thunderforest) +
+   geom_spatial(longlake_waterdf, fill = NA, color = "black")
Converting coordinates to lat/lon (epsg:4326)
Zoom: 15

Output Plot:

enter image description here

10
On

It doesn't look like there's currently a way to do this with ggspatial or rosm at the moment. So, I forked rosm and modified one of the functions to include an api key if it's found in your environment.

Short Term Solution

You can just use the forked repo.

# Packages
devtools::install_github("jonathande4/rosm")
library(rosm)
library(ggspatial)

# Set environment.
Sys.setenv("THUNDERFOREST_KEY" = "YOUR_API_KEY")

# Plot
ggosm(type = "thunderforestoutdoors") + 
  geom_spatial(longlake_waterdf, fill = NA, color = "black")

Which outputs this map, free from watermarks.

output

Long Term Solution

I'd like to try and submit a pull request for this change. If there are any changes in implementation that deviate from the original solution, I'll post an update.

Hope that helps.