Alternative for R package concaveman to create polygons - r

I have a script using the R package 'concaveman', but due to issues on the ubuntu platform that I need to run the code on I cannot install this package (it has taken me three days trying to solve it). So I am looking for an alternative.
I have a random set of points ranging from 3 to 1000s of points. I want to draw a convex hull/polygon around the outer most points (step after would be to rasterize). I have been trying to do it by converting the points to a raster, then use rastertopolygons, but in rare occasions points would be in the same raster cell resulting in only two unique points. Convaveman would make this into a linear polygon (which is what I want, without using concaveman). Here is the input data that would be problematic:
x <- structure(list(x = c(166.867, 166.867, 167.117, 166.8667), y = c(-20.6333,
-20.633, -20.833, -20.6333)), row.names = c(NA, -4L), class = c("tbl_df",
"tbl", "data.frame"))
This is what I tried not (with the error I get):
SP_pt <- SpatialPoints(x, proj4string=crs("+proj=longlat +ellps=WGS84 `+towgs84=0,0,0,0,0,0,0 +no_defs"))`
gridded(SP_pt) <- T
SP_pt_R <- raster(SP_pt)
SP_poly <- rasterToPolygons(SP_pt_R, dissolve = T)
suggested tolerance minimum: 0.333333
Error in points2grid(points, tolerance, round) :
dimension 1 : coordinate intervals are not constant

You can use chull in base R:
sp::Polygon(x[c(chull(x), chull(x)[1]), ])
#> An object of class "Polygon"
#> Slot "labpt":
#> [1] 166.95023 -20.69977
#>
#> Slot "area":
#> [1] 6.75e-05
#>
#> Slot "hole":
#> [1] FALSE
#>
#> Slot "ringDir":
#> [1] 1
#>
#> Slot "coords":
#> x y
#> [1,] 167.1170 -20.8330
#> [2,] 166.8667 -20.6333
#> [3,] 166.8670 -20.6330
#> [4,] 167.1170 -20.8330
Or if you want to use the sf package:
sf::st_polygon(list(as.matrix(x[c(chull(x), chull(x)[1]),])))
#> POLYGON ((167.117 -20.833, 166.8667 -20.6333, 166.867 -20.633, 167.117 -20.833))

You can use dismo::convHull and then use predict or rasterize
library(dismo)
xy <- cbind(x=c(1,1,2,2), y=c(3,2,1,2))
# must be matrix or data.frame, not a tbl
ch <- convHull(xy)
plot(ch)
# predict
r <- raster(xmn=0, xmx=5, ymn=0, ymx=5, res=.25)
p <- predict(ch, r)
# Or rasterize
sp <- polygons(ch)
x <- rasterize(sp, r)
For faster rasterization you can use terra
library(terra)
v <- vect(sp)
rr <- rast(r)
y <- rasterize(v, rr)
To cast sp to sf
sf <- as(sp, "sf")

Related

How to change a raster to a specific spatial resolution?

I would like to change the resolution of a raster. For example, let’s take
this Landsat 7 images at ~ 30m resolution.
library(terra)
#> terra 1.5.21
f <- system.file("tif/L7_ETMs.tif", package = "stars")
r <- rast(f)
# 30m x 30m resolution
res(r)
#> [1] 28.5 28.5
plot(r, 1)
I can use aggregate() with an integer factor such as:
# 10 * 28.5
r2 <- aggregate(r, fact = 10)
res(r2)
#> [1] 285 285
plot(r2, 1)
My question is, how can I specify an exact resolution. For example, I would
like to have a pixel resolution of 1.234 km (1234 m).
fact <- 1234 / 28.5
fact
#> [1] 43.29825
r3 <- aggregate(r, fact = fact)
res(r3)
#> [1] 1225.5 1225.5
plot(r3, 1)
The documentation says that fact should be an integer, so here it is
flooring fact to 43.
res(aggregate(r, 43))
#> [1] 1225.5 1225.5
Any ways to have an exact resolution of 1234 m?
Created on 2022-04-28 by the reprex package (v2.0.1)
I came up with this solution which seems to give me what I need.
library(terra)
#> terra 1.5.21
f <- system.file("tif/L7_ETMs.tif", package = "stars")
r <- rast(f)
plot(r, 1)
r2 <- r
res(r2) <- 1234
r2 <- resample(r, r2)
plot(r2, 1)
res(r2)
#> [1] 1234 1234
Created on 2022-04-28 by the reprex package (v2.0.1)
I also propose (as described in the terra vignette) that you first aggregate the raster as close as possible and then resample. Resampling can be done e.g. using a template raster to guarantee correct crs, dimensions etc.

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)

How to get coordinates of a selected point in R plot?

I need to pass to R program coordinates of a point selected by the mouse pointer, to perform some calculations. I have problems getting it to work.
I know that this code should identify point on a plot:
plot(kk2$k2,kk2$k1)
identify(kk2$k2,kk2$k1)
But even that doesn't work. On a plot appears some meaningless number, while point has two coordinates. why?
How to fix at least that?
My goal is to have the point coordinates returned to R and perform some calculations on them. The dataset kk2 has only two columns - k1 and k2, nothing more.
The package "gatepoints" available on CRAN will allow you to draw a gate returning your points of interest.
If you are using RStudio it is better to plot in a separate x11 window by first opening a new x11 device:
X11()
Now plot your points, I've made up some simple data:
kk2 <- data.frame(k2=1:10, k1=1:10)
plot(kk2, col = "red", pch = 16)
Run the command below and then select your points by left clicking and right clicking to close the polygon:
selectedPoints <- fhs(kk2)
This will return:
selectedPoints
#> [1] "4" "5" "7"
#> attr(,"gate")
#> k2 k1
#> 1 6.099191 8.274120
#> 2 8.129107 7.048649
#> 3 8.526881 5.859404
#> 4 5.700760 6.716428
#> 5 5.605314 5.953430
#> 6 6.866882 3.764390
#> 7 3.313575 3.344069
#> 8 2.417270 5.217868
locator {graphics} R Documentation
Graphical Input
Description
Reads the position of the graphics cursor when the (first) mouse button is pressed.
![> pts <- locator(4)
> polygon(pts)
> png(); plot(1,1)
> pts <- locator(4)
> polygon(pts)
> dev.off()][1]
Try something like this, since identify returns the seq_along(x) for the point that you click near (what you refer to as 'some meaningless number'):
x <- rnorm(10)
y <- rnorm(10)
plot(x,y)
out <- sapply(list(x,y),"[",identify(x,y))
# do some clicking
out
# something like this is returned for the x/y points
# [,1] [,2]
#[1,] -0.62221766 -0.73838314
#[2,] -0.69896643 0.40186536
#[3,] 0.06077831 -1.63940474
#[4,] -0.09900270 0.00062011
The key is using the result as an index. This can then be used to identify the specific xy coordinates:
n <- 10
x <- runif(n)
y <- runif(n)
df <- data.frame(x=x, y=y)
plot(y ~ x, data=df)
df[identify(x, y, n=1),]

How to create SpatialLine object

I am using sp package to create SpatialLines object and save it in the list of objects allLines. Later on I will need to compare SpatialLines to each other, but this goes beyond the current question.
So far I only need to construct SpatialLines objects. This is the last code based on the answer of hrbrmstr:
library(sp)
allLines <- NULL
x <- c(1,5,4,8)
y <- c(1,3,4,7)
xy <- cbind(x,y)
xy.sp = sp::SpatialPoints(xy)
spl <- SpatialLines(list(Lines(Line(xy.sp), ID="a")))
allLines <- rbind(allLines,spl)
Error message:
Error in (function (classes, fdef, mtable) : unable to find an
inherited method for function ‘proj4string’ for signature ‘"NULL"’
How to solve this issue?
Is:
library(sp)
x <- c(1,5,4,8)
y <- c(1,3,4,7)
SpatialLines(list(Lines(Line(cbind(x,y)), ID="a")))
## An object of class "SpatialLines"
## Slot "lines":
## [[1]]
## An object of class "Lines"
## Slot "Lines":
## [[1]]
## An object of class "Line"
## Slot "coords":
## x y
## [1,] 1 1
## [2,] 5 3
## [3,] 4 4
## [4,] 8 7
##
##
##
## Slot "ID":
## [1] "a"
##
##
##
## Slot "bbox":
## min max
## x 1 8
## y 1 7
##
## Slot "proj4string":
## CRS arguments: NA
what you're looking for?
Getting back to your last question, try
library(sp)
as(xy.spdf, "SpatialLines")
or, to create a Lines object (which may not be what you want),
as(xy.spdf, "SpatialLines")#lines[[1]]
If you came to this question to find out how to make a group of lines (as implied by the name of the function, SpatialLines) you can find examples in the sp library, filed under "SpatialLines-class".
I found their example a little strange, so I edited it to make more sense for how I normally see the data.
## Make some line segments from points
## Note, l1a and l1b are a group of two lines
l1a <- rbind(c(1, 3), c(2,2) ,c(3,2))
l1b <- l1a + .05
l2 <- rbind(c(1,1), c(2,1.5), c(3,1))
## At this point it's just a matrix, and you can plot the points
plot(l1a, type="l", xlim=c(1,3.25), ylim=c(2,3.25), xlab="", ylab="")
lines(l1b)
## Make convert the matrix objects to line objects
Sl1a <- Line(l1a)
Sl1b <- Line(l1b)
Sl2 <- Line(l2)
## Group the individual lines into "lines"
S1 <- Lines(list(Sl1a, Sl1b), ID="a")
S2 <- Lines(list(Sl2), ID="b")
## Now combine the line groups into a "spatial line object"
Sl <- SpatialLines(list(S1,S2))
## Plot the group, then (for illustration) add each line
## separately with color to illustrate the groups
plot(Sl)
plot(SpatialLines(list(S1)), add=T, col="red")
plot(SpatialLines(list(S2)), add=T, col="blue")
## Examine the properties
summary(Sl)
plot(Sl, col = c("red", "blue"))
Both spatial line plots look like this:
Note the matrix object has named rows in the example. I don't see any benefit to doing this, and it's confusing because the names overlap but do not correspond with the IDs given.

Calculte the whole center of gravity/geometric center of a polygon list

I am looking for a method to calculate the center of gravity of each polygon in the list spatialpolygons:
I thought used a loop, but he gets me for the first polygon, I don't know the way, I am new to R, can someone please help me
Code:
for ( i in 1:length(polys1_T)) {
xx=mean(coordinates(polys1_T[[i]])[,1])
yy=mean(coordinates(polys1_T[[i]])[,2])
aa<-as.data.frame(cbind(xx,yy))
}
Edit:
Code:
inter1 <- read.table("c:/inter1.csv", header=TRUE)
# add a category (required for later rasterizing/polygonizing)
inter1 <- cbind(inter1,
cat
= rep(1L, nrow(inter1)), stringsAsFactors = FALSE)
# convert to spatial points
coordinates(inter1) <- ~long + lat
# gridify your set of points
gridded(inter1) <- TRUE
# convert to raster
r <- raster(inter1)
# convert raster to polygons
sp <- rasterToPolygons(r, dissolve = T)
plot(sp)
# addition transformation to distinguish well the set of polygons
polys <- slot(sp#polygons[[1]], "Polygons")
# plot
plot(sp, border = "gray", lwd = 2) # polygonize result
inter1.csv
result:
Polys is list of 9 polygons :is that it is possible to calculate the center of gravity for each polygon?
Give rgeos::gCentroid a look. You can apply it in many ways. If you have a SpatialPolygons object, say, from a call to readOGR, you can do:
map <- readOGR(dsn, layer)
centers <- data.frame(gCentroid(map, byid=TRUE))
to get all the centroids from it.
As an aside: while accurate—a more common term is "geometric center"/"centroid" vs "center of gravity"
EDIT
For plain, ol Polygons (the "hard" way, but slightly more accurate):
library(rgdal)
library(sp)
library(PBSmapping)
library(maptools)
do.call("rbind", lapply(polys, function(x) {
calcCentroid(SpatialPolygons2PolySet(SpatialPolygons(list(Polygons(list(x), ID=1)))))
}))[,3:4]
## X Y
## 1 5.8108434 20.16466
## 2 -3.2619048 29.38095
## 3 5.5600000 34.72000
## 4 3.8000000 32.57037
## 5 6.3608108 32.49189
## 6 -2.2500000 31.60000
## 7 -8.1733333 27.61333
## 8 0.3082011 27.44444
## 9 8.6685714 26.78286
and, to use your nearly-equivalent by-hand-method:
do.call("rbind", lapply(polys, function(x) {
data.frame(mean(coordinates(x)[,1]), mean(coordinates(x)[,2]))
}))
## mean.coordinates.x....1.. mean.coordinates.x....2..
## 1 5.819892 20.15484
## 2 -3.242593 29.37778
## 3 5.539474 34.71579
## 4 3.815517 32.56552
## 5 6.323034 32.47191
## 6 -2.230952 31.60000
## 7 -8.140476 27.61905
## 8 0.350000 27.40885
## 9 8.746825 26.92063
Each method gives you the centroid for each list element (and there are 9—not 5—in the example you provided).
If you ever have a huge list of these, consider using rbindlist from the data.table package (speedier + more memory efficient).

Resources