I want to plot rivers (lines) in a map containing polygons (counties, etc) from South Dakota. The river data is here, https://www.weather.gov/gis/Rivers. Use the subset of rivers data set. The county download can be obtained from here, https://www2.census.gov/geo/tiger/TIGER2020/COUNTY/.
I only want the rivers that lie within the county boundaries of South Dakota, so I am using rgeos::intersection to perform that, which produces a Large SpatialLines object, which ggplot2 doesn't like when I try to plot it with geom_line (I get an error that says "Error: data
must be a data frame, or other object coercible by fortify()
, not an S4 object with class SpatialLines.")
Here is my code:
library(rgdal)
library(raster)
counties <- readOGR('D:\\Shapefiles\\Counties\\tl_2020_us_county.shp')
counties <- counties[which(counties$STATEFP == '46'),]
counties <- spTransform(counties, CRS("+init=epsg:3395"))
rivers <- readOGR('D:\\Shapefiles\\Main_Rivers\\rs16my07.shp')
proj4string(rivers) <- CRS("+proj=longlat")
rivers <- spTransform(rivers, CRS("+init=epsg:3395"))
rivers <- as.SpatialLines.SLDF(rgeos::gIntersection(counties, rivers))
The raster packages "intersect" function does not work for doing the intersection. I think I need to change the SpatialLines object to a spatialLinesDataFrame object to get ggplot2 to plot the rivers. How do I do that? The as.SpatialLines.SLDF function is not doing it. Is there another way to get this to plot? My plotting code is here:
ggplot() +
geom_path(counties, mapping = aes(x = long, y = lat, group = group, col = 'darkgreen')) +
geom_path(rivers, mapping = aes(x = long, y = lat, color = 'blue'))
I would recommend handling your spatial data with the sf
library. Firstly, it plays well with ggplot
. Also, according to my very much infant understanding of GIS and spatial data in R, I believe that the idea is the sf
will eventually take over from sp
and the Spatial*
data formats. sf
is I think a standard format across multiple platforms. See this link for more details on sf
.
Onto your question - this is quite simple using sf
. To find the rivers inside a specific county, we use st_intersection()
(the sf
version of gIntersection
).
library(sf)
# read in the rivers data
st_read(dsn = 'so_data/rs16my07', layer = 'rs16my07') %>%
{. ->> my_rivers}
# set the CRS for the rivers data
st_crs(my_rivers) <- crs('+proj=longlat')
# transform crs
my_rivers %>%
st_transform('+init=epsg:3395') %>%
{. ->> my_rivers_trans}
# read in counties data
st_read(dsn = 'so_data/tl_2020_us_county') %>%
{. ->> my_counties}
# keep state 46
my_counties %>%
filter(
STATEFP == 46
) %>%
{. ->> state_46}
# transform crs
state_46 %>%
st_transform('+init=epsg:3395') %>%
{. ->> state_46_trans}
# keep only rivers inside state 46
my_rivers_trans %>%
st_intersection(state_46_trans) %>%
{. ->> my_rivers_46}
Then we can plot the sf
objects using ggplot
and geom_sf()
, just like you would plot lines using geom_line()
etc. geom_sf()
seems to know if you are plotting point data, line data or polygon data, and plots accordingly. It is quite easy to use.
# plot it
state_46_trans %>%
ggplot()+
geom_sf()+
geom_sf(data = my_rivers_46, colour = 'red')
Hopefully this looks right - I don't know my US states so have no idea if this is South Dakota or not.