Install necessary packages
install.packages("sf", dependencies = TRUE)
install.packages("terra", dependencies = TRUE)
install.packages(c("spData", "mapview", "ggplot2", "tmap"))
install.packages("tidyverse", dependencies = TRUE)
Load packages
library(sf)
library(spData)
Example dataset: Bicycle rental points across London (Source: OSM)
data("cycle_hire_osm", package = "spData")
head(cycle_hire_osm) # plot first few rows
## Simple feature collection with 6 features and 5 fields
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: -0.1293092 ymin: 51.52583 xmax: -0.090836 ymax: 51.53402
## Geodetic CRS: WGS 84
## osm_id name capacity cyclestreets_id description
## 1 108539 Windsor Terrace 14 <NA> <NA>
## 2 598093293 Pancras Road, King's Cross NA <NA> <NA>
## 3 772536185 Clerkenwell, Ampton Street 11 <NA> <NA>
## 4 772541878 <NA> NA <NA> <NA>
## 5 781506147 <NA> NA <NA> <NA>
## 6 783824668 Finsbury Library, EC1 NA <NA> <NA>
## geometry
## 1 POINT (-0.0933878 51.52913)
## 2 POINT (-0.1293092 51.53402)
## 3 POINT (-0.1182352 51.52729)
## 4 POINT (-0.090836 51.52583)
## 5 POINT (-0.1210572 51.53001)
## 6 POINT (-0.1038272 51.52594)
Visualise the points in the geometry column
plot(cycle_hire_osm["geometry"], axes = TRUE)
Examine the CRS
st_crs(cycle_hire_osm)
## Coordinate Reference System:
## User input: EPSG:4326
## wkt:
## GEOGCS["WGS 84",
## DATUM["WGS_1984",
## SPHEROID["WGS 84",6378137,298.257223563,
## AUTHORITY["EPSG","7030"]],
## AUTHORITY["EPSG","6326"]],
## PRIMEM["Greenwich",0,
## AUTHORITY["EPSG","8901"]],
## UNIT["degree",0.0174532925199433,
## AUTHORITY["EPSG","9122"]],
## AUTHORITY["EPSG","4326"]]
Transform the CRS
cycle_hire_osm <- st_transform(cycle_hire_osm, crs = st_crs(32630)) # UTM zone 30N for London
head(cycle_hire_osm)
## Simple feature collection with 6 features and 5 fields
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: 699095.4 ymin: 5712291 xmax: 701799.3 ymax: 5713119
## Projected CRS: WGS 84 / UTM zone 30N
## osm_id name capacity cyclestreets_id description
## 1 108539 Windsor Terrace 14 <NA> <NA>
## 2 598093293 Pancras Road, King's Cross NA <NA> <NA>
## 3 772536185 Clerkenwell, Ampton Street 11 <NA> <NA>
## 4 772541878 <NA> NA <NA> <NA>
## 5 781506147 <NA> NA <NA> <NA>
## 6 783824668 Finsbury Library, EC1 NA <NA> <NA>
## geometry
## 1 POINT (701607.8 5712674)
## 2 POINT (699095.4 5713119)
## 3 POINT (699892.7 5712401)
## 4 POINT (701799.3 5712314)
## 5 POINT (699685.1 5712696)
## 6 POINT (700897.9 5712291)
Interactive map of the ‘capacity
’ of each
bicycle rental point using the package mapview
library(mapview)
mapview(cycle_hire_osm["capacity"], layer.name = "Rental capacity")
Example dataset: A river network in France
data("seine", package = "spData")
head(seine)
## Simple feature collection with 3 features and 1 field
## Geometry type: MULTILINESTRING
## Dimension: XY
## Bounding box: xmin: 518344.7 ymin: 6660431 xmax: 879955.3 ymax: 6938864
## Projected CRS: RGF93 / Lambert-93
## name geometry
## 1 Marne MULTILINESTRING ((879955.3 ...
## 2 Seine MULTILINESTRING ((828893.6 ...
## 3 Yonne MULTILINESTRING ((773482.1 ...
Plot the lines using ggplot2
library(ggplot2)
ggplot(seine) +
geom_sf(aes(color = name)) +
geom_sf_label(aes(label = name)) # add labels for river names
Example dataset: Country polygons worldwide
data(world, package = "spData")
head(world)
## Simple feature collection with 6 features and 10 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: -180 ymin: -18.28799 xmax: 180 ymax: 83.23324
## Geodetic CRS: WGS 84
## # A tibble: 6 × 11
## iso_a2 name_long continent region_un subregion type area_km2 pop lifeExp
## <chr> <chr> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
## 1 FJ Fiji Oceania Oceania Melanesia Sove… 1.93e4 8.86e5 70.0
## 2 TZ Tanzania Africa Africa Eastern … Sove… 9.33e5 5.22e7 64.2
## 3 EH Western S… Africa Africa Northern… Inde… 9.63e4 NA NA
## 4 CA Canada North Am… Americas Northern… Sove… 1.00e7 3.55e7 82.0
## 5 US United St… North Am… Americas Northern… Coun… 9.51e6 3.19e8 78.8
## 6 KZ Kazakhstan Asia Asia Central … Sove… 2.73e6 1.73e7 71.6
## # ℹ 2 more variables: gdpPercap <dbl>, geom <MULTIPOLYGON [°]>
Plot the per capita GDP across all countries using an appropriate colour scale
breaks <- quantile(world$gdpPercap, probs = c(0.025, 0.25, 0.5, 0.75, 0.975), na.rm = TRUE) # get quantile values
ggplot(world) + geom_sf(aes(fill = gdpPercap)) +
scale_fill_gradientn(colours= c("#ffffcc", "#a1dab4" , "#41b6c4", "#2c7fb8", "#253494"), # binned color scale
trans = 'log', # log-transformation
breaks = breaks) +
theme_minimal()
Use the package tmap to quickly plot thematic and customisable maps
library(tmap)
tmap_mode("view")
tm_shape(world) +
tm_polygons("gdpPercap", palette = "YlGnBu", style = "quantile") +
tm_view(set.view = 1) # set zoom level
Locate the center point of geometries
world_pts <- st_centroid(world)
Simplify shapes by removing vertices
world_projected <- st_transform(world, "EPSG: 3857") # need to transform to projected CRS
world_simple <- st_simplify(world_projected, dTolerance = 1e5) # 100km simplification
Cast the simplified POLYGON
objects back to type
‘MULTIPOLYGON
’, and remove the rows with empty
geometries
world_simple <- st_cast(world_simple, "MULTIPOLYGON")
world_simple <- world_simple[!st_is_empty(world_simple),] # subset rows
Extend the boundaries of vectors
world_buffered <- st_buffer(world_simple, dist = 1e5) # 100km
Remove overlaps among the polygons in
world_simple
and merge all polygons
world_diff <- st_difference(world_simple) # exclude overlaps
world_union <- st_union(world_simple)
Plot the manipulated objects derived from the country polygons
tmap_options(check.and.fix = TRUE)
sf_use_s2(FALSE)
tm_shape(world_pts) + tm_dots() + # polygon centroids
tm_shape(world_simple) + tm_polygons(alpha = 0.5) + # simplified
tm_shape(world_buffered) + tm_polygons(alpha = 0.5) + # buffered
tm_shape(world_union) + tm_polygons(alpha = 0.5) + # merged (union)
tm_view(set.view = 2) # zoom in
Subset rows of an sf
(‘target’) object based on
its spatial relationship with another
uk <- st_filter(world_projected, # target: world polygons
st_transform(cycle_hire_osm, st_crs(world_projected)), # bicycle rental points in London - crs must be the same
.predicate = st_intersects) # default setting
tm_shape(uk) + tm_polygons() +
tm_shape(cycle_hire_osm) + tm_dots()
Match rows (and append columns) to an sf
(‘target’) object based on its spatial relationship with
another
cycle_hire_osm <- st_join(cycle_hire_osm, # target
st_transform(world_projected, st_crs(cycle_hire_osm)))
head(cycle_hire_osm)
## Simple feature collection with 6 features and 15 fields
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: 699095.4 ymin: 5712291 xmax: 701799.3 ymax: 5713119
## Projected CRS: WGS 84 / UTM zone 30N
## osm_id name capacity cyclestreets_id description
## 1 108539 Windsor Terrace 14 <NA> <NA>
## 2 598093293 Pancras Road, King's Cross NA <NA> <NA>
## 3 772536185 Clerkenwell, Ampton Street 11 <NA> <NA>
## 4 772541878 <NA> NA <NA> <NA>
## 5 781506147 <NA> NA <NA> <NA>
## 6 783824668 Finsbury Library, EC1 NA <NA> <NA>
## iso_a2 name_long continent region_un subregion type area_km2
## 1 GB United Kingdom Europe Europe Northern Europe Country 249986.4
## 2 GB United Kingdom Europe Europe Northern Europe Country 249986.4
## 3 GB United Kingdom Europe Europe Northern Europe Country 249986.4
## 4 GB United Kingdom Europe Europe Northern Europe Country 249986.4
## 5 GB United Kingdom Europe Europe Northern Europe Country 249986.4
## 6 GB United Kingdom Europe Europe Northern Europe Country 249986.4
## pop lifeExp gdpPercap geometry
## 1 64613160 81.30488 38251.79 POINT (701607.8 5712674)
## 2 64613160 81.30488 38251.79 POINT (699095.4 5713119)
## 3 64613160 81.30488 38251.79 POINT (699892.7 5712401)
## 4 64613160 81.30488 38251.79 POINT (701799.3 5712314)
## 5 64613160 81.30488 38251.79 POINT (699685.1 5712696)
## 6 64613160 81.30488 38251.79 POINT (700897.9 5712291)
Combine the official dataset cycle_hire
with the
OSM dataset cycle_hire_osm
of bicycle rental points across
London, but only those beyond a distance of 10 m (remove duplicate
points)
Load package
library(terra)
Example raster: Elevation in Luxembourg
filepath <- system.file("ex/elev.tif", package = "terra")
elevation <- rast(filepath)
elevation_utm <- project(elevation, "EPSG:23032") # project to UTM zone 32N for Luxembourg
Example vector: Polygons of Luxembourg
filepath <- system.file("ex/lux.shp", package = "terra")
luxembourg <- vect(filepath)
luxembourg
## class : SpatVector
## geometry : polygons
## dimensions : 12, 6 (geometries, attributes)
## extent : 5.74414, 6.528252, 49.44781, 50.18162 (xmin, xmax, ymin, ymax)
## source : lux.shp
## coord. ref. : lon/lat WGS 84 (EPSG:4326)
## names : ID_1 NAME_1 ID_2 NAME_2 AREA POP
## type : <num> <chr> <num> <chr> <num> <int>
## values : 1 Diekirch 1 Clervaux 312 18081
## 1 Diekirch 2 Diekirch 218 32543
## 1 Diekirch 3 Redange 259 18664
Summarise the mean raster values for each sub-region (polygon) in Luxembourg
elev_mean <- extract(elevation, luxembourg,
fun = mean, na.rm = TRUE)
luxembourg$elevation_mean <- elev_mean$elevation # add results to new column
Plot the rasters and polygons
tm_shape(elevation) +
tm_raster(palette = "RdYlGn", n = 10) + # use similar color palette
tm_shape(st_as_sf(luxembourg)) + # convert to sf, as tmap currently does not support SpatVect
tm_polygons(col = "elevation_mean", palette = "RdYlGn", alpha = 0.7)
Narrow down raster data to a specific area of interest
luxembourg_subset <- luxembourg[luxembourg$NAME_2 == "Mersch"]
elevation_subset <- crop(elevation, luxembourg_subset) # reduce spatial extent
elevation_subset <- mask(elevation_subset, luxembourg_subset) # convert values outside polygon to NA
plot(elevation_subset)
Classify values into a discrete (categorical) raster
elevation_2class <- ifel(elevation > 400, 1, 0) # E.g., threshold for 'high' elevation at 400m
plot(elevation_2class) # low/high
elevation_3class <-
classify(elevation,
rbind(c(400, Inf, 2), # matrix of 3 columns, 'from', 'to' and 'becomes'
c(300, 400, 1),
c(-Inf, 300, 0)))
plot(elevation_3class) # low/mid/high
Vectorize rasters into points, lines and polygons
elevation_pts <- as.points(elevation_subset) # points
elevation_contours <- as.contour(elevation_subset) # lines
elevation_polygons <- as.polygons(elevation_subset) # polygons
Visualise all output
tm_shape(elevation_subset) + tm_raster(palette = "RdYlGn", n = 10) + # original raster
tm_shape(st_as_sf(elevation_pts)) + tm_dots() +
tm_shape(st_as_sf(elevation_contours)) + tm_lines() +
tm_shape(st_as_sf(elevation_polygons)) + tm_polygons(alpha = 0) +
tm_view(set.view = 11)
Rasterize the points dataset cycle_hire_osm
based on the column ‘capacity
’
# first create a raster template at a specified resolution and crs
rast_template <- rast(ext(cycle_hire_osm),
resolution = 1000,
crs = st_crs(cycle_hire_osm)$wkt) # input format is WKT
# then, rasterize the points dataset based on the template
cyclehire_capacity <-
rasterize(cycle_hire_osm, rast_template,
field = "capacity",
fun = sum, # add up values of points within each pixel
na.rm = TRUE)
Overlay the original points over the per-pixel capacity of bicycle docking stations
tm_shape(cyclehire_capacity) +
tm_raster(title = "Capacity (sum)", palette = "YlGn") +
tm_shape(st_as_sf(cycle_hire_osm)) +
tm_dots(size = 0.01)
© XP Song
Disclaimer: These notes exclusively reserved for use
with the Geospatial Data
Science with R online course. Please do not distribute or reproduce
these materials, they are intended solely for personal use.