Introducing sfnetworks v1.0 🚀 #281
luukvdmeer
started this conversation in
General
Replies: 2 comments
-
cc @Fradenti (since I remember you told me one of your colleagues is working on these topics) |
Beta Was this translation helpful? Give feedback.
0 replies
-
This looks wonderful, @luukvdmeer ! Thanks for the ping. |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Dear sfnetworks community! After working on it since the start of summer, I am please to announce we just released sfnetworks v1.0 to the main branch on GitHub 🎉. This is still work in progress, but we feel it is now mature enough to be on the main branch. The CRAN release will still take some more time, we first want everything to be properly tested. And for that, we need your help. If you feel adventurous, do not hesitate to install the development version and play around with it. When something is not working, not clear, or not appreciated, open an issue about it. That would really help us a lot!
Besides tons of new features, there are also many changes to existing functionalities, with a quite a few that are not backwards compatible with previous versions. But those changes are made with the goal to create a clearer, more consistent, and more powerful package, that fits better within tidy analysis workflows than it did before.
Highlights
{igraph}
, but it is now also possible to choose{dodgr}
as a routing backend. All conversion is handled internally, the user can use the same interface independent of the chosen routing backend. Thanks @mpadge for the great package, and both @latot and @rafapereirabr for coming up with this idea.{igraph}
routing backend.{dodgr}
routing backend.{tidygraph}
.st_network_*
functions to work with spatial networks and other spatial features:st_network_iso()
to draw isodistance polygons,st_network_travel()
to solve traveling salesman problems,st_network_faces()
to extract the faces of a network. Thanks @idshklein for the idea of the faces, and @agila5 for implementing it.to_spatial_unique()
to merge nodes at the same spatial location,to _spatial_mixed()
to mimic mixed networks with both directed and undirected edges,to_spatial_reversed()
to reverse edge geometries,to_spatial_implicit()
to drop edge geometries. Furthermore,to_spatial_subdivision()
now allows to subdivide edges at all interior points, creating one edge per segment. Thanks @agila5 for the first implementation of the segmentation.group_spatial_dbscan()
to spatially group nodes using the DBSCAN clustering algorithm, based on the network distance matrix.centrality_straightness()
to compute the straightness centrality of nodes. Inspired by the Python package momepy from @martinfleissf::set_set_geometry()
. The network structure will be updated automatically.Furthermore, there is now a smooth integration between
{sfnetworks}
and{ggraph}
to visualize spatial networks in ways that go beyond the standard plot method. Thanks to @loreabad6 and @thomasp85cc @Robinlovelace @loreabad6 @agila5. Tag other who may be interested in the comment!
Full changelog
Network creation
create_from_spatial_lines()
. Theas_sfnetwork()
method forsf
objects will call this function when geometries are linestrings, and forward the...
arguments to it. This now also allows to already subdivide the edges at locations where interior points are shared, settingsubdivide = TRUE
.create_from_spatial_points()
. Theas_sfnetwork()
method forsf
objects will call this function when geometries are points, and forward the...
arguments to it.{sf}
. Furthermore,{sfnetworks}
can create the adjacency matrix for you according to a specified method. In that case, you only need to specify the name of the method. Supported options are: a complete graph, a sequence, a minimum spanning tree, a delaunay triangulation, a Gabriel graph, a relative nearest neighbor graph, an a k nearest neighbor graph. See here for a detailed explanation with examples.play_geometric()
can create random geometric networks.as_sfnetwork()
to createdodgr_streetnet
objects from the {dodgr} package directly into asfnetwork
. This internally callsdodgr_to_sfnetwork()
. For the conversion in the other direction, usesfnetwork_to_dodgr()
.sfnetwork
objects and neighbor lists, using the new functionsnb_to_sfnetwork()
andsfnetwork_to_nb()
. Neighbor lists are sparse adjacency matrices that can be found e.g. in the{spdep}
package and the{sf}
package (as the output of spatial predicate functions).weights = NULL
changed (see below), the argumentlength_as_weight
of thesfnetwork()
construction function has been deprecated. Instead, you can now setcompute_length = TRUE
to store edge lengths in a attribute named length. However, this attribute will not anymore automatically be recognized as edge weights in routing functions.Routing
weights = NULL
has changed for all routing functions. Before, an edge attribute named weight would be automatically recognized as edge weights, just like in{igraph}
. Although convenient, it proved to be very confusing since in{tidygraph}
, the settingweights = NULL
does not have the same meaning. There is always means that no edge weights are used, no matter if a weight attribute is present. We now decided that (since we are primarily integrating{tidygraph}
and{sf}
) to follow the{tidygraph}
design choice, meaning thatweights = NULL
always means that no edge weights are used. However, in the routing functions of{sfnetworks}
the default is no longerweights = NULL
, butweights = edge_length()
. Hence, geographic length is the default edge weight in all routing functions of{sfnetworks}
weight
argument. This also allows to provide custom edge measure functions, for example ones that are time-dependent. Furthermore, to reference a column in the edges table, you can now do this using tidy evaluation, i.e. unquoted column names rather than quoted ones. For dual weighted routing, the newdual_weights()
function can be used. See here for a full overview of possible specification formats.from
andto
arguments. Furthermore, to reference a column in the nodes table, you can now do this using tidy evaluation, i.e. unquoted column names rather than quoted ones. See here for a full overview of possible specification formats.router
argument in all routing functions. The default routing backend is igraph, meaning that routing functions from the{igraph}
package will be called internally. The second supported routing backend now is dodgr, which will call routing functions from the{dodgr}
package instead. All conversion happens internally, such that as a user you can use the same functions and arguments independent from which routing engine you choose. See here for more details.st_network_paths()
is restructured. Instead oftbl_df
, the function will now return asf
object, with the course of each path stored as a linestring geometry. It will also return the total cost of each path in a column named cost. Columns node_paths and edge_paths are renamed to node_path and edge_path, respectively. The boolean column path_found specifies if the requested path was found. If not, the path will be assigned an infinite cost and empty geometry. New boolean argumentsreturn_cost
andreturn_geometry
can be set toFALSE
if you do not want the cost and/or geometry columns to be returned.type
argument ofst_network_paths()
is deprecated. To compute all shortest paths instead of a single shortest path, setall = TRUE
instead. Support for computing all simple paths is dropped.st_network_paths()
function now supports one-to-one k shortest paths routing. This is implemented through the newk
argument, which you can set to an integer higher than 1.use_names
argument ofst_network_paths()
now has a default value ofFALSE
. This means that even if the nodes have a name column, they will be encoded by their integer indices in the output object.use_names
argument is now also added tost_network_cost()
, letting you specify if you want node names to be used for column and rownames in the returned matrix. Also here, it defaults toFALSE
.st_network_distance()
is added as a synonym forst_network_cost()
where the edge weights are fixed to be geographic distance. This is done to provide an intuitive network-specific alternative tosf::st_distance()
.st_network_travel()
now provides an interface to theTSP
package to solve traveling salesman problems. This requiresTSP
to be installed. See here for an example.st_network_iso()
now implements the computation of isodistance/isochrone polygons around a given source node. It first computes the neighborhood of the node, and then draws a concave hull around it. See here for an example. For concave hulls that are more detailed than the convex hull, i.e. with a ratio smaller than 1, GEOS >= 3.11 is required.Morphers
to_spatial_unique()
allows to contract nodes at equal spatial locations, while specifying how their attributes should be combined.to_spatial_mixed()
allows to mimic a mixed network representation (i.e. a network with both directed and undirected edges) by duplicating and reversing those edges that should be undirected.to_spatial_reversed()
reverses edges, including their linestring geometries. Selected edges can be protected from reversion using theprotect
argument.to_spatial_implicit()
drops edge geometries.summarise_attributes
argument that appears in several morphers is renamed toattribute_summary
, to avoid differences between UK and US spelling. For now,summarise_attributes
will be automatically converted toattribute_summary
, while giving a soft deprecation warning.to_spatial_subdivision()
morpher now has the argumentall
. If set toTRUE
, edges will be subdivided at each interior point (i.e. creating one edge per segment), instead of only at interior points that are shared between multiple edges.to_spatial_subdivision()
morpher now has the argumentmerge_equal
. If set toTRUE
, edges will only be subvidived, but subdivision points that are shared between multiple edges will not be merged into a single node.to_spatial_subdivision()
moprher now has the argumentprotect
, which allows to protect specified edges from being subdivided. The edges to be protected can be specified in several ways, e.g. by their integer index, by using edge query functions, or by referencing a column using tidy evaluation.protect
argument of theto_spatial_smooth()
morpher is now updated to fit better in tidy data analysis workflows. Nodes to be protected from being smoothed can be specified in the same way as origins and destination nodes in routing functions, see above.require_equal
argument of theto_spatial_smooth()
morpher is now updated to fit better in tidy data analysis workflows. Attributes to check for equality can now be specified using tidy selection. This means you can also use tidy selection helpers from{dplyr}
.to_spatial_contracted()
morpher now has the argumentcompute_centroid
. If set toFALSE
, contracted groups of nodes will not have their centroid as new geometry, but simply the geometry of the first node in the group. This can improve performance significantly on large networks.simplify
argument of theto_spatial_contracted()
morpher now hasTRUE
as the default value. This means that by default the contracted network will be simplified.to_spatial_shortest_paths()
morpher now automatically orders the nodes and edges in each returned network to match the order in which they are visited by the path.from
argument ofto_spatial_neighborhood()
is renamed tonode
. For now,from
will be automatically converted tonode
, while giving a soft deprecation warning.to_spatial_neighborhood()
morpher now internally callsst_network_cost()
, and forwards...
arguments to it.to_spatial_neighborhood()
morpher now accepts multiple threshold values, returning one network per specified threshold.simplify_network()
forto_spatial_simple()
,subdivide_edges()
forto_spatial_subdivision()
,smooth_pseudo_nodes()
forto_spatial_smooth()
, and contract nodes forto_spatial_contracted()
.Spatial grouping
group_spatial_dbscan()
provides a tidy interface to the{dbscan}
package to group nodes spatially using the DBSCAN spatial clustering algorithm, based on network distances between nodes.Blending
st_network_blend()
now allows to blend points that have the same projected location on the network, by settingignore_duplicates = FALSE
. All but the first one of those will be added as isolated nodes, which can then be merged using the new morpherto_spatial_unique()
.Node specific functions
centrality_straightness()
allows to compute the straightness centrality of nodes.node_is_pseudo()
andnode_is_dangling()
allow to easily query pseudo (nodes with one incoming and one outgoing edges) and dangling (nodes with a degree centrality of 1) nodes.node_is_nearest()
defines if a node is the nearest node to any feature in a given set of spatial features.Edge specific functions
edge_segment_count()
returns the number of segments in each edge.edge_is_nearest()
defines if a edge is the nearest edge to any feature in a given set of spatial features.make_edges_valid()
makes edge geometries fit in the spatial network structure by either replacing their endpoints with the nodes that are referenced in the from and to columns (ifpreserve_geometries = FALSE
), or by adding unmatched endpoints as new nodes to the network and updating the from and to columns (ifpreserve_geometries = TRUE
).make_edges_directed()
turns a undirected network into a directed network by updating the from and to columns according to the direction given by the linestring geometries. This is the internal worker of the morpherto_spatial_directed()
.make_edges_mixed()
duplicates and reverses edges in a directed network that should be undirected. This is the internal worker of the morpherto_spatial_mixed()
.make_edges_explicit()
adds a geometry column to spatially implicit edges. This is the internal worker of the morpherto_spatial_explicit()
.make_edges_implicit()
drops the geometry column of spatially explicit edges. This is the internal worker of the morpherto_spatial_implicit()
.make_edges_follow_indices()
updates edge geometries in undirected networks to match the node indices specified in the from and to columns, in case they are swapped.Other new functions
st_network_faces()
allows to extract the faces of a spatial network as asf
object with polygons geometries.st_project_on_network()
replaces geometries ofsf
objects with their projection on a spatial network.bind_spatial_nodes()
andbind_spatial_edges()
allow to bind additional nodes or edges to the network. These are the spatial alternatives totidygraph::bind_nodes()
andtidygraph::bind_edges()
, which cannot handle geometry list columns.Methods for sf
sfnetwork
method forsf::st_segmentize()
, allowing you to add interior points to edge geometries at fixed intervals.sfnetwork
methods forsf::st_intersection()
,sf::st_difference()
, andsf::st_crop()
now also work as expected on undirected networks.st_geometry<-
method forsfnetwork
objects now allows to replace node geometries with any set of points, and edge geometries with any set of lines. Internally, the network structure will be kept valid by replacing endpoints of edge geometries (when replacing nodes), or by adding unmatched edge endpoints as new nodes to the network (when replacing edges).sf::st_join()
method forsfnetwork
objects now allows multiple matches for the same node. In these case, the node will be duplicated once per additional match, and duplicates are added as isolated nodes to the resulting network.Upkeep with tidygraph
{sfnetworks}
now work well with the new concept of focused graphs, as recently implemented in{tidygraph}
. See here for details.sfnetwork
method fortidygraph::reroute()
. However, if you only want to reverse edges, we recommend to use the morpherto_spatial_reversed()
instead, asreroute
will only replace endpoints of edge geometries, and not reverse complete linestring geometries.morph()
,unmorph()
,crystallize()
, andconvert()
are now re-exported by{sfnetworks}
, such that it is not needed anymore to load{tidygraph}
explicitly in order to use the spatial morphers. Furthermore, the utility functiontidygraph::with_graph()
is now re-exported.Plotting
plot()
method forsfnetwork
objects now allows different style settings for nodes and edges, using the newnode_args
andedge_args
arguments.plot()
method forsfnetwork
objects now allows to plot multiple networks on top of each other.Data extraction utilities
sfnetwork
object are now exported:node_data()
andedge_data()
extract the node and edge table, respectively. Nodes are always extracted as asf
object. Edges are extracted assf
object if they are spatially explicit, and as regulartbl_df
if they are spatially implicit.node_ids()
andedge_ids()
extract the indices of the nodes and edges, respectively. The indices correspond to rownumbers in the node and edge tables.nearest_nodes()
andnearest_edges()
return respectively the nearest nodes and nearest edges to a set of spatial features.nearest_node_ids()
andnearest_edge_ids()
return respectively the indices of the nearest nodes and nearest edges to a set of spatial features. The indices correspond to rownumbers in the node and edge tables.n_nodes()
andn_edges()
return the respectively the number of nodes and edges in the network.Other utilities
is_sfnetwork()
as an alias ofis.sfnetwork()
.validate_network()
allows to validate the spatial network structure of asfnetwork
object.wrap_igraph()
allows to wrap any function from{igraph}
that returns a network, and make it return asfnetwork
object instead of aigraph
object.st_duplicated()
,st_match()
andst_round()
are added as spatial variations to common base R functions, respectively for determining spatial duplicates, geometry matching, and coordinate rounding.Other updates
{sfnetworks}
now by default uses a 12-digit precision. This gives a considerable performance improvement especially on large networks. Precision can be changed by explicitly setting coordinate precision usingsf::st_set_precision()
.{sfnetworks}
are now raised using the{cli}
and{rlang}
packages.Bug fixes
{tidygraph}
.to_spatial_contracted()
now correctly handles group indices that are not ordered. Thanks to @MattArran for spotting it and implementing a first solution.plot()
method forsfnetwork
objects now correctly plots networks with spatially implicit edges that are active.st_network_bbox()
now also computes bounding boxes for networks with spatially implicit edges.Dependencies
{sf}
is now 1.0-11{tidygraph}
is now 1.3.0{igraph}
is now 2.1.0{crayon}
package is not a dependency anymore.{methods}
and{stats}
are added as new dependencies.{cli}
,{lifecycle}
,{pillar}
and{tidyselect}
are added as new dependencies.What is still to come
Some things are still in progress, but may be postponed to later versions:
{cppRouting}
as an additional routing backend.{dodgr}
.Beta Was this translation helpful? Give feedback.
All reactions