Answers from SCGIS listserv superheroes:
Chris DeRolph
In Arc, I would convert the raster to points, then use the Near
tool (or Spatial Join
) to get the value of the closest raster point to each green dot. Hope this helps.
Dave Richardson
If you are already creating the mask based on a 200 m. distance cutoff, then you could use Nibble
(ArcGIS/ArcPro) to assign the nearest floating point value to the mask, then extract the values to your points from there. It’ll work with floating point values.
Chris Nicholas
in GRASS, to make the mask one can use r.grow
, as per:
https://grass.osgeo.org/grass82/manuals/r.grow.html
and "grow" out a polygon to cover the area on shore, then use that mask to categorize everything else as "off shore"
Then you can "clump" adjacent offshore pixels into islands, as per:
https://grass.osgeo.org/grass82/manuals/r.clump.html
Then you can use GRASS r.distance, as per: https://grass.osgeo.org/grass82/manuals/r.distance.html
to find the shortest distance, or load the polygons into PostGIS, and do something like:
with on_shore as (select * from loaded_polys where category = 'A'),
category_B as (select * from loaded_polys where category = 'B'),
(
SELECT ST_Distance("table_A".geom, "table_B".geom)
FROM table_A, table_B
ORDER BY ST_Distance("table_A".geom, "table_B".geom)
LIMIT 1
)
You might perhaps also try something fancy with KD-tree min distancing using scikitlearn.neighbors, as per:
https://scikit-learn.org/stable/modules/classes.html#module-sklearn.neighbors
Eric McGregor
Something like this may work depending on how many locations you're dealing with. Essentially, buffering each point with your minimum distance (200m), cropping the raster within each buffer, converting raster to points, generating distance matrix from each point to raster points, and selecting the minimum value. Adding the result back to the original sf dataframe.
library(terra)
library(sf)
library(dplyr)
Set up example data ----------------
f <- system.file("ex/elev.tif", package = "terra")
s <- rast(f)
s <- project(s, "EPSG:5070")
plot(s)
Get bounding geometry from raster
bb <- ext(s)
bb <- st_bbox(bb)
bb <- st_as_sfc(bb)
Generate random points within raster extent
set.seed(18)
pt <- st_sample(bb, 5) %>%
st_as_sf()
st_crs(pt) <- st_crs(s)
Get value of nearest non-NA raster cell ----------------
Create a column to capture the value of the nearest non-NA raster cell
pt$nearval <- NA
Loop over points and get nearest raster value within min distance
for(i in 1:nrow(pt)){
# Buffer point locations by desired distance
ptBuff <- st_buffer(pt[i,], dist = 8000)
# Crop raster to buffered point
inbuff <- crop(s, vect(ptBuff))
# Convert to points
dat <- as.points(inbuff)
dat <- st_as_sf(dat)
# If all values are NA the dataframe will be empty, move to next
if(nrow(dat) == 0){
next
}
Get distance from point to all pixels within buffer
ptdist <- st_distance(pt[i,], dat)
Add the distance values as column to data
dat$pdistance <- as.numeric(ptdist)
Get the minimum distance
m <- as.numeric(min(ptdist))
filter based on minimum distance
val <- dat %>%
dplyr::filter(pdistance == m) %>%
select(elevation) %>%
st_drop_geometry()
val <- as.numeric(val)
add value to point dataset
pt[i,]$nearval <- val
}
Test based on points that overlap valid raster values.
test <- extract(s, vect(pt))
test
Amanda Suzzi-Simmons
If you have Arc, use nibble
. If using R, interpolate using knn
then extract.
+1 for Nibble in Arc (then Extract values to points, of course); works great for this.
Todd McDonnell
Near
would help with judging the distance from which the attribution was made since points (too) far from the edge of the original raster may need to be trimmed out of the analysis, but converting a large high-resolution raster (if this is the case) to points can be problematic. To avoid such problems, maybe try converting only the edge of the raster to points (...convert original raster to polygon and dissolve, then convert polygon to polyline/points...?), then run Near
to get the distance.
Eric McGregor
I agree that converting many pixels to points is not optimal. But if a programmatic solution is desirable here's a starting place for the interpolation technique that Amanda mentioned:
library(gstat)
library(terra)
library(sf)
## Set up example data ----------------
f <- system.file("ex/elev.tif", package = "terra")
r <- rast(f)
# Convert raster to points
dat <- as.points(r)
dat <- st_as_sf(dat)
# Build gstat model
# See here: https://github.com/rspatial/terra/issues/208
gs <- gstat(id = "elevation", formula=elevation~1, data=dat, nmax=5, set=list(idp = 0))
interpolate_gstat <- function(model, x, crs, ...) {
v <- st_as_sf(x, coords=c("x", "y"), crs=crs)
p <- predict(model, v, ...)
as.data.frame(p)[,1:2]
}
# Interpolate values
zsf <- interpolate(r, gs, debug.level = 0, fun = interpolate_gstat, crs = crs(r), index = 1)
Jeffrey Evans
Posted his excellent answer separately which I'll accept as the answer to give him kudos :)