Title: | Sustainable Transport Planning |
---|---|
Description: | Tools for transport planning with an emphasis on spatial transport data and non-motorized modes. The package was originally developed to support the 'Propensity to Cycle Tool', a publicly available strategic cycle network planning tool (Lovelace et al. 2017) <doi:10.5198/jtlu.2016.862>, but has since been extended to support public transport routing and accessibility analysis (Moreno-Monroy et al. 2017) <doi:10.1016/j.jtrangeo.2017.08.012> and routing with locally hosted routing engines such as 'OSRM' (Lowans et al. 2023) <doi:10.1016/j.enconman.2023.117337>. The main functions are for creating and manipulating geographic "desire lines" from origin-destination (OD) data (building on the 'od' package); calculating routes on the transport network locally and via interfaces to routing services such as <https://cyclestreets.net/> (Desjardins et al. 2021) <doi:10.1007/s11116-021-10197-1>; and calculating route segment attributes such as bearing. The package implements the 'travel flow aggregration' method described in Morgan and Lovelace (2020) <doi:10.1177/2399808320942779> and the 'OD jittering' method described in Lovelace et al. (2022) <doi:10.32866/001c.33873>. Further information on the package's aim and scope can be found in the vignettes and in a paper in the R Journal (Lovelace and Ellison 2018) <doi:10.32614/RJ-2018-053>, and in a paper outlining the landscape of open source software for geographic methods in transport planning (Lovelace, 2021) <doi:10.1007/s10109-020-00342-2>. |
Authors: | Robin Lovelace [aut, cre] , Richard Ellison [aut], Malcolm Morgan [aut] , Barry Rowlingson [ctb], Nick Bearman [ctb], Nikolai Berkoff [ctb], Scott Chamberlain [rev] (Scott reviewed the package for rOpenSci, see https://github.com/ropensci/onboarding/issues/10), Mark Padgham [ctb], Zhao Wang [ctb] , Andrea Gilardi [ctb] , Josiah Parry [ctb] |
Maintainer: | Robin Lovelace <[email protected]> |
License: | MIT + file LICENSE |
Version: | 1.2.2 |
Built: | 2024-11-21 05:38:38 UTC |
Source: | https://github.com/ropensci/stplanr |
The stplanr package provides functions to access and analyse data for transportation research, including origin-destination analysis, route allocation and modelling travel patterns.
Robin Lovelace [email protected]
https://github.com/ropensci/stplanr
This function was designed to find lines that are close to parallel and perpendicular to some pre-defined route. It can return results that are absolute (contain information on the direction of turn, i.e. + or - values for clockwise/anticlockwise), bidirectional (which mean values greater than +/- 90 are impossible).
angle_diff(l, angle, bidirectional = FALSE, absolute = TRUE)
angle_diff(l, angle, bidirectional = FALSE, absolute = TRUE)
l |
A spatial lines object |
angle |
an angle in degrees relative to North, with 90 being East and -90 being West. (direction of rotation is ignored). |
bidirectional |
Should the result be returned in a bidirectional format? Default is FALSE. If TRUE, the same line in the oposite direction would have the same bearing |
absolute |
If TRUE (the default) only positive values can be returned |
Building on the convention used in in the bearing()
function from the
geosphere
package and in many applications,
North is definied as 0, East as 90 and West as -90.
Other lines:
geo_toptail()
,
is_linepoint()
,
line2df()
,
line2points()
,
line_bearing()
,
line_breakup()
,
line_midpoint()
,
line_segment()
,
line_segment1()
,
line_via()
,
mats2line()
,
n_segments()
,
n_vertices()
,
onewaygeo()
,
points2line()
,
toptail_buff()
lib_versions <- sf::sf_extSoftVersion() lib_versions # fails on some systems (with early versions of PROJ) if (lib_versions[3] >= "6.3.1") { # Find all routes going North-South lines_sf <- od2line(od_data_sample, zones = zones_sf) angle_diff(lines_sf[2, ], angle = 0) angle_diff(lines_sf[2:3, ], angle = 0) }
lib_versions <- sf::sf_extSoftVersion() lib_versions # fails on some systems (with early versions of PROJ) if (lib_versions[3] >= "6.3.1") { # Find all routes going North-South lines_sf <- od2line(od_data_sample, zones = zones_sf) angle_diff(lines_sf[2, ], angle = 0) angle_diff(lines_sf[2:3, ], angle = 0) }
Takes a bounding box as an input and outputs a bounding box of a different size, centred at the same point.
bbox_scale(bb, scale_factor)
bbox_scale(bb, scale_factor)
bb |
Bounding box object |
scale_factor |
Numeric vector determining how much the bounding box will grow or shrink. Two numbers refer to extending the bounding box in x and y dimensions, respectively. If the value is 1, the output size will be the same as the input. |
Other geo:
bind_sf()
,
geo_bb()
,
geo_bb_matrix()
,
geo_buffer()
,
geo_length()
,
geo_projected()
,
geo_select_aeq()
,
quadrant()
bb <- matrix(c(-1.55, 53.80, -1.50, 53.83), nrow = 2) bb1 <- bbox_scale(bb, scale_factor = 1.05) bb2 <- bbox_scale(bb, scale_factor = c(2, 1.05)) bb3 <- bbox_scale(bb, 0.1) plot(x = bb2[1, ], y = bb2[2, ]) points(bb1[1, ], bb1[2, ]) points(bb3[1, ], bb3[2, ]) points(bb[1, ], bb[2, ], col = "red")
bb <- matrix(c(-1.55, 53.80, -1.50, 53.83), nrow = 2) bb1 <- bbox_scale(bb, scale_factor = 1.05) bb2 <- bbox_scale(bb, scale_factor = c(2, 1.05)) bb3 <- bbox_scale(bb, 0.1) plot(x = bb2[1, ], y = bb2[2, ]) points(bb1[1, ], bb1[2, ]) points(bb3[1, ], bb3[2, ]) points(bb[1, ], bb[2, ], col = "red")
Rapid row-binding of sf objects
bind_sf(x)
bind_sf(x)
x |
List of sf objects to combine |
An sf data frame
Other geo:
bbox_scale()
,
geo_bb()
,
geo_bb_matrix()
,
geo_buffer()
,
geo_length()
,
geo_projected()
,
geo_select_aeq()
,
quadrant()
These points represent population-weighted centroids of Medium Super Output Area (MSOA) zones within a 1 mile radius of of my home when I was writing this package.
A spatial dataset with 8 rows and 5 columns
geo_code the official code of the zone
MSOA11NM name zone name
percent_fem the percent female
avslope average gradient of the zone
Cents was generated from the data repository pct-data: https://github.com/npct/pct-data. This data was accessed from within the pct repo: https://github.com/npct/pct, using the following code:
Other data:
destinations_sf
,
flow
,
flow_dests
,
flowlines_sf
,
od_data_lines
,
od_data_routes
,
od_data_sample
,
osm_net_example
,
read_table_builder()
,
route_network_sf
,
route_network_small
,
routes_fast_sf
,
routes_slow_sf
,
zones_sf
cents_sf
cents_sf
This dataset represents trip destinations on a different geographic
level than the origins stored in the object cents_sf
.
A spatial dataset with 87 features
Other data:
cents_sf
,
flow
,
flow_dests
,
flowlines_sf
,
od_data_lines
,
od_data_routes
,
od_data_sample
,
osm_net_example
,
read_table_builder()
,
route_network_sf
,
route_network_small
,
routes_fast_sf
,
routes_slow_sf
,
zones_sf
destinations_sf
destinations_sf
This dataset represents commuter flows (work travel) between origin and destination zones. The data is from the UK and is available as open data: https://wicid.ukdataservice.ac.uk/.
A data frame with 49 rows and 15 columns
The variables are as follows:
Area.of.residence. id of origin zone
Area.of.workplace id of destination zone
All. Travel to work flows by all modes
[,4:15]
. Flows for different modes
id. unique id of flow
Although these variable names are unique to UK data, the data structure is generalisable and typical of flow data from any source. The key variables are the origin and destination ids, which link to the georeferenced spatial objects.
Other data:
cents_sf
,
destinations_sf
,
flow_dests
,
flowlines_sf
,
od_data_lines
,
od_data_routes
,
od_data_sample
,
osm_net_example
,
read_table_builder()
,
route_network_sf
,
route_network_small
,
routes_fast_sf
,
routes_slow_sf
,
zones_sf
Other data:
cents_sf
,
destinations_sf
,
flow_dests
,
flowlines_sf
,
od_data_lines
,
od_data_routes
,
od_data_sample
,
osm_net_example
,
read_table_builder()
,
route_network_sf
,
route_network_small
,
routes_fast_sf
,
routes_slow_sf
,
zones_sf
Data frame of invented commuter flows with destinations in a different layer than the origins
data(flow_dests)
data(flow_dests)
A data frame with 49 rows and 15 columns
Other data:
cents_sf
,
destinations_sf
,
flow
,
flowlines_sf
,
od_data_lines
,
od_data_routes
,
od_data_sample
,
osm_net_example
,
read_table_builder()
,
route_network_sf
,
route_network_small
,
routes_fast_sf
,
routes_slow_sf
,
zones_sf
## Not run: # This is how the dataset was constructed flow_dests <- flow flow_dests$Area.of.workplace <- sample(x = destinations$WZ11CD, size = nrow(flow)) flow_dests <- dplyr::rename(flow_dests, WZ11CD = Area.of.workplace) devtools::use_data(flow_dests) ## End(Not run)
## Not run: # This is how the dataset was constructed flow_dests <- flow flow_dests$Area.of.workplace <- sample(x = destinations$WZ11CD, size = nrow(flow)) flow_dests <- dplyr::rename(flow_dests, WZ11CD = Area.of.workplace) devtools::use_data(flow_dests) ## End(Not run)
Flow data after conversion to a spatial format..
A spatial lines dataset with 42 rows and 15 columns
Other data:
cents_sf
,
destinations_sf
,
flow
,
flow_dests
,
od_data_lines
,
od_data_routes
,
od_data_sample
,
osm_net_example
,
read_table_builder()
,
route_network_sf
,
route_network_small
,
routes_fast_sf
,
routes_slow_sf
,
zones_sf
Takes a geographic object or bounding box as an input and outputs a bounding box, represented as a bounding box, corner points or rectangular polygon.
geo_bb( shp, scale_factor = 1, distance = 0, output = c("polygon", "points", "bb") )
geo_bb( shp, scale_factor = 1, distance = 0, output = c("polygon", "points", "bb") )
shp |
Spatial object |
scale_factor |
Numeric vector determining how much the bounding box will grow or shrink. Two numbers refer to extending the bounding box in x and y dimensions, respectively. If the value is 1, the output size will be the same as the input. |
distance |
Distance in metres to extend the bounding box by |
output |
Type of object returned (polygon by default) |
bb_scale
Other geo:
bbox_scale()
,
bind_sf()
,
geo_bb_matrix()
,
geo_buffer()
,
geo_length()
,
geo_projected()
,
geo_select_aeq()
,
quadrant()
shp <- routes_fast_sf shp_bb <- geo_bb(shp, distance = 100) plot(shp_bb, col = "red", reset = FALSE) plot(geo_bb(routes_fast_sf, scale_factor = 0.8), col = "green", add = TRUE) plot(routes_fast_sf$geometry, add = TRUE) geo_bb(shp, output = "point")
shp <- routes_fast_sf shp_bb <- geo_bb(shp, distance = 100) plot(shp_bb, col = "red", reset = FALSE) plot(geo_bb(routes_fast_sf, scale_factor = 0.8), col = "green", add = TRUE) plot(routes_fast_sf$geometry, add = TRUE) geo_bb(shp, output = "point")
Converts a range of spatial data formats into a matrix representing the bounding box
geo_bb_matrix(shp)
geo_bb_matrix(shp)
shp |
Spatial object |
Other geo:
bbox_scale()
,
bind_sf()
,
geo_bb()
,
geo_buffer()
,
geo_length()
,
geo_projected()
,
geo_select_aeq()
,
quadrant()
geo_bb_matrix(routes_fast_sf) geo_bb_matrix(cents_sf[1, ]) geo_bb_matrix(c(-2, 54)) geo_bb_matrix(sf::st_coordinates(cents_sf))
geo_bb_matrix(routes_fast_sf) geo_bb_matrix(cents_sf[1, ]) geo_bb_matrix(c(-2, 54)) geo_bb_matrix(sf::st_coordinates(cents_sf))
This function solves the problem that buffers will not be circular when used on non-projected data.
geo_buffer(shp, dist = NULL, width = NULL, ...)
geo_buffer(shp, dist = NULL, width = NULL, ...)
shp |
A spatial object with a geographic CRS (e.g. WGS84) around which a buffer should be drawn |
dist |
The distance (in metres) of the buffer (when buffering simple features) |
width |
The distance (in metres) of the buffer (when buffering sp objects) |
... |
Arguments passed to the buffer (see |
Requires recent version of PROJ (>= 6.3.0).
Buffers on sf
objects with geographic (lon/lat) coordinates can also
be done with the s2
package.
Other geo:
bbox_scale()
,
bind_sf()
,
geo_bb()
,
geo_bb_matrix()
,
geo_length()
,
geo_projected()
,
geo_select_aeq()
,
quadrant()
lib_versions <- sf::sf_extSoftVersion() lib_versions if (lib_versions[3] >= "6.3.1") { buff_sf <- geo_buffer(routes_fast_sf, dist = 50) plot(buff_sf$geometry) geo_buffer(routes_fast_sf$geometry, dist = 50) }
lib_versions <- sf::sf_extSoftVersion() lib_versions if (lib_versions[3] >= "6.3.1") { buff_sf <- geo_buffer(routes_fast_sf, dist = 50) plot(buff_sf$geometry) geo_buffer(routes_fast_sf$geometry, dist = 50) }
Generate a lat/long pair from data using Google's geolocation API.
geo_code( address, service = "nominatim", base_url = "https://maps.google.com/maps/api/geocode/json", return_all = FALSE, pat = NULL )
geo_code( address, service = "nominatim", base_url = "https://maps.google.com/maps/api/geocode/json", return_all = FALSE, pat = NULL )
address |
Text string representing the address you want to geocode |
service |
Which service to use? Nominatim by default |
base_url |
The base url to query |
return_all |
Should the request return all information returned by Google Maps?
The default is |
pat |
Personal access token |
## Not run: geo_code(address = "Hereford") geo_code("LS7 3HB") geo_code("hereford", return_all = TRUE) # needs api key in .Renviron geo_code("hereford", service = "google", pat = Sys.getenv("GOOGLE"), return_all = TRUE) ## End(Not run)
## Not run: geo_code(address = "Hereford") geo_code("LS7 3HB") geo_code("hereford", return_all = TRUE) # needs api key in .Renviron geo_code("hereford", service = "google", pat = Sys.getenv("GOOGLE"), return_all = TRUE) ## End(Not run)
Takes a line (represented in sf or sp classes) and returns a numeric value representing distance in meters.
geo_length(shp)
geo_length(shp)
shp |
A spatial line object |
Other geo:
bbox_scale()
,
bind_sf()
,
geo_bb()
,
geo_bb_matrix()
,
geo_buffer()
,
geo_projected()
,
geo_select_aeq()
,
quadrant()
lib_versions <- sf::sf_extSoftVersion() lib_versions if (lib_versions[3] >= "6.3.1") { geo_length(routes_fast_sf) }
lib_versions <- sf::sf_extSoftVersion() lib_versions if (lib_versions[3] >= "6.3.1") { geo_length(routes_fast_sf) }
This function performs operations on projected data.
geo_projected(shp, fun, crs, silent, ...)
geo_projected(shp, fun, crs, silent, ...)
shp |
A spatial object with a geographic (WGS84) coordinate system |
fun |
A function to perform on the projected object (e.g. from the sf package) |
crs |
An optional coordinate reference system (if not provided it is set
automatically by |
silent |
A binary value for printing the CRS details (default: TRUE) |
... |
Arguments to pass to |
Other geo:
bbox_scale()
,
bind_sf()
,
geo_bb()
,
geo_bb_matrix()
,
geo_buffer()
,
geo_length()
,
geo_select_aeq()
,
quadrant()
lib_versions <- sf::sf_extSoftVersion() lib_versions # fails on some systems (with early versions of PROJ) if (lib_versions[3] >= "6.3.1") { shp <- routes_fast_sf[2:4, ] geo_projected(shp, sf::st_buffer, dist = 100) }
lib_versions <- sf::sf_extSoftVersion() lib_versions # fails on some systems (with early versions of PROJ) if (lib_versions[3] >= "6.3.1") { shp <- routes_fast_sf[2:4, ] geo_projected(shp, sf::st_buffer, dist = 100) }
This function takes a spatial object with a geographic (WGS84) CRS and returns a custom projected CRS focussed on the centroid of the object. This function is especially useful for using units of metres in all directions for data collected anywhere in the world.
geo_select_aeq(shp)
geo_select_aeq(shp)
shp |
A spatial object with a geographic (WGS84) coordinate system |
The function is based on this stackexchange answer: https://gis.stackexchange.com/questions/121489
Other geo:
bbox_scale()
,
bind_sf()
,
geo_bb()
,
geo_bb_matrix()
,
geo_buffer()
,
geo_length()
,
geo_projected()
,
quadrant()
shp <- zones_sf geo_select_aeq(shp)
shp <- zones_sf geo_select_aeq(shp)
Takes lines and removes the start and end point, to a distance determined by the user.
geo_toptail(l, toptail_dist, ...)
geo_toptail(l, toptail_dist, ...)
l |
An |
toptail_dist |
The distance (in metres) to top and tail the line by. Can either be a single value or a vector of the same length as the SpatialLines object. |
... |
Arguments passed to |
Note: see the function
toptailgs()
in stplanr v0.8.5 for an implementation that uses the geosphere
package.
Other lines:
angle_diff()
,
is_linepoint()
,
line2df()
,
line2points()
,
line_bearing()
,
line_breakup()
,
line_midpoint()
,
line_segment()
,
line_segment1()
,
line_via()
,
mats2line()
,
n_segments()
,
n_vertices()
,
onewaygeo()
,
points2line()
,
toptail_buff()
lib_versions <- sf::sf_extSoftVersion() lib_versions # dont test due to issues with sp classes on some set-ups if (lib_versions[3] >= "6.3.1") { l <- routes_fast_sf[2:4, ] l_top_tail <- geo_toptail(l, 300) l_top_tail plot(sf::st_geometry(l_top_tail)) plot(sf::st_geometry(geo_toptail(l, 600)), lwd = 9, add = TRUE) }
lib_versions <- sf::sf_extSoftVersion() lib_versions # dont test due to issues with sp classes on some set-ups if (lib_versions[3] >= "6.3.1") { l <- routes_fast_sf[2:4, ] l_top_tail <- geo_toptail(l, 300) l_top_tail plot(sf::st_geometry(l_top_tail)) plot(sf::st_geometry(geo_toptail(l, 600)), lwd = 9, add = TRUE) }
Divides SpatialLinesDataFrame objects into separate Lines. Each new Lines object is the aggregate of a single number of aggregated lines.
gsection(sl, buff_dist = 0)
gsection(sl, buff_dist = 0)
sl |
SpatialLinesDataFrame with overlapping Lines to split by number of overlapping features. |
buff_dist |
A number specifying the distance in meters of the buffer to be used to crop lines before running the operation. If the distance is zero (the default) touching but non-overlapping lines may be aggregated. |
Other rnet:
islines()
,
overline()
,
rnet_breakup_vertices()
,
rnet_group()
lib_versions <- sf::sf_extSoftVersion() lib_versions # fails on some systems (with early versions of PROJ) if (lib_versions[3] >= "6.3.1") { sl <- routes_fast_sf[2:4, ] rsec <- gsection(sl) length(rsec) # sections plot(rsec, col = seq(length(rsec))) rsec <- gsection(sl, buff_dist = 50) length(rsec) # 4 features: issue plot(rsec, col = seq(length(rsec))) }
lib_versions <- sf::sf_extSoftVersion() lib_versions # fails on some systems (with early versions of PROJ) if (lib_versions[3] >= "6.3.1") { sl <- routes_fast_sf[2:4, ] rsec <- gsection(sl) length(rsec) # sections plot(rsec, col = seq(length(rsec))) rsec <- gsection(sl, buff_dist = 50) length(rsec) # 4 features: issue plot(rsec, col = seq(length(rsec))) }
OD matrices often contain 'intrazonal' flows, where the origin is the same point as the destination. This function can help identify such intrazonal OD pairs, using 2 criteria: the total number of vertices (2 or fewer) and whether the origin and destination are the same.
is_linepoint(l)
is_linepoint(l)
l |
A spatial lines object |
Returns a boolean vector. TRUE means that the associated line is in fact a point (has no distance). This can be useful for removing data that will not be plotted.
Other lines:
angle_diff()
,
geo_toptail()
,
line2df()
,
line2points()
,
line_bearing()
,
line_breakup()
,
line_midpoint()
,
line_segment()
,
line_segment1()
,
line_via()
,
mats2line()
,
n_segments()
,
n_vertices()
,
onewaygeo()
,
points2line()
,
toptail_buff()
islp <- is_linepoint(flowlines_sf) nrow(flowlines_sf) sum(islp) # Remove invisible 'linepoints' nrow(flowlines_sf[!islp, ])
islp <- is_linepoint(flowlines_sf) nrow(flowlines_sf) sum(islp) # Remove invisible 'linepoints' nrow(flowlines_sf[!islp, ])
This is a function required in overline()
. It identifies
whether sets of lines overlap (beyond shared points) or
not.
islines(g1, g2)
islines(g1, g2)
g1 |
A spatial object |
g2 |
A spatial object |
Other rnet:
gsection()
,
overline()
,
rnet_breakup_vertices()
,
rnet_group()
## Not run: # sf implementation islines(routes_fast_sf[2, ], routes_fast_sf[3, ]) islines(routes_fast_sf[2, ], routes_fast_sf[22, ]) ## End(Not run)
## Not run: # sf implementation islines(routes_fast_sf[2, ], routes_fast_sf[3, ]) islines(routes_fast_sf[2, ], routes_fast_sf[22, ]) ## End(Not run)
This function returns the bearing (in degrees relative to north) of lines.
line_bearing(l, bidirectional = FALSE)
line_bearing(l, bidirectional = FALSE)
l |
A spatial lines object |
bidirectional |
Should the result be returned in a bidirectional format? Default is FALSE. If TRUE, the same line in the oposite direction would have the same bearing |
Returns a boolean vector. TRUE means that the associated line is in fact a point (has no distance). This can be useful for removing data that will not be plotted.
Other lines:
angle_diff()
,
geo_toptail()
,
is_linepoint()
,
line2df()
,
line2points()
,
line_breakup()
,
line_midpoint()
,
line_segment()
,
line_segment1()
,
line_via()
,
mats2line()
,
n_segments()
,
n_vertices()
,
onewaygeo()
,
points2line()
,
toptail_buff()
l <- flowlines_sf[1:5, ] bearings_sf_1_9 <- line_bearing(l) bearings_sf_1_9 # lines of 0 length have NaN bearing b <- line_bearing(l, bidirectional = TRUE) r <- routes_fast_sf[1:5, ] b2 <- line_bearing(r, bidirectional = TRUE) plot(b, b2)
l <- flowlines_sf[1:5, ] bearings_sf_1_9 <- line_bearing(l) bearings_sf_1_9 # lines of 0 length have NaN bearing b <- line_bearing(l, bidirectional = TRUE) r <- routes_fast_sf[1:5, ] b2 <- line_bearing(r, bidirectional = TRUE) plot(b, b2)
This function breaks up a LINESTRING geometries into smaller pieces.
line_breakup(l, z)
line_breakup(l, z)
l |
An sf object with LINESTRING geometry |
z |
An sf object with |
An sf object with LINESTRING geometry created after breaking up the input object.
Other lines:
angle_diff()
,
geo_toptail()
,
is_linepoint()
,
line2df()
,
line2points()
,
line_bearing()
,
line_midpoint()
,
line_segment()
,
line_segment1()
,
line_via()
,
mats2line()
,
n_segments()
,
n_vertices()
,
onewaygeo()
,
points2line()
,
toptail_buff()
library(sf) z <- zones_sf$geometry l <- routes_fast_sf$geometry[2] l_split <- line_breakup(l, z) l l_split sf::st_length(l) sum(sf::st_length(l_split)) plot(z) plot(l, add = TRUE, lwd = 9, col = "grey") plot(l_split, add = TRUE, col = 1:length(l_split))
library(sf) z <- zones_sf$geometry l <- routes_fast_sf$geometry[2] l_split <- line_breakup(l, z) l l_split sf::st_length(l) sum(sf::st_length(l_split)) plot(z) plot(l, add = TRUE, lwd = 9, col = "grey") plot(l_split, add = TRUE, col = 1:length(l_split))
Without losing vertices
line_cast(x)
line_cast(x)
x |
Linestring object |
Find the mid-point of lines
line_midpoint(l, tolerance = NULL)
line_midpoint(l, tolerance = NULL)
l |
A spatial lines object |
tolerance |
The tolerance used to break lines at verteces.
See |
Other lines:
angle_diff()
,
geo_toptail()
,
is_linepoint()
,
line2df()
,
line2points()
,
line_bearing()
,
line_breakup()
,
line_segment()
,
line_segment1()
,
line_via()
,
mats2line()
,
n_segments()
,
n_vertices()
,
onewaygeo()
,
points2line()
,
toptail_buff()
l <- routes_fast_sf[2:5, ] plot(l$geometry, col = 2:5) midpoints <- line_midpoint(l) plot(midpoints, add = TRUE) # compare with sf::st_point_on_surface: midpoints2 <- sf::st_point_on_surface(l) plot(midpoints2, add = TRUE, col = "red")
l <- routes_fast_sf[2:5, ] plot(l$geometry, col = 2:5) midpoints <- line_midpoint(l) plot(midpoints, add = TRUE) # compare with sf::st_point_on_surface: midpoints2 <- sf::st_point_on_surface(l) plot(midpoints2, add = TRUE, col = "red")
This function keeps the attributes.
Note: results differ when use_rsgeo
is TRUE
:
the {rsgeo}
implementation will be faster.
Results may not always keep returned linestrings below
the segment_length
value.
The {rsgeo}
implementation does not always
return the number of segments requested due to an upstream issue in the
geo
Rust crate.
line_segment( l, segment_length = NA, n_segments = NA, use_rsgeo = NULL, debug_mode = FALSE )
line_segment( l, segment_length = NA, n_segments = NA, use_rsgeo = NULL, debug_mode = FALSE )
l |
A spatial lines object |
segment_length |
The approximate length of segments in the output (overrides n_segments if set) |
n_segments |
The number of segments to divide the line into. If there are multiple lines, this should be a vector of the same length. |
use_rsgeo |
Should the |
debug_mode |
Should debug messages be printed? Default is FALSE. |
Note: we recommend running these functions on projected data.
Other lines:
angle_diff()
,
geo_toptail()
,
is_linepoint()
,
line2df()
,
line2points()
,
line_bearing()
,
line_breakup()
,
line_midpoint()
,
line_segment1()
,
line_via()
,
mats2line()
,
n_segments()
,
n_vertices()
,
onewaygeo()
,
points2line()
,
toptail_buff()
library(sf) l <- routes_fast_sf[2:4, "ID"] l_seg_multi <- line_segment(l, segment_length = 1000, use_rsgeo = FALSE) l_seg_n <- line_segment(l, n_segments = 2) l_seg_n <- line_segment(l, n_segments = c(1:3)) # Number of subsegments table(l_seg_multi$ID) plot(l_seg_multi["ID"]) plot(l_seg_multi$geometry, col = seq_along(l_seg_multi), lwd = 5) round(st_length(l_seg_multi)) # rsgeo implementation (default if available): if (rlang::is_installed("rsgeo")) { rsmulti = line_segment(l, segment_length = 1000, use_rsgeo = TRUE) plot(rsmulti["ID"]) } # Check they have the same total length, to nearest mm: # round(sum(st_length(l_seg_multi)), 3) == round(sum(st_length(rsmulti)), 3) # With n_segments for 1 line (set use_rsgeo to TRUE to use rsgeo): l_seg_multi_n <- line_segment(l[1, ], n_segments = 3, use_rsgeo = FALSE) l_seg_multi_n <- line_segment(l$geometry[1], n_segments = 3, use_rsgeo = FALSE) # With n_segments for all 3 lines: l_seg_multi_n <- line_segment(l, n_segments = 2) nrow(l_seg_multi_n) == nrow(l) * 2
library(sf) l <- routes_fast_sf[2:4, "ID"] l_seg_multi <- line_segment(l, segment_length = 1000, use_rsgeo = FALSE) l_seg_n <- line_segment(l, n_segments = 2) l_seg_n <- line_segment(l, n_segments = c(1:3)) # Number of subsegments table(l_seg_multi$ID) plot(l_seg_multi["ID"]) plot(l_seg_multi$geometry, col = seq_along(l_seg_multi), lwd = 5) round(st_length(l_seg_multi)) # rsgeo implementation (default if available): if (rlang::is_installed("rsgeo")) { rsmulti = line_segment(l, segment_length = 1000, use_rsgeo = TRUE) plot(rsmulti["ID"]) } # Check they have the same total length, to nearest mm: # round(sum(st_length(l_seg_multi)), 3) == round(sum(st_length(rsmulti)), 3) # With n_segments for 1 line (set use_rsgeo to TRUE to use rsgeo): l_seg_multi_n <- line_segment(l[1, ], n_segments = 3, use_rsgeo = FALSE) l_seg_multi_n <- line_segment(l$geometry[1], n_segments = 3, use_rsgeo = FALSE) # With n_segments for all 3 lines: l_seg_multi_n <- line_segment(l, n_segments = 2) nrow(l_seg_multi_n) == nrow(l) * 2
Segment a single line, using lwgeom or rsgeo
line_segment1(l, n_segments = NA, segment_length = NA)
line_segment1(l, n_segments = NA, segment_length = NA)
l |
A spatial lines object |
n_segments |
The number of segments to divide the line into |
segment_length |
The approximate length of segments in the output (overrides n_segments if set) |
Other lines:
angle_diff()
,
geo_toptail()
,
is_linepoint()
,
line2df()
,
line2points()
,
line_bearing()
,
line_breakup()
,
line_midpoint()
,
line_segment()
,
line_via()
,
mats2line()
,
n_segments()
,
n_vertices()
,
onewaygeo()
,
points2line()
,
toptail_buff()
l <- routes_fast_sf[2, ] l_seg2 <- line_segment1(l = l, n_segments = 2) # Test with rsgeo (must be installed): # l_seg2_rsgeo = line_segment1(l = l, n_segments = 2) # waldo::compare(l_seg2, l_seg2_rsgeo) l_seg3 <- line_segment1(l = l, n_segments = 3) l_seg_100 <- line_segment1(l = l, segment_length = 100) l_seg_1000 <- line_segment1(l = l, segment_length = 1000) plot(sf::st_geometry(l_seg2), col = 1:2, lwd = 5) plot(sf::st_geometry(l_seg3), col = 1:3, lwd = 5) plot(sf::st_geometry(l_seg_100), col = seq(nrow(l_seg_100)), lwd = 5) plot(sf::st_geometry(l_seg_1000), col = seq(nrow(l_seg_1000)), lwd = 5)
l <- routes_fast_sf[2, ] l_seg2 <- line_segment1(l = l, n_segments = 2) # Test with rsgeo (must be installed): # l_seg2_rsgeo = line_segment1(l = l, n_segments = 2) # waldo::compare(l_seg2, l_seg2_rsgeo) l_seg3 <- line_segment1(l = l, n_segments = 3) l_seg_100 <- line_segment1(l = l, segment_length = 100) l_seg_1000 <- line_segment1(l = l, segment_length = 1000) plot(sf::st_geometry(l_seg2), col = 1:2, lwd = 5) plot(sf::st_geometry(l_seg3), col = 1:3, lwd = 5) plot(sf::st_geometry(l_seg_100), col = seq(nrow(l_seg_100)), lwd = 5) plot(sf::st_geometry(l_seg_1000), col = seq(nrow(l_seg_1000)), lwd = 5)
Takes an origin (A) and destination (B), represented by the linestring l
,
and generates 3 extra geometries based on points p
:
line_via(l, p)
line_via(l, p)
l |
A spatial lines object |
p |
A spatial points object |
From A to P1 (P1 being the nearest point to A)
From P1 to P2 (P2 being the nearest point to B)
From P2 to B
Other lines:
angle_diff()
,
geo_toptail()
,
is_linepoint()
,
line2df()
,
line2points()
,
line_bearing()
,
line_breakup()
,
line_midpoint()
,
line_segment()
,
line_segment1()
,
mats2line()
,
n_segments()
,
n_vertices()
,
onewaygeo()
,
points2line()
,
toptail_buff()
library(sf) l <- flowlines_sf[2:4, ] p <- destinations_sf lv <- line_via(l, p) lv # library(mapview) # mapview(lv) + # mapview(lv$leg_orig, col = "red") plot(lv[3], lwd = 9, reset = FALSE) plot(lv$leg_orig, col = "red", lwd = 5, add = TRUE) plot(lv$leg_via, col = "black", add = TRUE) plot(lv$leg_dest, col = "green", lwd = 5, add = TRUE)
library(sf) l <- flowlines_sf[2:4, ] p <- destinations_sf lv <- line_via(l, p) lv # library(mapview) # mapview(lv) + # mapview(lv$leg_orig, col = "red") plot(lv[3], lwd = 9, reset = FALSE) plot(lv$leg_orig, col = "red", lwd = 5, add = TRUE) plot(lv$leg_via, col = "black", add = TRUE) plot(lv$leg_dest, col = "green", lwd = 5, add = TRUE)
This function returns a data frame with fx and fy and tx and ty variables representing the beginning and end points of spatial line features respectively.
line2df(l)
line2df(l)
l |
A spatial lines object |
Other lines:
angle_diff()
,
geo_toptail()
,
is_linepoint()
,
line2points()
,
line_bearing()
,
line_breakup()
,
line_midpoint()
,
line_segment()
,
line_segment1()
,
line_via()
,
mats2line()
,
n_segments()
,
n_vertices()
,
onewaygeo()
,
points2line()
,
toptail_buff()
line2df(routes_fast_sf[5:6, ]) # beginning and end of routes
line2df(routes_fast_sf[5:6, ]) # beginning and end of routes
The number of points will be double the number of lines with line2points
. A
closely related function, line2pointsn
returns all the points that were
line vertices. The points corresponding with a given line, i
, will be
(2*i):((2*i)+1)
. The last function, line2vertices
, returns all the points
that are vertices but not nodes. If the input l
object is composed by only
1 LINESTRING with 2 POINTS, then it returns an empty sf
object.
line2points(l, ids = rep(1:nrow(l))) line2pointsn(l) line2vertices(l)
line2points(l, ids = rep(1:nrow(l))) line2pointsn(l) line2vertices(l)
l |
An |
ids |
Vector of ids (by default |
Other lines:
angle_diff()
,
geo_toptail()
,
is_linepoint()
,
line2df()
,
line_bearing()
,
line_breakup()
,
line_midpoint()
,
line_segment()
,
line_segment1()
,
line_via()
,
mats2line()
,
n_segments()
,
n_vertices()
,
onewaygeo()
,
points2line()
,
toptail_buff()
l <- routes_fast_sf[2, ] lpoints <- line2points(l) plot(l$geometry) plot(lpoints, add = TRUE) # test all vertices: plot(l$geometry) lpoints2 <- line2pointsn(l) plot(lpoints2$geometry, add = TRUE) # extract only internal vertices l_internal_vertices <- line2vertices(l) plot(sf::st_geometry(l), reset = FALSE) plot(l_internal_vertices, add = TRUE) # The boundary points are missing
l <- routes_fast_sf[2, ] lpoints <- line2points(l) plot(l$geometry) plot(lpoints, add = TRUE) # test all vertices: plot(l$geometry) lpoints2 <- line2pointsn(l) plot(lpoints2$geometry, add = TRUE) # extract only internal vertices l_internal_vertices <- line2vertices(l) plot(sf::st_geometry(l), reset = FALSE) plot(l_internal_vertices, add = TRUE) # The boundary points are missing
Convert 2 matrices to lines
mats2line(mat1, mat2, crs = NA)
mats2line(mat1, mat2, crs = NA)
mat1 |
Matrix representing origins |
mat2 |
Matrix representing destinations |
crs |
Number representing the coordinate system of the data, e.g. 4326 |
Other lines:
angle_diff()
,
geo_toptail()
,
is_linepoint()
,
line2df()
,
line2points()
,
line_bearing()
,
line_breakup()
,
line_midpoint()
,
line_segment()
,
line_segment1()
,
line_via()
,
n_segments()
,
n_vertices()
,
onewaygeo()
,
points2line()
,
toptail_buff()
m1 <- matrix(c(1, 2, 1, 2), ncol = 2) m2 <- matrix(c(9, 9, 9, 1), ncol = 2) l <- mats2line(m1, m2) class(l) l lsf <- sf::st_sf(l, crs = 4326) class(lsf) plot(lsf) # mapview::mapview(lsf)
m1 <- matrix(c(1, 2, 1, 2), ncol = 2) m2 <- matrix(c(9, 9, 9, 1), ncol = 2) l <- mats2line(m1, m2) class(l) l lsf <- sf::st_sf(l, crs = 4326) class(lsf) plot(lsf) # mapview::mapview(lsf)
Vectorised function to calculate number of segments given a max segment length
n_segments(line_length, max_segment_length)
n_segments(line_length, max_segment_length)
line_length |
The length of the line |
max_segment_length |
The maximum length of each segment |
Other lines:
angle_diff()
,
geo_toptail()
,
is_linepoint()
,
line2df()
,
line2points()
,
line_bearing()
,
line_breakup()
,
line_midpoint()
,
line_segment()
,
line_segment1()
,
line_via()
,
mats2line()
,
n_vertices()
,
onewaygeo()
,
points2line()
,
toptail_buff()
n_segments(50, 10) n_segments(50.1, 10) n_segments(1, 10) n_segments(1:9, 2)
n_segments(50, 10) n_segments(50.1, 10) n_segments(1, 10) n_segments(1:9, 2)
Returns a vector of the same length as the number of sf objects.
n_vertices(l)
n_vertices(l)
l |
An sf object with LINESTRING geometry |
Other lines:
angle_diff()
,
geo_toptail()
,
is_linepoint()
,
line2df()
,
line2points()
,
line_bearing()
,
line_breakup()
,
line_midpoint()
,
line_segment()
,
line_segment1()
,
line_via()
,
mats2line()
,
n_segments()
,
onewaygeo()
,
points2line()
,
toptail_buff()
l <- routes_fast_sf n_vertices(l) n_vertices(zones_sf)
l <- routes_fast_sf n_vertices(l) n_vertices(zones_sf)
This function takes a data frame of OD data and returns a data frame reporting summary statistics for each unique zone of origin.
od_aggregate_from(flow, attrib = NULL, FUN = sum, ..., col = 1)
od_aggregate_from(flow, attrib = NULL, FUN = sum, ..., col = 1)
flow |
A data frame representing origin-destination data.
The first two columns of this data frame should correspond
to the first column of the data in the zones. Thus in |
attrib |
character, column names in sl to be aggregated |
FUN |
A function to summarise OD data by |
... |
Additional arguments passed to |
col |
The column that the OD dataset is grouped by (1 by default, the first column usually represents the origin) |
It has some default settings: the default summary statistic is sum()
and the
first column in the OD data is assumed to represent the zone of origin.
By default, if attrib
is not set, it summarises all numeric columns.
Other od:
od2line()
,
od2odf()
,
od_aggregate_to()
,
od_coords()
,
od_coords2line()
,
od_id
,
od_id_order()
,
od_oneway()
,
od_to_odmatrix()
,
odmatrix_to_od()
,
points2flow()
,
points2odf()
od_aggregate_from(flow)
od_aggregate_from(flow)
This function takes a data frame of OD data and returns a data frame reporting summary statistics for each unique zone of destination.
od_aggregate_to(flow, attrib = NULL, FUN = sum, ..., col = 2)
od_aggregate_to(flow, attrib = NULL, FUN = sum, ..., col = 2)
flow |
A data frame representing origin-destination data.
The first two columns of this data frame should correspond
to the first column of the data in the zones. Thus in |
attrib |
character, column names in sl to be aggregated |
FUN |
A function to summarise OD data by |
... |
Additional arguments passed to |
col |
The column that the OD dataset is grouped by (1 by default, the first column usually represents the origin) |
It has some default settings: it assumes the destination ID column is the 2nd
and the default summary statistic is sum()
.
By default, if attrib
is not set, it summarises all numeric columns.
Other od:
od2line()
,
od2odf()
,
od_aggregate_from()
,
od_coords()
,
od_coords2line()
,
od_id
,
od_id_order()
,
od_oneway()
,
od_to_odmatrix()
,
odmatrix_to_od()
,
points2flow()
,
points2odf()
od_aggregate_to(flow)
od_aggregate_to(flow)
This function takes a wide range of input data types (spatial lines, points or text strings) and returns a matrix of coordinates representing origin (fx, fy) and destination (tx, ty) points.
od_coords(from = NULL, to = NULL, l = NULL)
od_coords(from = NULL, to = NULL, l = NULL)
from |
An object representing origins
(if lines are provided as the first argument, from is assigned to |
to |
An object representing destinations |
l |
Only needed if from and to are empty, in which case this should be a spatial object representing desire lines |
Other od:
od2line()
,
od2odf()
,
od_aggregate_from()
,
od_aggregate_to()
,
od_coords2line()
,
od_id
,
od_id_order()
,
od_oneway()
,
od_to_odmatrix()
,
odmatrix_to_od()
,
points2flow()
,
points2odf()
od_coords(from = c(0, 52), to = c(1, 53)) # lon/lat coordinates od_coords(cents_sf[1:3, ], cents_sf[2:4, ]) # sf points # od_coords("Hereford", "Leeds") # geocode locations od_coords(flowlines_sf[1:3, ])
od_coords(from = c(0, 52), to = c(1, 53)) # lon/lat coordinates od_coords(cents_sf[1:3, ], cents_sf[2:4, ]) # sf points # od_coords("Hereford", "Leeds") # geocode locations od_coords(flowlines_sf[1:3, ])
Convert origin-destination coordinates into desire lines
od_coords2line(odc, crs = 4326, remove_duplicates = TRUE)
od_coords2line(odc, crs = 4326, remove_duplicates = TRUE)
odc |
A data frame or matrix representing the coordinates of origin-destination data. The first two columns represent the coordinates of the origin (typically longitude and latitude) points; the third and fourth columns represent the coordinates of the destination (in the same CRS). Each row represents travel from origin to destination. |
crs |
A number representing the coordinate reference system of the result, 4326 by default. |
remove_duplicates |
Should rows with duplicated rows be removed? |
Other od:
od2line()
,
od2odf()
,
od_aggregate_from()
,
od_aggregate_to()
,
od_coords()
,
od_id
,
od_id_order()
,
od_oneway()
,
od_to_odmatrix()
,
odmatrix_to_od()
,
points2flow()
,
points2odf()
odf <- od_coords(l = flowlines_sf) odlines <- od_coords2line(odf) odlines <- od_coords2line(odf, crs = 4326) plot(odlines) x_coords <- 1:3 n <- 50 d <- data.frame(lapply(1:4, function(x) sample(x_coords, n, replace = TRUE))) names(d) <- c("fx", "fy", "tx", "ty") l <- od_coords2line(d) plot(l) nrow(l) l_with_duplicates <- od_coords2line(d, remove_duplicates = FALSE) plot(l_with_duplicates) nrow(l_with_duplicates)
odf <- od_coords(l = flowlines_sf) odlines <- od_coords2line(odf) odlines <- od_coords2line(odf, crs = 4326) plot(odlines) x_coords <- 1:3 n <- 50 d <- data.frame(lapply(1:4, function(x) sample(x_coords, n, replace = TRUE))) names(d) <- c("fx", "fy", "tx", "ty") l <- od_coords2line(d) plot(l) nrow(l) l_with_duplicates <- od_coords2line(d, remove_duplicates = FALSE) plot(l_with_duplicates) nrow(l_with_duplicates)
Derived from od_data_sample
showing movement between points represented in cents_sf
A data frame (tibble) object
Other data:
cents_sf
,
destinations_sf
,
flow
,
flow_dests
,
flowlines_sf
,
od_data_routes
,
od_data_sample
,
osm_net_example
,
read_table_builder()
,
route_network_sf
,
route_network_small
,
routes_fast_sf
,
routes_slow_sf
,
zones_sf
od_data_lines
od_data_lines
See data-raw/generate-data.Rmd
for details on how this was created.
The dataset shows routes between origins and destinations represented in
od_data_lines
A data frame (tibble) object
Other data:
cents_sf
,
destinations_sf
,
flow
,
flow_dests
,
flowlines_sf
,
od_data_lines
,
od_data_sample
,
osm_net_example
,
read_table_builder()
,
route_network_sf
,
route_network_small
,
routes_fast_sf
,
routes_slow_sf
,
zones_sf
od_data_routes
od_data_routes
See data-raw/generate-data.Rmd
for details on how this was created.
A data frame (tibble) object
Other data:
cents_sf
,
destinations_sf
,
flow
,
flow_dests
,
flowlines_sf
,
od_data_lines
,
od_data_routes
,
osm_net_example
,
read_table_builder()
,
route_network_sf
,
route_network_small
,
routes_fast_sf
,
routes_slow_sf
,
zones_sf
od_data_sample
od_data_sample
Combine two ID values to create a single ID number
od_id_szudzik(x, y, ordermatters = FALSE) od_id_max_min(x, y) od_id_character(x, y)
od_id_szudzik(x, y, ordermatters = FALSE) od_id_max_min(x, y) od_id_character(x, y)
x |
a vector of numeric, character, or factor values |
y |
a vector of numeric, character, or factor values |
ordermatters |
logical, does the order of values matter to pairing, default = FALSE |
In OD data it is common to have many 'oneway' flows from "A to B" and "B to A". It can be useful to group these an have a single ID that represents pairs of IDs with or without directionality, so they contain 'twoway' or bi-directional values.
od_id*
functions take two vectors of equal length and return a vector of IDs,
which are unique for each combination but the same for twoway flows.
the Szudzik pairing function, on two vectors of equal length. It returns a vector of ID numbers.
This function superseeds od_id_order as it is faster on large datasets
Other od:
od2line()
,
od2odf()
,
od_aggregate_from()
,
od_aggregate_to()
,
od_coords()
,
od_coords2line()
,
od_id_order()
,
od_oneway()
,
od_to_odmatrix()
,
odmatrix_to_od()
,
points2flow()
,
points2odf()
(d <- od_data_sample[2:9, 1:2]) (id <- od_id_character(d[[1]], d[[2]])) duplicated(id) od_id_szudzik(d[[1]], d[[2]]) od_id_max_min(d[[1]], d[[2]])
(d <- od_data_sample[2:9, 1:2]) (id <- od_id_character(d[[1]], d[[2]])) duplicated(id) od_id_szudzik(d[[1]], d[[2]]) od_id_max_min(d[[1]], d[[2]])
Generate ordered ids of OD pairs so lowest is always first This function is slow on large datasets, see szudzik_pairing for faster alternative
od_id_order(x, id1 = names(x)[1], id2 = names(x)[2])
od_id_order(x, id1 = names(x)[1], id2 = names(x)[2])
x |
A data frame or SpatialLinesDataFrame, representing an OD matrix |
id1 |
Optional (it is assumed to be the first column) text string referring to the name of the variable containing the unique id of the origin |
id2 |
Optional (it is assumed to be the second column) text string referring to the name of the variable containing the unique id of the destination |
Other od:
od2line()
,
od2odf()
,
od_aggregate_from()
,
od_aggregate_to()
,
od_coords()
,
od_coords2line()
,
od_id
,
od_oneway()
,
od_to_odmatrix()
,
odmatrix_to_od()
,
points2flow()
,
points2odf()
x <- data.frame(id1 = c(1, 1, 2, 2, 3), id2 = c(1, 2, 3, 1, 4)) od_id_order(x) # 4th line switches id1 and id2 so stplanr.key is in order
x <- data.frame(id1 = c(1, 1, 2, 2, 3), id2 = c(1, 2, 3, 1, 4)) od_id_order(x) # 4th line switches id1 and id2 so stplanr.key is in order
For example, sum total travel in both directions.
od_oneway( x, attrib = names(x[-c(1:2)])[vapply(x[-c(1:2)], is.numeric, TRUE)], id1 = names(x)[1], id2 = names(x)[2], stplanr.key = NULL )
od_oneway( x, attrib = names(x[-c(1:2)])[vapply(x[-c(1:2)], is.numeric, TRUE)], id1 = names(x)[1], id2 = names(x)[2], stplanr.key = NULL )
x |
A data frame or SpatialLinesDataFrame, representing an OD matrix |
attrib |
A vector of column numbers or names, representing variables to be aggregated. By default, all numeric variables are selected. aggregate |
id1 |
Optional (it is assumed to be the first column) text string referring to the name of the variable containing the unique id of the origin |
id2 |
Optional (it is assumed to be the second column) text string referring to the name of the variable containing the unique id of the destination |
stplanr.key |
Optional key of unique OD pairs regardless of the order,
e.g., as generated by |
Flow data often contains movement in two directions: from point A to point B and then from B to A. This can be problematic for transport planning, because the magnitude of flow along a route can be masked by flows the other direction. If only the largest flow in either direction is captured in an analysis, for example, the true extent of travel will by heavily under-estimated for OD pairs which have similar amounts of travel in both directions. Flows in both direction are often represented by overlapping lines with identical geometries which can be confusing for users and are difficult to plot.
oneway
outputs a data frame (or sf
data frame) with rows containing
results for the user-selected attribute values that have been aggregated.
Other od:
od2line()
,
od2odf()
,
od_aggregate_from()
,
od_aggregate_to()
,
od_coords()
,
od_coords2line()
,
od_id
,
od_id_order()
,
od_to_odmatrix()
,
odmatrix_to_od()
,
points2flow()
,
points2odf()
(od_min <- od_data_sample[c(1, 2, 9), 1:6]) (od_oneway <- od_oneway(od_min)) # (od_oneway_old = onewayid(od_min, attrib = 3:6)) # old implementation nrow(od_oneway) < nrow(od_min) # result has fewer rows sum(od_min$all) == sum(od_oneway$all) # but the same total flow od_oneway(od_min, attrib = "all") attrib <- which(vapply(flow, is.numeric, TRUE)) flow_oneway <- od_oneway(flow, attrib = attrib) colSums(flow_oneway[attrib]) == colSums(flow[attrib]) # test if the colSums are equal # Demonstrate the results from oneway and onewaygeo are identical flow_oneway_sf <- od_oneway(flowlines_sf) plot(flow_oneway_sf$geometry, lwd = flow_oneway_sf$All / mean(flow_oneway_sf$All))
(od_min <- od_data_sample[c(1, 2, 9), 1:6]) (od_oneway <- od_oneway(od_min)) # (od_oneway_old = onewayid(od_min, attrib = 3:6)) # old implementation nrow(od_oneway) < nrow(od_min) # result has fewer rows sum(od_min$all) == sum(od_oneway$all) # but the same total flow od_oneway(od_min, attrib = "all") attrib <- which(vapply(flow, is.numeric, TRUE)) flow_oneway <- od_oneway(flow, attrib = attrib) colSums(flow_oneway[attrib]) == colSums(flow[attrib]) # test if the colSums are equal # Demonstrate the results from oneway and onewaygeo are identical flow_oneway_sf <- od_oneway(flowlines_sf) plot(flow_oneway_sf$geometry, lwd = flow_oneway_sf$All / mean(flow_oneway_sf$All))
This function takes a data frame representing travel between origins
(with origin codes in name_orig
, typically the 1st column)
and destinations
(with destination codes in name_dest
, typically the second column) and returns a matrix
with cell values (from attrib
, the third column by default) representing travel between
origins and destinations.
od_to_odmatrix(flow, attrib = 3, name_orig = 1, name_dest = 2)
od_to_odmatrix(flow, attrib = 3, name_orig = 1, name_dest = 2)
flow |
A data frame representing flows between origin and destinations |
attrib |
A number or character string representing the column containing the attribute data
of interest from the |
name_orig |
A number or character string representing the zone of origin |
name_dest |
A number or character string representing the zone of destination |
Other od:
od2line()
,
od2odf()
,
od_aggregate_from()
,
od_aggregate_to()
,
od_coords()
,
od_coords2line()
,
od_id
,
od_id_order()
,
od_oneway()
,
odmatrix_to_od()
,
points2flow()
,
points2odf()
od_to_odmatrix(flow) od_to_odmatrix(flow[1:9, ]) od_to_odmatrix(flow[1:9, ], attrib = "Bicycle")
od_to_odmatrix(flow) od_to_odmatrix(flow[1:9, ]) od_to_odmatrix(flow[1:9, ], attrib = "Bicycle")
Origin-destination ('OD') flow data is often provided in the form of 1 line per flow with zone codes of origin and destination centroids. This can be tricky to plot and link-up with geographical data. This function makes the task easier.
od2line( flow, zones, destinations = NULL, zone_code = names(zones)[1], origin_code = names(flow)[1], dest_code = names(flow)[2], zone_code_d = NA, silent = FALSE )
od2line( flow, zones, destinations = NULL, zone_code = names(zones)[1], origin_code = names(flow)[1], dest_code = names(flow)[2], zone_code_d = NA, silent = FALSE )
flow |
A data frame representing origin-destination data.
The first two columns of this data frame should correspond
to the first column of the data in the zones. Thus in |
zones |
A spatial object representing origins (and destinations if no separate destinations object is provided) of travel. |
destinations |
A spatial object representing destinations of travel flows. |
zone_code |
Name of the variable in |
origin_code |
Name of the variable in |
dest_code |
Name of the variable in |
zone_code_d |
Name of the variable in |
silent |
TRUE by default, setting it to TRUE will show you the matching columns |
Origin-destination (OD) data is often provided
in the form of 1 line per OD pair, with zone codes of the trip origin in the first
column and the zone codes of the destination in the second column
(see the vignette("stplanr-od")
) for details.
od2line()
creates a spatial (linestring) object representing movement from the origin
to the destination for each OD pair.
It takes data frame containing
origin and destination cones (flow
) that match the first column in a
a spatial (polygon or point) object (zones
).
Other od:
od2odf()
,
od_aggregate_from()
,
od_aggregate_to()
,
od_coords()
,
od_coords2line()
,
od_id
,
od_id_order()
,
od_oneway()
,
od_to_odmatrix()
,
odmatrix_to_od()
,
points2flow()
,
points2odf()
od_data <- stplanr::flow[1:20, ] l <- od2line(flow = od_data, zones = cents_sf) plot(sf::st_geometry(cents_sf)) plot(l, lwd = l$All / mean(l$All), add = TRUE)
od_data <- stplanr::flow[1:20, ] l <- od2line(flow = od_data, zones = cents_sf) plot(sf::st_geometry(cents_sf)) plot(l, lwd = l$All / mean(l$All), add = TRUE)
Extract coordinates from OD data
od2odf(flow, zones)
od2odf(flow, zones)
flow |
A data frame representing origin-destination data.
The first two columns of this data frame should correspond
to the first column of the data in the zones. Thus in |
zones |
A spatial object representing origins (and destinations if no separate destinations object is provided) of travel. |
Origin-destination (OD) data is often provided
in the form of 1 line per OD pair, with zone codes of the trip origin in the first
column and the zone codes of the destination in the second column
(see the vignette("stplanr-od")
) for details.
od2odf()
creates an 'origin-destination data frame', with columns containing
origin and destination codes (flow
) that match the first column in a
a spatial (polygon or point sf
) object (zones
).
The function returns a data frame with coordinates for the origin and destination.
Other od:
od2line()
,
od_aggregate_from()
,
od_aggregate_to()
,
od_coords()
,
od_coords2line()
,
od_id
,
od_id_order()
,
od_oneway()
,
od_to_odmatrix()
,
odmatrix_to_od()
,
points2flow()
,
points2odf()
od2odf(flow[1:2, ], zones_sf)
od2odf(flow[1:2, ], zones_sf)
This function takes a matrix representing travel between origins
(with origin codes in the rownames
of the matrix)
and destinations
(with destination codes in the colnames
of the matrix)
and returns a data frame representing origin-destination pairs.
odmatrix_to_od(odmatrix)
odmatrix_to_od(odmatrix)
odmatrix |
A matrix with row and columns representing origin and destination zone codes and cells representing the flow between these zones. |
The function returns a data frame with rows ordered by origin and then destination
zone code values and with names orig
, dest
and flow
.
Other od:
od2line()
,
od2odf()
,
od_aggregate_from()
,
od_aggregate_to()
,
od_coords()
,
od_coords2line()
,
od_id
,
od_id_order()
,
od_oneway()
,
od_to_odmatrix()
,
points2flow()
,
points2odf()
odmatrix <- od_to_odmatrix(flow) odmatrix_to_od(odmatrix) flow[1:9, 1:3] odmatrix_to_od(od_to_odmatrix(flow[1:9, 1:3]))
odmatrix <- od_to_odmatrix(flow) odmatrix_to_od(odmatrix) flow[1:9, 1:3] odmatrix_to_od(od_to_odmatrix(flow[1:9, 1:3]))
Flow data often contains movement in two directions: from point A to point B and then from B to A. This can be problematic for transport planning, because the magnitude of flow along a route can be masked by flows the other direction. If only the largest flow in either direction is captured in an analysis, for example, the true extent of travel will by heavily under-estimated for OD pairs which have similar amounts of travel in both directions.
onewaygeo(x, attrib)
onewaygeo(x, attrib)
x |
A dataset containing linestring geometries |
attrib |
A text string containing the name of the line's attribute to aggregate or a numeric vector of the columns to be aggregated |
This function aggregates directional flows into non-directional flows, potentially halving the number of lines objects and reducing the number of overlapping lines to zero.
onewaygeo
outputs a SpatialLinesDataFrame with single lines
and user-selected attribute values that have been aggregated. Only lines
with a distance (i.e. not intra-zone flows) are included
Other lines:
angle_diff()
,
geo_toptail()
,
is_linepoint()
,
line2df()
,
line2points()
,
line_bearing()
,
line_breakup()
,
line_midpoint()
,
line_segment()
,
line_segment1()
,
line_via()
,
mats2line()
,
n_segments()
,
n_vertices()
,
points2line()
,
toptail_buff()
Example of OpenStreetMap road network
An sf object
Other data:
cents_sf
,
destinations_sf
,
flow
,
flow_dests
,
flowlines_sf
,
od_data_lines
,
od_data_routes
,
od_data_sample
,
read_table_builder()
,
route_network_sf
,
route_network_small
,
routes_fast_sf
,
routes_slow_sf
,
zones_sf
osm_net_example
osm_net_example
This function takes a series of overlapping lines and converts them into a single route network.
This function is intended as a replacement for overline() and is significantly faster especially on large datasets. However, it also uses more memory.
overline( sl, attrib, ncores = 1, simplify = TRUE, regionalise = 1e+09, quiet = ifelse(nrow(sl) < 1000, TRUE, FALSE), fun = sum ) overline2( sl, attrib, ncores = 1, simplify = TRUE, regionalise = 1e+07, quiet = ifelse(nrow(sl) < 1000, TRUE, FALSE), fun = sum )
overline( sl, attrib, ncores = 1, simplify = TRUE, regionalise = 1e+09, quiet = ifelse(nrow(sl) < 1000, TRUE, FALSE), fun = sum ) overline2( sl, attrib, ncores = 1, simplify = TRUE, regionalise = 1e+07, quiet = ifelse(nrow(sl) < 1000, TRUE, FALSE), fun = sum )
sl |
A spatial object representing routes on a transport network |
attrib |
character, column names in sl to be aggregated |
ncores |
integer, how many cores to use in parallel processing, default = 1 |
simplify |
logical, if TRUE group final segments back into lines, default = TRUE |
regionalise |
integer, during simplification regonalisation is used if the number of segments exceeds this value |
quiet |
Should the the function omit messages? |
fun |
Named list of functions to summaries the attributes by? |
The function can be used to estimate the amount of transport 'flow' at the
route segment level based on input datasets from routing services, for
example linestring geometries created with the route()
function.
The overline()
function breaks each line into many straight
segments and then looks for duplicated segments. Attributes are summed for
all duplicated segments, and if simplify is TRUE the segments with identical
attributes are recombined into linestrings.
The following arguments only apply to the sf
implementation of overline()
:
ncores
, the number of cores to use in parallel processing
simplify
, should the final segments be converted back into longer lines? The default
setting is TRUE
. simplify = FALSE
results in straight line segments consisting
of only 2 vertices (the start and end point),
resulting in a data frame with many more rows than the simplified results (see examples).
regionalise
the threshold number of rows above which
regionalisation is used (see details).
For sf
objects Regionalisation breaks the dataset into a 10 x 10 grid and
then performed the simplification across each grid. This significantly
reduces computation time for large datasets, but slightly increases the final
file size. For smaller datasets it increases computation time slightly but
reduces memory usage and so may also be useful.
A known limitation of this method is that overlapping segments of different lengths are not aggregated. This can occur when lines stop halfway down a road. Typically these errors are small, but some artefacts may remain within the resulting data.
For very large datasets nrow(x) > 1000000, memory usage can be significant. In these cases is is possible to overline subsets of the dataset, rbind the results together, and then overline again, to produce a final result.
Multicore support is only enabled for the regionalised simplification stage as it does not help with other stages.
An sf
object representing a route network
Barry Rowlingson
Malcolm Morgan
Morgan M and Lovelace R (2020). Travel flow aggregation: Nationally scalable methods for interactive and online visualisation of transport behaviour at the road network level. Environment and Planning B: Urban Analytics and City Science. July 2020. doi:10.1177/2399808320942779.
Rowlingson, B (2015). Overlaying lines and aggregating their values for overlapping segments. Reproducible question from https://gis.stackexchange.com. See https://gis.stackexchange.com/questions/139681/.
Other rnet:
gsection()
,
islines()
,
rnet_breakup_vertices()
,
rnet_group()
Other rnet:
gsection()
,
islines()
,
rnet_breakup_vertices()
,
rnet_group()
sl <- routes_fast_sf[2:4, ] sl$All <- flowlines_sf$All[2:4] rnet <- overline(sl = sl, attrib = "All") nrow(sl) nrow(rnet) plot(rnet) rnet_mean <- overline(sl, c("All", "av_incline"), fun = list(mean = mean, sum = sum)) plot(rnet_mean, lwd = rnet_mean$All_sum / mean(rnet_mean$All_sum)) rnet_sf_raw <- overline(sl, attrib = "length", simplify = FALSE) nrow(rnet_sf_raw) summary(n_vertices(rnet_sf_raw)) plot(rnet_sf_raw) rnet_sf_raw$n <- 1:nrow(rnet_sf_raw) plot(rnet_sf_raw[10:25, ])
sl <- routes_fast_sf[2:4, ] sl$All <- flowlines_sf$All[2:4] rnet <- overline(sl = sl, attrib = "All") nrow(sl) nrow(rnet) plot(rnet) rnet_mean <- overline(sl, c("All", "av_incline"), fun = list(mean = mean, sum = sum)) plot(rnet_mean, lwd = rnet_mean$All_sum / mean(rnet_mean$All_sum)) rnet_sf_raw <- overline(sl, attrib = "length", simplify = FALSE) nrow(rnet_sf_raw) summary(n_vertices(rnet_sf_raw)) plot(rnet_sf_raw) rnet_sf_raw$n <- 1:nrow(rnet_sf_raw) plot(rnet_sf_raw[10:25, ])
This function takes overlapping LINESTRING
s stored in an
sf
object and returns a route network composed of non-overlapping
geometries and aggregated values.
overline_intersection(sl, attrib, fun = sum)
overline_intersection(sl, attrib, fun = sum)
sl |
An |
attrib |
character, column names in sl to be aggregated |
fun |
Named list of functions to summaries the attributes by? |
routes_fast_sf$value <- 1 sl <- routes_fast_sf[4:6, ] attrib <- c("value", "length") rnet <- overline_intersection(sl = sl, attrib) plot(rnet, lwd = rnet$value) # A larger example sl <- routes_fast_sf[4:7, ] rnet <- overline_intersection(sl = sl, attrib = c("value", "length")) plot(rnet, lwd = rnet$value) rnet_sf <- overline(routes_fast_sf[4:7, ], attrib = c("value", "length")) plot(rnet_sf, lwd = rnet_sf$value) # An even larger example (not shown, takes time to run) # rnet = overline_intersection(routes_fast_sf, attrib = c("value", "length")) # rnet_sf <- overline(routes_fast_sf, attrib = c("value", "length"), buff_dist = 10) # plot(rnet$geometry, lwd = rnet$value * 2, col = "grey") # plot(rnet_sf$geometry, lwd = rnet_sf$value, add = TRUE)
routes_fast_sf$value <- 1 sl <- routes_fast_sf[4:6, ] attrib <- c("value", "length") rnet <- overline_intersection(sl = sl, attrib) plot(rnet, lwd = rnet$value) # A larger example sl <- routes_fast_sf[4:7, ] rnet <- overline_intersection(sl = sl, attrib = c("value", "length")) plot(rnet, lwd = rnet$value) rnet_sf <- overline(routes_fast_sf[4:7, ], attrib = c("value", "length")) plot(rnet_sf, lwd = rnet_sf$value) # An even larger example (not shown, takes time to run) # rnet = overline_intersection(routes_fast_sf, attrib = c("value", "length")) # rnet_sf <- overline(routes_fast_sf, attrib = c("value", "length"), buff_dist = 10) # plot(rnet$geometry, lwd = rnet$value * 2, col = "grey") # plot(rnet_sf$geometry, lwd = rnet_sf$value, add = TRUE)
Takes a series of geographical points and converts them into a spatial (linestring) object representing the potential flows, or 'spatial interaction', between every combination of points.
points2flow(p)
points2flow(p)
p |
A spatial (point) object |
Other od:
od2line()
,
od2odf()
,
od_aggregate_from()
,
od_aggregate_to()
,
od_coords()
,
od_coords2line()
,
od_id
,
od_id_order()
,
od_oneway()
,
od_to_odmatrix()
,
odmatrix_to_od()
,
points2odf()
flow_sf <- points2flow(cents_sf[1:4, ]) plot(flow_sf)
flow_sf <- points2flow(cents_sf[1:4, ]) plot(flow_sf)
This function makes that makes the creation of sf
objects with LINESTRING geometries easy.
points2line(p)
points2line(p)
p |
A spatial (points) obect or matrix representing the coordinates of points. |
Other lines:
angle_diff()
,
geo_toptail()
,
is_linepoint()
,
line2df()
,
line2points()
,
line_bearing()
,
line_breakup()
,
line_midpoint()
,
line_segment()
,
line_segment1()
,
line_via()
,
mats2line()
,
n_segments()
,
n_vertices()
,
onewaygeo()
,
toptail_buff()
l_sf <- points2line(cents_sf) plot(l_sf)
l_sf <- points2line(cents_sf) plot(l_sf)
Takes a series of geographical points and converts them into a data.frame representing the potential flows, or 'spatial interaction', between every combination of points.
points2odf(p)
points2odf(p)
p |
A spatial points object |
Other od:
od2line()
,
od2odf()
,
od_aggregate_from()
,
od_aggregate_to()
,
od_coords()
,
od_coords2line()
,
od_id
,
od_id_order()
,
od_oneway()
,
od_to_odmatrix()
,
odmatrix_to_od()
,
points2flow()
points2odf(cents_sf)
points2odf(cents_sf)
Returns a character vector of NE, SE, SW, NW corresponding to north-east, south-east quadrants respectively. If number_out is TRUE, returns numbers from 1:4, respectively.
quadrant(x, cent = NULL, number_out = FALSE)
quadrant(x, cent = NULL, number_out = FALSE)
x |
Object of class sf |
cent |
The centrepoint of the region of interest. Quadrants will be defined based on this point. By default this will be the geographic centroid of the zones. |
number_out |
Should the result be returned as a number? |
Other geo:
bbox_scale()
,
bind_sf()
,
geo_bb()
,
geo_bb_matrix()
,
geo_buffer()
,
geo_length()
,
geo_projected()
,
geo_select_aeq()
x = zones_sf (quads <- quadrant(x)) plot(x$geometry, col = factor(quads))
x = zones_sf (quads <- quadrant(x)) plot(x$geometry, col = factor(quads))
Import and format Australian Bureau of Statistics (ABS) TableBuilder files
read_table_builder(dataset, filetype = "csv", sheet = 1, removeTotal = TRUE)
read_table_builder(dataset, filetype = "csv", sheet = 1, removeTotal = TRUE)
dataset |
Either a dataframe containing the original data from TableBuilder or a character string containing the path of the unzipped TableBuilder file. |
filetype |
A character string containing the filetype. Valid values are 'csv', 'legacycsv' and 'xlsx' (default = 'csv'). Required even when dataset is a dataframe. Use 'legacycsv' for csv files derived from earlier versions of TableBuilder for which csv outputs were csv versions of the xlsx files. Current csv output from TableBuilder follow a more standard csv format. |
sheet |
An integer value containing the index of the sheet in the xlsx file (default = 1). |
removeTotal |
A boolean value. If TRUE removes the rows and columns with totals (default = TRUE). |
The Australian Bureau of Statistics (ABS) provides customised tables for census and other datasets in a format that is difficult to use in R because it contains rows with additional information. This function imports the original (unzipped) TableBuilder files in .csv or .xlsx format before creating an R dataframe with the data.
Note: we recommend using the readabs package for this purpose.
Other data:
cents_sf
,
destinations_sf
,
flow
,
flow_dests
,
flowlines_sf
,
od_data_lines
,
od_data_routes
,
od_data_sample
,
osm_net_example
,
route_network_sf
,
route_network_small
,
routes_fast_sf
,
routes_slow_sf
,
zones_sf
Add a node to route network
rnet_add_node(rnet, p)
rnet_add_node(rnet, p)
rnet |
A route network of the type generated by |
p |
A point represented by an |
sample_routes <- routes_fast_sf[2:6, NULL] sample_routes$value <- rep(1:3, length.out = 5) rnet <- overline2(sample_routes, attrib = "value") p <- sf::st_sfc(sf::st_point(c(-1.540, 53.826)), crs = sf::st_crs(rnet)) r_split <- route_split(rnet, p) plot(rnet$geometry, lwd = rnet$value * 5, col = "grey") plot(p, cex = 9, add = TRUE) plot(r_split, col = 1:nrow(r_split), add = TRUE, lwd = r_split$value)
sample_routes <- routes_fast_sf[2:6, NULL] sample_routes$value <- rep(1:3, length.out = 5) rnet <- overline2(sample_routes, attrib = "value") p <- sf::st_sfc(sf::st_point(c(-1.540, 53.826)), crs = sf::st_crs(rnet)) r_split <- route_split(rnet, p) plot(rnet$geometry, lwd = rnet$value * 5, col = "grey") plot(p, cex = 9, add = TRUE) plot(r_split, col = 1:nrow(r_split), add = TRUE, lwd = r_split$value)
Get points at the beginner and end of linestrings
rnet_boundary_points(rnet) rnet_boundary_df(rnet) rnet_boundary_unique(rnet) rnet_boundary_points_lwgeom(rnet) rnet_duplicated_vertices(rnet, n = 2)
rnet_boundary_points(rnet) rnet_boundary_df(rnet) rnet_boundary_unique(rnet) rnet_boundary_points_lwgeom(rnet) rnet_duplicated_vertices(rnet, n = 2)
rnet |
An sf or sfc object with LINESTRING geometry representing a route network. |
n |
The minimum number of time a vertex must be duplicated to be returned |
has_sfheaders <- requireNamespace("sfheaders", quietly = TRUE) if(has_sfheaders) { rnet <- rnet_roundabout bp1 <- rnet_boundary_points(rnet) bp2 <- line2points(rnet) # slower version with lwgeom bp3 <- rnet_boundary_points_lwgeom(rnet) # slower version with lwgeom bp4 <- rnet_boundary_unique(rnet) nrow(bp1) nrow(bp3) identical(sort(sf::st_coordinates(bp1)), sort(sf::st_coordinates(bp2))) identical(sort(sf::st_coordinates(bp3)), sort(sf::st_coordinates(bp4))) plot(rnet$geometry) plot(bp3, add = TRUE) }
has_sfheaders <- requireNamespace("sfheaders", quietly = TRUE) if(has_sfheaders) { rnet <- rnet_roundabout bp1 <- rnet_boundary_points(rnet) bp2 <- line2points(rnet) # slower version with lwgeom bp3 <- rnet_boundary_points_lwgeom(rnet) # slower version with lwgeom bp4 <- rnet_boundary_unique(rnet) nrow(bp1) nrow(bp3) identical(sort(sf::st_coordinates(bp1)), sort(sf::st_coordinates(bp2))) identical(sort(sf::st_coordinates(bp3)), sort(sf::st_coordinates(bp4))) plot(rnet$geometry) plot(bp3, add = TRUE) }
This function breaks up a LINESTRING geometry into multiple LINESTRING(s). It is used mainly for preserving routability of an object that is created using Open Street Map data. See details, stplanr/issues/282, and stplanr/issues/416.
rnet_breakup_vertices(rnet, verbose = FALSE)
rnet_breakup_vertices(rnet, verbose = FALSE)
rnet |
An sf or sfc object with LINESTRING geometry representing a route network. |
verbose |
Boolean. If TRUE, the function prints additional messages. |
A LINESTRING geometry is broken-up when one of the two following conditions are met:
two or more LINESTRINGS share a POINT which is a boundary point for some LINESTRING(s), but not all of them (see the rnet_roundabout example);
two or more LINESTRINGS share a POINT which is not in the boundary of any LINESTRING (see the rnet_cycleway_intersection example).
The problem with the first example is that, according to algorithm behind
SpatialLinesNetwork()
, two LINESTRINGS are connected if and only if they
share at least one point in their boundaries. The roads and the roundabout
are clearly connected in the "real" world but the corresponding LINESTRING
objects do not share two distinct boundary points. In fact, by Open Street
Map standards, a roundabout is represented as a closed and circular
LINESTRING, and this implies that the roundabout is not connected to the
other roads according to SpatialLinesNetwork()
definition. By the same
reasoning, the roads in the second example are clearly connected in the
"real" world, but they do not share any point in their boundaries. This
function is used to solve this type of problem.
An sf or sfc object with LINESTRING geometry created after breaking up the input object.
Other rnet:
gsection()
,
islines()
,
overline()
,
rnet_group()
library(sf) def_par <- par(no.readonly = TRUE) par(mar = rep(0, 4)) # Check the geometry of the roundabout example. The dots represent the # boundary points of the LINESTRINGS. The "isolated" red point in the # top-left is the boundary point of the roundabout, and it is not shared # with any other street. plot(st_geometry(rnet_roundabout), lwd = 2, col = rainbow(nrow(rnet_roundabout))) boundary_points <- st_geometry(line2points(rnet_roundabout)) points_cols <- rep(rainbow(nrow(rnet_roundabout)), each = 2) plot(boundary_points, pch = 16, add = TRUE, col = points_cols, cex = 2) # Clean the roundabout example. rnet_roundabout_clean <- rnet_breakup_vertices(rnet_roundabout) plot(st_geometry(rnet_roundabout_clean), lwd = 2, col = rainbow(nrow(rnet_roundabout_clean))) boundary_points <- st_geometry(line2points(rnet_roundabout_clean)) points_cols <- rep(rainbow(nrow(rnet_roundabout_clean)), each = 2) plot(boundary_points, pch = 16, add = TRUE, col = points_cols) # The roundabout is now routable since it was divided into multiple pieces # (one for each colour), which, according to SpatialLinesNetwork() function, # are connected. # Check the geometry of the overpasses example. This example is used to test # that this function does not create any spurious intersection. plot(st_geometry(rnet_overpass), lwd = 2, col = rainbow(nrow(rnet_overpass))) boundary_points <- st_geometry(line2points(rnet_overpass)) points_cols <- rep(rainbow(nrow(rnet_overpass)), each = 2) plot(boundary_points, pch = 16, add = TRUE, col = points_cols, cex = 2) # At the moment the network is not routable since one of the underpasses is # not connected to the other streets. # Check interactively. # mapview::mapview(rnet_overpass) # Clean the network. It should not create any spurious intersection between # roads located at different heights. rnet_overpass_clean <- rnet_breakup_vertices(rnet_overpass) plot(st_geometry(rnet_overpass_clean), lwd = 2, col = rainbow(nrow(rnet_overpass_clean))) # Check interactively. # mapview::mapview(rnet_overpass) # Check the geometry of the cycleway_intersection example. The black dots # represent the boundary points and we can see that the two roads are not # connected according to SpatialLinesNetwork() function. plot( rnet_cycleway_intersection$geometry, lwd = 2, col = rainbow(nrow(rnet_cycleway_intersection)), cex = 2 ) plot(st_geometry(line2points(rnet_cycleway_intersection)), pch = 16, add = TRUE) # Check interactively # mapview::mapview(rnet_overpass) # Clean the rnet object and plot the result. rnet_cycleway_intersection_clean <- rnet_breakup_vertices(rnet_cycleway_intersection) plot( rnet_cycleway_intersection_clean$geometry, lwd = 2, col = rainbow(nrow(rnet_cycleway_intersection_clean)), cex = 2 ) plot(st_geometry(line2points(rnet_cycleway_intersection_clean)), pch = 16, add = TRUE) par(def_par)
library(sf) def_par <- par(no.readonly = TRUE) par(mar = rep(0, 4)) # Check the geometry of the roundabout example. The dots represent the # boundary points of the LINESTRINGS. The "isolated" red point in the # top-left is the boundary point of the roundabout, and it is not shared # with any other street. plot(st_geometry(rnet_roundabout), lwd = 2, col = rainbow(nrow(rnet_roundabout))) boundary_points <- st_geometry(line2points(rnet_roundabout)) points_cols <- rep(rainbow(nrow(rnet_roundabout)), each = 2) plot(boundary_points, pch = 16, add = TRUE, col = points_cols, cex = 2) # Clean the roundabout example. rnet_roundabout_clean <- rnet_breakup_vertices(rnet_roundabout) plot(st_geometry(rnet_roundabout_clean), lwd = 2, col = rainbow(nrow(rnet_roundabout_clean))) boundary_points <- st_geometry(line2points(rnet_roundabout_clean)) points_cols <- rep(rainbow(nrow(rnet_roundabout_clean)), each = 2) plot(boundary_points, pch = 16, add = TRUE, col = points_cols) # The roundabout is now routable since it was divided into multiple pieces # (one for each colour), which, according to SpatialLinesNetwork() function, # are connected. # Check the geometry of the overpasses example. This example is used to test # that this function does not create any spurious intersection. plot(st_geometry(rnet_overpass), lwd = 2, col = rainbow(nrow(rnet_overpass))) boundary_points <- st_geometry(line2points(rnet_overpass)) points_cols <- rep(rainbow(nrow(rnet_overpass)), each = 2) plot(boundary_points, pch = 16, add = TRUE, col = points_cols, cex = 2) # At the moment the network is not routable since one of the underpasses is # not connected to the other streets. # Check interactively. # mapview::mapview(rnet_overpass) # Clean the network. It should not create any spurious intersection between # roads located at different heights. rnet_overpass_clean <- rnet_breakup_vertices(rnet_overpass) plot(st_geometry(rnet_overpass_clean), lwd = 2, col = rainbow(nrow(rnet_overpass_clean))) # Check interactively. # mapview::mapview(rnet_overpass) # Check the geometry of the cycleway_intersection example. The black dots # represent the boundary points and we can see that the two roads are not # connected according to SpatialLinesNetwork() function. plot( rnet_cycleway_intersection$geometry, lwd = 2, col = rainbow(nrow(rnet_cycleway_intersection)), cex = 2 ) plot(st_geometry(line2points(rnet_cycleway_intersection)), pch = 16, add = TRUE) # Check interactively # mapview::mapview(rnet_overpass) # Clean the rnet object and plot the result. rnet_cycleway_intersection_clean <- rnet_breakup_vertices(rnet_cycleway_intersection) plot( rnet_cycleway_intersection_clean$geometry, lwd = 2, col = rainbow(nrow(rnet_cycleway_intersection_clean)), cex = 2 ) plot(st_geometry(line2points(rnet_cycleway_intersection_clean)), pch = 16, add = TRUE) par(def_par)
This function takes an sf object representing a road network and returns only the parts of the network that are in the largest group.
rnet_connected(rnet)
rnet_connected(rnet)
rnet |
An sf object representing a road network |
An sf object representing the largest group in the network
rnet <- rnet_breakup_vertices(stplanr::osm_net_example) rnet_largest_group <- rnet_connected(rnet) plot(rnet$geometry) plot(rnet_largest_group$geometry)
rnet <- rnet_breakup_vertices(stplanr::osm_net_example) rnet_largest_group <- rnet_connected(rnet) plot(rnet$geometry) plot(rnet_largest_group$geometry)
See data-raw/rnet_cycleway_intersection
for details on how this was created.
A sf object
rnet_cycleway_intersection
rnet_cycleway_intersection
Extract nodes from route network
rnet_get_nodes(rnet, p = NULL)
rnet_get_nodes(rnet, p = NULL)
rnet |
A route network of the type generated by |
p |
A point represented by an |
rnet_get_nodes(route_network_sf)
rnet_get_nodes(route_network_sf)
This function assigns linestring features, many of which in an
sf
object can form route networks, into groups.
By default, the function igraph::clusters()
is used to determine
group membership, but any igraph::cluster*()
function can be used.
See examples and the web page
igraph.org/r/doc/communities.html
for more information. From that web page, the following clustering
functions are available:
rnet_group(rnet, ...) ## Default S3 method: rnet_group(rnet, ...) ## S3 method for class 'sfc' rnet_group( rnet, cluster_fun = igraph::clusters, d = NULL, as.undirected = TRUE, ... ) ## S3 method for class 'sf' rnet_group( rnet, cluster_fun = igraph::clusters, d = NULL, as.undirected = TRUE, ... )
rnet_group(rnet, ...) ## Default S3 method: rnet_group(rnet, ...) ## S3 method for class 'sfc' rnet_group( rnet, cluster_fun = igraph::clusters, d = NULL, as.undirected = TRUE, ... ) ## S3 method for class 'sf' rnet_group( rnet, cluster_fun = igraph::clusters, d = NULL, as.undirected = TRUE, ... )
rnet |
An sf, sfc, or sfNetwork object representing a route network. |
... |
Arguments passed to other methods. |
cluster_fun |
The clustering function to use. Various clustering functions
are available in the |
d |
Optional distance variable used to classify segments that are
close (within a certain distance specified by |
as.undirected |
Coerce the graph created internally into an undirected
graph with |
cluster_edge_betweenness, cluster_fast_greedy, cluster_label_prop,
cluster_leading_eigen, cluster_louvain, cluster_optimal, cluster_spinglass, cluster_walktrap
If the input rnet is an sf/sfc object, it returns an integer vector reporting the groups of each network element. If the input is an sfNetwork object, it returns an sfNetwork object with an extra column called rnet_group representing the groups of each network element. In the latter case, the connectivity of the spatial object is derived from the sfNetwork object.
These functions rely on the igraph package. If igraph is not installed, the function will return a message.
Other rnet:
gsection()
,
islines()
,
overline()
,
rnet_breakup_vertices()
if (requireNamespace("igraph", quietly = TRUE)) { rnet <- rnet_breakup_vertices(stplanr::osm_net_example) rnet$group <- rnet_group(rnet) plot(rnet["group"]) # mapview::mapview(rnet["group"]) rnet$group_25m <- rnet_group(rnet, d = 25) plot(rnet["group_25m"]) rnet$group_walktrap <- rnet_group(rnet, igraph::cluster_walktrap) plot(rnet["group_walktrap"]) rnet$group_louvain <- rnet_group(rnet, igraph::cluster_louvain) plot(rnet["group_louvain"]) rnet$group_fast_greedy <- rnet_group(rnet, igraph::cluster_fast_greedy) plot(rnet["group_fast_greedy"]) }
if (requireNamespace("igraph", quietly = TRUE)) { rnet <- rnet_breakup_vertices(stplanr::osm_net_example) rnet$group <- rnet_group(rnet) plot(rnet["group"]) # mapview::mapview(rnet["group"]) rnet$group_25m <- rnet_group(rnet, d = 25) plot(rnet["group_25m"]) rnet$group_walktrap <- rnet_group(rnet, igraph::cluster_walktrap) plot(rnet["group_walktrap"]) rnet$group_louvain <- rnet_group(rnet, igraph::cluster_louvain) plot(rnet["group_louvain"]) rnet$group_fast_greedy <- rnet_group(rnet, igraph::cluster_fast_greedy) plot(rnet["group_fast_greedy"]) }
Join function that adds columns to a
'target' route network sf
object from a 'source' route
network that contains the base geometry, e.g. from OSM
rnet_join( rnet_x, rnet_y, dist = 5, length_y = TRUE, key_column = 1, subset_x = FALSE, dist_subset = NULL, segment_length = 0, endCapStyle = "FLAT", contains = TRUE, max_angle_diff = NULL, crs = geo_select_aeq(rnet_x), ... )
rnet_join( rnet_x, rnet_y, dist = 5, length_y = TRUE, key_column = 1, subset_x = FALSE, dist_subset = NULL, segment_length = 0, endCapStyle = "FLAT", contains = TRUE, max_angle_diff = NULL, crs = geo_select_aeq(rnet_x), ... )
rnet_x |
Target route network, the output will have the same geometries as features in this object. |
rnet_y |
Source route network. Columns from this route network object will be copied across to the new network. |
dist |
The buffer width around rnet_y in meters. 1 m by default. |
length_y |
Add a new column called |
key_column |
The index of the key (unique identifier) column in |
subset_x |
Subset the source route network by the target network before
creating buffers? This can lead to faster and better results. Default:
|
dist_subset |
The buffer distance in m to apply when breaking up the
source object |
segment_length |
Should the source route network be split?
|
endCapStyle |
Type of buffer. See |
contains |
Should the join be based on |
max_angle_diff |
The maximum angle difference between x and y nets for a value to be returned |
crs |
The CRS to use for the buffer operation. See |
... |
Additional arguments passed to |
The output is an sf object containing polygons representing
buffers around the route network in rnet_x
.
The examples below demonstrate how to join attributes from
a route network object created with the function overline()
onto
OSM geometries.
Note: The main purpose of this function is to join an ID from rnet_x
onto rnet_y
. Subsequent steps, e.g. with dplyr::inner_join()
are needed to join the attributes back onto rnet_x
.
There are rarely 1-to-1 relationships between spatial network geometries
so we take care when using this function.
See #505 for details and a link to an interactive example of inputs and outputs shown below.
library(sf) library(dplyr) plot(osm_net_example$geometry, lwd = 5, col = "grey", add = TRUE) plot(route_network_small["flow"], add = TRUE) rnetj <- rnet_join(osm_net_example, route_network_small, dist = 9) rnetj2 <- rnet_join(osm_net_example, route_network_small, dist = 9, segment_length = 10) # library(mapview) # mapview(rnetj, zcol = "flow") + # mapview(rnetj2, zcol = "flow") + # mapview(route_network_small, zcol = "flow") plot(sf::st_geometry(rnetj)) plot(rnetj["flow"], add = TRUE) plot(rnetj2["flow"], add = TRUE) plot(route_network_small["flow"], add = TRUE) summary(rnetj2$length_y) rnetj_summary <- rnetj2 %>% filter(!is.na(length_y)) %>% sf::st_drop_geometry() %>% group_by(osm_id) %>% summarise( flow = weighted.mean(flow, length_y, na.rm = TRUE), ) osm_joined_rnet <- dplyr::left_join(osm_net_example, rnetj_summary) plot(sf::st_geometry(route_network_small)) plot(route_network_small["flow"], lwd = 3, add = TRUE) plot(sf::st_geometry(osm_joined_rnet), add = TRUE) # plot(osm_joined_rnet[c("flow")], lwd = 9, add = TRUE) # Improve fit between geometries and performance by subsetting rnet_x osm_subset <- rnet_subset(osm_net_example, route_network_small, dist = 5) osm_joined_rnet <- dplyr::left_join(osm_subset, rnetj_summary) plot(route_network_small["flow"]) # plot(osm_joined_rnet[c("flow")]) # mapview(joined_network) + # mapview(route_network_small)
library(sf) library(dplyr) plot(osm_net_example$geometry, lwd = 5, col = "grey", add = TRUE) plot(route_network_small["flow"], add = TRUE) rnetj <- rnet_join(osm_net_example, route_network_small, dist = 9) rnetj2 <- rnet_join(osm_net_example, route_network_small, dist = 9, segment_length = 10) # library(mapview) # mapview(rnetj, zcol = "flow") + # mapview(rnetj2, zcol = "flow") + # mapview(route_network_small, zcol = "flow") plot(sf::st_geometry(rnetj)) plot(rnetj["flow"], add = TRUE) plot(rnetj2["flow"], add = TRUE) plot(route_network_small["flow"], add = TRUE) summary(rnetj2$length_y) rnetj_summary <- rnetj2 %>% filter(!is.na(length_y)) %>% sf::st_drop_geometry() %>% group_by(osm_id) %>% summarise( flow = weighted.mean(flow, length_y, na.rm = TRUE), ) osm_joined_rnet <- dplyr::left_join(osm_net_example, rnetj_summary) plot(sf::st_geometry(route_network_small)) plot(route_network_small["flow"], lwd = 3, add = TRUE) plot(sf::st_geometry(osm_joined_rnet), add = TRUE) # plot(osm_joined_rnet[c("flow")], lwd = 9, add = TRUE) # Improve fit between geometries and performance by subsetting rnet_x osm_subset <- rnet_subset(osm_net_example, route_network_small, dist = 5) osm_joined_rnet <- dplyr::left_join(osm_subset, rnetj_summary) plot(route_network_small["flow"]) # plot(osm_joined_rnet[c("flow")]) # mapview(joined_network) + # mapview(route_network_small)
This is a small wrapper around rnet_join()
.
In most cases we recommend using rnet_join()
directly,
as it gives more control over the results
rnet_merge( rnet_x, rnet_y, dist = 5, funs = NULL, sum_flows = TRUE, crs = geo_select_aeq(rnet_x), ... )
rnet_merge( rnet_x, rnet_y, dist = 5, funs = NULL, sum_flows = TRUE, crs = geo_select_aeq(rnet_x), ... )
rnet_x |
Target route network, the output will have the same geometries as features in this object. |
rnet_y |
Source route network. Columns from this route network object will be copied across to the new network. |
dist |
The buffer width around rnet_y in meters. 1 m by default. |
funs |
A named list of functions to apply to named columns, e.g.:
|
sum_flows |
Should flows be summed? |
crs |
The CRS to use for the buffer operation. See |
... |
Additional arguments passed to |
An sf object with the same geometry as rnet_x
# The source object: rnet_y <- route_network_small["flow"] # The target object rnet_x <- rnet_subset(osm_net_example[1], rnet_y) plot(rnet_x$geometry, lwd = 5) plot(rnet_y$geometry, add = TRUE, col = "red", lwd = 2) rnet_y$quietness <- rnorm(nrow(rnet_y)) funs <- list(flow = sum, quietness = mean) rnet_merged <- rnet_merge(rnet_x[1], rnet_y[c("flow", "quietness")], dist = 9, segment_length = 20, funs = funs ) plot(rnet_y$geometry, lwd = 5, col = "lightgrey") plot(rnet_merged["flow"], add = TRUE, lwd = 2) # # With a different CRS rnet_xp <- sf::st_transform(rnet_x, "EPSG:27700") rnet_yp <- sf::st_transform(rnet_y, "EPSG:27700") rnet_merged <- rnet_merge(rnet_xp[1], rnet_yp[c("flow", "quietness")], dist = 9, segment_length = 20, funs = funs ) plot(rnet_merged["flow"]) # rnet_merged2 = rnet_merge(rnet_x[1], rnet_y[c("flow", "quietness")], # dist = 9, segment_length = 20, funs = funs, # crs = "EPSG:27700") # waldo::compare(rnet_merged, rnet_merged2) # plot(rnet_merged$flow, rnet_merged2$flow) # # Larger example # system("gh release list") # system("gh release upload v1.0.2 rnet_*") # List the files released in v1.0.2: # system("gh release download v1.0.2") # rnet_x = sf::read_sf("rnet_x_ed.geojson") # rnet_y = sf::read_sf("rnet_y_ed.geojson") # rnet_merged = rnet_merge(rnet_x, rnet_y, dist = 9, segment_length = 20, funs = funs)
# The source object: rnet_y <- route_network_small["flow"] # The target object rnet_x <- rnet_subset(osm_net_example[1], rnet_y) plot(rnet_x$geometry, lwd = 5) plot(rnet_y$geometry, add = TRUE, col = "red", lwd = 2) rnet_y$quietness <- rnorm(nrow(rnet_y)) funs <- list(flow = sum, quietness = mean) rnet_merged <- rnet_merge(rnet_x[1], rnet_y[c("flow", "quietness")], dist = 9, segment_length = 20, funs = funs ) plot(rnet_y$geometry, lwd = 5, col = "lightgrey") plot(rnet_merged["flow"], add = TRUE, lwd = 2) # # With a different CRS rnet_xp <- sf::st_transform(rnet_x, "EPSG:27700") rnet_yp <- sf::st_transform(rnet_y, "EPSG:27700") rnet_merged <- rnet_merge(rnet_xp[1], rnet_yp[c("flow", "quietness")], dist = 9, segment_length = 20, funs = funs ) plot(rnet_merged["flow"]) # rnet_merged2 = rnet_merge(rnet_x[1], rnet_y[c("flow", "quietness")], # dist = 9, segment_length = 20, funs = funs, # crs = "EPSG:27700") # waldo::compare(rnet_merged, rnet_merged2) # plot(rnet_merged$flow, rnet_merged2$flow) # # Larger example # system("gh release list") # system("gh release upload v1.0.2 rnet_*") # List the files released in v1.0.2: # system("gh release download v1.0.2") # rnet_x = sf::read_sf("rnet_x_ed.geojson") # rnet_y = sf::read_sf("rnet_y_ed.geojson") # rnet_merged = rnet_merge(rnet_x, rnet_y, dist = 9, segment_length = 20, funs = funs)
See data-raw/rnet_overpass.R
for details on how this was created.
A sf object
rnet_overpass
rnet_overpass
See data-raw/rnet_roundabout.R
for details on how this was created.
A sf object
rnet_roundabout
rnet_roundabout
Subset one route network based on overlaps with another
rnet_subset( rnet_x, rnet_y, dist = 10, crop = TRUE, min_length = 20, rm_disconnected = TRUE )
rnet_subset( rnet_x, rnet_y, dist = 10, crop = TRUE, min_length = 20, rm_disconnected = TRUE )
rnet_x |
The route network to be subset |
rnet_y |
The subsetting route network |
dist |
The buffer width around y in meters. 1 m by default. |
crop |
Crop |
min_length |
Segments shorter than this multiple of dist and which were longer before the cropping process will be removed. 3 by default. |
rm_disconnected |
Remove ways that are |
rnet_x <- osm_net_example[1] rnet_y <- route_network_small["flow"] plot(rnet_x$geometry, lwd = 5) plot(rnet_y$geometry, add = TRUE, col = "red", lwd = 3) rnet_x_subset <- rnet_subset(rnet_x, rnet_y) plot(rnet_x_subset, add = TRUE, col = "blue")
rnet_x <- osm_net_example[1] rnet_y <- route_network_small["flow"] plot(rnet_x$geometry, lwd = 5) plot(rnet_y$geometry, add = TRUE, col = "red", lwd = 3) rnet_x_subset <- rnet_subset(rnet_x, rnet_y) plot(rnet_x_subset, add = TRUE, col = "blue")
Takes origins and destinations, finds the optimal routes between them and returns the result as a spatial (sf or sp) object. The definition of optimal depends on the routing function used
route( from = NULL, to = NULL, l = NULL, route_fun = cyclestreets::journey, wait = 0, n_print = 10, list_output = FALSE, cl = NULL, ... )
route( from = NULL, to = NULL, l = NULL, route_fun = cyclestreets::journey, wait = 0, n_print = 10, list_output = FALSE, cl = NULL, ... )
from |
An object representing origins
(if lines are provided as the first argument, from is assigned to |
to |
An object representing destinations |
l |
A spatial (linestring) object |
route_fun |
A routing function to be used for converting the lines to routes |
wait |
How long to wait between routes? 0 seconds by default, can be useful when sending requests to rate limited APIs. |
n_print |
A number specifying how frequently progress updates should be shown |
list_output |
If FALSE (default) assumes spatial (linestring) object output. Set to TRUE to save output as a list. |
cl |
Cluster |
... |
Arguments passed to the routing function |
Other routes:
route_dodgr()
,
route_osrm()
Other routes:
route_dodgr()
,
route_osrm()
# Todo: add examples
# Todo: add examples
This function assumes that elevations and distances are in the same units.
route_average_gradient(elevations, distances)
route_average_gradient(elevations, distances)
elevations |
Elevations, e.g. those provided by the |
distances |
Distances, e.g. those provided by the |
Other route_funs:
route_rolling_average()
,
route_rolling_diff()
,
route_rolling_gradient()
,
route_sequential_dist()
,
route_slope_matrix()
,
route_slope_vector()
r1 <- od_data_routes[od_data_routes$route_number == 2, ] elevations <- r1$elevations distances <- r1$distances route_average_gradient(elevations, distances) # an average of a 4% gradient
r1 <- od_data_routes[od_data_routes$route_number == 2, ] elevations <- r1$elevations distances <- r1$distances route_average_gradient(elevations, distances) # an average of a 4% gradient
See bikecitizens.net for an interactive version of the routing engine used by BikeCitizens.
route_bikecitizens( from = NULL, to = NULL, base_url = "https://map.bikecitizens.net/api/v1/locations/route.json", cccode = "gb-leeds", routing_profile = "balanced", bike_profile = "citybike", from_lat = 53.8265, from_lon = -1.576195, to_lat = 53.80025, to_lon = -1.51577 )
route_bikecitizens( from = NULL, to = NULL, base_url = "https://map.bikecitizens.net/api/v1/locations/route.json", cccode = "gb-leeds", routing_profile = "balanced", bike_profile = "citybike", from_lat = 53.8265, from_lon = -1.576195, to_lat = 53.80025, to_lon = -1.51577 )
from |
A numeric vector representing the start point |
to |
A numeric vector representing the end point |
base_url |
The base URL for the routes |
cccode |
The city code for the routes |
routing_profile |
What type of routing to use? |
bike_profile |
What type of bike? |
from_lat |
Latitude of origin |
from_lon |
Longitude of origin |
to_lat |
Latitude of destination |
to_lon |
Longitude of destination |
See the bikecitizens.R file in the data-raw directory of the package's development repository for details on usage and examples.
Route on local data using the dodgr package
route_dodgr(from = NULL, to = NULL, l = NULL, net = NULL)
route_dodgr(from = NULL, to = NULL, l = NULL, net = NULL)
from |
An object representing origins
(if lines are provided as the first argument, from is assigned to |
to |
An object representing destinations |
l |
A spatial (linestring) object |
net |
sf object representing the route network |
Other routes:
route()
,
route_osrm()
if (requireNamespace("dodgr")) { from <- c(-1.5327, 53.8006) # from <- geo_code("pedallers arms leeds") to <- c(-1.5279, 53.8044) # to <- geo_code("gzing") # next 4 lines were used to generate `stplanr::osm_net_example` # pts <- rbind(from, to) # colnames(pts) <- c("X", "Y") # net <- dodgr::dodgr_streetnet(pts = pts, expand = 0.1) # osm_net_example <- net[c("highway", "name", "lanes", "maxspeed")] r <- route_dodgr(from, to, net = osm_net_example) plot(osm_net_example$geometry) plot(r$geometry, add = TRUE, col = "red", lwd = 5) }
if (requireNamespace("dodgr")) { from <- c(-1.5327, 53.8006) # from <- geo_code("pedallers arms leeds") to <- c(-1.5279, 53.8044) # to <- geo_code("gzing") # next 4 lines were used to generate `stplanr::osm_net_example` # pts <- rbind(from, to) # colnames(pts) <- c("X", "Y") # net <- dodgr::dodgr_streetnet(pts = pts, expand = 0.1) # osm_net_example <- net[c("highway", "name", "lanes", "maxspeed")] r <- route_dodgr(from, to, net = osm_net_example) plot(osm_net_example$geometry) plot(r$geometry, add = TRUE, col = "red", lwd = 5) }
Find the shortest path using Google's services.
See the mapsapi
package for details.
route_google(from, to, mode = "walking", key = Sys.getenv("GOOGLE"), ...)
route_google(from, to, mode = "walking", key = Sys.getenv("GOOGLE"), ...)
from |
An object representing origins
(if lines are provided as the first argument, from is assigned to |
to |
An object representing destinations |
mode |
Mode of transport, walking (default), bicycling, transit, or driving |
key |
Google key. By default it is |
... |
Arguments passed to the routing function |
## Not run: from <- "university of leeds" to <- "pedallers arms leeds" r <- route(from, to, route_fun = cyclestreets::journey) plot(r) # r_google <- route(from, to, route_fun = mapsapi::mp_directions) # fails r_google1 <- route_google(from, to) plot(r_google1) r_google <- route(from, to, route_fun = route_google) ## End(Not run)
## Not run: from <- "university of leeds" to <- "pedallers arms leeds" r <- route(from, to, route_fun = cyclestreets::journey) plot(r) # r_google <- route(from, to, route_fun = mapsapi::mp_directions) # fails r_google1 <- route_google(from, to) plot(r_google1) r_google <- route(from, to, route_fun = route_google) ## End(Not run)
This function was written as a drop-in replacement for sf::st_nearest_feature()
,
which only works with recent versions of GEOS.
route_nearest_point(r, p, id_out = FALSE)
route_nearest_point(r, p, id_out = FALSE)
r |
The input route object from which the nearest route is to be found |
p |
The point whose nearest route will be found |
id_out |
Should the index of the matching feature be returned? |
r <- routes_fast_sf[2:6, NULL] p <- sf::st_sfc(sf::st_point(c(-1.540, 53.826)), crs = sf::st_crs(r)) route_nearest_point(r, p, id_out = TRUE) r_nearest <- route_nearest_point(r, p) plot(r$geometry) plot(p, add = TRUE) plot(r_nearest, lwd = 5, add = TRUE)
r <- routes_fast_sf[2:6, NULL] p <- sf::st_sfc(sf::st_point(c(-1.540, 53.826)), crs = sf::st_crs(r)) route_nearest_point(r, p, id_out = TRUE) r_nearest <- route_nearest_point(r, p) plot(r$geometry) plot(p, add = TRUE) plot(r_nearest, lwd = 5, add = TRUE)
The flow of commuters using different segments of the road network represented in the
flowlines_sf()
and routes_fast_sf()
datasets
A spatial lines dataset 80 rows and 1 column
Other data:
cents_sf
,
destinations_sf
,
flow
,
flow_dests
,
flowlines_sf
,
od_data_lines
,
od_data_routes
,
od_data_sample
,
osm_net_example
,
read_table_builder()
,
route_network_small
,
routes_fast_sf
,
routes_slow_sf
,
zones_sf
The flow between randomly selected vertices on the osm_net_example
.
See data-raw/route_network_small.R
for details.
A spatial lines dataset with one column: flow
Other data:
cents_sf
,
destinations_sf
,
flow
,
flow_dests
,
flowlines_sf
,
od_data_lines
,
od_data_routes
,
od_data_sample
,
osm_net_example
,
read_table_builder()
,
route_network_sf
,
routes_fast_sf
,
routes_slow_sf
,
zones_sf
This function is a simplified and (because it uses GeoJSON not binary polyline format)
slower R interface to OSRM routing services compared with the excellent
osrm::osrmRoute()
function (which can be used via the route()
) function.
route_osrm( from, to, osrm.server = "https://routing.openstreetmap.de/", osrm.profile = "foot" )
route_osrm( from, to, osrm.server = "https://routing.openstreetmap.de/", osrm.profile = "foot" )
from |
An object representing origins
(if lines are provided as the first argument, from is assigned to |
to |
An object representing destinations |
osrm.server |
The base URL of the routing server. getOption("osrm.server") by default. |
osrm.profile |
The routing profile to use, e.g. "car", "bike" or "foot" (when using the routing.openstreetmap.de test server). getOption("osrm.profile") by default. |
profile |
Which routing profile to use? One of "foot" (default) "bike" or "car" for the default open server. |
Other routes:
route()
,
route_dodgr()
# Examples no longer working due to API being down # l1 = od_data_lines[49, ] # l1m = od_coords(l1) # from = l1m[, 1:2] # to = l1m[, 3:4] # if(curl::has_internet()) { # r_foot = route_osrm(from, to) # r_bike = route_osrm(from, to, osrm.profile = "bike") # r_car = route_osrm(from, to, osrm.profile = "car") # plot(r_foot$geometry, lwd = 9, col = "grey") # plot(r_bike, col = "blue", add = TRUE) # plot(r_car, col = "red", add = TRUE) # }
# Examples no longer working due to API being down # l1 = od_data_lines[49, ] # l1m = od_coords(l1) # from = l1m[, 1:2] # to = l1m[, 3:4] # if(curl::has_internet()) { # r_foot = route_osrm(from, to) # r_bike = route_osrm(from, to, osrm.profile = "bike") # r_car = route_osrm(from, to, osrm.profile = "car") # plot(r_foot$geometry, lwd = 9, col = "grey") # plot(r_bike, col = "blue", add = TRUE) # plot(r_car, col = "red", add = TRUE) # }
This function calculates a simple rolling mean in base R. It is useful for calculating route characteristics such as mean distances of segments and changes in gradient.
route_rolling_average(x, n = 3)
route_rolling_average(x, n = 3)
x |
Numeric vector to smooth |
n |
The window size of the smoothing function. The default, 3, will take the mean of values before, after and including each value. |
Other route_funs:
route_average_gradient()
,
route_rolling_diff()
,
route_rolling_gradient()
,
route_sequential_dist()
,
route_slope_matrix()
,
route_slope_vector()
y <- od_data_routes$elevations[od_data_routes$route_number == 2] y route_rolling_average(y) route_rolling_average(y, n = 1) route_rolling_average(y, n = 2) route_rolling_average(y, n = 3)
y <- od_data_routes$elevations[od_data_routes$route_number == 2] y route_rolling_average(y) route_rolling_average(y, n = 1) route_rolling_average(y, n = 2) route_rolling_average(y, n = 3)
This function calculates a simple rolling mean in base R. It is useful for calculating route characteristics such as mean distances of segments and changes in gradient.
route_rolling_diff(x, lag = 1, abs = TRUE)
route_rolling_diff(x, lag = 1, abs = TRUE)
x |
Numeric vector to smooth |
lag |
The window size of the smoothing function. The default, 3, will take the mean of values before, after and including each value. |
abs |
Should the absolute (always positive) change be returned? True by default |
Other route_funs:
route_average_gradient()
,
route_rolling_average()
,
route_rolling_gradient()
,
route_sequential_dist()
,
route_slope_matrix()
,
route_slope_vector()
r1 <- od_data_routes[od_data_routes$route_number == 2, ] y <- r1$elevations route_rolling_diff(y, lag = 1) route_rolling_diff(y, lag = 2) r1$elevations_diff_1 <- route_rolling_diff(y, lag = 1) r1$elevations_diff_n <- route_rolling_diff(y, lag = 1, abs = FALSE) d <- cumsum(r1$distances) - r1$distances / 2 diff_above_mean <- r1$elevations_diff_1 + mean(y) diff_above_mean_n <- r1$elevations_diff_n + mean(y) plot(c(0, cumsum(r1$distances)), c(y, y[length(y)]), ylim = c(80, 130)) lines(c(0, cumsum(r1$distances)), c(y, y[length(y)])) points(d, diff_above_mean) points(d, diff_above_mean_n, col = "blue") abline(h = mean(y))
r1 <- od_data_routes[od_data_routes$route_number == 2, ] y <- r1$elevations route_rolling_diff(y, lag = 1) route_rolling_diff(y, lag = 2) r1$elevations_diff_1 <- route_rolling_diff(y, lag = 1) r1$elevations_diff_n <- route_rolling_diff(y, lag = 1, abs = FALSE) d <- cumsum(r1$distances) - r1$distances / 2 diff_above_mean <- r1$elevations_diff_1 + mean(y) diff_above_mean_n <- r1$elevations_diff_n + mean(y) plot(c(0, cumsum(r1$distances)), c(y, y[length(y)]), ylim = c(80, 130)) lines(c(0, cumsum(r1$distances)), c(y, y[length(y)])) points(d, diff_above_mean) points(d, diff_above_mean_n, col = "blue") abline(h = mean(y))
Calculate rolling average gradient from elevation data at segment level
route_rolling_gradient(elevations, distances, lag = 1, n = 2, abs = TRUE)
route_rolling_gradient(elevations, distances, lag = 1, n = 2, abs = TRUE)
elevations |
Elevations, e.g. those provided by the |
distances |
Distances, e.g. those provided by the |
lag |
The window size of the smoothing function. The default, 3, will take the mean of values before, after and including each value. |
n |
The window size of the smoothing function. The default, 3, will take the mean of values before, after and including each value. |
abs |
Should the absolute (always positive) change be returned? True by default |
Other route_funs:
route_average_gradient()
,
route_rolling_average()
,
route_rolling_diff()
,
route_sequential_dist()
,
route_slope_matrix()
,
route_slope_vector()
r1 <- od_data_routes[od_data_routes$route_number == 2, ] y <- r1$elevations distances <- r1$distances route_rolling_gradient(y, distances) route_rolling_gradient(y, distances, abs = FALSE) route_rolling_gradient(y, distances, n = 3) route_rolling_gradient(y, distances, n = 4) r1$elevations_diff_1 <- route_rolling_diff(y, lag = 1) r1$rolling_gradient <- route_rolling_gradient(y, distances, n = 2) r1$rolling_gradient3 <- route_rolling_gradient(y, distances, n = 3) r1$rolling_gradient4 <- route_rolling_gradient(y, distances, n = 4) d <- cumsum(r1$distances) - r1$distances / 2 diff_above_mean <- r1$elevations_diff_1 + mean(y) par(mfrow = c(2, 1)) plot(c(0, cumsum(r1$distances)), c(y, y[length(y)]), ylim = c(80, 130)) lines(c(0, cumsum(r1$distances)), c(y, y[length(y)])) points(d, diff_above_mean) abline(h = mean(y)) rg <- r1$rolling_gradient rg[is.na(rg)] <- 0 plot(c(0, d), c(0, rg), ylim = c(0, 0.2)) points(c(0, d), c(0, r1$rolling_gradient3), col = "blue") points(c(0, d), c(0, r1$rolling_gradient4), col = "grey") par(mfrow = c(1, 1))
r1 <- od_data_routes[od_data_routes$route_number == 2, ] y <- r1$elevations distances <- r1$distances route_rolling_gradient(y, distances) route_rolling_gradient(y, distances, abs = FALSE) route_rolling_gradient(y, distances, n = 3) route_rolling_gradient(y, distances, n = 4) r1$elevations_diff_1 <- route_rolling_diff(y, lag = 1) r1$rolling_gradient <- route_rolling_gradient(y, distances, n = 2) r1$rolling_gradient3 <- route_rolling_gradient(y, distances, n = 3) r1$rolling_gradient4 <- route_rolling_gradient(y, distances, n = 4) d <- cumsum(r1$distances) - r1$distances / 2 diff_above_mean <- r1$elevations_diff_1 + mean(y) par(mfrow = c(2, 1)) plot(c(0, cumsum(r1$distances)), c(y, y[length(y)]), ylim = c(80, 130)) lines(c(0, cumsum(r1$distances)), c(y, y[length(y)])) points(d, diff_above_mean) abline(h = mean(y)) rg <- r1$rolling_gradient rg[is.na(rg)] <- 0 plot(c(0, d), c(0, rg), ylim = c(0, 0.2)) points(c(0, d), c(0, r1$rolling_gradient3), col = "blue") points(c(0, d), c(0, r1$rolling_gradient4), col = "grey") par(mfrow = c(1, 1))
Calculate the sequential distances between sequential coordinate pairs
route_sequential_dist(m, lonlat = TRUE)
route_sequential_dist(m, lonlat = TRUE)
m |
Matrix containing coordinates and elevations |
lonlat |
Are the coordinates in lon/lat order? |
Other route_funs:
route_average_gradient()
,
route_rolling_average()
,
route_rolling_diff()
,
route_rolling_gradient()
,
route_slope_matrix()
,
route_slope_vector()
x <- c(0, 2, 3, 4, 5, 9) y <- c(0, 0, 0, 0, 0, 1) m <- cbind(x, y) route_sequential_dist(m)
x <- c(0, 2, 3, 4, 5, 9) y <- c(0, 0, 0, 0, 0, 1) m <- cbind(x, y) route_sequential_dist(m)
Calculate the gradient of line segments from a matrix of coordinates
route_slope_matrix(m, e = m[, 3], lonlat = TRUE)
route_slope_matrix(m, e = m[, 3], lonlat = TRUE)
m |
Matrix containing coordinates and elevations |
e |
Elevations in same units as x (assumed to be metres) |
lonlat |
Are the coordinates in lon/lat order? |
Other route_funs:
route_average_gradient()
,
route_rolling_average()
,
route_rolling_diff()
,
route_rolling_gradient()
,
route_sequential_dist()
,
route_slope_vector()
x <- c(0, 2, 3, 4, 5, 9) y <- c(0, 0, 0, 0, 0, 9) z <- c(1, 2, 2, 4, 3, 1) / 10 m <- cbind(x, y, z) plot(x, z, ylim = c(-0.5, 0.5), type = "l") (gx <- route_slope_vector(x, z)) (gxy <- route_slope_matrix(m, lonlat = FALSE)) abline(h = 0, lty = 2) points(x[-length(x)], gx, col = "red") points(x[-length(x)], gxy, col = "blue") title("Distance (in x coordinates) elevation profile", sub = "Points show calculated gradients of subsequent lines" )
x <- c(0, 2, 3, 4, 5, 9) y <- c(0, 0, 0, 0, 0, 9) z <- c(1, 2, 2, 4, 3, 1) / 10 m <- cbind(x, y, z) plot(x, z, ylim = c(-0.5, 0.5), type = "l") (gx <- route_slope_vector(x, z)) (gxy <- route_slope_matrix(m, lonlat = FALSE)) abline(h = 0, lty = 2) points(x[-length(x)], gx, col = "red") points(x[-length(x)], gxy, col = "blue") title("Distance (in x coordinates) elevation profile", sub = "Points show calculated gradients of subsequent lines" )
Calculate the gradient of line segments from distance and elevation vectors
route_slope_vector(x, e)
route_slope_vector(x, e)
x |
Vector of locations |
e |
Elevations in same units as x (assumed to be metres) |
Other route_funs:
route_average_gradient()
,
route_rolling_average()
,
route_rolling_diff()
,
route_rolling_gradient()
,
route_sequential_dist()
,
route_slope_matrix()
x <- c(0, 2, 3, 4, 5, 9) e <- c(1, 2, 2, 4, 3, 1) / 10 route_slope_vector(x, e)
x <- c(0, 2, 3, 4, 5, 9) e <- c(1, 2, 2, 4, 3, 1) / 10 route_slope_vector(x, e)
Split route in two at point on or near network
route_split(r, p)
route_split(r, p)
r |
An |
p |
A point represented by an |
An sf object with 2 feature
sample_routes <- routes_fast_sf[2:6, NULL] r <- sample_routes[2, ] p <- sf::st_sfc(sf::st_point(c(-1.540, 53.826)), crs = sf::st_crs(r)) plot(r$geometry, lwd = 9, col = "grey") plot(p, add = TRUE) r_split <- route_split(r, p) plot(r_split, col = c("red", "blue"), add = TRUE)
sample_routes <- routes_fast_sf[2:6, NULL] r <- sample_routes[2, ] p <- sf::st_sfc(sf::st_point(c(-1.540, 53.826)), crs = sf::st_crs(r)) plot(r$geometry, lwd = 9, col = "grey") plot(p, add = TRUE) r_split <- route_split(r, p) plot(r_split, col = c("red", "blue"), add = TRUE)
Split route based on the id or coordinates of one of its vertices
route_split_id(r, id = NULL, p = NULL)
route_split_id(r, id = NULL, p = NULL)
r |
An |
id |
The index of the point on the number to be split |
p |
A point represented by an |
sample_routes <- routes_fast_sf[2:6, 3] r <- sample_routes[2, ] id <- round(n_vertices(r) / 2) r_split <- route_split_id(r, id = id) plot(r$geometry, lwd = 9, col = "grey") plot(r_split, col = c("red", "blue"), add = TRUE)
sample_routes <- routes_fast_sf[2:6, 3] r <- sample_routes[2, ] id <- round(n_vertices(r) / 2) r_split <- route_split_id(r, id = id) plot(r$geometry, lwd = 9, col = "grey") plot(r_split, col = c("red", "blue"), add = TRUE)
Simulated travel route allocated to the transport network
representing the 'fastest' between cents_sf
objects.
routes_fast_sf
routes_fast_sf
A spatial lines dataset with 42 rows and 15 columns
Other data:
cents_sf
,
destinations_sf
,
flow
,
flow_dests
,
flowlines_sf
,
od_data_lines
,
od_data_routes
,
od_data_sample
,
osm_net_example
,
read_table_builder()
,
route_network_sf
,
route_network_small
,
routes_slow_sf
,
zones_sf
Simulated travel route allocated to the transport network
representing the 'quietest' between cents_sf
.
A spatial lines dataset 42 rows and 15 columns
Other data:
cents_sf
,
destinations_sf
,
flow
,
flow_dests
,
flowlines_sf
,
od_data_lines
,
od_data_routes
,
od_data_sample
,
osm_net_example
,
read_table_builder()
,
route_network_sf
,
route_network_small
,
routes_fast_sf
,
zones_sf
sf
LINESTRING objectsTakes lines and removes the start and end point, to a distance determined
by the nearest buff
polygon border.
toptail_buff(l, buff, ...)
toptail_buff(l, buff, ...)
l |
An |
buff |
An |
... |
Arguments passed to |
Other lines:
angle_diff()
,
geo_toptail()
,
is_linepoint()
,
line2df()
,
line2points()
,
line_bearing()
,
line_breakup()
,
line_midpoint()
,
line_segment()
,
line_segment1()
,
line_via()
,
mats2line()
,
n_segments()
,
n_vertices()
,
onewaygeo()
,
points2line()
l <- routes_fast_sf buff <- zones_sf r_toptail <- toptail_buff(l, buff) nrow(l) nrow(r_toptail) plot(zones_sf$geometry) plot(l$geometry, add = TRUE) plot(r_toptail$geometry, lwd = 5, add = TRUE)
l <- routes_fast_sf buff <- zones_sf r_toptail <- toptail_buff(l, buff) nrow(l) nrow(r_toptail) plot(zones_sf$geometry) plot(l$geometry, add = TRUE) plot(r_toptail$geometry, lwd = 5, add = TRUE)
These correspond to the cents_sf
data.
geo_code. the official code of the zone
Other data:
cents_sf
,
destinations_sf
,
flow
,
flow_dests
,
flowlines_sf
,
od_data_lines
,
od_data_routes
,
od_data_sample
,
osm_net_example
,
read_table_builder()
,
route_network_sf
,
route_network_small
,
routes_fast_sf
,
routes_slow_sf
library(sf) zones_sf plot(zones_sf)
library(sf) zones_sf plot(zones_sf)