Title:  Sustainable Transport Planning 

Description:  Tools for transport planning with an emphasis on spatial transport data and nonmotorized 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 (MorenoMonroy 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 origindestination (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/s11116021101971>; 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/RJ2018053>, and in a paper outlining the landscape of open source software for geographic methods in transport planning (Lovelace, 2021) <doi:10.1007/s10109020003422>. 
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:  20241022 06:19:17 UTC 
Source:  https://github.com/ropensci/stplanr 
The stplanr package provides functions to access and analyse data for transportation research, including origindestination 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 predefined 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 NorthSouth 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 NorthSouth 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 rowbinding 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 populationweighted 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 pctdata: https://github.com/npct/pctdata. 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 nonprojected 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 setups 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 setups 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 nonoverlapping 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 midpoint 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 origindestination 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 origindestination 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 origindestination 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 origindestination 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 dataraw/generatedata.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 dataraw/generatedata.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 bidirectional 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 underestimated 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 userselected 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")
Origindestination ('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 linkup 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 origindestination 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 
Origindestination (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("stplanrod")
) 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 origindestination 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. 
Origindestination (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("stplanrod")
) for details.
od2odf()
creates an 'origindestination 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 origindestination 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 underestimated 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 nondirectional 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 userselected attribute values that have been aggregated. Only lines
with a distance (i.e. not intrazone 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 nonoverlapping
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 northeast, southeast 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 brokenup 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 # topleft 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 # topleft 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 dataraw/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 1to1 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 dataraw/rnet_overpass.R
for details on how this was created.
A sf object
rnet_overpass
rnet_overpass
See dataraw/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 = "gbleeds", 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 = "gbleeds", 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 dataraw 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 dropin 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 dataraw/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)