Date: 2024-06-19
R
version: 3.5.0
*Corresponding author: matthew.malishev [at] gmail.com
This document can be found at https://github.com/darwinanddavis/UsefulCode
Accessing sf, sp, geos,a nd geojson objects, such as attributes, accessing lower levels of their structure, latlon and MULTIPOLYGONS layers, etc.
Read and convert json to df
# opt 1
require(jsonlite)
<- fromJSON("data.json", simplifyDataFrame = T, flatten = T) %>% as.data.frame(data)
data
# opt 2
<- "my_geo_data.json" %>% readr::read_lines
file
# opt 3
<- "my_geo_data.json" %>% read_sf file
Convert sf to geojson
require(geojsonio)
%>% geojsonio::geojson_list() sf
Save R obj as geojson
# save R obj as geojson
%>% geojson_write(file = here::here("data”,”new.json"))
sfjson
# opt 2
%>% st_write("sf.geojson")
sf
# opt 3 convert sf to json string and save to dir
%>% geojsonio::geojson_list() %>% rjson::toJSON(method = "C") %>% readr::write_lines("sf.json") sf
Loop through sf and convert and save as geojson
require(geojsonio)
for (i in sf$name) {
%>% filter(name %in% i) %>% geojson_list() %>% geojson_write(here::here("data", paste0("newfile",
sf ".json")))
i, }
Read in json and convert to df
require(jsonlite)
read_json("test.json", simplifyVector = T)
Write json/geojson to dir
# write json methods working but only points
::p_load(jsonlite, geojsonio)
pacman%>% st_write("data.shp") # write shp
sf %>% st_write("data.json") # write json
sf %>% st_write("data.geojson", driver = "geojson") # write geojson
sf %>% st_write("data.json", driver = "geojson", delete_dsn = T) # overwrite file
sf %>% geojsonio::geojson_write(file = "data.json")
sf %>% rjson::toJSON(indent = "2")
sf %>% geojsonio::geojson_list() %>% rjson::toJSON(method = "C") %>% readr::write_lines("sf.json")
sf %>% geojsonio::file_to_geojson(output = "data.geojson")
sf
# convert sf to json
%>% jsonlite::toJSON() %>% jsonlite::prettify()
sf
# convert list to geojson
%>% rjson::toJSON()
ls %>% jsonify::to_json(unbox = T) %>% jsonify::pretty_json() ls
Check available drivers for writing shp, json, geojson, kml
require(rgdal)
ogrDrivers() %>% View
Get bbox coords
require(sf)
methods(class = "sf")
$geometry %>% attr(which = "bbox") obj
Get first element or range of sf
$geometry
obj$geometry[1] %>% unlist %>% .[1] # first element
obj%>% st_coordinates() %>% .[1:3] # coord range obj
Get polygons (and range) in geometry layer
%>% st_geometry() %>% .[1:10] # polygon range obj
Remove or keep/select specific sf types from sf obj
%>% st_collection_extract("POINT") # get just points
obj %>% st_geometry %>% st_collection_extract("POINT") # get geometry of just points obj
Get boundary/outlines
# good for plotting US state border lines over other sf files
%>% st_geometry() %>% st_boundary %>% ggplot() + geom_sf()
obj
# boundary of bbox
%>% st_bbox() %>% st_as_sfc() %>% ggplot() + geom_sf() mp
Break up multipolygon into separate polygons/pull specific polygon e.g. main France polygon and rm islands
<- ne_countries("large",type = "countries",returnclass = "sf") %>%
french_guiana filter(name %in% c("France")) %>% # add french guiana
st_cast("POLYGON") %>% slice(1) # get just french guiana
Get areas of individual polygons within multipolygon
%>% filter(name == "Netherlands") %>% st_cast("POLYGON") %>% st_geometry %>% st_area() dd
Filter sf geometry only
%>% filter(st_geometry_type(.) == "LINESTRING") dat
Convert matrix directly to sf
require(sfheaders)
%>% sf_point()
m %>% sf_multipolygon()
m %>% sf_multilinestring()
m %>% sf_linestring()
m
# convert list of matrices/arrays into sf
require(purrr)
require(magrittr)
require(rlist)
%>%
ls map(matrix, ncol = 2) %>%
map(magrittr::set_colnames, c("lon","lat")) %>% # rename cols
compact() %>% # drop empty lists
map(sfheaders::sf_multipolygon, multipolygon_id = NULL) %>% # convert to sf
::list.rbind() # bind sfs
rlist
Return Z range for sf obj
%>% st_z_range() sf
Drop zm components
%>% st_zm() sf
Read in .shp file
require(rgdal) # \ rgdal
<- readOGR(".shp", layer = "X") # layer = name of geometry element shp
Get and transform projection
require(rgdal) # \ rgdal
proj4string(shp) # get projection
spTransform(shp, CRS("+proj=longlat +datum=WGS84")) # transform projection
Base map from OSM
# osm
<- get_map(location, source = "osm", color = "bw")
gg
ggmap(gg, extent = "device", darken = c(0.9, "white")) + geom_point(data = df, aes(x, y))
Crop out sf objects
%>% st_crop(dd %>% filter(name == "Victoria"))
sf %>% st_intersection(dd %>% filter(name == "Victoria")) sf
Crop plot region to country border
ggplot() + borders(regions = "Germany") + geom_tile()
Mask/crop raster data
Crop raster/tiff to bbox
<- pathm # sf object
obj <- "raster.tiff" %>% raster # load raster
ras <- as(obj %>% extent(), "SpatialPolygons") # get bbox/extent as Sp
e crs(e) <- "+proj=longlat +datum=WGS84 +no_defs" # add prj
<- crop(ras, e) # crop raster to this bbox
r <- r %>% rasterToPoints() %>% as.data.frame # convert to df for plotting
r colnames(r) <- c("x", "y", "z")
Get inverse mask (hole within sf obj)
require(raster)
mask(sf1, sf2, inverse = T)
Crop multipolyon boundary to surround sf obj boundary and combine into one polygon
%>% st_intersection(boundary_osm %>% st_boundary()) %>% st_cast("POLYGON")
dd
# option 2
%>% st_union %>% st_cast("POLYGON") %>% st_boundary() sf
Shrink sf obj (reverse buffer)
# subtract centroid from geometry, then halve and reposition centroid
$geometry - sf$geometry %>% st_centroid)/2 + sf$geometry %>% st_centroid() (sf
Displace sf obj
# displace x by 0 and y by 500
<- sf %>% st_geometry() + c(0, 500)
sf_disp
# plot using aes
ggplot() + geom_sf(aes(geometry = sf_disp))
Crop sf based on area
%>% filter(st_area %>% as.numeric %in% sf %>% st_area %>% as.numeric %>% max)
sf
# filter out polygons based on area
<- 0.3
quant %>% filter(st_area(.) > st_area(.) %>% quantile(quant)) # get polygons > 30% of all polygon areas sf
Make custom bbox
require(ggmap)
<- 144.93
lon <- -37.79
lat ::make_bbox(lon, lat, f = 0.5) ggmap
Crop satellite map to bbox
# see blood gold for example
<- c(10.5,23.4)
cropx <- c(-1.5,44.0)
cropy ggplot() +
ggmap(basemap) + # sat map from google
scale_x_continuous(limits = cropx, expand = c(0,0)) +
scale_y_continuous(limits = cropy,expand = c(0,0))
Create concave polygon hull of multipoints
require(concaveman)
%>% summarise() %>% concaveman::concaveman(concavity = 1) sf
Crop raster
require(raster)
= raster(vals = rnorm(400), nrows = 20, ncols = 20, ext = extent(c(0, 20, 0, 20)))
r
= Polygon(matrix(5, 5, 15, 12, 7, 16, 3, 10), ncol = 2, byrow = T)
p
= SpatialPolygons(list(Polygons(list(p), "p")))
p
= mask(r, p)
r2 plot(r2)
Get any city centroid, add buffer, then crop
require(raster)
require(maps)
data(world.cities)
<- 2
buff <- "Australia"
country <- "Melbourne"
city
# get centroid
<- world.cities %>% filter(country.etc == country & name == city) %>% select(long, lat) %>% unlist %>%
bb st_point() %>% st_buffer(buff)
# crop with polygon
%>% st_crop(bb)
sf
# crop with bbox
%>% st_crop(bb %>% st_bbox() %>% st_as_sfc()) sf
Crop one side of sf object
# https://spencerschien.info/post/spatial_buffer/
sf_use_s2(FALSE) # set first to enable geos
%>% st_buffer(100, singleSide = T) sf
Databases - maps, world data, natural earth, raster, DEM
# country polygons and natural earth data - rivers/lakes, urban areas, etc
require(rnaturalearth)
# hires country polygons
::install_github("ropensci/rnaturalearthhires")
devtoolsrequire(rnaturalearthhires)
require(elevatr)
<- c(-140.5, 35.6)
pts <- pts %>% st_buffer(1 * 10^4) # create buffer around centroid
pts_buff get_elev_raster(pts_buff, z = 12)
Remove empty geometries
# rm empty geos for ''spsample': error in evaluating the argument 'x' in selecting a method for
# function 'addAttrToGeom': empty geometries are not supported by sp classes: conversion failed'
%>% filter(!st_is_empty(.), drop = F)
sf %>% filter(is.na(st_dimension(.)) == F) # opt 2 to rm empty geos sf
Align sf objects with Google map satellite base map
<- "Alice Springs, Northern Territory"
addy <- geocode(addy, output = "more") %>% pull(address)
extent <- "satellite"
maptype <- 4
zoom <- "bw"
color <- 0.3
darken
# get google map
<- 3857 # set proj to match google map prj
prj <- get_map(extent, maptype = maptype, color = color, zoom = zoom)
base_map
# function to fix the bbox to be in EPSG:3857
<- function(map) {
ggmap_bbox if (!inherits(map, "ggmap"))
stop("map must be a ggmap object")
# Extract the bounding box (in lat/lon) from the ggmap to a numeric vector, and set the names to what
# sf::st_bbox expects:
<- setNames(unlist(attr(map, "bb")), c("ymin", "xmin", "ymax", "xmax"))
map_bbox # Coonvert the bbox to an sf polygon, transform it to 3857, and convert back to a bbox (convoluted,
# but it works)
<- st_bbox(st_transform(st_as_sfc(st_bbox(map_bbox, crs = 4326)), prj))
bbox_3857 # Overwrite the bbox of the ggmap object with the transformed coordinates
attr(map, "bb")$ll.lat <- bbox_3857["ymin"]
attr(map, "bb")$ll.lon <- bbox_3857["xmin"]
attr(map, "bb")$ur.lat <- bbox_3857["ymax"]
attr(map, "bb")$ur.lon <- bbox_3857["xmax"]
map
}<- ggmap_bbox(base_map) # transform proj of sat map
map
# plot
%>% ggmap(extent = "device", legend = "bottom", darken = c(0.5, "#FFFFFF")) map
Plot sf data on Google maps basemap
<- 3857 # set proj to match google map prj
prj
%>% ggmap(extent = "device", legend = "bottom", darken = c(0.5, "#FFFFFF")) + geom_sf(data = sf %>%
map st_transform(prj), size = 0.05, inherit.aes = F) # include inherit.aes = F to show data
.gdb
/.gdbtable
)Read in gdb
data
<- "polygons.gdbtable" # 'polygons.gdb'
fh <- fh %>% st_layers() %>% .[[1]] # get layer name
fhl <- fh %>% st_read(layer = fhl) # read in data using layer sf
Read .gpx files (GPS track software/apps)
# option 1
<- st_read("user.gpx", "track_points")
gpx <- gpx %>%
pathm group_by(user_ID) %>% # get grouping variable
::summarize(do_union = F) %>%
dplyrst_cast("LINESTRING")
# option 2
::p_load(XML,lubridate)
pacman<- "/Volumes/Matt_timemachine/maptracks/2021/sep/sam/gpx/"
fd <- "day182.gpx"
fh <- paste0(fd,fh) %>% # parse gpx file
gpx1 htmlTreeParse(error = function(...) {}, useInternalNodes = T)
<- as.numeric(xpathSApply(gpx1, path = "//trkpt/ele", xmlValue))
elev <- xpathSApply(gpx1, path = "//trkpt/time", xmlValue)
times <- xpathSApply(gpx1, path = "//trkpt", xmlAttrs)
coord
<- tibble(lat = coord["lat",] %>% as.numeric(),
city_df lon = coord["lon",] %>% as.numeric(),
elev = elev,
time = times %>% ymd_hms()
)
# elev profile
ggplot(data = city_df) +
geom_line(aes(time,elev)) +
scale_x_datetime(date_breaks = "6 hours"
# date_minor_breaks = "1 hour", # optional
# date_labels = "%M%D" # full month and year
)
# convert gpx coords to linestring
<- city_df %>%
pathgpx ::select(lat,lon) %>%
dplyras.matrix() %>% # convert points to linestring
st_linestring(dim = "XY") %>%
st_sfc(crs = 4326) %>% # convert sfg object to geometry
st_transform(crs = 4326) # then convert projection
Parse gpx data within kml
First manually convert file from ‘.kml’ to ‘.gpx’
require(XML)
require(dplyr)
require(tidyr)
<- "//coord" # tag where lat/lon/elev data are located in file
coord_id <- "data/full.gpx" %>%
gpx2 htmlTreeParse(error = function(...) {}, useInternalNodes = T)
<- xpathSApply(gpx2, path = coord_id, xmlValue) %>% # read coords (lon,lat,elev)
pathgpx as.data.frame() %>%
::separate(col = ".",into = c("lon", "lat", "elev"), sep = " ", remove = T) %>% # separate char into individual values
tidyrmutate_all(as.numeric)
# timestamp
<- "//when" # may vary with data
time_id <- "data/full.gpx" %>%
gpx2 xpathSApply(gpx2, path = time_id, xmlValue) # get timestamp
library("ggspatial")
ggplot(data = world) +
geom_sf() +
annotation_scale(location = "bl", width_hint = 0.5) +
annotation_north_arrow(location = "bl", which_north = "true",
pad_x = unit(0.75, "in"), pad_y = unit(0.5, "in"),
style = north_arrow_fancy_orienteering) +
coord_sf(xlim = c(-102.15, -74.12), ylim = c(7.65, 33.97))
# scale bar
+ ggspatial::annotation_scale(
p location = "bl",
bar_cols = c(bg,fg),
line_width = 0.5,
pad_x = unit(1.5,"cm"),
pad_y = unit(1.5,"cm"),
text_col = fg, line_col = fg,
style = "ticks", # "bar"
width_hint = 0.1 # set scale to 10% of map
)
## Scale on map varies by more than 10%, scale bar may be inaccurate
# accessing coords in sf (complex) shp <- here::here('worldmaps','data','day14.shp') %>% readOGR() d
# <- shp@data # df vars bb <- shp@bbox #bbox ply <- shp@polygons # polygons plyl <- ply %>%
# purrr::map(ggplot2::fortify) # melt polygon class names(plyl) <- d$VARNAME_3 # get district name
# ggplot() + geom_polygon(data = ply_df[[600]], aes(long, lat))
# ply[[1]]@Polygons[[1]]@coords # works ply[[1]] %>% attr(which='Polygons') %>% purrr::map('coords')
# # works
#list of proj
# https://proj.org/operations/projections/patterson.html
# https://spatialreference.org/ref/?search=albers&srtext=Search
require(mapdata,rnaturalworld)
###
<- ne_countries(scale = "large",
d type = "countries",
country = "Japan",
returnclass = "sf")
<- d$geometry %>% st_bbox()
bb
# get current proj
%>% st_crs() # /sf
d %>% crs() # /raster
d
# transform proj options
<- 3995 # 3995 3033
crsn <- 50
plat <- 0
plon <- paste0("+lat_0=",plat," +lon_0=",plon," +init=epsg:",crsn) # opt1
prj <- "+init=epsg:4326" # opt2
prj
# transforming for diff data types
# da
@crs@projargs <- prj
ras%>% st_as_sf(coords = c("lon","lat"), crs = 4326) %>% # convert city_df to sp. NB need to set initial crs to one compatible with latlon coords, e.g. 4326
ras ::st_transform(crs = prj) # now transform proj
sf
%>% st_transform(prj) # shp
dl
<- d %>% st_transform("+proj=utm +zone=19 +ellps=GRS80 +datum=NAD83")
dt <- d %>% st_transform(crsn)
dt
# quartz()
<- ggplot(data = d) +
p geom_sf() +
coord_sf(crs = prj) # option 1
coord_sf(crs = st_crs(crsn)) # option 2
# proj in string format
<- projection(pathm) # get proj in string format
prj # site2 <- elevatr::get_elev_raster(city_df,
# z = zoom,
# prj = prj,
# expand = 3) %>% # get elev raster
# rasterToPoints() %>% # convert to df
# as.data.frame
# coerce projection
%>% st_set_crs(prj) %>% st_transform(prj2) # or use prj again to coerce uncooperative projections sf
Get projection info
# find proj
= rgdal::make_EPSG() # get all espg code and info
crs_codes %>% filter(code == 27700) %>% pull # pull espg name and code
crs_codes $note %>% str_which("Austra"), "code"] crs_codes[crs_codes
require(sf)
<- "/data.kml" %>% sf::st_read() # single layer kml
path <- "/data.kml" %>% sf::st_layers() # multi layer kml
path # for kmz, change .kmz to .zip, then unzip and read as kml
Save sf as kml/kmz
# opt 1
st_write(sf, "test.kml", driver = "kml", delete_dsn = T)
# opt 2
require(rgdal)
writeOGR(sf, dsn = "sf.kml", layer = "polygonWGS", driver = KML)
library(rlas)
# 3d plotting of lidar data
::install_github("AckerDWM/gg3D")
devtoolslibrary(gg3D)
Convert raster to sf
library(stars)
library(sf)
<- system.file("tif/L7_ETMs.tif", package = "stars")
tifpath <- read_stars(tifpath)
tif <- st_as_sf(tif) sf
Convert sp/sf/spdf data to data frame (fortify)
require(rworldmap)
require(broom)
<- getMap(resolution = "high") # get world data
d <- tidy(d, region = "NAME") # fortify d_fort
Convert sf geometry to df
require(sfheaders)
%>% sf_to_df sf
Convert points to linestring
<- rbind(df1,df2,df3) %>%
inter_df as.matrix() %>%
st_linestring(dim = "XY") %>% # convert points to single linestring
st_sfc(crs = 4326) # convert sfg object to geometry
# option 2
<- city_df %>%
pathm ::select(lat,lon) %>%
dplyras.matrix() %>% # convert points to linestring
st_linestring(dim = "XY") %>%
st_sfc(crs = 4326) %>% # convert sfg object to geometry
Convert each row in df to linestring
<- rbind(df1, df2, df3) %>% st_as_sf(coords = c("lon", "lat"), crs = 4326) %>% st_geometry() %>%
inter_df st_cast("LINESTRING")
Replace coordinates in sf obj
<- sf %>% st_coordinates %>% data_frame # convert to df
sf_df 1:3, "Y"] <- c(10.1, 10.2, 10.3) # replace with new coords
sf_df[<- sf_df[, c("X", "Y")] %>% as.matrix() %>% st_linestring(dim = c("X", "Y")) # convert back to sf sf
Flip XY coordinates
%>% st_coordinates() %>% .[, c(2, 1)] sf
Convert list of sf obj to sfc
do.call(rbind, my_list)
Convert coordinates to text (for simple copy paste)
<- dd %>% filter(name %in% "Netherlands")
bbc %>% st_centroid() %>% st_geometry() %>% st_as_text() bbc
Read in raster, change projection, and convert to matrix/df
require(raster)
<- "ras.tif" %>% raster()
ras <- ras %>% projection() # get proj
prj <- ras %>% extent() # get bbox
bb
require(raster)
%>% raster() %>% crop(bb) %>% rasterToPoints() %>% as.data.frame
ras
require(rayshader)
%>% raster %>% crop(bb) %>% raster_to_matrix()
ras
require(terra)
%>% rast() %>% crop(bb)
ras
# read in .adf files
require(rayshader)
"data.adf" %>% raster %>% raster_to_matrix %>% data.frame
Convert raster to various data stuctures
require(raster)
= getData("alt", country = "CHE")
alt = terrain(alt, opt = "slope")
slope = terrain(alt, opt = "aspect")
aspect = hillShade(slope, aspect, 40, 270)
hill
# raster to df
<- alt %>% rasterToPoints() %>% as.data.frame() # terrain
df <- aspect %>% rasterToPoints() %>% as.data.frame() # aspect
df <- hill %>% rasterToPoints() %>% as.data.frame() # hillshade
df
# raster to sf
<- hill %>% rasterToPoints() %>% as.data.frame %>% st_as_sf(coords = c("x", "y"), crs = 4326)
sf <- sf %>% .[sf %>% nrow/2, ] %>% st_buffer(5 * 10^4) # circle crop based on middle centroid
bb <- sf %>% .[sf %>% nrow/2, ] %>% st_buffer(5 * 10^4) %>% st_bbox() # bbox crop
bb <- sf %>% st_crop(bb)
sf
# raster to spdf
<- hill %>% as("SpatialPixelsDataFrame") %>% data.frame # ras to spdf to df
spp
# plot
ggplot() + geom_raster(data = df, aes(x, y, fill = layer)) # plot df
geom_sf(data = sf, aes(fill = layer, col = layer)) # plot sf
geom_sf(data = spp, aes(fill = layer, col = layer)) # plot spdf
Elevation profiles
::p_load(topoDistance,elevatr)
pacman
# elevation raster (country tiles)
<- getData('alt', country='FRA', mask = T)
elevp $FRA_msk_alt@crs # crs
elevp$FRA_msk_alt@extent # bbox
elevp
# elevation raster
<- elevatr::get_elev_raster(city_df[,c("lon","lat")], # get elev raster
elevp2 z = 3,
<- "+proj=longlat +datum=WGS84 +no_defs"
prj # expand = 3
%>%
) rasterToPoints() %>% as.data.frame
<- elevp2 %>% st_as_sf(coords = c("x","y"), crs = 4326) # df to sf
elevp2sf
# elevation points (usa only)
<- elevatr::get_elev_point(df,
elevp3 <- "+init=epsg:4326")
prj <- tidy(elevp3$elevation, region = "elevation") # fortify
elevp3_fort
# convert raster to diff classes
<- elevp %>% rasterToPoints() %>% as.data.frame # raster to df
elevdf <- elevp %>% rasterToPoints() %>% as.data.frame %>%
elevdf st_as_sf(coords = c("x","y"), crs = 4326) # raster to sp
# convert lines to elev profile
<- df[,c("lon","lat")] %>% as.matrix() %>% SpatialPoints() # df to points
pts <- topoDist(elevp,pts)
elevpath topoProfile(elevp,sl,pts = 100, type = "base") # generic elev profile plot
# plot
qplot(data = elevp2[1:10000,],x,layer,geom = "line")
Summarise raster layers
cellStats(raster, stat = "min", na.rm = T) # summarise raster layers
Read .tiff
require(raster)
<- "srtm_20_05.tif" %>% raster() dem1
Decrease/increase raster resolution
%>% aggregate(fact = 3) # lower res by factor of 3
ras %>% disaggregate(fact = 3, method = "bilinear") # increase res by factor of 3
ras
require(terra)
%>% disagg(3)
ras %>% aggregate(3)
ras %>% aggregate(c(2, 3), fun = sum) # aggregate by diff factor for rows and cols plus change method ras
Match geometry/proj of raster from different sources
require(terra)
%>% res() # get resolution of raster
ras %>% resample(ras2) # resample ras2 to ras1 resolution
ras
# change projection
%>% project("+proj=utm +zone=32") ras
Change resolution of raster
require(raster)
res(ras)
xres(ras)
yres(ras)
res(ras) <- 1/120
res(ras) <- c(1/120, 1/60) # change res of x and y
Get zscale/elevation of raster
require(geoviz)
%>% raster_zscale() ras
Shaded contour maps (Tanaka maps)
# https://github.com/riatelab/tanaka/ https://rgeomatic.hypotheses.org/1536
library(tanaka)
library(terra)
<- rast(system.file("tif/elev.tif", package = "tanaka"))
ras tanaka(ras, breaks = seq(80, 400, 20), legend.pos = "topright", legend.title = "Elevation\n(meters)")
Convert sf to spatiallines obj
<- df[, c("lon", "lat")] %>% as.matrix() %>% Line() # df to lines
lns <- df[, c("lon", "lat")] %>% as.matrix() %>% SpatialPoints() # df to points
pts
# sf to spatiallines
<- sf$geometry %>% .[1] %>% st_zm() %>% as_Spatial() # opt 1
sl <- sf$geometry %>% .[1] %>% st_zm() %>% as("Spatial") # opt 2 sl
Convert spatial class to sf
%>% st_as_sf() sp
Hi-res globe map
::p_load(rnaturalearth,dplyr,sf)
pacman
<- ne_countries("medium", type = "sovereignty", returnclass = "sf") # %>%
inset_globe # filter(!continent %in% c("Africa","Europe"))
<- ne_countries("medium", country = c("United States of America","Australia"), type = "sovereignty", returnclass = "sf")
inset_countries <- 2163
crsn <- 5
plat <- 180
plon <- paste0("+lat_0=",plat," +lon_0=",plon," +init=epsg:",crsn) # set centered projection
inset_prj
<- inset_globe %>% st_transform(inset_prj)
inset_globe <- inset_countries %>% st_transform(inset_prj)
inset_countries <- inter_df1 %>% st_transform(inset_prj) %>% st_as_sf() # convert into sf
inset_flight
<- ggplot() +
gp geom_sf(data = inset_globe, fill = adjustcolor(fg,0.7), col = NA, size = 0.1) +
geom_sf(data = inset_countries, fill = adjustcolor(flight_col,0.7), col = NA, size = 0.1) +
geom_my_sf(inset_flight,flight_col,0.5,3) + # melb to la flight
coord_sf(crs = inset_prj) +
# theme(plot.background = element_rect(fill = "transparent")) +
theme_nothing()
gp
Getting gc flight path from sf obj
library(geosphere)
require(sf)
<- function(sc1,sc2){
get_inter_func gcIntermediate(
$city %in% sc1,] %>% st_geometry() %>% unlist,
sites_flight[sites_flight$city %in% sc2,] %>% st_geometry() %>% unlist,
sites_flight[sites_flightn=100, addStartEnd=T, breakAtDateLine=F) %>%
as.data.frame()
%>%
} as.matrix() %>% # convert points to linestring
st_linestring(dim = "XY") %>%
st_sfc(crs = 4326) # convert sfg object to geometry
Combine multiple sf/sfc geometries into one
<- "kml" %>% st_layers %>% .[[1]] # get layer ids
multipath <- c()
pathm for (pid in multipath) {
<- kmldata %>% st_read(layer = multipath[pid], fid_column_name = "IDS")
path <- rbind(pathm, path)
pathm
}# combine separated geometries into one geometry
<- st_combine(pathm)
patht
# dissolve seperate geometries into one
%>% st_union()
sf
# join two different sf objs and combine into one geometry
# opt 1
::bind_rows(list(sf1, sf2)) %>% st_union()
dplyr
# opt 2
st_sfc(sf1, sf2) %>% st_combine()
Join/merge df vars with sf obj (ideal for heatmap/chloropleth)
<- df$country_name %>% unique # identify country names
countries1 <- ne_countries(scale = "medium", returnclass = "sf") %>%
mp filter(name %in% countries1) %>%
rename("country_name" = name) %>% # match var names between df and sp obj
left_join(df,by = "country_name") # join by matched var name
Merge/dissolve overlapping polygon/sf boundaries into one
<- rnaturalearth::ne_states(countries, returnclass = "sf")
dd <- ne_countries("large", "sovereignty", returnclass = "sf")
dd2 plot(gUnion(dd, dd2[dd, ]))
Snap sf objects based on proximity
library(units)
library(sfnetworks)
<- sf %>%
sf st_snap(.,., tolerance = 10000) %>% # coerces the snapping using a big tolerance value
as_sfnetwork() # convert to network obj
autoplot(sf)
Validate sfs (useful when trying combining sfs and connecting lines make weird shapes)
%>% st_is_valid()
sf !st_is_valid(sf), ] %>% st_make_valid() sf[
Find intersecting geo points within sf objects
require(osmdata)
require(sf)
<- function(loc,key,value,geo){
get_osm_feature <- opq(loc) %>% # find location
oo add_osm_feature(key = key, value = value) %>% # source key-value pair
osmdata_sf() %>% # get sf obj
unname_osmdata_sf() # combat sf error
# %>% # pull geometry
oo[[geo]]
}
# location, key, value
<- "melbourne australia"
location1 <- get_osm_feature(location1, "leisure","park|natu","osm_polygons") # get osm features
polyg
# check if point intersects with polygon
= c(144.7695,-38.2724) %>%
pts_inter st_point() %>% # convert into sf point
st_sfc(crs = 4326) %>% # set prj
st_as_sf() # convert into sf
<- polyg %>% st_transform(4326) # match proj to above sf coords
polyg <- st_intersection(polyg,pts_inter) # find intersecting point within polygon
polyg_inter
# plot intersecting polygon and point
st_intersection(pts_inter,polyg) %>% st_geometry()
%>% filter(osm_id == "234385754")
polyg ggplot() +
geom_sf(data = polyg %>% filter(osm_id == polyg_inter$osm_id)) + # plot polygon w intersecting point
geom_sf(data = pts_inter, col = "red") + # add intersecting point
geom_sf_text(data = pts_inter, label = "Intersecting\npoint") +
theme_bw()
Fixing intersecting sf objs (self/non-coding intersection error)
# 2023
sf_use_s2(FALSE) # run at start of session to avoid 'Error in overlapping polygons' error
require(sf)
require(lwgeom)
# various options
%>% st_make_valid # validate obj
sf %>% st_buffer(0) %>% st_intersection(polys) # add 0 buffer
sf %>% st_snap(polys, tolerance = 0.09) # snap to polygon edge
sf %>% lwgeom::st_snap_to_grid(10) # snap to grid
sf 5:1] %>% st_intersection() # try reversing order of obj
sf[%>% st_is_valid, ] # index only valid objs sf[sf
Get intersecting underlying polygons using point data
# pull underlying polygon data where point data intersects/overlays
%>% st_join(sf_point, join = st_intersects)
sf_polygon
# option 2
%>%
sf_polygon st_filter(sf_dataframe,
.predicate = st_intersects
# join = st_overlaps
)
# option 3 - check other predicate options and switch point/poly order
st_intersects(sf_point,sf_polygon,sparse = T, prepared = T) %>% unlist,] %>%
sf_polygon[st_join(sf_point, join = st_intersects, largest = T)
Merge sequential/overlapping sf polygons
# merge first 20 polygons into one polygon
1:20, ] %>% st_union
sf[# merge first 20 polygons into one multilinestring
1:20, ] %>% st_boundary %>% st_line_merge sf[
Read in multiple files and combine into one sfc
<- dir %>% list.files(full.names = T)
fhs <- 4326
prj <- st_sfc(crs = prj) # create empty sfc
slist for (f in fhs) {
<- f %>% read_sf()
s <- rbind(s, slist)
slist
}<- slist %>% mutate(id = LETTERS[1:length(fhs)]) slist
Other predicate funcs for sf pairs
st_intersects()
st_disjoint()
st_touches()
st_crosses()
st_within()
st_contains()
st_contains_properly()
st_overlaps()
st_equals()
st_covers()
st_covered_by()
st_equals_exact()
st_is_within_distance()
Create interactive HTML knitted from Rmd
{rdeck}
Change spatial polygons into hexagon tiles
<- rnaturalearth::ne_states(c("New Zealand"), returnclass = "sf")
dd
# opt 1
# convert sf files using st_make_grid (with x = st_as_sf(sf_obj))
st_make_grid(st_as_sf(dd),
square=FALSE,
cellsize = 1,
n = 200,
# crs = prj,
flat_topped = T)[st_as_sf(dd)]
# opt 2
# change sf to sp
<- df %>% st_as_sf(coords = c("lon","lat"), crs = 4163) # need to keep regular prj
df_hex <- df_hex %>% as_Spatial() %>% spsample(type = "hexagonal", cellsize = 0.3) # set cell size according to data density
hex_points <- HexPoints2SpatialPolygons(hex_points) %>%
hex_polygons st_as_sf(crs = st_crs(df_hex)) # %>% st_intersection(., df_hex)
$fill <- lengths(st_intersects(hex_polygons, df_hex)) # create fill col based on data length
hex_polygons
# now project data
<- maps::world.cities %>% filter(country.etc == "Australia", name == "Brisbane") # %>% st_as_sf(coords = c("long","lat"), crs = prj) %>% st_buffer(1.2*10^6) %>% st_bbox
bb <- 2163
crsn <- bb[,"lat"]
plat <- bb[,"long"]
plon <- paste0("+lat_0=",plat," +lon_0=",plon," +init=epsg:",crsn) # opt1
prj <- ne_countries("large",returnclass = "sf") %>% st_transform(prj)
dd <- hex_polygons %>% st_transform(prj)
hex_polygons <- bb %>% st_as_sf(coords = c("long","lat")) %>% st_buffer(5*10^6) %>% st_bbox
bbox
# plot
<- hex_polygons %>% filter(fill != 0) # rm all 0's (optional)
hex_polygons <- sequential_hcl(hex_polygons$fill %>% n_distinct() %>% log + 1, "Purples") %>% rev # use log + 1 scale to improve col depth
colpal ggplot() +
geom_sf(data = hex_polygons, aes(fill = fill %>% log + 1, col = fill %>% log + 1),size = 0.05) +
scale_fill_gradientn(name = "Activity", colours = colpal, aesthetics = c("col"), na.value = "transparent"
# , guide = "none"
)
Add buffer decay border around hexes
# using above hex_polygons sf data
<- hex_polygons %>% filter(fill != 0)
ddd <- 5*10^4 # create buffer decay
hex_buff <- ddd %>% st_combine %>% st_boundary() %>% st_buffer(hex_buff,endCapStyle = "SQUARE",joinStyle = "MITRE")
hexbuff_outer <- ddd %>% st_combine %>% st_boundary() %>% st_buffer(hex_buff/2,endCapStyle = "SQUARE",joinStyle = "MITRE")
hexbuff_inner <- sequential_hcl(ddd$fill %>% n_distinct() %>% log + 1, "Purples") %>% rev # colpal for hex with fill = 0
colpal
ggplot() +
# hex with buffer decay
geom_sf(data = hexbuff_outer, col = NA, fill = adjustcolor(colpal[1] %>% darken(0.1),opac/2), size = 0.2) +
geom_sf(data = hexbuff_inner, col = NA, fill = adjustcolor(colpal[1] %>% darken(0.2),opac/2), size = 0.2) +
geom_sf(data = ddd , aes(fill = fill %>% log + 1, col = fill %>% log + 1),size = 0.1) +
scale_fill_gradientn(name = "Activity", colours = colpal, aesthetics = c("col"), na.value = "transparent"
# , guide = "none"
)
Turn sf into hex for mapping
library(h3jsr)
<- sf %>%
df_hex select(var1) %>%
mutate(hex=point_to_h3(geometry, res=8)) %>%
st_drop_geometry() %>%
count(hex) %>% # get count for var1 hex
h3_to_polygon(simple=FALSE) # turn into geometry
# add hex to leaflet
require(leaflet)
leaflet(elementId='HexCounts') %>%
addProviderTiles(dependencyLocation=here::here()) %>%
addGlPolygons(data=df_hex, opacity=1, fillColor='n')
Add sequential colpal to flight paths/geometry/sf
%>% mutate(colpal = path_col %>% rep(7) %>% lighten(seq(0.1, 0.5, 0.1))) inter_df
Add labels to sf obj
ggplot() +
geom_sf_label(data=state_names, aes(label=state),family=city_label_font,fill = NA, color="red", size = 5, label.size = 0, nudge_x = 0.5, nudge_y = 0.5) +
require(raster)
<- getData(country = c("USA"), level = 1) # download data
dem1 <- getData("SRTM", lat = 37.83, lon = -83.68) %>% aggregate(fact = 3) # get srtm and reduce res
dem1 <- "srtm_20_05.tif" %>% raster() # or read from file
dem1 <- 40
angle_zen <- 270
angle_dir <- 0.3 # quantile to filter elev data
quant
<- dem1 %>% terrain(opt = "slope")
dems <- dem1 %>% terrain(opt = "aspect")
demr <- hillShade(dems, demr, angle_zen, angle_dir)
demh
# conver to df
<- rasterToPoints(demh) %>% as.data.frame()
dem_df colnames(dem_df) <- c("lon", "lat", "hill")
# take only upper quantile
<- dem_df %>% filter(hill > hill %>% quantile(quant), hill < hill %>% quantile(quant + 0.1)) # get elev > given quantile
dem_df
# colpal
<- c(border_col, fg, "#FFFFFF")
colpal <- colpal %>% length # number of colour hues
cc
ggplot() + geom_raster(data = dem_df, aes(lon, lat, fill = hill), show.legend = F)
New osm data method
-------------------------------# https://www.urbandemographics.org/post/mapping-walking-time-osm-r5r/
::oe_download(provider = "openstreetmap_fr", file_url = osmextract::oe_match("Rio De Janeiro")[[1]],
osmextractdownload_directory = here::here("data"), force_download = T)
Access levels within sf variable (e.g. Name) using attributes
$Name %>% attr(which = "levels") sf
Fix intersecting layers and wrapping error (spherical geometry error)
sf_use_s2(FALSE)
require(maps,sf,rmapshaper)
bind_rows(list(
"a" = sf1,
"b" = sf2
%>% st_as_sf %>% st_combine()
))
# filter out dust
<- 0.1
quant bind_rows(list(
sf1,
sf2%>%
)) filter(st_area(.) > st_area(.) %>% quantile(quant))
%>% nrow
osm_list_quant
bind_rows(list(
sf1,
sf2%>% st_simplify()
))
# set smoothness of polygons [0,1] (lower = less smooth)
<- 0.2
keep bind_rows(list(
sf1,
sf2%>%
)) ms_simplify(keep = keep)
# set smoothness of polygons [0,1] (lower = less smooth)
<- 0.2
keep bind_rows(list(
sf1,
sf2%>%
)) as('Spatial') %>% # convert to spoly then reconvert to sp
::ms_simplify(keep = keep) %>%
rmapshaperst_as_sf()
# combine tolerance and area quantile
<- 0.2
keep <- 0.1
quant <- bind_rows(list(
osm_list_ms_simp3
sf1,
sf2%>%
)) as('Spatial') %>%
::ms_simplify(keep = keep) %>%
rmapshaperst_as_sf() %>% filter(st_area(.) > st_area(.) %>% quantile(quant))
library(tidyverse)
library(sf)
library(nngeo)
<- read_csv("https://gist.githubusercontent.com/fletchjeff/b60f46ca3c1aa51e9aea9fd86d0ab433/raw/81ee446939110daf8b5c3a79425ccf1100070578/airports.csv") %>%
airports mutate(row_num = row_number()) %>% st_as_sf(coords = c("Longitude", "Latitude"))
# African airports
<- airports %>% filter(str_detect(`Tz database time zone`, "Africa|Indian/Antananarivo"))
airports_africa
# Nearest neighbours
<- st_nn(airports_africa, airports_africa, k = 10, progress = F)
nn <- st_connect(airports_africa, airports_africa, ids = nn, progress = F)
airports_connexions
# plot
%>% ggplot() + geom_sf(size = 0.75) + theme_minimal() + theme(plot.title = element_text(family = "Gotham Black",
airports_connexions size = rel(4)))