library(sf)
library(dplyr)
library(tmap)
library(spatstat)
library(terra)
One of the joys (ahem) of R spatial is moving data around between formats so you can use the best packages for particular jobs. Here’s an example using IDW interpolation in spatstat
.
Libraries
Libraries are the usual suspects plus spatstat
(duh) and maptools
for some extra conversions. We also need terra
for the data prep.
Introduction
As is often the case there is useful functionality in a package that doesn’t play nice with the core R-spatial packages. spatstat
is really great for lots of things, but does not support sf
and even needs a bit of persuading to handle sp
data. Its implementation of IDW interpolation is nice however, so it’s nice to know how to use it. Whether or not you should ever use IDW is another question altogether, but we can worry about that some other time.
Data
First we need a set of values to interpolate. I made a projected version of the R core dataset volcano which is a nice place to start.
<- rast("maungawhau.tif") maungawhau
Some random control points and a study area
We can get a dataframe of random points on the surface using terra::spatSample
. We’ll make this into a sf
object as a starting point because that’s the most likely situation when you want to interpolate data (you will have an sf
source).
<- maungawhau |>
pts spatSample(500, xy = TRUE) |>
st_as_sf(coords = c("x", "y")) |>
st_set_crs(2193) |>
st_jitter(5)
We also need a spatial extent for the interpolation, so let’s just make a convex hull of the points
<- pts |>
spatial_extent st_union() |>
st_convex_hull() |>
st_sf()
And just to see where we are at
tm_shape(maungawhau) +
tm_raster(col.scale = tm_scale_continuous(values = "hcl.terrain2")) +
tm_shape(pts) +
tm_dots() +
tm_shape(spatial_extent) +
tm_borders() +
tm_layout(legend.frame = FALSE)
Make the data into a spatstat
point pattern
spatstat
has its own format for point patterns, including coordinates, marks (the values) and a window or owin
(the spatial extent). It’s best to make the window first and then we can make the whole thing all at once. spatstat
prefers sp
objects, so we go via ‘Spatial’ to get a spatstat::owin
object. maptools
provides the conversion to an owin
.
<- spatial_extent |>
W as.owin()
We also need the control point coordinates
<- pts |>
xy st_coordinates() |>
as_tibble()
Now we can make a spatstat::ppp
point pattern
<- ppp(x = xy$X, xy$Y, marks = pts$maungawhau, window = W)
pp plot(pp)
Success!
A previous notebook showed an even quicker way to do this, but where the window will be formed from a bounding box (and where’s the fun in that?)
pts |>
as("Spatial") |>
as.ppp()
Interpolation
It’s easy from here. power
is the inverse power applied to distances, and eps
is the resolution in units of the coordinate system.
<- idw(pp, power = 2, eps = 10)
result plot(result)
This can be converted back to a terra
raster for comparison with the original surface.
# stack the layers so we can 'facet' plot them
<- rast(
comparison_raster list(maungawhau = maungawhau,
interpolation = rast(result) |> resample(maungawhau)
))
tm_shape(comparison_raster) +
tm_raster(col.scale = tm_scale_continuous(values = "hcl.terrain2"),
col.free = FALSE,
col.legend = tm_legend(
title = "Elevation",
position = tm_pos_out("right", "center"),
frame = FALSE))
Like I said, IDW is not necessarily a great interpolation method!