11

I'm wondering whether it is possible to add a basemap to a map made in tmap when the tmap mode is set to plot and not interactive mode.

This is the code I have used to produce my map

library(sf)

library(spData)

library(tmap)

tm_shape(nz) +
  tm_polygons(
    "Median_income",
    palette = "Blues",
    n = 10,
  ) +
  tm_layout(basemaps = leaflet::providers$OpenStreetMap)

With this code I can view the basemap whilst it is in Plot as I would just like to visualize New Zealand and not the surrounding ocean.

This is the map my code produces enter image description here

T.2001
  • 147
  • 1
  • 6

3 Answers3

15

There are at least two ways:

Using tmaptools:

Via the read_osm function in tmaptools which uses the OpenStreetMap package to get tiles. Example here ripped straight from the docs:

> library(tmaptools)
> library(tmap)
Warning message:
replacing previous import ‘gdalUtils::gdal_rasterize’ by ‘sf::gdal_rasterize’ when loading ‘mapview’ 
> data(NLD_muni)
> osm_NLD <- read_osm(NLD_muni, ext=1.1)
> tm_shape(osm_NLD) + tm_rgb()
> 

map image

Note you should modify your mapping chain if you want to do an interactive web-browser map by removing this rgb layer and adding a standard basemap layer.

Or alternatively:

Using rosm.

Call osm.raster to get a raster image given a spatial object. Will reproject the raster to the source coordinate system.

> bg = osm.raster(NLD_muni)
Zoom: 8
> bg
class      : RasterBrick 
dimensions : 1046, 1074, 1123404, 3  (nrow, ncol, ncell, nlayers)
resolution : 373, 372  (x, y)
extent     : -28610.91, 371991.1, 303384.1, 692496.1  (xmin, xmax, ymin, ymax)
crs        : +init=epsg:28992 +proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +towgs84=565.4171,50.3319,465.5524,-0.398957388243134,0.343987817378283,-1.87740163998045,4.0725 +units=m +no_defs 
source     : memory
names      :   layer.1,   layer.2,   layer.3 
min values : -6.848990, 41.156879,  1.062173 
max values :  266.3927,  290.5641,  263.3747 

This will not quite work with tm_rgb() yet because the min-max values are outside 0-255 for some of the layers. I think this is because the projection function used internally is doing an interpolation that is extrapolating in places. Fix roughly with:

> bg[bg[]>255]=255
> bg[bg[]<0]=0

Then you can

> tm_shape(bg) + tm_rgb() + tm_shape(NLD_muni) + tm_borders()

note the projection results in a non-square raster so there's NA values around it.

enter image description here

Spacedman
  • 92,590
  • 12
  • 140
  • 224
5

The ceramic package provides a great solution for this. You can produce basemaps via Mapbox, which requires you to register but they offer a generous free tier so you shouldn't need to pay anything.


library(sf)
library(spData)
library(tmap)
library(ceramic)

basemap <- cc_location(loc=nz, max_tiles = 4,
                       base_url = "https://api.mapbox.com/styles/v1/mdsumner/cjs6yn9hu0coo1fqhdqgw3o18/tiles/512/{zoom}/{x}/{y}")


tm_shape(basemap) +
  tm_rgb() +
  tm_shape(nz) +
  tm_polygons(
    "Median_income",
    palette = "Blues",
    n = 10,
    alpha = 0.3
  ) 

Created on 2020-05-04 by the reprex package (v0.3.0)

Michael Harper
  • 14,721
  • 2
  • 60
  • 84
3

As per the {tmap} documentation the basemap feature is available only in view mode https://www.rdocumentation.org/packages/tmap/versions/2.2/topics/tm_basemap

This seems logical, as the basemap comes not from directly tmap, but from the leaflet js package.

If you absolutely positively require a basemap for a static r plot you should be able to get one via {ggplot} / {ggmap} workflow (eg. ggmap::get_stamenmap() + usual ggplot techniques).

Jindra Lacko
  • 7,814
  • 3
  • 22
  • 44