I have a large dataframe of point coordinates and I am trying to make a polygon out of the edge of the coordinates. Some of the points fall near the middle, so I do not want those points to shape the boundary of the polygon created. These are spatial data and I do convert them into sf or terra objects, but in the simplest form they are just (x,y) coordinates.
I've looked and I can't find any already developed function for this task.
Here's a basic set of coordinates that replicate the problem:
lat <- c(-34.8861,-34.9845,-34.9839,-34.4555,-34.3272,-34.922,-34.7)
lon <- c(118,117.721,117.118,116.789,115.785,115.843,116.5)
plot(lon,lat)
Any ideas for creating a polygon out of the outermost points of these coordinates?
something like this:
library(sf)
df <- data.frame(x = lon, y = lat)
pts1 <- st_as_sf(x = df, coords = c('x', 'y'))
my_hull <- st_convex_hull(st_union(pts1))
plot(my_hull)
With 'terra' it goes like this:
lat <- c(-34.8861,-34.9845,-34.9839,-34.4555,-34.3272,-34.922,-34.7)
lon <- c(118,117.721,117.118,116.789,115.785,115.843,116.5)
library(terra)
v <- vect(cbind(lon, lat), crs="+proj=longlat")
x <- convHull(v)
plot(x, col="red")
points(v, cex=2, col="blue")
Related
I am trying to smoothen the edges of an irregular polygon in R, namely to turn its sharp corners into round edges. I am trying to do this using smoothr::smooth, but this function operates on objects from packages sf or sp while all I have is a set of coordinates. Somehow, the result of turning my data.frame into a SpatialPolygonsDataFrame object (an object class from package sp) is a rectangle whose limits are the extreme limits of the original polygon. Does anyone know how to turn my set of coordinates in an object of a class compatible with smoothr::smooth while maintaining the original polygon shape? Here is what I did, partially following instructions at this page:
rm(list=ls()) # my compulsive habit of making sure R's memory is a clean slate
# Example dataset:
dd <- data.frame(
Lon = c(18.95379, 18.82409, 18.58987, 18.80541, 18.92427, 19.00264),
Lat = c(-32.42492, -32.32498, -31.89642, -31.73606, -32.16217, -32.37052)
)
plot(0,0,
xlim=c(18.5,19.1), ylim=c(-32.5,-31.6),
xlab="Longitude", ylab="Latitude"
)
polygon(dd[,"Lon"],dd[,"Lat"], border="red")
# To make it smooth I plan on using
library(smoothr)
# But smoothr:: smooth works on objects from packages sf or sp so I need to convert dd.
#convert to spatial points
library(sp)
coordinates(dd) = ~Lon + Lat
# convert to raster
library(raster)
rr <- raster::raster(dd)
#convert raster to polygons
sp = rasterToPolygons(rr, dissolve = T)
map(sp, add=T, col="green", fill=F)
# somehow my irregular polygon turned into a rectangle.
sps <- smooth(sp, method = "ksmooth", smoothness=5)
# this works, but of course is only rounding the corners of sp
map(sps, add=T, col="blue", fill=F)
In red is my original polygon from the data.frame dd, in green is object sp, in blue is the smooth version of sp, sps. Function smooth does the job, the problem is somewhere in the conversion of dd into an sp-compatible object. I suspect the problem is caused by raster() but I am not sure why or of how to fix it.
Many thanks in advance.
Here I've used sf because I personally find that much easier:
library(sf)
library(smoothr)
# Example dataset:
dd <- data.frame(
Lon = c(18.95379, 18.82409, 18.58987, 18.80541, 18.92427, 19.00264),
Lat = c(-32.42492, -32.32498, -31.89642, -31.73606, -32.16217, -32.37052)
)
# cast to polygon, use multipoint first though.
polygon <- as.matrix(dd) %>%
sf::st_multipoint() %>%
sf::st_cast("POLYGON")
# smooth polygon
polygon_smoothed <- smoothr::smooth(polygon, method = "ksmooth", smoothness = 0.5)
# plot to check
plot(polygon, col = "red")
plot(polygon_smoothed, col = "blue", add = T)
I found another solution here: https://rstudio-pubs-static.s3.amazonaws.com/202536_7a122ff56e9f4062b6b012d9921afd80.html
# Example dataset:
dd <- data.frame(
Lon = c(18.95379, 18.82409, 18.58987, 18.80541, 18.92427, 19.00264),
Lat = c(-32.42492, -32.32498, -31.89642, -31.73606, -32.16217, -32.37052)
)
plot(0,0,
xlim=c(18.5,19.1), ylim=c(-32.5,-31.6),
xlab="Longitude", ylab="Latitude"
)
polygon(dd[,"Lon"],dd[,"Lat"], border="red")
library(sp)
p = Polygon(dd)
p2 = Polygons(list(p),1) # I believe this aggregates polygons, so in this case it doesn't do anything.
sp = SpatialPolygons(list(p2))
sps <- smooth(sp, method = "ksmooth", smoothness=0.7)
plot(sps, add=T, border="blue")
I have a raster and some points. I want to snap the points closest to the raster based on some general conditions.
library(raster)
##create a diagonal matrix
xy = diag(1, 100, 100)
# Turn the matrix into a raster
rast <- raster(xy)
# Give it lat/lon coords
extent(rast) <- c(-180,180,-90,90)
# ... and assign a projection
projection(rast) <- CRS("+proj=longlat +datum=WGS84")
##create two points just for reference
lonlat <- data.frame(x = c(50,130), y = c(75,-50))
coordinates(lonlat)<-~x+y
crs(lonlat)<- CRS("+proj=longlat +datum=WGS84")
plot(rast)
plot(lonlat,add=T)
It results in
Now I want to snap the points(+) on the green diagonal line. Here, I have provided a diagonal matrix to make it easy but it could be of any shape (for instance curved shape like rivers).
I have found some methods which only snaps the closest raster grid to the points.
##snap raster grid closest to point
Idx = sapply(lonlat$x,function(i) which.min(abs(unique(rasterToPoints(rast, spatial = TRUE)#coords[,1])-i)))
Idy = sapply(lonlat$y,function(i) which.min(abs(unique(rasterToPoints(rast, spatial = TRUE)#coords[,2])-i)))
I basically want two things (a) snap the closest point based on some simple condition ( rast == 1 ). (b) snap the points based on some search radius (lets say neighboring some points).
I'm trying to find the Radii on this map that intercept state borders in R.
Here is my code so far. Thanks to user Gregoire Vincke for providing much of the solution.
library("maps")
library("mapproj")
library("RColorBrewer")
library("mapdata")
library("ggplot2")
library("rgeos")
library("dismo")
library("ggmap")
library("rgdal")
data("stateMapEnv") #US state map
dat <- read.csv("R/longlat.csv",header = T)
map('state',fill = T, col = brewer.pal(9,"Pastel2"))
#draws circles around a point, given lat, long and radius
plotCircle <- function(lonDec, latDec, mile) {
ER <- 3959
angdeg <- seq(1:360)
lat1rad <- latDec*(pi/180)
lon1rad <- lonDec*(pi/180)
angrad <- angdeg*(pi/180)
lat2rad <- asin(sin(lat1rad)*cos(mile/ER) + cos(lat1rad)*sin(mile/ER)*cos(angrad))
lon2rad <- lon1rad + atan2(sin(angrad)*sin(mile/ER)*cos(lat1rad),cos(mile/ER)-sin(lat1rad)*sin(lat2rad))
lat2deg <- lat2rad*(180/pi)
lon2deg <- lon2rad*(180/pi)
polygon(lon2deg,lat2deg,lty = 1 , col = alpha("blue",0.35))
}
point <- mapproject(dat$lng,dat$lat)
points(point, col = alpha("black",0.90), cex = 0.4, pch = 20) #plots points
plotCircle(-71.4868,42.990684,20)
plotCircle(-72.57085,41.707932,12)
...
#this goes on for every point
I want to store the points that intercept state borders in a new data frame, any help would be appreciated!
EDIT: Here's a broad overview of the workflow using the geospatial analyses packages in R (sp, rgdal, rgeos).
Instead of using the maps package and stateMapEnv, you want a polygon shapefile of state boundaries, like one that can be found here:
https://www.census.gov/geo/maps-data/data/cbf/cbf_state.html
You can then load that shapefile in R with readOGR from the rgdal package to get a SpatialPolygons (let's call it state_poly) with one Polygons object per state.
Create a SpatialPoints object from your long/lat coordinates:
pts <- SpatialPoints(dat[, c("lng", "lat")], proj4string = CRS("+proj=longlat"))
At this point your pts and state_poly should be in longitude/latitude coordinates, but to draw circles of a fixed radius around points, you need to convert them to projected coordinates (i.e. in meters). See this question for more details:
Buffer (geo)spatial points in R with gbuffer
Create a vector with the radii of your circles around each point, and use it with gBuffer (from rgeos) and your points layer:
circ <- gBuffer(pts, width = radii, byid = TRUE)
The byid argument means it does it separately for each point, using the different values in radii in the same order as the points.
Convert the state polygons to lines: state_lines <- as(state_poly, "SpatialLines")
Use gIntersects(circ, state_lines, byid = TRUE) .
Because of byid = TRUE, the return value is a matrix with one row per circle in your spgeom1 and one column per state boundaries in spgeom2. Note that if the circle intersect a boundary between two states, it should have two "TRUE" values in that row (one for each state). If it intersects with water or the external perimeter of the US it may have only one "TRUE" value in the row.
Here is the Final Code!
library("maps")
library("mapproj")
library("RColorBrewer")
library("mapdata")
library("ggplot2")
library("rgeos")
library("dismo")
library("ggmap")
library("rgdal")
#import shape file (.shp), make sure all the other files in the zip are included in
#your file location!
state_poly <- readOGR(dsn = 'C:/Users/chopp/Documents/R', layer='cb_2015_us_state_500k')
#data containing lng and lat coordinates with radii
data <- read.csv("R/longlat.csv", header = T)
#create spatial point objects out of your lng and lat data
pts <- SpatialPoints(data[,c("lng","lat")], proj4string = CRS("+proj=longlat"))
#convert spatial points to projected coordinates (points and map lines)
ptsproj <- spTransform(pts, CRS("+init=epsg:3347"))
state_poly_proj<- spTransform(state_poly, CRS("+init=epsg:3347"))
#convert radii units to meters, used in our gBuffer argument later on
radii <- data$rad*1609.344
#create circular polygons with. byid = TRUE will create a circle for each point
circ <- gBuffer(ptsproj, width = radii, byid = TRUE)
#convert state polygons to state lines
state_lines<- as(state_poly_proj, "SpatialLines")
#use gIntersects with byid = TRUE to return a matrix where "TRUE" represents
#crossing state boundaries or water
intdata <- gIntersects(circ, state_lines, byid = TRUE)
#write the matrix out into a csv file
write.csv(intdata,"R/Agents Intercepts 2.csv")
I retrieved a map to plot points on using the (RgoogleMaps) package
using the following Rcode:
library(RgoogleMaps)
lat = c(-30.3022,-30.5000,-33.48569)
lon = c(153.1189,151.6500,145.5316)
center = c(mean(lat), mean(lon))
zoom <- min(MaxZoom(range(lat), range(lon)))
mymap <- GetMap(center=center, zoom=zoom, maptype= "terrain", destfile = "MyTile1.png")
I've also successfully plotted my 3 points on that map using:
NewMap <- PlotOnStaticMap(mymap, lat = c(-30.3022,-30.5000,-32.24300),
lon = c(153.1189,151.6500,148.6019), destfile = "MyTile1.png", cex=1.5,pch=20,
col=c('red', 'purple', 'green'), add=FALSE)
Now I need to over-lay a grid of Lat-long values, or even just a grid of any
type. Any Ideas?
I've done some fairly extensive research and it appears that (RgoogleMaps)
doesn't have a simple way of doing this.
Thanks,
D.A.S.
You can use dismo::gmap for this. It returns a google map as a RasterLayer and you can use it to overlay Spatial* objects (sp package), e.g. SpatialPolygons or SpatialLines, and Raster* objects (raster objects), or just use points or lines
library(dismo)
lat = c(-30.3022,-30.5000,-33.48569)
lon = c(153.1189,151.6500,145.5316)
xy <- cbind(lon, lat)
g <- gmap(xy, lonlat=TRUE, scale=2)
plot(g, interpolate=TRUE)
points(xy, col='red', pch=20)
I am having issues plotting true to geographic extent pixels in R. the files come with a list of daily single coordinates and pixel size (area). There is also a Z element separate from this. The data structure looks this way:
X <- c(1,3,6,7)
Y <- c(3,2,7,8)
Z <- c(38,23,12,12)
Area <- c(32,23,45,67)
The X and Y are in degrees longitude and latitude while the area is in square kilometres. I create the point features easily using:
library(sp)
A <- cbind(X,Y,Z,Area)
B <- SpatialPoints(A)
I plot these easily using the area values to determine the "cex" for plotting. The Z column is intensity and I use these values to determine the colours . How do I create spatial polygons features using the areas for each point in R? I would be using these points to create gridded rasters.
This should do the trick:
library(rgeos) ## for gBuffer()
library(raster) ## for bind()
ww <- sqrt(B$Area)/2 ## Widths of buffers needed to produce desired areas
pp <- list()
for(i in seq_along(B)) {
pp[i] <- gBuffer(B[i], width=ww[i], quadsegs=1, capStyle="SQUARE")
}
PP <- do.call(bind, pp)
## Check that it worked
plot(PP)
plot(B, add=TRUE)
text(B, labels=1:4, adj=c(-1,0), col="red")
Just to update this a bit, here is an sf solution.
Function to create the square buffers
bSquare <- function(x, a) {
a <- sqrt(a)/2
return( sf::st_buffer(x, dist = a, nQuadSegs=1,
endCapStyle = "SQUARE") )
}
Some example data
library(sf)
xy <- st_as_sf(data.frame(x = c(1,3,6,7),
y = c(3,2,7,8), z = c(38,23,12,12),
area = c(32,23,45,67)),
coords = c("x", "y"),
agr = "constant")
With variable buffer
sb.var <- bSquare(xy, xy$area)
plot(st_geometry(sb.var))
plot(st_geometry(xy), pch=20, add=TRUE)
With fixed buffer
sb <- bSquare(xy, 32)
plot(st_geometry(sb))
plot(st_geometry(xy), pch=20, add=TRUE)