diff --git a/src/snkit/network.py b/src/snkit/network.py index 7799745..cdc6588 100644 --- a/src/snkit/network.py +++ b/src/snkit/network.py @@ -107,6 +107,50 @@ def to_crs(self, crs=None, epsg=None): self.edges.to_crs(crs, epsg, inplace) self.nodes.to_crs(crs, epsg, inplace) + def to_file(self, filename, nodes_layer="nodes", edges_layer="edges", **kwargs): + """Write nodes and edges to a geographic data file with layers. + + Any additional keyword arguments are passed through to `geopandas.GeoDataFrame.to_file`. + + Parameters + ---------- + filename : str + Path to geographic data file with layers + nodes_layer : str, optional, default 'nodes' + Layer name for nodes. + edges_layer : str, optional, default 'edges' + Layer name for edges. + """ + self.nodes.to_file(filename, layer=nodes_layer, **kwargs) + self.edges.to_file(filename, layer=edges_layer, **kwargs) + + +def read_file(filename, nodes_layer="nodes", edges_layer="edges", **kwargs): + """Read a geographic data file with layers containing nodes and edges. + + Any additional keyword arguments are passed through to `geopandas.read_file`. + + Parameters + ---------- + filename : str + Path to geographic data file with layers + nodes_layer : str, optional, default 'nodes' + Layer name for nodes, or None if nodes should not be read. + edges_layer : str, optional, default 'edges' + Layer name for edges, or None if edges should not be read. + """ + if nodes_layer is not None: + nodes = geopandas.read_file(filename, layer=nodes_layer, **kwargs) + else: + nodes = None + + if edges_layer is not None: + edges = geopandas.read_file(filename, layer=edges_layer, **kwargs) + else: + edges = None + + return Network(nodes, edges) + def add_ids(network, id_col="id", edge_prefix="edge", node_prefix="node"): """Add or replace an id column with ascending ids""" diff --git a/tests/test_init.py b/tests/test_init.py index b9db5b1..af150e4 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -455,6 +455,14 @@ def test_init(): assert len(net.edges) == 0 +def test_roundtrip(connected, tmp_path): + """Should write and read back a network""" + connected.to_file(tmp_path / "connected.gpkg", driver="GPKG") + actual = snkit.network.read_file(tmp_path / "connected.gpkg") + assert_frame_equal(actual.nodes, connected.nodes) + assert_frame_equal(actual.edges, connected.edges) + + def test_round_geometries(misaligned, connected): """Should round coordinates to some tolerance""" rounded = snkit.network.round_geometries(misaligned, precision=0)