I have the following data:
dt1 <- data.table(
code=c("A00111", "A00112","A00113","A00211","A00212","A00213","A00214","A00311","A00312","A00472"),
x=c(325147,323095,596020,257409,241206,248371,261076,595218,596678,597678),
y=c(286151,284740,335814,079727,084266,078283,062045,333889,337836,339836),
point_id=c("P01","P02","P03","P04","P05","P06","P07","P08","P09","P10"))
sf1 <- st_as_sf(dt1, coords = c("x","y"), crs=27700, na.fail=FALSE)
I would like to create a single line that joins the points together based on point_id (so point P01 to P02 to P03 etc.) as a new sf object. I would then like to visualise this line using tmap by appending it to something like this:
tmap_mode("view")
map <- tm_shape(sf1) + tm_dots()
map
I've been developing the sfheaders library to help with this sort of thing
library(sfheaders)
library(sf)
## given a data.frame, call sf_linestring() and it will return an sf object of LINESTRINGS
sf <- sf_linestring(
obj = dt1
, x = "x"
, y = "y"
)
sf::st_crs( sf ) <- 27700
sf
# Simple feature collection with 1 feature and 1 field
# geometry type: LINESTRING
# dimension: XY
# bbox: xmin: 241206 ymin: 62045 xmax: 597678 ymax: 339836
# epsg (SRID): 27700
# proj4string: +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs
# id geometry
# 1 1 LINESTRING (325147 286151, ...
If you summarise your sf1 you get a MULTIPOINT-object. do_union = F tells R not to "union" but to "combine" them, which preserves the order of the points. So you order them by the id beforehand.
Finally you cast the MULTIPOINT to LINESTRING.
library(tmap)
library(sf)
sf1 %>%
summarise(do_union = F) %>%
st_cast("LINESTRING") -> sf_line
tm_shape(sf_line) + tm_lines()
Related
I have various points (2000+) for observation stations in the alps. I would like to use them to represent the closest geographic area, that is not closer to another observation station. I have done some research, and think that using Varanoi polygons may be the best way to do this.
After having attempting to build these in R, the polygon plot does not quite match my graphing in R.
I have attached the sample data points I am experimenting with, as well as the two images that show the dissimilar graphing of the points.
What do I need to be do differently to make sure that the points line up?
Points:
Longitude:
15.976667 12.846389 14.457222 13.795556 9.849167 16.055278 13.950833 15.666111 9.654722 15.596389 13.226667 15.106667 13.760000 12.226111 9.612222 17.025278 9.877500 15.368056 13.423056 12.571111 16.842222 13.711667 14.003056 12.308056 13.536389
Latitude:
48.40167 48.14889 47.56778 46.72750 47.45833 48.04472 47.82389 47.49472 47.35917 48.64917 48.25000 48.87139 47.87444 47.42806 47.20833 47.77556 47.40389 47.87583 47.53750 46.77694 47.74250 46.55000 48.37611 47.38333 47.91833
Pictures:
Map of the 25 sample points in Leaflet:
Voronoi plot:
Clearly these two are not the same images, so I must be doing something wrong. Here's the code I'm using to generate the Voronoi plot and the leaflet map.
meta25%>%
st_as_sf(coords = c("Longitude", "Latitude"),
crs = sp::CRS("+proj=longlat +datum=WGS84")) %>%
mapview()
m1 = matrix(meta25$Longitude,meta25$Latitude,ncol=2,nrow=25) %>% st_multipoint()
voronoi_grid <- st_voronoi(m1)
plot(voronoi_grid, col = NA)
plot(m1, add = TRUE, col = "blue", pch = 16)
I'm not sure what the problem is, but the matrix is not necessary. Stick to sf objects and you should be fine.
library(tidyverse)
library(sf)
# create pts from lat & lon data
pts <- tibble(latitude = y, longitude = x) %>%
st_as_sf(coords = c('latitude', 'longitude')) %>%
st_set_crs(4326)
# voronoi of pts
vor <- st_voronoi(st_combine(pts))
head(vor)
#> Geometry set for 1 feature
#> Geometry type: GEOMETRYCOLLECTION
#> Dimension: XY
#> Bounding box: xmin: 2.199166 ymin: 39.13694 xmax: 24.43833 ymax: 56.28445
#> Geodetic CRS: WGS 84
#> GEOMETRYCOLLECTION (POLYGON ((2.199166 49.37841...
# st_voronoi returns a GEOMETRYCOLLECTION,
# some plotting methods can't use a GEOMETRYCOLLECTION.
# this returns polygons instead
vor_poly <- st_collection_extract(vor)
head(vor_poly)
#> Geometry set for 6 features
#> Geometry type: POLYGON
#> Dimension: XY
#> Bounding box: xmin: 2.199166 ymin: 39.13694 xmax: 18.32787 ymax: 56.28445
#> Geodetic CRS: WGS 84
#> First 5 geometries:
#> POLYGON ((2.199166 49.37841, 2.199166 56.28445,...
#> POLYGON ((9.946349 39.13694, 2.199166 39.13694,...
#> POLYGON ((18.32787 39.13694, 11.64381 39.13694,...
#> POLYGON ((9.794868 47.23828, 9.766296 47.38061,...
#> POLYGON ((5.225657 56.28445, 9.393793 56.28445,...
plot(pts, col = 'blue', pch = 16)
plot(vor_poly, add = T, fill = NA)
Created on 2021-04-05 by the reprex package (v0.3.0)
Thanks everyone for your help, not sure if it got quite to where I was looking for. I've since adapted the answer from here: Creating bordering polygons from spatial point data for plotting in leaflet
I'd like to create a new shapefile or a new geometry variable that allows me to plot borders around regions in R. I'm using the sf and mapping with tmap. Basically, I'm adding a character vector to an sf object and would like to make the character vector the new/preferred mapping border.
Here is an example of my approach, which doesn't do what I'd like. I can't tell that it does anything.
library(tidyverse)
library(sf)
library(tmap)
## use North Carolina example
nc = st_read(system.file("shape/nc.shp", package="sf"))
nc_new.region <- nc %>% ## add new region variable
mutate(new.region = sample(c('A', 'B', 'C'), nrow(.),replace = T))
nc_union <- nc_new.region %>%
group_by(new.region) %>% # group by the new character vector
mutate(new_geometry = st_union(geometry)) # union on the geometry variable
# map with tmap package
tm_shape(nc_union)+
tm_borders()
This happens because mutate(new_geometry = st_union(geometry)) creates a "new" column within the original sf object, but plotting still uses the "original" geometry column. Indeed, if you have a look at your nc_union object, you'll see that it still contains 100 features (therefore, no "dissolving" was really done).
To do what you wish, you should instead create a "new" sf object using summarize over the groups:
library(tidyverse)
library(sf)
library(tmap)
## use North Carolina example
nc = st_read(system.file("shape/nc.shp", package="sf"))
#> Reading layer `nc' from data source `D:\Documents\R\win-library\3.5\sf\shape\nc.shp' using driver `ESRI Shapefile'
#> Simple feature collection with 100 features and 14 fields
#> geometry type: MULTIPOLYGON
#> dimension: XY
#> bbox: xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965
#> epsg (SRID): 4267
#> proj4string: +proj=longlat +datum=NAD27 +no_defs
nc_new.region <- nc %>% ## add new region variable
mutate(new.region = sample(c('A', 'B', 'C'), nrow(.),replace = T))
nc_union <- nc_new.region %>%
group_by(new.region) %>%
summarize()
> nc_union
Simple feature collection with 3 features and 1 field
geometry type: MULTIPOLYGON
dimension: XY
bbox: xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965
epsg (SRID): 4267
proj4string: +proj=longlat +datum=NAD27 +no_defs
# A tibble: 3 x 2
new.region geometry
<chr> <MULTIPOLYGON [°]>
1 A (((-78.65572 33.94867, -79.0745 34.30457, -79.04095 34.3193, -79.02947 34.34737, -7~
2 B (((-79.45597 34.63409, -79.6675 34.80066, -79.68596 34.80526, -79.66015 34.8179, -7~
3 C (((-78.65572 33.94867, -78.63472 33.97798, -78.63027 34.0102, -78.58778 34.03061, -~
tm_shape(nc_union)+
tm_borders()
You can see that now nc_union contains only 3 MULTIPOLYGONS, and plot reflects the "aggregation".
See also: https://github.com/r-spatial/sf/issues/290
Created on 2019-08-23 by the reprex package (v0.3.0)
I have an sf object containing multiple (square) polygons of a grid. What I like would be to get a column containing for each polygon the four values, which define the bounding box (bottom-left and top-right).
Here is an example for the the canton of Zurich in Switzerland:
library(raster)
library(sf)
library(dplyr)
ch <- getData('GADM', country = 'CH', level = 1)
ch_grid <- ch %>%
st_as_sf() %>%
filter(NAME_1 == "Zürich") %>%
st_make_grid(cellsize = 0.1, what = "polygons")
This gives me a 6x7 grid. Now I'm looking for a way to get for each of the square polygons the two coordinate pairs, which define the bounding box - preferably in a new column.
I hope it's clear what I mean. I would very much appreciate your help.
Given your ch_grid sfc object, this one-liner:
> ch_grid_df = cbind(st_sf(geometry=ch_grid),do.call(rbind,lapply(ch_grid, st_bbox)))
creates an sf data frame with four columns as required:
> ch_grid_df
Simple feature collection with 42 features and 4 fields
geometry type: POLYGON
dimension: XY
bbox: xmin: 8.358933 ymin: 47.16357 xmax: 9.058933 ymax: 47.76357
epsg (SRID): 4326
proj4string: +proj=longlat +datum=WGS84 +no_defs
First 10 features:
xmin ymin xmax ymax ch_grid
1 8.358933 47.16357 8.458933 47.26357 POLYGON ((8.358933 47.16357...
2 8.458933 47.16357 8.558933 47.26357 POLYGON ((8.458933 47.16357...
3 8.558933 47.16357 8.658933 47.26357 POLYGON ((8.558933 47.16357...
4 8.658933 47.16357 8.758933 47.26357 POLYGON ((8.658933 47.16357...
5 8.758933 47.16357 8.858933 47.26357 POLYGON ((8.758933 47.16357...
6 8.858933 47.16357 8.958933 47.26357 POLYGON ((8.858933 47.16357...
This only uses base R functions and so will be robust against the vagaries of time and fashion.
The newest tidyverse pattern for iterating over rows is using dplyr::mutate(new_col = purrr::map(existing_col, func)), which works well with spatial objects including the geometry column in sf objects.
So something like this to return a bounding box and a grid layout for each row:
library(purrr)
ch_grid <- ch %>%
st_as_sf() %>%
mutate(bbox = map(geometry, st_bbox),
grid = map(geometry, ~ st_make_grid(., cellsize = 0.1, what = "polygons")))
If don't want to use purrr::map(), lapply() can be subbed in:
ch_grid <- ch %>%
st_as_sf() %>%
mutate(bbox = lapply(geometry, st_bbox),
grid = lapply(geometry, st_make_grid, cellsize = 0.1, what = "polygons"))
I am newbie to SF and stack, hope my question is clear enough.
I was able to create a set of lines connecting 1 point to a set of points all over the US.
The I can read the US counties into multipolygons.
My goal is to find and geolocate all the points where the lines I created cross the county borders.
So far I was able to create the lines from the points:
points_to_lines <- dt %>%
st_as_sf(coords = c("lon", "lat"), crs = 4326) %>%
group_by(lineid) %>%
summarize(do_union = FALSE) %>% lineid
st_cast("LINESTRING")
This is the head of the lines
Simple feature collection with 1628 features and 1 field
geometry type: LINESTRING
dimension: XY
bbox: xmin: 30.1127 ymin: -91.32484 xmax: 37.23671 ymax: -82.31262
epsg (SRID): 4326
proj4string: +proj=longlat +datum=WGS84 +no_defs
# A tibble: 1,628 x 2
lineid geometry
<int> <LINESTRING [°]>
1 1 (33.51859 -86.81036, 36.16266 -86.7816)
2 2 (33.51859 -86.81036, 34.61845 -82.47791)
This is the head of the county dataset.
Reading layer `US_county_1930_conflated' from data source `~/county_gis/1930' using driver `ESRI Shapefile'
Simple feature collection with 3110 features and 18 fields
geometry type: MULTIPOLYGON
dimension: XY
bbox: xmin: -7115608 ymin: -1337505 xmax: 2258244 ymax: 4591848
epsg (SRID): NA
proj4string: +proj=aea +lat_1=29.5 +lat_2=45.5 +lat_0=37.5 +lon_0=-96 +x_0=0 +y_0=0 +datum=NAD83 +units=m +no_defs
Very naively I have tried to give them both the same set of coordinates, and then st_intersects them. The non sparse matrix seams to say that all the lines intersect only one county.
gis1930_p <- st_set_crs(gis1930, 4326) %>% st_transform(4326)
st_intersects(points, gis1930_p, sparse=FALSE)
I also plot the lines on top of the counties but only the map of the US counties is mapped.
plot(gis1930_p[0], reset = FALSE)
plot(points[0], add = TRUE)
Any help would be greatly appreciated and please let me know if I can provide any additional details.
You didn't provide your data so I am going to use the dataset provided in: https://gis.stackexchange.com/a/236922/32531
The main thing you need is the st_intersection function:
library(sf)
line_1 <- st_as_sfc("LINESTRING(458.1 768.23, 455.3 413.29, 522.3 325.77, 664.8 282.01, 726.3 121.56)")
poly_1 <- st_as_sfc("MULTIPOLYGON(((402.2 893.03, 664.8 800.65, 611.7 666.13, 368.7 623.99, 215.1 692.06, 402.2 893.03)), ((703.9 366.29, 821.2 244.73, 796.1 25.93, 500.0 137.76, 703.9 366.29)))")
pnts <- st_intersection(line_1,
st_cast(poly_1, "MULTILINESTRING", group_or_split = FALSE))
plot(poly_1)
plot(line_1, add = TRUE)
plot(pnts, add = TRUE, col = "red", pch = 21)
Example:
bbox <- c(-0.1178, 51.4232, -0.0185, 51.5147) # I know it needs to be sf df object
# we have
df
#> Geometry set for 300 features
#> geometry type: LINESTRING
#> dimension: XY
#> bbox: xmin: -0.113894 ymin: 51.49739 xmax: -0.0764779 ymax: 51.59839
#> epsg (SRID): 4326
#> proj4string: +proj=longlat +datum=WGS84 +no_defs
#> LINESTRING (-0.113894 51.50631, -0.1135137 51.5...
#> LINESTRING (-0.0767875 51.59837, -0.0764779 51....
#> ....
How can I do something like
df[bbox]
and keep the linestrings which are within the bbox. Thanks.
Here's an example using an sf object from tigris, just for reproducibility. I'm using towns in New Haven County, Connecticut, plotting it the way it comes in. Then I crop it to a bounding box I made up, using st_crop, which I believe was added fairly recently to sf. If I had the bbox as a shape, instead of a vector of coordinates, I could have used st_intersection.
I don't have a linestring object handy, but I'd assume it works the same way.
library(tidyverse)
library(sf)
# selecting just to limit the amount of data in my sf
ct_sf <- tigris::county_subdivisions(state = "09", county = "09", cb = T, class = "sf") %>%
select(NAME, geometry)
plot(ct_sf)
crop_bbox <- c(xmin = -73, ymin = 41.2, xmax = -72.7, ymax = 41.5)
ct_cropped <- st_crop(ct_sf, crop_bbox)
plot(ct_cropped)