Chapter 6 Spatial data and making maps
We will cover: 1. Basics of using sf package, converting lat/lon to a spatial data frame. 2. Importing shapefiles 3. Making a map of your lats/lons 4. Add inset map 5. Add scale bar, north arrow, labels 6. Add basemap 7. Writing map image and map files
Packages
library(ggplot2)
library(dplyr)
library(sf)
library(viridis)
6.1 Load data
Latitude/Longitude data from IEP zooplankton data
# Latitude/Longitude data
<- "https://portal.edirepository.org/nis/dataviewer?packageid=edi.539.3&entityid=343cb43b41eb112ac36b605f1cff1f92"
stations_URL
# Create a fake n variable to have something to plot.
<- readr::read_csv(stations_URL) %>%
stations mutate(n = round(runif(n = 1:nrow(.), min = 1, max = 100),0)) %>%
mutate(Source = factor(Source)) %>%
filter(!is.na(Latitude))
::glimpse(stations) dplyr
## Rows: 368
## Columns: 5
## $ Source <fct> EMP, EMP, EMP, EMP, EMP, EMP, EMP, EMP, EMP, EMP, EMP, EMP, ~
## $ Station <chr> "NZ002", "NZ003", "NZ004", "NZ005", "NZ020", "NZ022", "NZ024~
## $ Latitude <dbl> 38.06028, 38.05250, 38.02917, 38.03167, 38.05972, 38.07194, ~
## $ Longitude <dbl> -122.2069, -122.1783, -122.1583, -122.1353, -122.1097, -122.~
## $ n <dbl> 63, 63, 46, 80, 66, 81, 47, 92, 22, 21, 85, 51, 12, 5, 54, 9~
# create a subset of stations
<- sample_n(stations, 20) %>% select(Station)
stationlist_filt <- stationlist_filt$Station stationlist_filt
Shapefiles
# Delta waterways
library(deltamapr)
plot(WW_Delta)
glimpse(WW_Delta)
## Rows: 282
## Columns: 10
## $ AREA <dbl> 73544304.00, 87637.30, 7915130.00, 103906.00, 106371.00, 15~
## $ PERIMETER <dbl> 1033340.000, 3319.230, 87427.898, 2718.730, 2798.310, 3391.~
## $ HYDRO_POLY <int> 791, 1965, 1967, 1970, 1977, 1982, 1992, 2001, 2006, 2008, ~
## $ HYDRO_PO_1 <int> 797, 1963, 1965, 1969, 1974, 1978, 1989, 2008, 2012, 2011, ~
## $ HYDRO_24K_ <int> 798, 1964, 1966, 1970, 1975, 1979, 1990, 2009, 2013, 2012, ~
## $ TYPE <chr> "MR", "S", "C", "L", "L", "S", "S", "MR", "MR", "MR", "MR",~
## $ HNAME <chr> "SACRAMENTO RIVER", "W", "SACTO. R DEEP WATER SH CHAN", "GR~
## $ Shape_Leng <dbl> 2.448454165, 0.035719722, 0.828813375, 0.026377690, 0.02830~
## $ Shape_Area <dbl> 3.476418e-03, 9.063090e-06, 8.166341e-04, 1.074391e-05, 1.0~
## $ geometry <POLYGON [°]> POLYGON ((-121.5099 38.2471..., POLYGON ((-121.5673~
# Regions
<- deltamapr::R_EDSM_Subregions_Mahardja_FLOAT
Regions plot(Regions)
glimpse(Regions)
## Rows: 40
## Columns: 4
## $ SubRegion <chr> "Cache Slough and Lindsey Slough", "Carquinez Strait", "Conf~
## $ SQM <dbl> 471689278, 48215634, 39171738, 182085789, 94761329, 23726124~
## $ Region <chr> "North", "Far West", "West", "South", "South", "South", "Wes~
## $ geometry <POLYGON [m]> POLYGON ((611964 4246976, 6..., POLYGON ((567190.8 4~
# States
library(USAboundaries)
<- us_states(states = "California")
California_sf plot(California_sf[1])
6.2 Get data into spatial form
# Define the projection of your points, usually WGS 84 (= crs 4326)
<- st_as_sf(stations, coords = c("Longitude", "Latitude"), crs = 4326)
stations_sf
# Look at shapefile
head(stations_sf)
## Simple feature collection with 6 features and 3 fields
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: -122.2069 ymin: 38.02917 xmax: -122.0961 ymax: 38.07194
## Geodetic CRS: WGS 84
## # A tibble: 6 x 4
## Source Station n geometry
## <fct> <chr> <dbl> <POINT [°]>
## 1 EMP NZ002 63 (-122.2069 38.06028)
## 2 EMP NZ003 63 (-122.1783 38.0525)
## 3 EMP NZ004 46 (-122.1583 38.02917)
## 4 EMP NZ005 80 (-122.1353 38.03167)
## 5 EMP NZ020 66 (-122.1097 38.05972)
## 6 EMP NZ022 81 (-122.0961 38.07194)
plot(stations_sf$geometry)
plot(stations_sf)
6.2.1 Spatial projections
You want to make sure all your different files are in the same projection, or they will look mis-aligned.
st_crs(WW_Delta) # In 4269
## Coordinate Reference System:
## User input: NAD83
## wkt:
## GEOGCRS["NAD83",
## DATUM["North American Datum 1983",
## ELLIPSOID["GRS 1980",6378137,298.257222101,
## LENGTHUNIT["metre",1]]],
## PRIMEM["Greenwich",0,
## ANGLEUNIT["degree",0.0174532925199433]],
## CS[ellipsoidal,2],
## AXIS["latitude",north,
## ORDER[1],
## ANGLEUNIT["degree",0.0174532925199433]],
## AXIS["longitude",east,
## ORDER[2],
## ANGLEUNIT["degree",0.0174532925199433]],
## ID["EPSG",4269]]
st_crs(stations_sf) # In 4326
## Coordinate Reference System:
## User input: EPSG:4326
## wkt:
## GEOGCRS["WGS 84",
## DATUM["World Geodetic System 1984",
## ELLIPSOID["WGS 84",6378137,298.257223563,
## LENGTHUNIT["metre",1]]],
## PRIMEM["Greenwich",0,
## ANGLEUNIT["degree",0.0174532925199433]],
## CS[ellipsoidal,2],
## AXIS["geodetic latitude (Lat)",north,
## ORDER[1],
## ANGLEUNIT["degree",0.0174532925199433]],
## AXIS["geodetic longitude (Lon)",east,
## ORDER[2],
## ANGLEUNIT["degree",0.0174532925199433]],
## USAGE[
## SCOPE["Horizontal component of 3D system."],
## AREA["World."],
## BBOX[-90,-180,90,180]],
## ID["EPSG",4326]]
st_crs(California_sf) # In 4326
## Coordinate Reference System:
## User input: EPSG:4326
## wkt:
## GEOGCRS["WGS 84",
## DATUM["World Geodetic System 1984",
## ELLIPSOID["WGS 84",6378137,298.257223563,
## LENGTHUNIT["metre",1]]],
## PRIMEM["Greenwich",0,
## ANGLEUNIT["degree",0.0174532925199433]],
## CS[ellipsoidal,2],
## AXIS["geodetic latitude (Lat)",north,
## ORDER[1],
## ANGLEUNIT["degree",0.0174532925199433]],
## AXIS["geodetic longitude (Lon)",east,
## ORDER[2],
## ANGLEUNIT["degree",0.0174532925199433]],
## USAGE[
## SCOPE["Horizontal component of 3D system."],
## AREA["World."],
## BBOX[-90,-180,90,180]],
## ID["EPSG",4326]]
<- st_transform(stations_sf, crs = 4269)
stations_4269 <- st_transform(California_sf, crs = 4269) california_4269
6.3 Basic Spatial Operations
Assign points to regions
Nearest neighbor
Intersections
6.4 Make maps
6.4.1 Basic maps
Color by Monitoring Program
ggplot() +
geom_sf(data = WW_Delta) +
geom_sf(data = stations_4269, aes(fill = Source), shape = 21) +
scale_fill_viridis(discrete = TRUE) +
ggtitle("Number of Zooplankton Samples by Station") +
theme_bw()
You can also modify the size of the points by size
# Making a smaller dataset
<- stations_4269 %>%
stations_filtered_4269 filter(Station %in% stationlist_filt)
<- ggplot() +
(simplemap geom_sf(data = WW_Delta, fill = "lightblue", color = "lightblue") +
geom_sf(data = stations_filtered_4269, aes(fill = Source, size = n), shape = 21, alpha = 0.7) +
scale_fill_viridis(discrete = TRUE, option = "plasma") +
scale_size_continuous(range = c(0,3)) +
ggtitle("Number of Zooplankton Samples by Station") +
theme_bw())
You can add region delineations as well
ggplot() +
geom_sf(data = WW_Delta, fill = "lightblue", color = "lightblue") +
geom_sf(data = Regions, aes(color = Region), alpha = 0.3) +
geom_sf(data = stations_filtered_4269, aes(fill = Source, size = n), shape = 21) +
scale_fill_viridis(discrete = TRUE, option = "plasma") +
scale_color_viridis(discrete = TRUE, option = "turbo") +
scale_size_continuous(range = c(0,3)) +
ggtitle("Number of Zooplankton Samples by Station") +
theme_bw()
6.4.2 Add arrows and scale bars, dotted lines
require(ggspatial)
# https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html
<- simplemap +
(simplemap2 annotation_north_arrow(location = "tr", which_north = "true",
pad_x = unit(0.1, "in"), pad_y = unit(0.1, "in"),
style = north_arrow_fancy_orienteering) +
annotation_scale(location = "bl", bar_cols = c("pink", "white", "pink", "white")) +
theme(axis.title = element_blank(),
panel.grid.major = element_line(color = "grey80", linetype = "dashed", size = 0.5)))
6.4.3 Add labels to map
# Adding text
+
simplemap2 geom_text(data = filter(stations, Station %in% stationlist_filt), aes(x = Longitude, y = Latitude, label = Station), size = 2, check_overlap = FALSE, color = "darkblue", nudge_x = 0.02, nudge_y = 0.02) +
annotate(geom = "text", x = -122.4, y = 37.85, label = "San Francisco Bay", fontface = "italic", color = "grey22", size = 3.5 )
6.4.4 Add inset map
# Figure out boundary box for your stations; perhaps add a small buffer
= st_as_sfc(st_bbox(WW_Delta))
insetbbox0 = st_buffer(insetbbox0, 0.2)
insetbbox
<- ggplot() +
(inset geom_sf(data = california_4269, fill = "white") +
geom_sf(data = insetbbox0, fill = NA, color = "red", size = 0.5) +
theme_void())
Combine main map with inset map
Will need to play with where you want the inset to be so as not to overlap with your map
library(cowplot)
= ggdraw() +
inset_map draw_plot(simplemap2) +
draw_plot(inset, x = 0.15, y = 0.63, width = 0.3, height = 0.3)
inset_map
6.5 Basemaps
Download basemaps from get_stamenmap
library(ggmap)
Define coordinate bounding box. You could also use lat/lon if you want.
= 0.2
buffer = list(
coordDict 'minLat' = min(stations$Latitude) - buffer,
'maxLat' = max(stations$Latitude) -0.1,
'minLon' = min(stations$Longitude) - buffer,
'maxLon' = max(stations$Longitude) + buffer
)
# Create map object using your bounded coordinates
<- get_stamenmap(
map_obj bbox = c(left = coordDict[['minLon']], bottom = coordDict[['minLat']], right = coordDict[['maxLon']], top = coordDict[['maxLat']]), # the bounding box
zoom = 9, # zoom lvl; higher number = more detail (but also more processing power)
maptype = 'terrain-background'# type of basemap; 'terrain' is my default, but check help(get_stamenmap) for a full list
)
Plot your basemap
# Plot the map
<- ggmap(map_obj, legend = "right")
map map
Add basemap to earlier map.
<- ggmap(map_obj) +
map2 geom_sf(data = WW_Delta, fill = "lightblue", color = "lightblue", inherit.aes = FALSE) +
geom_sf(data = stations_filtered_4269, aes(fill = Source), shape = 21, alpha = 0.7, size = 2.5, inherit.aes = FALSE) +
annotate(geom = "text", x = -122.4, y = 37.85, label = "San Francisco Bay", fontface = "italic", color = "grey22", size = 3.5 ) +
annotation_north_arrow(location = "tr", which_north = "true",
pad_x = unit(0.1, "in"), pad_y = unit(0.1, "in"),
style = north_arrow_fancy_orienteering) +
annotation_scale(location = "bl", bar_cols = c("black", "white", "black", "white")) +
scale_fill_viridis(discrete = TRUE, option = "plasma") +
ggtitle("Number of Zooplankton Samples by Station") +
theme_bw()+ theme(axis.title = element_blank(),
panel.grid.major = element_line(color = "grey80", linetype = "dashed", size = 0.5))
map2