How to read & plot from .shp files using sf package in r? - r

I am new to geospatial data & trying to plot using .shp file but getting an error.
The geometry type in this .shp file is LINESTRING which seems to be different from MULTIPOLYGON which I have plotted before using sf
shape file source: https://github.com/johnsnow09/covid19-df_stack-code/blob/main/in_country_boundaries.shp
original source of shapefile: https://github.com/wri/wri-bounds/blob/master/dist/in_countries.zip
Expecting result:
code attempt:
library(tidyverse)
library(sf)
ind_global <- sf::read_sf("path/in_country_boundaries.shp")
ind_global
output
Simple feature collection with 412 features and 9 fields
Geometry type: LINESTRING
Dimension: XY
Bounding box: xmin: -141.0056 ymin: -54.88624 xmax: 140.9776 ymax: 70.07531
Geodetic CRS: WGS 84
ind_global %>%
st_as_sf() %>%
ggplot() +
geom_sf()
Error in st_cast.POINT(X[[i]], ...) : cannot create MULTILINESTRING
from POINT
Do I need to handle LINESTRING geometry .shp file in some other way?

The code is running fine after removing st_as_sf() %>%. I have downloaded the shapefile from https://github.com/wri/wri-bounds/blob/master/dist/in_countries.zip and it is MULTIPOLYGON only.
library(tidyverse)
library(sf)
ind_global <- sf::read_sf("...\\in_countries.shp")
ind_global
ind_global %>%
ggplot() +
geom_sf()

Related

Create random points over a line spatvector in R

I have a spatVector composed of a single-line geometry that covers the entire road network of my study area.
I would like to create a set of N random points over this geometry. I know how to do it in QGIS but I want to do it in R since I have to iterate this process 1'000 times and I want to create a loop.
Do you know any function to do this?
EDIT
First of all, I read my line shapefile using:
Road_network <- vect("path/to/file.shp)
Then I converted it into an SF object:
Road_network_SF <- st_as_sf(Road_network)
And finally, I use both the st_sample, getting the following results:
Random_points <- st_sample(Road_network_SF, size = 1799)
Random_points
Geometry set for 46350 features (with 44694 geometries empty)
Geometry type: MULTIPOINT
Dimension: XY
Bounding box: xmin: 4503139 ymin: 2504751 xmax: 4622797 ymax: 2613276
Projected CRS: ETRS89-extended / LAEA Europe
First 5 geometries:
MULTIPOINT EMPTY
MULTIPOINT EMPTY
MULTIPOINT EMPTY
MULTIPOINT ((4503139 2574957))
MULTIPOINT EMPTY
and the st_line_sample function, getting the following error:
Random_points <- st_line_sample(Road_network_SF, n = 1799)
Error in st_line_sample(Road_network_SF, n = 1799) :
inherits(x, "sfc_LINESTRING") non รจ TRUE
When I converted the spatVector to an sf object, this is what I get:
Road_network_SF
Simple feature collection with 1 feature and 2 fields
Geometry type: MULTILINESTRING
Dimension: XY
Bounding box: xmin: 4500176 ymin: 2504157 xmax: 4626207 ymax: 2616041
Projected CRS: ETRS89-extended / LAEA Europe
FURTHER EDIT
The workflow proposed by #Gregory work really good, my error was due to a problem with the road shapefile. I changed it and no further problems occurred, thank you!
Thanks in advance!
You can sample random points along a vector geometry (like roads) with sf::st_sample(), however the results might seem confusing depending on how you look at them. Here's a reproducible example.
library(sf, quietly = TRUE)
#> Linking to GEOS 3.10.2, GDAL 3.4.2, PROJ 8.2.1; sf_use_s2() is TRUE
library(tigris, quietly = TRUE)
#> To enable
#> caching of data, set `options(tigris_use_cache = TRUE)` in your R script or .Rprofile.
library(ggplot2)
suppressMessages(
roads <- roads(state = "NC",
county = "Mecklenburg")
)
set.seed(1)
rpoints <- st_sample(roads, size = 5)
#> although coordinates are longitude/latitude, st_sample assumes that they are
#> planar
ggplot() +
geom_sf(data = roads, color = "grey") +
geom_sf(data = rpoints, color = "black")
We see on the map that we have generated 5 random points, as intended. Surprisingly, if you examine the structure of the rpoints object you'll see that it is a multipoint of length 21672, which you might think is the number of points. However, all but 5 of them have empty geometries. The reason is that there is a geometry (empty for most) for each of the objects that makes up the roads vector.
str(rpoints)
#> sfc_MULTIPOINT of length 21672; first list element: 'XY' num[0 , 1:2] MULTIPOINT EMPTY
head(rpoints)
#> Geometry set for 6 features (with 6 geometries empty)
#> Geometry type: MULTIPOINT
#> Dimension: XY
#> Bounding box: xmin: NA ymin: NA xmax: NA ymax: NA
#> Geodetic CRS: NAD83
#> First 5 geometries:
#> MULTIPOINT EMPTY
#> MULTIPOINT EMPTY
#> MULTIPOINT EMPTY
#> MULTIPOINT EMPTY
#> MULTIPOINT EMPTY
Here's how to get the real points out of there.
rpoints <- rpoints[!st_is_empty(rpoints)]
rpoints
#> Geometry set for 5 features
#> Geometry type: MULTIPOINT
#> Dimension: XY
#> Bounding box: xmin: -81.01691 ymin: 35.07471 xmax: -80.62246 ymax: 35.2948
#> Geodetic CRS: NAD83
#> MULTIPOINT ((-80.88764 35.2948))
#> MULTIPOINT ((-80.62246 35.18395))
#> MULTIPOINT ((-81.01691 35.07471))
#> MULTIPOINT ((-80.78909 35.12663))
#> MULTIPOINT ((-80.83055 35.16959))
Created on 2023-02-01 by the reprex package (v2.0.1)

How to dissolve separated polygons into a large one?

I have a shape file that I can read like this in R:
library(rgdal)
shape <- readOGR(dsn = "~/path", layer = "a")
I am interested in the whole region that cover all polygons (black curve here). How to dissolve all polygons even those separated into one polygon like this?
I am open to solutions from R or Qgis
Using R & the sf package you can make a convex hull of the unioned (if necessary) shapefile. Since you haven't included data, I've used the nc data included with the sf package to illustrate the method.
library(dplyr)
library(sf)
library(ggplot2)
# setting up sample data,
# you'll need to use st_read() to read your shapefile, not readOGR()
nc <- st_read(system.file("shape/nc.shp", package="sf"))
#> Reading layer `nc' from data source
#> `.../sf/shape/nc.shp'
#> using driver `ESRI Shapefile'
#> Simple feature collection with 100 features and 14 fields
#> Geometry type: MULTIPOLYGON
#> Dimension: XY
#> Bounding box: xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965
#> Geodetic CRS: NAD27
nc <- nc[c(1:30, 85:81),] #Use some non-contiguous counties
# make a convex hull of the unioned geometries
nc_hull <- st_convex_hull(st_union(nc))
ggplot() +
geom_sf(data = nc, fill = NA, color = 'red') +
geom_sf(data = nc_hull, fill = NA, color = 'black')
Created on 2022-03-18 by the reprex package (v2.0.1)

Spatial join longitude and latitude points to multipolygon shapefile

I have a spatial data frame of the longitude and latitude of wildfire origins that I am trying to perform a spatial join on to a US Census TIGER/Line shapefile (places) to see if/where there is spatial intersection of fire origins and places.
I am converting the longitude and latitude to coordinate geometry using st_as_sf then attempting to st_join this to the places file, but am encountering an error as the CRS are different. The shapefile is in NAD83 projection, so I am attempting to match that.
library(tidyverse)
library(sf)
> head(fires)
# Longitude Latitude FireName
#1 -106.46667 34.66000 TRIGO
#2 -81.92972 35.87111 SUNRISE
#3 -103.76944 37.52694 BRIDGER
#4 -122.97556 39.37500 BACK
#5 -121.15611 39.62778 FREY
#6 -106.38306 34.77056 BIG SPRING
#convert df to sf
fires_sf <- st_as_sf(fires, coords = c("Longitude", "Latitude"), crs = 4269, agr = "constant")
head(fires_sf$geometry)
#Geometry set for 6 features
#Geometry type: POINT
#Dimension: XY
#Bounding box: xmin: -122.9756 ymin: 34.66 xmax: -81.92972 ymax: 39.62778
#Geodetic CRS: NAD83
#POINT (-106.4667 34.66)
#POINT (-81.92972 35.87111)
#POINT (-103.7694 37.52694)
#POINT (-122.9756 39.375)
#POINT (-121.1561 39.62778)
head(places$geometry)
#Geometry set for 6 features
#Geometry type: MULTIPOLYGON
#Dimension: XY
#Bounding box: xmin: -1746916 ymin: -395761.6 xmax: -1655669 ymax: -212934.8
#Projected CRS: USA_Contiguous_Albers_Equal_Area_Conic
#First 5 geometries:
#MULTIPOLYGON (((-1657066 -233757.7, -1657192 -2...
#MULTIPOLYGON (((-1668181 -273428.5, -1669420 -2...
#MULTIPOLYGON (((-1735046 -389578.2, -1735146 -3...
#MULTIPOLYGON (((-1732841 -376703.9, -1732642 -3...
#MULTIPOLYGON (((-1693749 -377716, -1693286 -377..
joined <- st_join(places, fires_sf)
Error in st_geos_binop("intersects", x, y, sparse = sparse, prepared = prepared, :
st_crs(x) == st_crs(y) is not TRUE
To work around this, I have tried st_transform to change the projection to longitude and latitude coordinates, as the places shapefile may be using UTM coordinates, and the datum to NAD83 in both spatial frames. I am getting an error for this as well.
#transform CRS projections
places_transform <- st_transform(places, "+proj=longlat +datum=NAD83")
fires_sf_transform <- st_transform(fires_sf, "+proj=longlat +datum=NAD83")
joined_new <- st_join(places_transform, fires_sf_transform)
Error in s2_geography_from_wkb(x, oriented = oriented, check = check) :
Evaluation error: Found 1045 features with invalid spherical geometry.
[3] Loop 0 is not valid: Edge 280 has duplicate vertex with edge 306
I have attempted to convert the geometry from longitude and latitude coordinates in the fires dataset to UTM coordinates to match the places shapefile, but this was also unsuccessful.
Any advice on how I can properly perform the spatial join of these points and multipolygons would be greatly appreciated.

Reverse coordinates for (multi-) polygon

I'm working with shapefiles of subdivisions of a number of countries, and for one country (Iceland), the X and Y coordinates appear to be swapped around in the shapefile.
The data can be downloaded here: shapefile data; IS_50V:mork_kjordaemi is the relevant dataset, select the "shape-zip" option in the download dropdown menu.
I've been using the "sf" package in R for all the shapefile work, and it has worked flawlessly with all the other shapefile data I have.
library(sf)
ic_2003 <- downloaded_data
st_crs(ic_2003) gives me
Coordinate Reference System:
User input: ISN2016
wkt:
GEOGCRS["ISN2016",
DATUM["Islands Net 2016",
ELLIPSOID["GRS 1980",6378137,298.257222101,
LENGTHUNIT["metre",1]]],
PRIMEM["Greenwich",0,
ANGLEUNIT["degree",0.0174532925199433]],
CS[ellipsoidal,2],
AXIS["geodetic latitude (Lat)",north,
ORDER[1],
ANGLEUNIT["degree",0.0174532925199433]],
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
ANGLEUNIT["degree",0.0174532925199433]],
USAGE[
SCOPE["unknown"],
AREA["Iceland"],
BBOX[59.96,-30.87,69.59,-5.55]],
ID["EPSG",8086]]
head(ic_2003) gives me
Simple feature collection with 6 features and 15 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: 63.29577 ymin: -24.53268 xmax: 66.56644 ymax: -13.49462
Geodetic CRS: ISN2016
I've tried ic_2003 <- st_transform(ic_2003, 4326) but this doesn't fix the problem.
I've also tried ic_2003 <- st_transform(ic_2003, pipeline = "+proj=pipeline +step +proj=axisswap +order=2,1"), as done here , but this also does not solve the issue.
If I plot the data
ggplot(ic_2003) +
geom_sf() +
coord_sf()
I get the right shape, but rotated 90 degrees and in the wrong place on a world map.
Any help you could give me would be greatly appreciated.
There must be a sf way of doing this easily, but you can also use purrr::modify (which works like map) to swap all of the geometry lat/lon columns (a matrix within a list within a list within a list) without changing the sf attributes...
library(sf)
library(tidyverse)
ic_2003 <- st_read("mork_kjordaemiPolygon.shp") #from link above
ic_2003 <- ic_2003 %>%
mutate(geometry = modify(geometry, modify, ~list(.[[1]][,c(2,1)])))
ggplot(ic_2003) +
geom_sf() +
coord_sf()

How to manipulate the geometry of a shapefile polygon using sf in R

I have two shapefile polygons, one with a defined CRS (shapefile A) and another with an undefined CRS (shapefile B). I have lat/long coordinates with geometries similar to that of shapefile A. However, I want to count these points in the polygons of shapefile B. I realize that simply setting the CRS in shapefile B to that of shapefile A does not solve the problem as the geometries are different.
How do I go about modifying the geometry of shapefile B so that it matches with shapefile A? The closest help I found was to apply an affine transformation on the polygons, but I'm not sure how to go about doing so, if that is the correct method.
Here are some descriptions of the two shapefiles (note the bbox and CRS fields in particular):
Shapefile A
Simple feature collection with 61 features and 29 fields
geometry type: MULTIPOLYGON
dimension: XY
bbox: xmin: -61.93033 ymin: 10.04404 xmax: -60.90916 ymax: 10.84451
geographic CRS: WGS 84
Shapefile B
Simple feature collection with 534 features and 13 fields
geometry type: POLYGON
dimension: XY
bbox: xmin: 617063.2 ymin: 1110279 xmax: 728431.5 ymax: 1199347
CRS: NA
I imagine that I would need to multiply the geometries of shapefile B by -10^-4 for the x coordinates and 10^-5 for the y coordinates but I'm not sure if the negative will reflect the shapefile as well. If so, is it still possible to apply another viable transformation?

Resources