Introducing sfnetworks v1.0 🚀 #281
Replies: 5 comments 3 replies
-
|
cc @Fradenti (since I remember you told me one of your colleagues is working on these topics) |
Beta Was this translation helpful? Give feedback.
-
|
This looks wonderful, @luukvdmeer ! Thanks for the ping. |
Beta Was this translation helpful? Give feedback.
-
|
Great work @luukvdmeer, just picking up on notifications after paternity leave, cc @wangzhao0217 and @juanfonsecaLS1 who have been using this, I imagine they're already aware of and using the functionality, any comments on the new version welcome guys! |
Beta Was this translation helpful? Give feedback.
-
|
cc @temospena |
Beta Was this translation helpful? Give feedback.
-
|
Hi, links for documentation of version 1 are broken. is it possible to create them on a separate branch? |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
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 forsfobjects 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 forsfobjects 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_streetnetobjects from the {dodgr} package directly into asfnetwork. This internally callsdodgr_to_sfnetwork(). For the conversion in the other direction, usesfnetwork_to_dodgr().sfnetworkobjects 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 = NULLchanged (see below), the argumentlength_as_weightof thesfnetwork()construction function has been deprecated. Instead, you can now setcompute_length = TRUEto 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 = NULLhas 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 = NULLdoes 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 = NULLalways 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}weightargument. 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.fromandtoarguments. 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.routerargument 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 asfobject, 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_costandreturn_geometrycan be set toFALSEif you do not want the cost and/or geometry columns to be returned.typeargument ofst_network_paths()is deprecated. To compute all shortest paths instead of a single shortest path, setall = TRUEinstead. 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 newkargument, which you can set to an integer higher than 1.use_namesargument 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_namesargument 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 theTSPpackage to solve traveling salesman problems. This requiresTSPto 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 theprotectargument.to_spatial_implicit()drops edge geometries.summarise_attributesargument that appears in several morphers is renamed toattribute_summary, to avoid differences between UK and US spelling. For now,summarise_attributeswill 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.protectargument 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_equalargument 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.simplifyargument of theto_spatial_contracted()morpher now hasTRUEas 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.fromargument ofto_spatial_neighborhood()is renamed tonode. For now,fromwill 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 asfobject with polygons geometries.st_project_on_network()replaces geometries ofsfobjects 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
sfnetworkmethod forsf::st_segmentize(), allowing you to add interior points to edge geometries at fixed intervals.sfnetworkmethods forsf::st_intersection(),sf::st_difference(), andsf::st_crop()now also work as expected on undirected networks.st_geometry<-method forsfnetworkobjects 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 forsfnetworkobjects 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.sfnetworkmethod fortidygraph::reroute(). However, if you only want to reverse edges, we recommend to use the morpherto_spatial_reversed()instead, asreroutewill 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 forsfnetworkobjects now allows different style settings for nodes and edges, using the newnode_argsandedge_argsarguments.plot()method forsfnetworkobjects now allows to plot multiple networks on top of each other.Data extraction utilities
sfnetworkobject are now exported:node_data()andedge_data()extract the node and edge table, respectively. Nodes are always extracted as asfobject. Edges are extracted assfobject if they are spatially explicit, and as regulartbl_dfif 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 asfnetworkobject.wrap_igraph()allows to wrap any function from{igraph}that returns a network, and make it return asfnetworkobject instead of aigraphobject.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 forsfnetworkobjects 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