Skip to content

Commit

Permalink
Add merge_parts option to split_multilinestrings
Browse files Browse the repository at this point in the history
Closes #46
  • Loading branch information
tomalrussell committed Nov 13, 2023
1 parent 8370162 commit aaf52fb
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 2 deletions.
9 changes: 8 additions & 1 deletion src/snkit/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,16 +279,23 @@ def round_geometries(network: Network, precision: int = 3) -> Network:
return network


def split_multilinestrings(network: Network) -> Network:
def split_multilinestrings(network: Network, merge_parts: bool = False) -> Network:
"""
Create multiple edges from any MultiLineString edge
Ensures that edge geometries are all LineStrings, duplicates attributes
over any created multi-edges.
Parameters
----------
merge_parts : default False
Merge parts of geometries if they are connected, before splitting to multiple edges
"""

edges = network.edges
geom_col: str = geometry_column_name(edges)
if merge_parts:
edges.geometry = edges.geometry.apply(merge_multilinestring)
split_edges = edges.explode(column=geom_col, ignore_index=True)

geo_types = set(split_edges.geom_type)
Expand Down
60 changes: 59 additions & 1 deletion tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ def test_split_multilinestrings():
mls_coords = [
(
(0, 0),
(0, 1),
(0, 1.1),
),
(
(1, 1),
Expand Down Expand Up @@ -681,6 +681,64 @@ def test_split_multilinestrings():
assert list(split_network.edges.index.values) == list(range(4))


def test_split_multilinestrings_merge():
"""Explode multilinestrings into linestrings - with parts merged if possible"""

# point coordinates comprising three linestrings
mls_coords = [
(
(0, 0),
(0, 1), # should link to third part
),
(
(1, 1),
(2, 2),
(2, 1),
),
(
(0, 1), # should merge with first part
(-1, 1),
(-1, 2),
(-1, 0),
),
]
# point coordsinate comprising a single linestring
ls_coords = [(4, 0), (4, 1), (4, 2)]

# make input network edges
edges = GeoDataFrame(
{
"data": ["MLS", "LS"],
"geometry": [MultiLineString(mls_coords), LineString(ls_coords)],
}
)
multi_network = snkit.network.Network(edges=edges)
# check we have two rows (multilinestring and linestring) in the input network
assert len(multi_network.edges) == 2

# split and check we have three rows (the resulting linestrings) in the output network
split_network = snkit.network.split_multilinestrings(multi_network)
assert len(split_network.edges) == 4

# check data is replicated from multilinestring to child linestrings
assert list(split_network.edges["data"].values) == ["MLS"] * 3 + ["LS"]

# check everything is reindexed
assert list(split_network.edges.index.values) == list(range(4))

# Check again, with merge_parts
split_network_merged = snkit.network.split_multilinestrings(
multi_network, merge_parts=True
)
assert len(split_network_merged.edges) == 3

# check data is replicated from multilinestring to child linestrings
assert list(split_network_merged.edges["data"].values) == ["MLS"] * 2 + ["LS"]

# check everything is reindexed
assert list(split_network_merged.edges.index.values) == list(range(3))


def test_link_nodes_to_edges_within(gap, bridged):
"""Nodes should link to edges within a distance, splitting the node at a new point if
necessary.
Expand Down

0 comments on commit aaf52fb

Please sign in to comment.