Skip to contents

mapSpain provides an interface for working with static map tiles. It can download tiles as .png or .jpeg, depending on the tile service, and use them alongside your sf objects.

mapSpain also provides a plugin for leaflet maps, which lets you add multiple basemaps to interactive maps.

The services are implemented with the Leaflet plugin leaflet-providersESP. You can view the available provider options at that link.

Static map tiles

This example shows how to combine multiple tiles in a static map. We focus on layers provided by La Rioja’s Infraestructura de Datos Espaciales (IDERioja).

Warning

When working with static map tiles, set moveCAN = FALSE. Otherwise, imagery for the Canary Islands may be inaccurate.

library(mapSpain)
library(sf)
library(ggplot2)
library(tidyterra)

# Logroño
lgn_borders <- esp_get_munic_siane(munic = "Logroño")

# Convert to Mercator (EPSG:3857), a common choice when working with tiles.
lgn_borders <- st_transform(lgn_borders, 3857)

tile_lgn <- esp_get_tiles(lgn_borders, "IDErioja", bbox_expand = 0.5)

ggplot(lgn_borders) +
  geom_spatraster_rgb(data = tile_lgn) +
  geom_sf(fill = NA, linewidth = 2, color = "blue")
Figure 1: Municipal boundaries of Logroño using a tile as a basemap

Alpha values in tiles

Some tiles can be loaded with or without an alpha (transparency) value, which controls layer transparency:

galicia <- esp_get_ccaa_siane("Galicia", epsg = 3857)

# Example without transparency.
basemap <- esp_get_tiles(
  galicia,
  "IDErioja.Claro",
  zoommin = 1,
  crop = TRUE,
  bbox_expand = 0
)
tile_opaque <- esp_get_tiles(
  galicia,
  "CaminoDeSantiago",
  verbose = TRUE,
  transparent = FALSE,
  crop = TRUE,
  bbox_expand = 0
)

ggplot() +
  geom_spatraster_rgb(data = basemap) +
  geom_spatraster_rgb(data = tile_opaque) +
  theme_void()
Figure 2: Map of the Way of St. James in Galicia

The following example repeats the same code with transparent = TRUE:

# Example with transparency.
tile_alpha <- esp_get_tiles(
  galicia,
  "CaminoDeSantiago",
  transparent = TRUE,
  crop = TRUE,
  bbox_expand = 0
)

# Use the same plotting code as above.
ggplot() +
  geom_spatraster_rgb(data = basemap) +
  geom_spatraster_rgb(data = tile_alpha) +
  theme_void()
Figure 3: Example of using alpha values to combine different map tile layers.

Now the two tiles overlap with the desired transparency.

Masking tiles

Another useful feature is tile masking, which supports more advanced maps:

rioja <- esp_get_prov_siane("La Rioja", epsg = 3857)

basemap <- esp_get_tiles(rioja, "PNOA", bbox_expand = 0.1, zoommin = 1)

masked <- esp_get_tiles(rioja, "IDErioja", mask = TRUE, zoommin = 1)

ggplot() +
  geom_spatraster_rgb(data = basemap, maxcell = 10e6) +
  geom_spatraster_rgb(data = masked, maxcell = 10e6)
Figure 4: Example of combining tile types by masking to an sf object.

Custom providers

You can use esp_get_tiles() to get static map tiles from other providers, for example OpenStreetMap.

osm_spec <- list(
  id = "OSM",
  q = "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
)

madrid_city <- esp_get_munic_siane(munic = "^Madrid$", epsg = 3857)
madrid_osm <- esp_get_tiles(madrid_city, type = osm_spec, zoommin = 1)

ggplot() +
  geom_spatraster_rgb(data = madrid_osm) +
  geom_sf(data = madrid_city, fill = NA)
Figure 5: Example of a basemap using OpenStreetMap

This example uses ThunderForest, a provider that requires an API key:

# Skip if no API key is available.
apikey <- Sys.getenv("THUNDERFOREST_API_KEY", "")
if (nzchar(apikey)) {
  thunder_spec <- list(
    id = "ThunderForest",
    q = paste0(
      "https://tile.thunderforest.com/transport/{z}/{x}/{y}.png?apikey=",
      apikey
    )
  )
  madrid_thunder <- esp_get_tiles(madrid_city, type = thunder_spec, zoommin = 1)

  ggplot() +
    geom_spatraster_rgb(data = madrid_thunder) +
    geom_sf(data = madrid_city, fill = NA)
}
Figure 6: Example of a basemap using ThunderForest

Dynamic maps with leaflet

mapSpain provides a plugin for leaflet maps. Here are some quick examples:

Earthquakes in Tenerife during the last year

library(leaflet)

tenerife_leaf <- esp_get_nuts(
  region = "Tenerife",
  resolution = 3,
  epsg = 4326,
  moveCAN = FALSE
)

bbox <- as.double(round(st_bbox(tenerife_leaf) + c(-1, -1, 1, 1), 2))

# Start the leaflet map.
m <- leaflet(
  tenerife_leaf,
  elementId = "tenerife-earthquakes",
  width = "100%",
  height = "60vh",
  options = leafletOptions(minZoom = 9, maxZoom = 18)
)

# Add layers.
m <- m |>
  addProviderEspTiles("IDErioja.Relieve") |>
  addPolygons(color = NA, fillColor = "red", group = "Polygon") |>
  addProviderEspTiles("Geofisica.Terremotos365dias", group = "Earthquakes")

# Add layer controls and bounds.
m |>
  addLayersControl(
    overlayGroups = c("Polygon", "Earthquakes"),
    options = layersControlOptions(collapsed = FALSE)
  ) |>
  setMaxBounds(bbox[1], bbox[2], bbox[3], bbox[4])

Population density in Spain

This example maps population density in Spain as of 2025:

library(leaflet)
library(dplyr)
munic <- esp_get_munic_siane(
  year = "2025-01-01",
  epsg = 4326,
  moveCAN = FALSE,
  rawcols = TRUE
) |>
  # Use the area field available in the SIANE data.
  mutate(area_km2 = st_area_sh * 10000)

# Get population data.
pop <- mapSpain::pobmun25 |>
  select(-name)

# Join population data.
munic_pop <- munic |>
  left_join(pop) |>
  mutate(
    dens = pob25 / area_km2,
    dens_label = prettyNum(round(dens, 2), big.mark = ".", decimal.mark = ",")
  )

# Create leaflet map.
bins <- c(0, 10, 25, 100, 200, 500, 1000, 5000, 10000, Inf)

pal <- colorBin("inferno", domain = munic_pop$dens, bins = bins, reverse = TRUE)

labels <- sprintf(
  "<strong>%s</strong><br/><em>%s</em><br/>%s pers. / km<sup>2</sup>",
  munic_pop$rotulo,
  munic_pop$ine.prov.name,
  munic_pop$dens_label
) |>
  lapply(htmltools::HTML)

leaflet(elementId = "SpainDemo", width = "100%", height = "60vh") |>
  setView(lng = -3.684444, lat = 40.308611, zoom = 5) |>
  addProviderEspTiles("IDErioja") |>
  addPolygons(
    data = munic_pop,
    fillColor = ~ pal(dens),
    fillOpacity = 0.6,
    color = "#44444",
    weight = 0.5,
    smoothFactor = 0.1,
    opacity = 1,
    highlightOptions = highlightOptions(
      color = "white",
      weight = 1,
      bringToFront = TRUE
    ),
    popup = labels
  ) |>
  addLegend(
    pal = pal,
    values = bins,
    opacity = 0.7,
    title = paste0(
      "<small>Pop. Density km<sup>2</sup></small><br><small>",
      "(2025)</small>"
    ),
    position = "bottomright"
  )

Available providers

The esp_tiles_providers list contains metadata for the tile providers used by the functions above. It includes all arguments required to reproduce each API request. The table below shows the static URL for each provider: