I have a shapefile (with several polygons) and a dataframe with coordinates. I want to assign each coordinate in a dataframe to a polygon in a shapefile. So to add a column in a data frame with a polygon name or id
Here is the link to the data
library(sf)
library(readr)
shape <- read_sf("data/Provinces_v1_2017.shp")
data<- read_csv("data/data.csv")
But when I try to join them, I always get the error
pts = st_as_sf(data, coords = c("dec_lon", "dec_lat"), crs= 4326)
st_join(pts, shape)
i tried over() functions, and other tricks like st_make_valid() but I always get this error:
Error in s2_geography_from_wkb(x, oriented = oriented, check = check) : Evaluation error: Found 30 features with invalid spherical geometry.
It is a recent issue (before my code worked), but now I am unable to use sf package to do this task, I always end up with this error. I updated the libraries to see whether it would help, but I could not make it work.
I would really appreciate your help on this matter
You have two options:
turn off the s2 processing via sf::sf_use_s2(FALSE) in your script; in theory the behaviour should revert to the one before release 1.0
repair the spherical geometry of your polygons object; this will depend on the actual nature of your errors.
I can't access your file & make certain, but this piece of code has helped me in the past:
yer_object$geometry <- yer_object$geometry %>%
s2::s2_rebuild() %>%
sf::st_as_sfc()
I find that this 'invalid spherical geometry' does keep on popping up. If the s2::s2_rebuild() solution above doesn't work, a solution which usually works for me involves projecting and simplifying (reducing the map resolution a little). If your application can work with less resolution try this.
library(tidyverse)
library(sf)
crs_N = 3995 #northern polar projection
# example of FAILING map - with bad spherical geometry.
m_RU <- rnaturalearthdata::countries50 %>%
st_as_sf() %>%
filter((admin %in% c("Russia") )) |>
st_as_s2()
In the example, I chose Russia because it crosses the dateline, which can be one of the challenges. I switch to an Arctic polar projection, and reduce the map to 10km resolution (5km is not enough in this case!).
# with 2 extra lines the problem is gone
m_RU <- rnaturalearthdata::countries50 %>%
st_as_sf() %>%
filter((admin %in% c("Russia") )) |>
st_transform(crs = crs_N) |>
st_simplify(dTolerance = 10000) |> # to get rid of duplicate vertex (reduce to 10km steps)
st_as_s2()
Related
I have a shapefile (with several polygons) and a dataframe with coordinates. I want to assign each coordinate in a dataframe to a polygon in a shapefile. So to add a column in a data frame with a polygon name or id
Here is the link to the data
library(sf)
library(readr)
shape <- read_sf("data/Provinces_v1_2017.shp")
data<- read_csv("data/data.csv")
But when I try to join them, I always get the error
pts = st_as_sf(data, coords = c("dec_lon", "dec_lat"), crs= 4326)
st_join(pts, shape)
i tried over() functions, and other tricks like st_make_valid() but I always get this error:
Error in s2_geography_from_wkb(x, oriented = oriented, check = check) : Evaluation error: Found 30 features with invalid spherical geometry.
It is a recent issue (before my code worked), but now I am unable to use sf package to do this task, I always end up with this error. I updated the libraries to see whether it would help, but I could not make it work.
I would really appreciate your help on this matter
You have two options:
turn off the s2 processing via sf::sf_use_s2(FALSE) in your script; in theory the behaviour should revert to the one before release 1.0
repair the spherical geometry of your polygons object; this will depend on the actual nature of your errors.
I can't access your file & make certain, but this piece of code has helped me in the past:
yer_object$geometry <- yer_object$geometry %>%
s2::s2_rebuild() %>%
sf::st_as_sfc()
I find that this 'invalid spherical geometry' does keep on popping up. If the s2::s2_rebuild() solution above doesn't work, a solution which usually works for me involves projecting and simplifying (reducing the map resolution a little). If your application can work with less resolution try this.
library(tidyverse)
library(sf)
crs_N = 3995 #northern polar projection
# example of FAILING map - with bad spherical geometry.
m_RU <- rnaturalearthdata::countries50 %>%
st_as_sf() %>%
filter((admin %in% c("Russia") )) |>
st_as_s2()
In the example, I chose Russia because it crosses the dateline, which can be one of the challenges. I switch to an Arctic polar projection, and reduce the map to 10km resolution (5km is not enough in this case!).
# with 2 extra lines the problem is gone
m_RU <- rnaturalearthdata::countries50 %>%
st_as_sf() %>%
filter((admin %in% c("Russia") )) |>
st_transform(crs = crs_N) |>
st_simplify(dTolerance = 10000) |> # to get rid of duplicate vertex (reduce to 10km steps)
st_as_s2()
I want to make a world map with a voronoi tessellation using the spherical nature of the world (not a projection of it), similar to this using D3.js, but with R.
As I understand ("Goodbye flat Earth, welcome S2 spherical geometry") the sf package is now fully based on the s2 package and should perform as I needed. But I don't think that I am getting the results as expected. A reproducible example:
library(tidyverse)
library(sf)
library(rnaturalearth)
library(tidygeocoder)
# just to be sure
sf::sf_use_s2(TRUE)
# download map
world_map <- rnaturalearth::ne_countries(
scale = 'small',
type = 'map_units',
returnclass = 'sf')
# addresses that you want to find lat long and to become centroids of the voronoi tessellation
addresses <- tribble(
~addr,
"Juneau, Alaska" ,
"Saint Petersburg, Russia" ,
"Melbourne, Australia"
)
# retrive lat long using tidygeocoder
points <- addresses %>%
tidygeocoder::geocode(addr, method = 'osm')
# Transform lat long in a single geometry point and join with sf-base of the world
points <- points %>%
dplyr::rowwise() %>%
dplyr::mutate(point = list(sf::st_point(c(long, lat)))) %>%
sf::st_as_sf() %>%
sf::st_set_crs(4326)
# voronoi tessellation
voronoi <- sf::st_voronoi(sf::st_union( points ) ) %>%
sf::st_as_sf() %>%
sf::st_set_crs(4326)
# plot
ggplot2::ggplot() +
geom_sf(data = world_map,
mapping = aes(geometry = geometry),
fill = "gray95") +
geom_sf(data = points,
mapping = aes(geometry = point),
colour = "red") +
geom_sf(data = voronoi,
mapping = aes(geometry = x),
colour = "red",
alpha = 0.5)
The whole Antarctica should be closer to Melbourne than to the other two points. What am I missing here? How to calculate a voronoi on a sphere using sf?
(This answer doesn't tell you how to do it, but does tell you what's going wrong.)
When I ran this code I got
Warning message:
In st_voronoi.sfc(sf::st_union(points)) :
st_voronoi does not correctly triangulate longitude/latitude data
From digging into the code it looks like this is a known limitation. Looking at the C++ code for CPL_geos_voronoi, it looks like it directly calls a GEOS method for building Voronoi diagrams. It might be worth opening an sf issue to indicate that this is a feature you would value (if no-one tells the developer that particular features would be useful, they don't get prioritized ...) It doesn't surprise me that GEOS doesn't automatically do computations that account for spherical geometry. Although the S2 code base mentions Voronoi diagrams in a variety of places, it doesn't look like there is a drop-in replacement for the GEOS algorithm ... there are a variety of implementations in other languages for spherical Voronoi diagrams (e.g. Python), but someone would probably have to port them to R (or C++) ...
If I really needed to do this I would probably try to figure out how to call the Python code from within R (exporting the data from sf format to whatever Python needs, then re-importing the results into an appropriate sf format ...)
Printing the code for sf:::st_voronoi.sfc:
function (x, envelope = st_polygon(), dTolerance = 0, bOnlyEdges = FALSE)
{
if (compareVersion(CPL_geos_version(), "3.5.0") > -1) {
if (isTRUE(st_is_longlat(x)))
warning("st_voronoi does not correctly triangulate longitude/latitude data")
st_sfc(CPL_geos_voronoi(x, st_sfc(envelope), dTolerance = dTolerance,
bOnlyEdges = as.integer(bOnlyEdges)))
}
else stop("for voronoi, GEOS version 3.5.0 or higher is required")
}
In other words, if the GEOS version is less than 3.5.0, the operation fails completely. If it is >= 3.5.0 (sf:::CPL_geos_version() reports that I have version 3.8.1), and long-lat data is being used, a warning is supposed to be issued (but the computation is done anyway).
The first time I ran this I didn't get the warning; I checked and options("warn") was set to -1 (suppressing warnings). I'm not sure why — running from a clean session did give me the warning. Maybe something in the pipeline (e.g. rnaturalearth telling me I needed to install the rnaturalearthdata package) accidentally set the option?
I need to extend the boundary of a field (only boundary) by x meters. I tried using gBuffer from rgeos R package - output of the transformation gives me only boundary of the field and rest polygons inside the field are lost with data.
How I can use gBuffer / any other way to extend only boundary of spatial polygon object (shape file) by 10m and keeping everything intact (inside polygons and data)
Tried Code -
field <- raster::shapefile("test.shp")
class(field)
plot(field)
View(field#data)
field <- sp::spTransform(field,CRS("+init=epsg:32632"))
plot(field)
field10m <- rgeos::gBuffer(field , width = 10)
plot(field10m)
Test shapefile can be downloaded from here https://drive.google.com/file/d/1s4NAinDeBow95hxr6gELHHkhwiR3z6Z9/view?usp=sharing
I suggest you consider a workflow based on the {sf} package; it makes for a bit clearer code than sp and rgeos (it will use the same geometry engine, but hide the gritty parts under the hood).
The code preserves all data features (actually, only one - a column named Rx) of your shapefile.
Note that since the yellow element / Rx = 120 / consists of multiple polygons each of them is buffered, resulting in overlaid features. This is expected outcome.
Should this be undesired behavior you can consider using a dplyr::group_by(Rx) followed by dplyr::summarise() to dissolve the internal boundary lines before applying the sf::st_buffer() call.
library(sf)
library(dplyr)
library(mapview) # needed only for the final overview
library(leafsync) # dtto.
test_map <- sf::st_read("./Map/test.shp")
# find an appropriate projected metric CRS
crsuggest::suggest_crs(test_map, type = "projected")
result <- test_map %>%
sf::st_transform(5683) %>% # transform to a metric CRS
sf::st_buffer(10) # buffer by 10 meters
# a visual check / note how the polygons are overlaid
leafsync::latticeview(mapview::mapview(test_map),
mapview::mapview(result))
Using the R package sf, I'm trying to determine whether some points occur within the bounds of a shapefile (in this case, Hawai‘i's, EEZ). The shapefile in question can be found here. Unfortunately, the boundaries of the area in question span +/-180 longitude, which I think is what's messing me up. (I read on the sf website some business about spherical geometry in the new version, but I haven't been able to get that version to install. I think the polygons I'm dealing with are sufficiently "flat" to avoid any of those issues anyway). Part of the issue seems to be that my shapefile contains multiple geometries broken up by the dateline but I'm not sure how to combine them.
How do you tell, using sf, whether some points are inside of the bounds of some object in a shapefile (that happens to span the dateline)?
I have tried various combinations of st_shift_longitude to no avail. I have also tried transforming to what I think is a planar projection (2163), and that didn't work.
Here's how I'm currently trying to do this:
library(sf)
library(maps)
library(ggplot2)
library(tidyverse)
# this is the shapefile from the link above
eez_unshifted <- read_sf("USMaritimeLimitsAndBoundariesSHP/USMaritimeLimitsNBoundaries.shp") %>%
filter(OBJECTID == 1206) %>%
st_transform(4326)
eez_shifted <- read_sf("USMaritimeLimitsAndBoundariesSHP/USMaritimeLimitsNBoundaries.shp") %>%
filter(OBJECTID == 1206) %>%
st_transform(4326) %>%
st_shift_longitude()
# four points, in and out of the geometry, on either side of the dateline
pnts <- tibble(x=c(-171.952474,176.251978,179.006220,-167.922929),y=c(25.561970,17.442716,28.463375,15.991429)) %>%
st_as_sf(coords=c('x','y'),crs=st_crs(eez_unshifted))
# these all return false for every point
st_within(pnts,eez_unshifted)
st_within(st_shift_longitude(pnts),eez_unshifted)
st_within(pnts,eez_shifted)
st_within(st_shift_longitude(pnts),eez_shifted)
# these also all return false for every point
st_intersects(pnts,eez_unshifted)
st_intersects(st_shift_longitude(pnts),eez_unshifted)
st_intersects(pnts,eez_shifted)
st_intersects(st_shift_longitude(pnts),eez_shifted)
# plot the data just to show that it looks right
wrld2 <- st_as_sf(maps::map('world2', plot=F, fill=T))
ggplot() +
geom_sf(data=wrld2, fill='gray20',color="lightgrey",size=0.07) +
geom_sf(data=eez_shifted) +
geom_sf(data=st_shift_longitude(pnts)) +
coord_sf(xlim=c(100,290), ylim=c(-60,60)) +
xlab("Longitude") +
ylab("Latitude")
The answer is to make sure the geometry you're checking against is a polygon:
> eez_poly <- st_polygonize(eez_shifted)
> st_within(pnts,eez_poly)
although coordinates are longitude/latitude, st_within assumes that they are planar
Sparse geometry binary predicate list of length 4, where the predicate was `within'
1: 1
2: (empty)
3: 1
4: (empty)
I am working with shapefiles in R, one is point.shp the other is a polygon.shp.
Now, I would like to intersect the points with the polygon, meaning that all the values from the polygon should be attached to the table of the point.shp.
I tried overlay() and spRbind in package sp, but nothing did what I expected them to do.
Could anyone give me a hint?
With the new sf package this is now fast and easy:
library(sf)
out <- st_intersection(points, poly)
Additional options
If you do not want all fields from the polygon added to the point feature, just call dplyr::select() on the polygon feature before:
library(magrittr)
library(dplyr)
library(sf)
poly %>%
select(column-name1, column-name2, etc.) -> poly
out <- st_intersection(points, poly)
If you encounter issues, make sure that your polygon is valid:
st_is_valid(poly)
If you see some FALSE outputs here, try to make it valid:
poly <- st_make_valid(poly)
Note that these 'valid' functions depend on a sf installation compiled with liblwgeom.
If you do overlay(pts, polys) where pts is a SpatialPointsDataFrame object and polys is a SpatialPolygonsDataFrame object then you get back a vector the same length as the points giving the row of the polygons data frame. So all you then need to do to combine the polygon data onto the points data frame is:
o = overlay(pts, polys)
pts#data = cbind(pts#data, polys[o,])
HOWEVER! If any of your points fall outside all your polygons, then overlay returns an NA, which will cause polys[o,] to fail, so either make sure all your points are inside polygons or you'll have to think of another way to assign values for points outside the polygon...
You do this in one line with point.in.poly fom spatialEco package.
library(spatialEco)
new_shape <- point.in.poly(pts, polys)
from the documentation: point.in.poly "intersects point and polygon feature classes and adds polygon attributes to points".