diff --git a/sgeop/artifacts.py b/sgeop/artifacts.py index 895703a..b9d5926 100644 --- a/sgeop/artifacts.py +++ b/sgeop/artifacts.py @@ -23,13 +23,13 @@ def get_artifacts( roads: gpd.GeoDataFrame, - threshold: None | float = None, - threshold_fallback: None | float = None, - area_threshold_blocks: float = 1e5, - isoareal_threshold_blocks: float = 0.5, - area_threshold_circles: float = 5e4, - isoareal_threshold_circles_enclosed: float = 0.75, - isoperimetric_threshold_circles_touching: float = 0.9, + threshold: None | float | int = None, + threshold_fallback: None | float | int = None, + area_threshold_blocks: float | int = 1e5, + isoareal_threshold_blocks: float | int = 0.5, + area_threshold_circles: float | int = 5e4, + isoareal_threshold_circles_enclosed: float | int = 0.75, + isoperimetric_threshold_circles_touching: float | int = 0.9, exclusion_mask: None | gpd.GeoSeries = None, predicate: str = "intersects", ) -> tuple[gpd.GeoDataFrame, float]: @@ -40,24 +40,24 @@ def get_artifacts( ---------- roads : geopandas.GeoDataFrame Input roads that have been preprocessed. - threshold : None | float = None + threshold : None | float | int = None First option threshold used to determine face artifacts. See the ``artifact_threshold`` keyword argument in ``simplify.simplify_network()``. - threshold_fallback : None | float = None + threshold_fallback : None | float | int = None Second option threshold used to determine face artifacts. See the ``artifact_threshold_fallback`` keyword argument in ``simplify.simplify_network()``. - area_threshold_blocks : float = 1e5 + area_threshold_blocks : float | int = 1e5 Areal theshold for block detection. - isoareal_threshold_blocks : float = 0.5 + isoareal_threshold_blocks : float | int = 0.5 Isoareal theshold for block detection. See ``esda.shape.isoareal_quotient``. - area_threshold_circles : float = 5e4 + area_threshold_circles : float | int = 5e4 Areal theshold for circle detection. - isoareal_threshold_circles_enclosed : float = 0.75 + isoareal_threshold_circles_enclosed : float | int = 0.75 Isoareal theshold for enclosed circle detection. See ``esda.shape.isoareal_quotient``. - isoperimetric_threshold_circles_touching : float = 0.9 + isoperimetric_threshold_circles_touching : float | int = 0.9 Isoperimetric theshold for enclosed circle touching. See ``esda.shape.isoperimetric_quotient``. exclusion_mask : None | gpd.GeoSeries = None diff --git a/sgeop/continuity.py b/sgeop/continuity.py index a5b2dff..bc99a07 100644 --- a/sgeop/continuity.py +++ b/sgeop/continuity.py @@ -58,9 +58,9 @@ def get_stroke_info( Parameters ---------- - artifacts : GeoSeries | GeoDataFrame + artifacts : geopandas.GeoSeries | geopandas.GeoDataFrame Polygons representing the artifacts. - roads : GeoSeries | GeoDataFrame + roads : geopandas.GeoSeries | geopandas.GeoDataFrame LineStrings representing the road network. Returns diff --git a/sgeop/geometry.py b/sgeop/geometry.py index f153902..952e47c 100644 --- a/sgeop/geometry.py +++ b/sgeop/geometry.py @@ -99,11 +99,11 @@ def voronoi_skeleton( lines: list | np.ndarray | gpd.GeoSeries, poly: None | shapely.Polygon = None, snap_to: None | gpd.GeoSeries = None, - max_segment_length: int = 1, + max_segment_length: float | int = 1, buffer: None | float | int = None, secondary_snap_to: None | gpd.GeoSeries = None, - clip_limit: None | int = 2, - consolidation_tolerance: None | float = None, + clip_limit: None | float | int = 2, + consolidation_tolerance: None | float | int = None, ) -> tuple[np.ndarray]: """ Returns average geometry. @@ -117,20 +117,20 @@ def voronoi_skeleton( Polygon enclosed by ``lines``. snap_to : None | gpd.GeoSeries = None Series of geometries that shall be connected to the skeleton. - max_segment_length: int = 1 + max_segment_length: float | int = 1 Additional vertices will be added so that all line segments are no longer than this value. Must be greater than 0. buffer : None | float | int = None Optional custom buffer distance for dealing with Voronoi infinity issues. secondary_snap_to : None | gpd.GeoSeries = None Fall-back series of geometries that shall be connected to the skeleton. - clip_limit : None | int = 2 + clip_limit : None | float | int = 2 Following generation of the Voronoi linework, we clip to fit inside the polygon. To ensure we get a space to make proper topological connections from the linework to the actual points on the edge of the polygon, we clip using a polygon with a negative buffer of ``clip_limit`` or the radius of maximum inscribed circle, whichever is smaller. - consolidation_tolerance : None | float = None + consolidation_tolerance : None | float | int = None Tolerance passed to node consolidation within the resulting skeleton. If ``None``, no consolidation happens. @@ -291,7 +291,7 @@ def _as_parts(edgelines: np.ndarray) -> np.ndarray: def _consolidate( - edgelines: np.ndarray, consolidation_tolerance: float | int + edgelines: np.ndarray, consolidation_tolerance: None | float | int ) -> np.ndarray: """Return ``edgelines`` from consolidated nodes, if criteria met.""" if consolidation_tolerance and edgelines.shape[0] > 0: diff --git a/sgeop/simplify.py b/sgeop/simplify.py index 76da9e0..3800721 100644 --- a/sgeop/simplify.py +++ b/sgeop/simplify.py @@ -469,39 +469,83 @@ def get_solution(group, roads): def simplify_network( - roads, + roads: gpd.GeoDataFrame, *, - max_segment_length=1, - min_dangle_length=20, - clip_limit: int = 2, - simplification_factor=2, - consolidation_tolerance=10, - artifact_threshold=None, - artifact_threshold_fallback=None, - area_threshold_blocks=1e5, - isoareal_threshold_blocks=0.5, - area_threshold_circles=5e4, - isoareal_threshold_circles_enclosed=0.75, - isoperimetric_threshold_circles_touching=0.9, - eps=1e-4, - exclusion_mask=None, - predicate="intersects", -): - """ + max_segment_length: float | int = 1, + min_dangle_length: float | int = 20, + clip_limit: float | int = 2, + simplification_factor: float | int = 2, + consolidation_tolerance: float | int = 10, + artifact_threshold: None | float | int = None, + artifact_threshold_fallback: None | float | int = None, + area_threshold_blocks: float | int = 1e5, + isoareal_threshold_blocks: float | int = 0.5, + area_threshold_circles: float | int = 5e4, + isoareal_threshold_circles_enclosed: float | int = 0.75, + isoperimetric_threshold_circles_touching: float | int = 0.9, + eps: float = 1e-4, + exclusion_mask: None | gpd.GeoSeries = None, + predicate: str = "intersects", +) -> gpd.GeoDataFrame: + """Top-level workflow for simplifying networks. The input raw road network data is + first preprocessed (topological corrections & node consolidation) before two + iterations of artifact detection and simplification. For further information on + face artifact detection and extraction see :cite:`fleischmann2023`. Parameters ---------- - - clip_limit : int = 2 - Following generation of the Voronoi linework in ``geometry.voronoi_skeleton()``, - we clip to fit inside the polygon. To ensure we get a space to make proper - topological connections from the linework to the actual points on the edge of - the polygon, we clip using a polygon with a negative buffer of ``clip_limit`` - or the radius of maximum inscribed circle, whichever is smaller. + roads : geopandas.GeoDataFrame + Raw road network data. + max_segment_length : float | int = 1 + Additional vertices will be added so that all line segments + are no longer than this value. Must be greater than 0. + Used in multiple internal geometric operations. + min_dangle_length : float | int + The threshold for determining if linestrings are dangling slivers to be + removed or not. + clip_limit : float | int = 2 + Following generation of the Voronoi linework, we clip to fit inside the + polygon. To ensure we get a space to make proper topological connections + from the linework to the actual points on the edge of the polygon, we clip + using a polygon with a negative buffer of ``clip_limit`` or the radius of + maximum inscribed circle, whichever is smaller. + simplification_factor : float | int = 2 + The factor by which singles, pairs, and clusters are simplified. The + ``max_segment_length`` is multiplied by this factor to get the + simplification epsilon. + consolidation_tolerance : float | int = 10 + Tolerance passed to node consolidation when generating Voronoi skeletons. + artifact_threshold : None | float | int = None + When ``artifact_threshold`` is passed, the computed value from + ``momepy.FaceArtifacts.threshold`` is not used in favor of the + given value. This is useful for small networks where artifact + detection may fail or become unreliable. + artifact_threshold_fallback : None | float | int = None + If artifact threshold detection fails, this value is used as a fallback. + area_threshold_blocks : float | int = 1e5 + Areal theshold for block detection. + isoareal_threshold_blocks : float | int = 0.5 + Isoareal theshold for block detection. + See ``esda.shape.isoareal_quotient``. + area_threshold_circles : float | int = 5e4 + Areal theshold for circle detection. + isoareal_threshold_circles_enclosed : float | int = 0.75 + Isoareal theshold for enclosed circle detection. + See ``esda.shape.isoareal_quotient``. + isoperimetric_threshold_circles_touching : float | int = 0.9 + Isoperimetric theshold for enclosed circle touching. + See ``esda.shape.isoperimetric_quotient``. + eps : float = 1e-4 + Tolerance epsilon used in multiple internal geometric operations. + exclusion_mask : None | geopandas.GeoSeries = None + Polygons used to determine face artifacts to exclude from returned output. + predicate : str = 'intersects' + The spatial predicate used to exclude face artifacts from returned output. Returns ------- - + geopandas.GeoDataFrame + The final, simplified road network line data. """ roads = fix_topology(roads, eps=eps)