Affine transformations on Geographical objects with {sf}
Using {ggplot2} and {sf} to demonstrate shifting, scaling and rotating geometrical and geographical objects in R
Geocomputation
Maps
{sf}
Author
Aditya Dahiya
Published
December 18, 2024
How I made this graphic?
Loading required libraries, data import & creating custom functions.
Code
library(sf) # Simple Features in Rlibrary(terra) # Handling rasters in Rlibrary(tidyterra) # For plotting rasters in ggplot2library(magrittr) # Using pipes with raster objectslibrary(tidyverse) # All things tidy; Data Wranglinglibrary(spData) # Spatial Datasets# Final plot toolslibrary(scales) # Nice Scales for ggplot2library(fontawesome) # Icons display in ggplot2library(ggtext) # Markdown text support for ggplot2library(showtext) # Display fonts in ggplot2library(colorspace) # Lighten and Darken colours#### Base Mapindia_states <-read_sf( here::here("data","india_map","India_State_Boundary.shp" ))
Visualization Parameters
Code
# Font for titlesfont_add_google("Bree Serif",family ="title_font") # Font for the captionfont_add_google("Stint Ultra Condensed",family ="caption_font") # Font for plot textfont_add_google("Stint Ultra Condensed",family ="body_font") mypal <- paletteer::paletteer_d("nbapalettes::pacers_classic")showtext_auto()# A base Colourbg_col <-"white"seecolor::print_color(bg_col)# Colour for highlighted texttext_hil <-darken("#862633", 0.2)seecolor::print_color(text_hil)# Colour for the texttext_col <-darken("#862633", 0.5)seecolor::print_color(text_col)# Define Base Text Sizebts <-90# Caption stuff for the plotsysfonts::font_add(family ="Font Awesome 6 Brands",regular = here::here("docs", "Font Awesome 6 Brands-Regular-400.otf"))github <-""github_username <-"aditya-dahiya"xtwitter <-""xtwitter_username <-"@adityadahiyaias"social_caption_1 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{github};</span> <span style='color: {text_hil}'>{github_username} </span>")social_caption_2 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{xtwitter};</span> <span style='color: {text_hil}'>{xtwitter_username}</span>")plot_caption <-paste0("**Data:** Census of India", " | **Code:** ", social_caption_1, " | **Graphics:** ", social_caption_2 )rm(github, github_username, xtwitter, xtwitter_username, social_caption_1, social_caption_2)# Add text to plot-------------------------------------------------plot_title <-"Affine transformations with {sf}"plot_subtitle <-"Scaling Shifting and Rotating geographical simple feature objects with the power of {sf}."
Exploratory Data Analysis and Wrangling
Code
df1 <- india_states |>filter(State_Name =="Andaman & Nicobar") |>st_cast("POLYGON") |>mutate(id =row_number()) |>filter(id <=10) |>mutate(name =case_when( id %in%c(4,8, 9, 7) ~"Nicobar Islands",.default ="Andaman Islands" ) ) |>select(-State_Name)# Pull out only sfc class (i.e. geometry for Andaman Islands)a1 <- df1 |>filter(name =="Andaman Islands") |>st_geometry()# Pull out only sfc class (i.e. geometry for Nicobar Islands)n1 <- df1 |>filter(name =="Nicobar Islands") |>st_geometry()# Compute centroid of n1 (Nicobar Islands): useful in future # scaling and rotatingn1_centroid <-st_centroid(n1)# Custom function defined for rotating something by "a" degreesrotation =function(a){ r = a * pi /180#degrees to radiansmatrix(c(cos(r), sin(r), -sin(r), cos(r)), nrow =2, ncol =2)} row1 <-c(a1, n1) |>st_as_sf() |>mutate(island =c(rep("Andaman Islands", 6), rep("Nicobar Islands", 4)),description ="Base Map of Andaman and Nicobar Islands",facet_title ="1. Base Map" )row2 <-c( a1, # Shifting the Nicobar Islands by 400 km north n1 |>add(c(0, 400000)) |>st_set_crs(st_crs(n1)) ) |>st_as_sf() |>mutate(island =c(rep("Andaman Islands", 6), rep("Nicobar Islands", 4)),description ="Shiting Nicobar Islands 400 km northwards",facet_title ="2. Shifting" )row3 <-c( a1, # Scaling the Nicobar Islands: enlarging them 3 times (n1 - n1_centroid) |>multiply_by(3) |>add(n1_centroid) |>st_set_crs(st_crs(n1)) ) |>st_as_sf() |>mutate(island =c(rep("Andaman Islands", 6), rep("Nicobar Islands", 4)),description ="Scaling Nicobar Islands: enlarging 3 times",facet_title ="3. Scaling" )row4 <-c( a1, # Scaling the Nicobar Islands: enlarging them 3 times (n1 - n1_centroid) |>multiply_by(rotation(90)) |>add(n1_centroid) |>st_set_crs(st_crs(n1)) ) |>st_as_sf() |>mutate(island =c(rep("Andaman Islands", 6), rep("Nicobar Islands", 4)),description ="Rotating Nicobar Islands clockwise 90 degrees",facet_title ="4. Rotating" )df2 <-bind_rows( row1, row2, row3, row4)rm(row1, row2, row3, row4, a1, n1, n1_centroid)
# Saving a thumbnaillibrary(magick)# Saving a thumbnail for the webpageimage_read(here::here("data_vizs", "sf_affine_transformations.png")) |>image_resize(geometry ="x400") |>image_write( here::here("data_vizs", "thumbnails", "sf_affine_transformations.png" ) )
Session Info
Code
library(sf) # Simple Features in Rlibrary(terra) # Handling rasters in Rlibrary(tidyterra) # For plotting rasters in ggplot2library(magrittr) # Using pipes with raster objectslibrary(tidyverse) # All things tidy; Data Wranglinglibrary(spData) # Spatial Datasets# Final plot toolslibrary(scales) # Nice Scales for ggplot2library(fontawesome) # Icons display in ggplot2library(ggtext) # Markdown text support for ggplot2library(showtext) # Display fonts in ggplot2library(colorspace) # Lighten and Darken colourssessioninfo::session_info()$packages |>as_tibble() |>select(package, version = loadedversion, date, source) |>arrange(package) |> janitor::clean_names(case ="title" ) |> gt::gt() |> gt::opt_interactive(use_search =TRUE ) |> gtExtras::gt_theme_espn()