3  Spatial Mapping in R

3.1 Overview

In this chapter we will learn about different types of spatial datasets (raster and vector). We will visualize these spatial datasets using static and interactive plotting options available in R. We will also explore different color palettes options available for generating spatial maps for scientific/technical reporting of spatial datasets. We will also learn briefly explore coordinate reference systems and map projections for spatial data representation.

3.1.1 Sample dataset

We will familiarize ourselves with several open-source global datasets and use them to practice spatial mapping and computing in R.

  1. Global surface soil moisture from NASA’s Soil Moisture Active Passive (SMAP) satellite (Entekhabi, Njoku, and O’Neill 2009)

https://smap.jpl.nasa.gov/

  1. Normalized Difference Vegetation Index (NDVI) from Moderate Resolution Imaging Spectroradiometer (MODIS) (Huete, Justice, and Van Leeuwen 1999)

https://modis.gsfc.nasa.gov/data/dataprod/mod13.php

  1. Global climate reference land regions from Coupled Model Intercomparison Project (CMIP) project (Iturbide et al. 2020)

https://essd.copernicus.org/articles/12/2959/2020/

  1. Climate classification (Hyper-arid, arid, semi-arid, sub-humid and humid) based on Global Aridity Index Database (Zomer, Xu, and Trabucco 2022)

https://csidotinfo.wordpress.com/2019/01/24/global-aridity-index-and-potential-evapotranspiration-climate-database-v3/

3.1.2 Data download

The sample dataset for this resource is uploaded to GitHub for easy access. Download the sample data manually as a zip file from: https://github.com/Vinit-Sehgal/SampleData . Once downloaded, extract the zip folder to the current working directory.

Alternatively, use the following script to programmatically download and extract the sample data from the GitHub repository.

###############################################################
#~~~ Import sample data from GitHub repository

if (dir.exists("SampleData-master")==FALSE){ 
# First we check if the folder already exists. If not, the download begins
download.file(url = "https://github.com/Vinit-Sehgal/SampleData/archive/master.zip",
destfile = "SampleData-master.zip")    # Download ".Zip"

# Unzip the downloaded .zip file
unzip(zipfile = "SampleData-master.zip")
}

# getwd()                           # Current working directory
list.files("./SampleData-master")   # List folder contents. Do you see sample datasets?
 [1] "CMIP_land"                               
 [2] "functions"                               
 [3] "images"                                  
 [4] "Largescale_geospatial_analysis_2022.html"
 [5] "Largescale_geospatial_analysis_2023.html"
 [6] "location_points.xlsx"                    
 [7] "ne_10m_coastline"                        
 [8] "raster_files"                            
 [9] "README.md"                               
[10] "sample_pdfs"                             
[11] "SMAP_L3_USA.nc"                          
[12] "SMAPL4_H5"                               
[13] "SMAPL4_rasters"                          
[14] "SMOS_nc"                                 
[15] "USA_states"                              
[16] "Workbook_DVGAR21-Part1.html"             
[17] "Workbook_DVGAR21-Part2.html"             

3.2 Using R as GIS

A geographic information system, or GIS refers to a platform which can map, analyzes and manipulate geographically referenced dataset. A geographically referenced data (or geo-referenced data) is a spatial dataset which can be related to a point on Earth with the help of geographic coordinates. Types of geo-referenced spatial data include: rasters (grids of regularly sized pixels) and vectors (polygons, lines, points).



A quick and helpful review of spatial data can be found here: https://spatialvision.com.au/blog-raster-and-vector-data-in-gis/

3.2.1 Plotting raster data: with terra and tmap

In this section, we plot global raster data of surface (~5 cm) soil moisture from SMAP. Let’s start by first importing the global soil moisture raster.

# Import package for raster operations
library(terra) 

# Import SMAP soil moisture raster from the downloaded folder
sm=terra::rast("./SampleData-master/raster_files/SMAP_SM.tif")

Once we have imported the SpatRaster (short for “spatial raster”) using rast() function from terra package, let’s note its attributes. Notice the dimensions, resolution, extent, crs i.e. coordinate reference system and values. Note that the cell of one raster layer can only hold a single numerical value.

# Print raster attributes
print(sm)

class : SpatRaster dimensions : 456, 964, 1 (nrow, ncol, nlyr) resolution : 0.373444, 0.373444 (x, y) extent : -180, 180, -85.24595, 85.0445 (xmin, xmax, ymin, ymax) coord. ref. : lon/lat WGS 84 (EPSG:4326) source : SMAP_SM.tif name : SMAP_SM min value : 0.01999998 max value : 0.87667608

# Try:
# dim(sm)   # Dimension (nrow, ncol, nlyr) of the raster
# terra::res(sm)   # X-Y resolution of the raster
# terra::ext(sm)   # Spatial extent of the raster
# terra::crs(sm)   # Coordinate reference system

Now let’s plot the raster using terra::plot.

# Basic Raster plot 
terra::plot(sm, main = "Soil Moisture") 

3.2.2 Color palettes

Using a good color palette is an important aspect of spatial mapping. Choice of a good colormap can help the readers understand the key aspects of the map. The selected colors must adequately represent the key features and their differences, wherever applicable, with the least distortion, ambiguity or effort. There are several libraries available in R specifically dedicated to generating color pelettes for scientific mapping. We will also learn the the usage of cetcolor and scico packages to generate perceptually uniform and color-blindness friendly palettes.


Some key packages for generating color palettes for scientific mapping are:

  1. RColorBrewer: https://colorbrewer2.org/#type=sequential&scheme=BuGn&n=3
  2. cetcolors (Perceptually Uniform Colour Maps): https://cran.r-project.org/web/packages/cetcolor/vignettes/cet_color_schemes.html
  3. scico (Scientific colormaps): https://github.com/thomasp85/scico
# Libraries for generating Colour palettes
library(RColorBrewer)
library(cetcolor)
library(scico)

# To view color palette 
library(unikn)   

#~~ A) User defined color palette using brewer.pal
mypal1 = RColorBrewer::brewer.pal(10, "Spectral") 

# Brewerpal outputs a max of 9-11 colors. So,the pelette may needs expansion.
mypal2= colorRampPalette(mypal1)(20)         # Expand pelette to 20 colors
unikn::seecol(mypal2)   

# Some more advanced options include opacity and interpolation method 
# mypal2= colorRampPalette(mypal1,                         
#                interpolate = c("linear"), # Choose btw linear/spline interpolation
#                alpha = 0.8)(20)           # Generate 20 colors, opacity set to 0.8
# unikn::seecol(mypal2)   

#~~ B) User defined color palette using scico package
mypal1 = scico::scico(20, alpha = 1.0, direction =  -1, palette = "vik") 
unikn::seecol(mypal1)   

#Check: scico_palette_names() for available palettes! 
# Try combinations of alpha=0.5, direction =1, and various different color palette  

#~~ C) User defined color palette using cetcolor package
mypal2 = cetcolor::cet_pal(20, name = "r2")   
unikn::seecol(mypal2)  

# Or reverse color pal 
mypal2 = rev(cetcolor::cet_pal(20, name = "r2") )  
unikn::seecol(mypal2) 

3.2.3 Customizing terra plot options

There is a long list of customization operations available while plotting rasters in R.

We will start with basic plot from terra, and then explore the function by changing different customization options , such as: Try horizontal=TRUE, interpolate=FALSE, change xlim=c(-180, 180) with asp=1, or try legend.shrink=0.4.

sm=rast("./SampleData-master/raster_files/SMAP_SM.tif") # SMAP soil moisture data

terra::plot(sm,
            main = "Scientific Plot of Raster",
            
            #Color options
            col = mypal2,                    # User Defined Color Palette
            breaks = seq(0, 1, by=0.1),      # Sequence from 0-1 with 0.1 increment
            colNA = "lightgray",             # Color of cells with NA values
            
            # Axis options      
            axes=TRUE,                       # Plot axes: TRUE/ FALSE
            xlim=c(-180, 180),               # X-axis limit
            ylim=c(-90, 90),                 # Y-axis limit
            xlab="Longitude",                # X-axis label
            ylab="Latitue",                  # Y-axis label
            
            # Legend options      
            legend=TRUE,                     # Plot legend: TRUE/ FALSE
            
            # Miscellaneous
            mar = c(3.1, 3.1, 2.1, 7.1),     # Margins
            grid = FALSE                     # Add grid lines
        )

3.2.4 Spatial plotting with tmap

library(tmap)
# Set tmap mode: Static plot="plot", Interactive plots="view"
tmap_mode("plot")          

tmap_SM = tm_shape(sm)+
  tm_grid(alpha = 0.2)+                             # Transparency of grid
  tm_raster(alpha = 0.7,                            # Transparency of raster plot
            palette = mypal2,                       # Color pellete
            style = "pretty",                       # Select style
            title = "Volumetric Soil Moisture")+    # Plot main title
  tm_layout(legend.position = 
              c("left", "bottom"))+                 # Placement of legend
  tm_xlab("Longitude")+                             # x-lab
  tm_ylab("Latitude")                               # y-lab 

tmap_SM

3.2.5 Interactive raster visualization aster data with mapview

Functionality of terra is largely similar to the legacy package raster (created by the same developer, Robert Hijmans). The development of terra is inspired by computational efficiency in geospatial operations. However, since terra is relatively new, and is continually developed, several other packages require conversion of the SpatRasters to rasterLayer for backwards compatibility.

To convert a SpatRaster to RasterLayer, use: sm2=as(sm, "Raster")

library(mapview)
library(raster)

# Convert SpatRaster to raster (from package raster)
sm2=as(sm, "Raster")
mapview(sm2,                  # RasterLayer
        col.regions = mypal2, # Color palette 
        at=seq(0, 0.8, 0.1)   # Breaks
)

3.2.6 Plotting raster data using tidyterra

tidyterra is a package that add common methods from the tidyverse for SpatRaster and SpatVectors objects created with the terra package. It also adds specific geom_spat*() functions for plotting rasters with ggplot2.

Note on Performance: tidyterra is conceived as a user-friendly wrapper of terra using the tidyverse} methods and verbs. This approach therefore has a cost in terms of performance.

library(tidyterra) 
library(ggplot2)  
ggplot() +   
  geom_spatraster(data = sm) +   
  scale_fill_gradientn(colors=mypal2,                 # Use user-defined colormap
         name = "SM",                                 #  Name of the colorbar
         na.value = "transparent",                    # transparent NA cells  
         labels=(c("0", "0.2", "0.4", "0.6", "0.8")), # Labels of colorbar
         breaks=seq(0,0.8,by=0.2),                    # Set breaks of colorbar
         limits=c(0,0.8))+   
    theme_void()  # Try other themes: theme_bw(), theme_gray(), theme_minimal() 

What if we are interested in a particular region and not the entire globe? We can plot the map for a specific extent (CONUS, in this case) by changing the range of coord_sfoption. We will also use a different theme: theme_bw. Try xlim = c(114,153) and ylim = c(-43,-11)! We will also add state boundaries and coastline to the plot.

sm_conus= ggplot() +
  geom_spatraster(data = sm) +
  scale_fill_gradientn(colors=mypal2,                               # User-defined colormap
                       name = "SM",                                 # Name of the colorbar
                       na.value = "transparent",                    # transparent NA cells
                       labels=(c("0", "0.2", "0.4", "0.6", "0.8")), # Labels of colorbar
                       breaks=seq(0,0.8,by=0.2),                    # Set breaks of colorbar
                       limits=c(0,0.8)) +
  coord_sf(xlim = c(-125,-67),                                      # Add extent for CONUS
           ylim = c(24,50))+               
  borders("world",                              # Add global landmass boundaries
          colour="gray43",                      # Fill light-gray color to the landmass
          fill="transparent")+                  # Transparent background
  borders("state",                              # Add US state borders
          colour="gray43",                      # Use light-gray color
          fill="transparent")+                  # Use transparent background  
  theme_bw()                                    # Black & white theme 

print(sm_conus)

3.2.7 Plotting vector data

Importing and plotting shapefiles is equally easy in R. We will import the shapefile of the updated global IPCC climate reference regions and global coastlineas Simple Feature (sf) Object. Terra can also be used to import shapefiles as vectors using the function vect. However, sf is more versatile, especially for shapefiles. Notice how the attributes of the sf objects resemble an Excel data sheet.

library(sf)  
library(terra)

##~~~~ Use sf package for shapefile 
# Import the shapefile of global IPCC climate reference regions (only for land) 
IPCC_shp = read_sf("./SampleData-master/CMIP_land/CMIP_land.shp")
# View attribute table of the shapefile
print(IPCC_shp) # Notice the attributes look like a data frame
Simple feature collection with 41 features and 4 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: -168 ymin: -56 xmax: 180 ymax: 85
Geodetic CRS:  WGS 84
# A tibble: 41 × 5
   V1              V2                V3       V4                        geometry
   <chr>           <chr>             <chr> <dbl>                   <POLYGON [°]>
 1 ARCTIC          Greenland/Iceland GIC       1 ((-10 58, -10.43956 58, -10.87…
 2 NORTH-AMERICA   N.E.Canada        NEC       2 ((-55 50, -55.4386 50, -55.877…
 3 NORTH-AMERICA   C.North-America   CNA       3 ((-90 50, -90 49.5614, -90 49.…
 4 NORTH-AMERICA   E.North-America   ENA       4 ((-70 25, -70.43478 25, -70.86…
 5 NORTH-AMERICA   N.W.North-America NWN       5 ((-105 50, -105.4386 50, -105.…
 6 NORTH-AMERICA   W.North-America   WNA       6 ((-130 50, -129.5614 50, -129.…
 7 CENTRAL-AMERICA N.Central-America NCA       7 ((-90 25, -90.37179 24.76923, …
 8 CENTRAL-AMERICA S.Central-America SCA       8 ((-75 12, -75.28 11.67333, -75…
 9 CENTRAL-AMERICA Caribbean         CAR       9 ((-75 12, -75.32609 12.28261, …
10 SOUTH-AMERICA   N.W.South-America NWS      10 ((-75 12, -74.57143 12, -74.14…
# ℹ 31 more rows
##~~~~ Use terra package for shapefile 
IPCC_shp = vect("./SampleData-master/CMIP_land/CMIP_land.shp") # Reference regions
print(IPCC_shp)
 class       : SpatVector 
 geometry    : polygons 
 dimensions  : 41, 4  (geometries, attributes)
 extent      : -168, 180, -56, 85  (xmin, xmax, ymin, ymax)
 source      : CMIP_land.shp
 coord. ref. : lon/lat WGS 84 (EPSG:4326) 
 names       :            V1                V2    V3    V4
 type        :         <chr>             <chr> <chr> <int>
 values      :        ARCTIC Greenland/Iceland   GIC     1
               NORTH-AMERICA        N.E.Canada   NEC     2
               NORTH-AMERICA   C.North-America   CNA     3
dim(IPCC_shp) # Notice the dimensions of the shapefile
[1] 41  4
# Load global coastline shapefile 
coastlines = vect("./SampleData-master/ne_10m_coastline/ne_10m_coastline.shp")
print(coastlines)
 class       : SpatVector 
 geometry    : lines 
 dimensions  : 4132, 2  (geometries, attributes)
 extent      : -180, 180, -85.22194, 83.6341  (xmin, xmax, ymin, ymax)
 source      : ne_10m_coastline.shp
 coord. ref. : lon/lat WGS 84 (EPSG:4326) 
 names       : scalerank featurecla
 type        :     <num>      <chr>
 values      :         6  Coastline
                       6  Coastline
                       6  Coastline
# Notice the difference in the geometry of coastlines and IPCC_shp


# Alternatively, download global coastlines from the web 
# NOTE: May not work if the online server is down
# download.file("https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/110m/physical/ne_110m_coastline.zip?version=4.0.1",destfile = 'ne_110m_coastline.zip')
# # Unzip the downloaded file
# unzip(zipfile = "ne_110m_coastline.zip",exdir = 'ne-coastlines-110m')

# Interactive polygon map with mapview

library(mapview)
mapview(IPCC_shp) # Scroll over the region of interest and find the attributes for the region

Subsetting vector data is similar to selecting a row from a data frame.

ENA_poly=IPCC_shp[4,] # Subset shapefile for Eastern North-America (ENA) 
ENA_poly              # Polygon contents - notice it has 4 attributes
 class       : SpatVector 
 geometry    : polygons 
 dimensions  : 1, 4  (geometries, attributes)
 extent      : -90, -55, 25, 50  (xmin, xmax, ymin, ymax)
 coord. ref. : lon/lat WGS 84 (EPSG:4326) 
 names       :            V1              V2    V3    V4
 type        :         <chr>           <chr> <chr> <int>
 values      : NORTH-AMERICA E.North-America   ENA     4
# Plot ENA polygon using terra
terra::plot(ENA_poly, main="Polygon for Eastern North-America")  

Let us see how to combine rasters with overlapping vectors with terra . Remember to execute the lines with add=TRUE with the base plot.

terra::plot(sm, col=mypal2)
terra::plot(IPCC_shp, add=TRUE, col="transparent", border = "black")
terra::plot(coastlines, add=TRUE, col="transparent", border = "black")

Lets mark the location of Baton Rouge on this map. We will first make a base plot, over which coastlines and spatial location will be added. Again, remember to run these lines together.

#~~~ Add spatial point to shapefile/ raster
#~~ Make base map
terra::plot(IPCC_shp[c(3,4,6,7),], # IPCC land regions for Contiguous US.
             col = "lightgray",     # Background color
             border = "black")      # Border color

#~~ Add coastline
terra::plot(coastlines, col= "maroon", add=TRUE)  

#~~ Add spatial point to the plot
Long=-91.0; Lat=30.62                  # Lat- Long of Baton Rouge, LA
points(cbind(Long,Lat),                # Lat-Long as Spatial Points
       col="blue", pch=16, cex=1.2)    # Shape, size and color of point

3.2.8 Reprojection of rasters using terra::project

A coordinate reference system (CRS) is used to relate locations on Earth (which is a 3-D spheroid) to a 2-D projected map using coordinates (for example latitude and longitude). Projected CRSs are usually expressed in Easting and Northing (x and y) values corresponding to long-lat values in Geographic CRS.

A good description of coordinate reference systems and their importance can be found here:



In R, the coordinate reference systems or CRS are commonly specified in EPSG (European Petroleum Survey Group) or PROJ4 format (See: https://epsg.io/). Few commonly used projection systems and their codes are summarized below:


Projection system PROJ.4 code EPSG code
NAD83 (North American Datum 1983) “+proj=longlat +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +no_defs +type=crs” EPSG:4269
Mercator “+proj=merc +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +R=6371000 +units=m +no_defs +type=crs” ESRI:53004
WGS 84 (World Geodetic System 1984) “+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs +type=crs” EPSG:3395
WGS 84 / Pseudo-Mercator – Spherical Mercator ( used in Google Maps, OpenStreetMap) “+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs +type=crs” | EPSG:3857
Robinson (better for plotting global data due to minimal distortion, except for higher latitudes) “+proj=robin +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs +type=crs” ESRI:54030

The SpatRaster reprojection process is done with project() from the terra package.

# Importing SMAP soil moisture data
sm=rast("./SampleData-master/raster_files/SMAP_SM.tif") 

#~~ Projection 1: NAD83 (EPSG: 4269)

sm_proj1 = terra::project(sm, "epsg:4269")

terra::plot(sm_proj1, 
             main = "NAD83",    # Title of the plot
             col = mypal2,      # Colormap for the plot
             axes = FALSE,      # Disable axes
             box = FALSE,       # Disable box around the plots
             asp = NA,          # No fixed aspect ratio; asp=NA fills plot to window
             legend=FALSE)      # Disable legend

#~~ Projection 2: World Robinson projection (ESRI:54030)

sm_proj2 = terra::project(sm, "ESRI:54030")

terra::plot(sm_proj2, 
             main = "Robinson", # Title of the plot
             col = mypal2,      # Colormap for the plot
             axes = FALSE,      # Disable axes
             box = FALSE,       # Disable box around the plots
             asp = NA,          # No fixed aspect ratio; asp=NA fills plot to window
             legend=FALSE)      # Disable legend

Let us now plot a map of global surface soil moisture, reprojected to Robinson projection. Notice that to add the raster and vector to the plot, we use the functions geom_spatraster and geom_spatvector respectively. Another package spData provides several important datasets for global mapping, including, US states polygons (us_states), World country polygons (world), global elevation (elev.tif), among others (https://jakubnowosad.com/spData/). We will use world country polygons (world) in our map.

library(spData)
# Import global political boundaries from spData package
WorldSHP=terra::vect(spData::world)             

# Generate plot
RobinsonPlot <- ggplot() +
  geom_spatraster(data = sm)+                   # Plot SpatRaster layer               
  geom_spatvector(data = WorldSHP, 
                  fill = "transparent") +       # Add world political map
  ggtitle("Robinson Projection") +              # Add title
  scale_fill_gradientn(colors=mypal2,           # Use user-defined colormap
                       name = "Soil Moisture",  # Name of the colorbar
                       na.value = "transparent",# Set color for NA values
                       lim=c(0,0.8))+           # Z axis limit
  theme_minimal()+                              # Select theme. Try 'theme_void'
  theme(plot.title = element_text(hjust =0.5),  # Place title in the middle of the plot
        text = element_text(size = 12))+        # Adjust plot text size for visibility
  coord_sf(crs = "ESRI:54030",                  # Reproject to World Robinson
           xlim = c(-152,152)*100000,           # Convert x-y limits from decimal Deg. to meter
           ylim = c(-55,90)*100000)

print(RobinsonPlot)

# Save high resolution plot to disk
  # ggsave(
  #   "globalSM.png",          # Name of the file to be created
  #   plot = RobinsonPlot,     # Plot to be exported
  #   bg = "white",            # Plot background
  #   height = 6,              # Height 
  #   width = 10,              # Width  
  #   units = c("in")          # Units ("in", "cm", "mm" or "px")
  # )

Run the commented script above, and view the exported plot saved on the disk.

3.2.9 Reclassify and reproject `SpatRasters`

So far we have used continuous color scales for plotting rasters. Now we will reclassify the raster in discrete domains based on the cell values. This operation is called RATifying the raster i.e. adding the Raster Attribute Table (RAT) to the file. This allows use of discrete color scales for plotting.

library(spData)

# Import global political boundaries from spData package
WorldSHP=terra::vect(spData::world)    

# Import raster file 
sm=rast("./SampleData-master/raster_files/SMAP_SM.tif") 

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  # Defite category breaks for the raster
  brk= seq(0,0.8, by=0.1)

  # Add attribute table to the raster
  library(dplyr)
  sm_rat=dplyr::mutate(sm,                   # input raster
               rat = cut(SMAP_SM,            # Raster attribute
               breaks = seq(0,0.8, by=0.1),  # Breaks for each class
               labels = levels(cut(values(sm$SMAP_SM), breaks=brk)) # Labels for each class
  ))

  # levels(cut(values(sm$SMAP_SM), breaks=brk)) is same as typing
  #   c("(0,0.1]", "(0.1,0.2]", "(0.2,0.3]", 
  #   "(0.3,0.4]", "(0.4,0.5]", "(0.5,0.6]", 
  #   "(0.6,0.7]", "(0.7,0.8]")
  
  plot(sm_rat$rat) # Simple plot with ratified values

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Generate global plot in Robinson projection for the RATifies raster
  
RobinsonPlot_cat <- ggplot() +
  geom_spatraster(data = sm_rat,    # Plot SpatRaster layer 
                  aes(fill = rat))+ # aes = the attribute to use for plotting
  geom_spatvector(data = WorldSHP,
                  fill = "transparent") +       # Add world political map
  ggtitle("Robinson Projection") +              # Add title
  scale_fill_manual(values = brewer.pal(length(brk)-1,"Spectral"), # Discrete palette 
                    na.value="transparent",      # Set transparent fill values for NA cells
                    name="SM",                   # Name of the legend bar
                    labels=levels(cut(values(sm$SMAP_SM), breaks=brk)))+
  theme_minimal()+                              # Select theme. Try 'theme_void'
  theme(plot.title = element_text(hjust =0.5),  # Place title in the middle of the plot
        text = element_text(size = 12))+        # Adjust plot text size for visibility
  coord_sf(crs = "ESRI:54030",                  # Reproject to World Robinson
           xlim = c(-152,152)*100000,           # Convert x-y limits from decimal Deg. to meter
           ylim = c(-55,90)*100000)

print(RobinsonPlot_cat)

Other resources: