From 9dbf8ba3afc47515a6db2b43ca522351167b6dc8 Mon Sep 17 00:00:00 2001 From: Gareth Simons Date: Tue, 16 Jul 2024 21:14:41 +0200 Subject: [PATCH] cleans up OSM tag handling --- pyproject.toml | 2 +- pysrc/cityseer/tools/graphs.py | 2 +- pysrc/cityseer/tools/io.py | 14 ++++++++++++++ pysrc/cityseer/tools/util.py | 12 ++++++------ tests/tools/test_io.py | 24 +++++++++++++++++++++++- 5 files changed, 45 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9c6809ba..45cafed5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "cityseer" -version = '4.15.0' +version = '4.15.1' description = "Computational tools for network-based pedestrian-scale urban analysis" readme = "README.md" requires-python = ">=3.10, <3.13" diff --git a/pysrc/cityseer/tools/graphs.py b/pysrc/cityseer/tools/graphs.py index 235a769e..7d421c9d 100644 --- a/pysrc/cityseer/tools/graphs.py +++ b/pysrc/cityseer/tools/graphs.py @@ -304,7 +304,7 @@ def _extract_tags_to_set( if tags_list is not None: if not isinstance(tags_list, (list, set, tuple)): raise ValueError(f"Tags should be provided as a `list` of `str` instead of {type(tags_list)}.") - tags_list = [t.lower() for t in tags_list] + tags_list = [t.strip().lower() for t in tags_list if t not in ["", " ", None]] tags.update(tags_list) return tags diff --git a/pysrc/cityseer/tools/io.py b/pysrc/cityseer/tools/io.py index 47e7f50b..c076965a 100644 --- a/pysrc/cityseer/tools/io.py +++ b/pysrc/cityseer/tools/io.py @@ -1166,6 +1166,20 @@ def _node_key(node_coords): for k in ["geom", "geometry"]: if k in props: del props[k] + # names, routes, highways + for k in ["names", "routes", "highways"]: + if not k in props: + props[k] = [] # type: ignore + else: + prop = props[k] + if isinstance(prop, str): + prop = prop.strip("()[] ,'\"") + prop = prop.split(",") + prop = [p.strip("\" '") for p in prop] + prop = [p for p in prop if p not in ["", " ", None]] + if not isinstance(prop, (tuple, list)): + raise TypeError(f"Expected key {k} to be a list type to retain compatibility with OSM workflows.") + props[k] = prop # type: ignore g_multi.add_edge(node_key_a, node_key_b, src_edge_idx=edge_idx, geom=edge_geom, **props) # deduplicate diff --git a/pysrc/cityseer/tools/util.py b/pysrc/cityseer/tools/util.py index 28ec6642..573a78d0 100644 --- a/pysrc/cityseer/tools/util.py +++ b/pysrc/cityseer/tools/util.py @@ -357,18 +357,18 @@ class EdgeInfo: @property def names(self): - """Returns a set of street names.""" - return tuple(set(self._names)) + """Returns a list of street names.""" + return list(set(self._names)) @property def routes(self): - """Returns a set of routes - e.g. route numbers.""" - return tuple(set(self._refs)) + """Returns a list of routes - e.g. route numbers.""" + return list(set(self._refs)) @property def highways(self): - """Returns a set of highway types - e.g. footway.""" - return tuple(set(self._highways)) + """Returns a list of highway types - e.g. footway.""" + return list(set(self._highways)) def __init__(self): """Initialises a network information structure.""" diff --git a/tests/tools/test_io.py b/tests/tools/test_io.py index d7137934..9b5e053c 100644 --- a/tests/tools/test_io.py +++ b/tests/tools/test_io.py @@ -1,7 +1,7 @@ import networkx as nx import numpy as np import pytest -from pyproj import CRS, Transformer +from pyproj import Transformer from shapely import geometry, wkt from cityseer import config @@ -782,3 +782,25 @@ def test_nx_from_generic_geopandas(primal_graph): for s, e, d in nx_from_generic.edges(data=True): total_lens_generic += d["geom"].length assert total_lens_input - total_lens_generic < config.ATOL + # test OSM keys + out_gpd = io.geopandas_from_nx(primal_graph, crs=3395) + out_gpd["names"] = "" + out_gpd["names"].iloc[0] = "(boo,)" + out_gpd["routes"] = "" + out_gpd["routes"].iloc[0] = "(boo,)" + out_gpd["highways"] = "" + out_gpd["highways"].iloc[0] = "boo" + out_gpd["highways"].iloc[1] = "boo," + out_gpd["highways"].iloc[2] = "(boo)" + out_gpd["highways"].iloc[3] = "(boo,)" + out_gpd["highways"].iloc[4] = "(boo)," + out_gpd["highways"].iloc[5] = "(boo, baa)" + in_G = io.nx_from_generic_geopandas(out_gpd) + G = graphs.nx_remove_filler_nodes(in_G) + for s, e in G.edges(): + hwy = G[s][e][0]["highways"] + assert isinstance(hwy, list) + if len(hwy) == 2: + assert hwy == ["boo", "baa"] + elif len(hwy) == 1: + assert hwy == ["boo"]