How to generate a SpatVector from SpatExtent in R terra? - r

Does anybody know if there is a built-in function in R terra to generate a SpatVector polygon from an ext object, please?
library(terra)
# A SpatExtent object
ext1 <- ext(c(-74.5, -72.5, 9.5, 12.0))
How I manually created the coordinates of the polygon vertices
box_coords <- rbind(
c(ext1[1], ext1[3]),
c(ext1[2], ext1[3]),
c(ext1[2], ext1[4]),
c(ext1[1], ext1[4]),
c(ext1[1], ext1[3])
)
box_coords
# [,1] [,2]
#[1,] -74.5 9.5
#[2,] -72.5 9.5
#[3,] -72.5 12.0
#[4,] -74.5 12.0
#[5,] -74.5 9.5
How I convert the coordinate into a SpatVector
box1 <- vect(box_coords, type = "lines")
The way I'd like it to be, but that doesn't work
box1 <- vect(ext1)

You can use as.polygons
library(terra)
e <- ext(-74.5, -72.5, 9.5, 12.0)
p <- as.polygons(e, crs="")
# or
p <- as.polygons(e, crs="+proj=longlat")
You can also use as.points and as.lines
To get a polygon for the extent of SpatRaster or SpatVector x you can do
x <- rast()
p <- as.polygons(x, extent=TRUE)
The latter has the advantage that the coordinate reference system is not lost, or is less convulted than the alternative
p <- as.polygons(ext(x), extent=TRUE, crs=crs(x))
I have now added this path as well (terra 1.7-6) .
ve <- vect(e, crs="+proj=longlat")
And, to get a numeric vector (not a SpatVector) you can do
as.vector(e)

Related

Finding the peak of a mountain

so I've combined those 2 rasters and made them into one dem raster which contains elevation values:
dem1 = read_stars("srtm_43_06.tif")
dem2 = read_stars("srtm_44_06.tif")
pol = st_read("israel_borders.shp")
dem = st_mosaic(dem1, dem2)
dem = dem[, 5687:6287, 2348:2948]
names(dem) = "elevation"
dem = st_warp(src = dem, crs = 32636, method = "near", cellsize = 90)
Now I need to calculate a point geometry of the peak of the mountain by finding the centroid of the pixel that has the highest elevation in the image, does anyone know what functions I can use?
Building on Grzegorz Sapijaszko's example, here is an alternative path to the top of the mountain.
library(terra)
f <- system.file("ex/elev.tif", package="terra")
x <- rast(f)
If there is a single maximum, you can do
g <- global(x, which.max)
xyFromCell(x, g[,1])
# x y
#[1,] 6.020833 50.17917
Now, consider a situation with multiple maxima. I add three more cells with the maximum value.
x[c(1000, 2500, 5000)] <- 547
We can find the four highest peaks with:
g <- global(x, which.max)[[1]]
v <- x[g] |> unlist()
y <- ifel(x == v, v, NA)
p <- as.points(y)
crds(p)
#[1,] 6.020833 50.17917
#[2,] 6.154167 50.10417
#[3,] 5.987500 49.97083
#[4,] 6.237500 49.75417
You should not warp (project with terra) the raster data first because that changes the cell values and potentially the location of the highest peak. You should find the peaks with the original data, but then you can transform the results like this.
pp <- project(p, "EPSG:32636")
crds(pp)
# x y
#[1,] -1411008 5916157
#[2,] -1404896 5904422
#[3,] -1422145 5894509
#[4,] -1413735 5864236
With your files, you could start with something like
ff <- c("srtm_43_06.tif", "srtm_44_06.tif")
v <- vrt(ff)
g <- global(x, which.max)
And then continue as in the examples above.
Let's use terra, however similar approach can be applied by raster package as well. For testing purposes we will use raster supplied with terra package
library(terra)
#> terra 1.5.12
f <- system.file("ex/elev.tif", package="terra")
v <- rast(f)
plot(v)
You can check the details of your raster just typing the raster object name and pressing enter, you can check the min and max values with minmax() function form terra:
minmax(v)
#> elevation
#> [1,] 141
#> [2,] 547
Let's create another raster by copying original one, however checking if the value is the max value of elevation:
w <- v == minmax(v)[2]
plot(w)
Let's create a substitution matrix, and substitute all FALSE with NA and TRUE with 1:
mx <- matrix(c(FALSE, NA, TRUE, 1), ncol = 2, byrow = TRUE)
w <- classify(w, mx)
plot(v)
plot(as.polygons(w), add=TRUE)
Let's find centroids of those polygon(s):
pts <- centroids(as.polygons(w))
plot(pts, add=TRUE)
Let's see our coordinates:
as.data.frame(pts, geom = "WKT")
#> elevation geometry
#> 1 1 POINT (6.020833 50.179167)
Created on 2022-01-29 by the reprex package (v2.0.1)

Extracting value of raster with xy and xy of buffer coordinates too

I have some difficulty if I want to also get the coordinates inside the buffer corresponding for each value in my result of the extract. In my example:
library(raster)
#Simulation of raster and some coordinates
r <- raster(ncol=36, nrow=18)
r[] <- 1:ncell(r)
xy <- cbind(-50, seq(-80, 80, by=20))
#Get coordinates of raster cells
v <- extract(x = r,
y = xy,
buffer=100,
df=TRUE)
cbind(v,coordinates(r))
Error in cbind(v, coordinates(r)) :
number of rows of matrices must match (see arg 2)
Obviously, because I have a list that represents each buffer and the famous solution:
ee <- t(data.frame(result))
rownames(ee) <- NULL
data.frame(xy, ee)
It doesn't work because I can recovery only xy coordinates and not each xy of the pixel values inside the buffer too.
What's the solution for I have an output data frame with the coordinates of xy and xy of buffer too for each value extracted like:
## cells layer x y x_buffer y_buffer
## [1,] 626 626 -45 -85 -44 -84
...
# get xy from buffer cells
cell <- extract(r, xy, buffer=100, cellnumbers=T)
xy_b <- xyFromCell(r, do.call(rbind, cell)[,1])
res<-NULL
res <- do.call(rbind, cell)
RES<-cbind(res,xy,xy_b)
colnames(RES)<-c("cells","layer","x","y","x_buffer","y_buffer")
head(RES)
cells layer x y x_buffer y_buffer
[1,] 4909 4909 -150 0 -149.4 0.9
[2,] 4937 4937 -50 0 -48.6 0.9
[3,] 4964 4964 50 0 48.6 0.9
[4,] 4992 4992 150 0 149.4 0.9

Issue with estimating weighted mean from raster for a polygon shape in R

I need to estimate the weighted average of raster values for the polygon shown in squares. I want to obtain raster value and its weight within each square in the polygon shape. (As shown in this post: How can I extract an area weighted sum from a raster into a polygon in R?)
But, please see my code below and the image of what I am getting as weights. Can somebody correct me what I am doing wrong here and why my output is different from as shown in the above post.? I want to obtain an output like in the post above. Seems likes the weights I am getting is wrong too.
Please see the attached input data set here:
https://bft.usu.edu/w8crs
Thanks.
library(raster)
library(sp)
library(rgdal)
library(rgeos)
rlist = list.files(getwd(), pattern = "tif$", full.names = TRUE)
inshp = "Test"
rdata <- rlist[1]
r <- raster(rdata)
sdata <- readOGR(dsn=getwd(), layer=inshp)
sdata <- spTransform(sdata, crs(r))
extract(r, sdata, weights=TRUE)
Output:
[[1]]
value weight
56.75139 1
[[2]]
value weight
61.18781 1
[[3]]
value weight
56.75139 1
[[4]]
value weight
61.18781 1
Here is a reproducible example
library(raster)
packageVersion("raster")
#[1] ‘2.8.4’
r <- raster(xmn=0, xmx=1, ymn=0, ymx=1, nrow=2, ncol=2)
values(r) <- 1:4
m <- matrix(c(0.4, 0.6, 0.8, 0.6, 0.7, 0.2, 0.3, 0.2), ncol=2, byrow=TRUE)
s <- spPolygons(m)
plot(r); lines(s)
extract(r, s, weights=TRUE)
#[[1]]
# value weight
#[1,] 1 0.0625
#[2,] 2 0.1875
#[3,] 3 0.3125
#[4,] 4 0.4375
This did not work for you, because your polygon was very small relative to the raster cell size. I have changed the function, such that it increases the precision for those cases. I now get this with your data:
> extract(r, sdata, weights=TRUE)
[[1]]
value weight
56.75139 1
[[2]]
value weight
[1,] 61.18781 0.6592593
[2,] 56.75139 0.3407407
[[3]]
value weight
56.75139 1
[[4]]
value weight
[1,] 61.18781 0.5522388
[2,] 56.75139 0.4477612
To make it reproducible without downloads, for one of your polygons:
library(raster)
r <- raster(ncol=2, nrow=1, xmn=596959.624056728, xmx=624633.120455544, ymn=568805.230192675, ymx=582641.978392083, crs='+proj=aea +lat_1=29.5 +lat_2=45.5 +lat_0=37.5 +lon_0=-96 +x_0=0 +y_0=0 +datum=NAD83 +units=m')
values(r) <- c(61.18781, 56.75139)
g <- data.frame(matrix(c(rep(1, 18), rep(0,6), 611318.079488842,611440.751254539,610712.115334383,609842.749239201, 609703.303842618,611318.079488842,581038.816616668,579434.971927127, 579381.167042005,579315.223934334,580917.724282178,581038.816616668), ncol=6))
colnames(g) <- c('object','part','cump','hole','x','y')
p <- as(g, "SpatialPolygons")
crs(p) <- crs(r)
extract(r, p, weights=TRUE)
#[[1]]
# value weight
#[1,] 61.18781 0.6592593
#[2,] 56.75139 0.3407407

Cropping raster to minimum extent not working in R

I am writing a script that will take any three rasters, and crop them to the minimum possible extent. All three rasters will have the same resolution and projection. However, cropping to the minimum extent does not change the extents for the three rasters. I've tried setExtent and the same thing happens. If anyone can give suggestions I would really appreciate it. Here is sample code:
library(raster)
#Projection of all three rasters
newproj<- "+proj=utm +zone=4 +datum=WGS84 +units=m +no_defs +ellps=WGS84
+towgs84=0,0,0"
#Create three rasters with varying extents
raster1p<- raster(crs = newproj)
extent(raster1p)<- c(531247, 691847, 7856684, 7987884)
res(raster1p)<- c(100, 100)
values(raster1p)<- NA
raster2p<- raster(crs = newproj)
extent(raster2p)<- c(533550.8, 646550.8, 7881307, 7973807)
res(raster2p)<- c(100, 100)
values(raster2p)<- NA
raster3p<- raster(crs = newproj)
extent(raster3p)<- c(525739, 689839, 7857305, 7996505)
res(raster3p)<- c(100, 100)
values(raster3p)<- NA
#Find minimum extent
xmin1<- c(xmin(extent(raster1p)), xmin(extent(raster2p)), xmin(extent(raster3p)))
xmax1<- c(xmax(extent(raster1p)), xmax(extent(raster2p)), xmax(extent(raster3p)))
ymin1<- c(ymin(extent(raster1p)), ymin(extent(raster2p)), ymin(extent(raster3p)))
ymax1<- c(ymax(extent(raster1p)), ymax(extent(raster2p)), ymax(extent(raster3p)))
xmin_new<- min(xmin1)
xmax_new<- min(xmax1)
ymin_new<- min(ymin1)
ymax_new<- min(ymax1)
newextent=c(xmin_new, xmax_new, ymin_new, ymax_new)
#Crop rasters to minimum extent
crop(raster1p, newextent)
crop(raster2p, newextent)
crop(raster3p, newextent)
#Compare extents
extent_check<- c(extent(raster1p), extent(raster2p), extent(raster3p))
However, when I look at the extent_check to see if the extents now match, I see that the extents have not changed at all:
> extent_check
[[1]]
class : Extent
xmin : 531247
xmax : 691847
ymin : 7856684
ymax : 7987884
[[2]]
class : Extent
xmin : 533550.8
xmax : 646550.8
ymin : 7881307
ymax : 7973807
[[3]]
class : Extent
xmin : 525739
xmax : 689839
ymin : 7857305
ymax : 7996505
Any idea what I could be doing wrong? Thank you for your time
I think it is not so much about doing something wrong, but rater a misconception (although there is a mistake in your code).
Example data
library(raster)
prj <- "+proj=utm +zone=4 +datum=WGS84"
r1 <- raster(res=100, ext=extent(c(531247, 691847, 7856684, 7987884)), crs=prj, vals=NA)
r2 <- raster(res=100, ext=extent(c(533550.8, 646550.8, 7881307, 7973807)), crs=prj, vals=NA)
r3 <- raster(res=100, ext=extent(c(525739, 689839, 7857305, 7996505)), crs=prj, vals=NA)
Find the "minimum extent"
e <- intersect(intersect(extent(r1), extent(r2)), extent(r3))
Note that the result is different from yours because you use
xmin_new <- min(xmin1) and ymin_new <- min(ymin1)
Where it should be
xmin_new <- max(xmin1) and ymin_new <- max(ymin1)
Now crop
r1e <- crop(r1, e)
r2e <- crop(r2, e)
r3e <- crop(r3, e)
Inspect the resulting extents
t(sapply(c(r1e, r2e, r3e), function(i) as.vector(extent(i))))
# [,1] [,2] [,3] [,4]
#[1,] 533547.0 646547.0 7881284 7973784
#[2,] 533550.8 646550.8 7881307 7973807
#[3,] 533539.0 646539.0 7881305 7973805
They are not exactly the same, because that is not possible because the rasters do not align. Their "origins" are different
t(sapply(c(r1e, r2e, r3e), origin))
# [,1] [,2]
#[1,] 47.0 -16
#[2,] -49.2 7
#[3,] 39.0 5
To make them align, you would need to do something like this
r1e <- crop(r1, e)
r2e <- resample(r2, r1e)
r3e <- resample(r3, r1e)
t(sapply(c(r1e, r2e, r3e), function(i) as.vector(extent(i))))
# [,1] [,2] [,3] [,4]
#[1,] 533547 646547 7881284 7973784
#[2,] 533547 646547 7881284 7973784
#[3,] 533547 646547 7881284 7973784

Drawing equidistant points from the sides of a polygon

I am drawing a polygon with the following vertices
x y
-0.02208709 -0.039161304
0.01184081 -0.020268029
0.04578401 -0.001351904
0.02210236 0.039176396
-0.01185226 0.020252146
-0.04578784 0.001352696
using the following code
plot(x,y)
polygon(x,y)
points(mean(x),mean(y),col="red")
Now I want to plot 50 equally-spaced points along the sides of polygon. Any suggestion how to do it?
You can do this with spsample from the sp package.
First we'll load the library and read in your vertices.
library(sp)
xy <- read.table(text='x y
-0.02208709 -0.039161304
0.01184081 -0.020268029
0.04578401 -0.001351904
0.02210236 0.039176396
-0.01185226 0.020252146
-0.04578784 0.001352696', header=TRUE)
Now create a SpatialLines object from the vertices. This is a bit messy - see ?SpatialLines and ?`SpatialLines-Class` if you get stuck.
l <- SpatialLines(list(Lines(Line(rbind(xy, xy[1, ])), ID=1)))
Then sample the points and coerce to a data.frame with as.data.frame(pts) or coordinates(pts).
pts <- spsample(l, 50, type="regular")
coordinates(pts) # only the head shown here
## x y
## [1,] -0.019343310 -0.03763339
## [2,] -0.014987452 -0.03520776
## [3,] -0.010631594 -0.03278213
## [4,] -0.006275735 -0.03035651
## [5,] -0.001919877 -0.02793088
## [6,] 0.002435981 -0.02550525
plot(l)
points(pts, pch=20)

Resources