Skip to content

Commit

Permalink
fix case, where the target type is a multipart geometry but the subse…
Browse files Browse the repository at this point in the history
…t is a singlepart geometry
  • Loading branch information
ungarj committed Sep 11, 2024
1 parent f07b15b commit d077c0f
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 17 deletions.
8 changes: 5 additions & 3 deletions mapchete/formats/default/vector_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,11 @@ def _read_from_cache(self, validity_check=True, clip_to_crs_bounds=False):
if checked not in self._cache:
self._cache[checked] = list(
read_vector_window(
self._in_memory_features
if self._memory_cache_active
else self.path,
(
self._in_memory_features
if self._memory_cache_active
else self.path
),
self.tile,
validity_check=validity_check,
clip_to_crs_bounds=clip_to_crs_bounds,
Expand Down
20 changes: 14 additions & 6 deletions mapchete/geometry/filter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Generator, Union
from typing import Generator, Type, Union

from mapchete.errors import GeometryTypeError
from mapchete.geometry.types import (
Expand Down Expand Up @@ -39,23 +39,27 @@ def multipart_to_singleparts(

def is_type(
geometry: Geometry,
target_type: Union[str, Geometry],
target_type: Union[str, Type[Geometry]],
allow_multipart: bool = True,
) -> bool:
"""
Checks whether geometry type is in alignment with target type.
"""
target_type = get_geometry_type(target_type)
if isinstance(geometry, target_type):
return True
elif isinstance(geometry, GeometryCollection):
return False

# SinglePart is MultiPart or MultiPart is SinglePart
if allow_multipart:
return isinstance(geometry, get_multipart_type(target_type))
return False


def filter_by_geometry_type(
geometry: Geometry,
target_type: Union[str, Geometry],
target_type: Union[str, Type[Geometry]],
allow_multipart: bool = True,
):
"""Yields geometries only if they match the target type.
Expand All @@ -64,12 +68,16 @@ def filter_by_geometry_type(
If allow_multipart is set to False, multipart geometries are broken down into their
subgeometries. If set to True, a MultiPoint will be yielded if the
"""
target_type = get_geometry_type(target_type)
if is_type(geometry, target_type=target_type, allow_multipart=allow_multipart):
target_geometry_type = get_geometry_type(target_type)
if is_type(
geometry, target_type=target_geometry_type, allow_multipart=allow_multipart
):
yield geometry

elif isinstance(geometry, MultipartGeometry):
for subgeometry in multipart_to_singleparts(geometry):
yield from filter_by_geometry_type(
subgeometry, target_type=target_type, allow_multipart=allow_multipart
subgeometry,
target_type=target_geometry_type,
allow_multipart=allow_multipart,
)
34 changes: 29 additions & 5 deletions mapchete/geometry/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class GeoInterface(Protocol):
CoordArrays = Tuple[Iterable[float], Iterable[float]]


def get_multipart_type(geometry: Geometry) -> MultipartGeometry:
def get_multipart_type(geometry_type: Union[Type[Geometry], str]) -> MultipartGeometry:
try:
return {
Point: MultiPoint,
Expand All @@ -62,14 +62,38 @@ def get_multipart_type(geometry: Geometry) -> MultipartGeometry:
MultiPoint: MultiPoint,
MultiLineString: MultiLineString,
MultiPolygon: MultiPolygon,
}[geometry]
}[
get_geometry_type(geometry_type)
] # type: ignore
except KeyError:
raise GeometryTypeError(
f"geometry type {geometry.type} has no corresponding multipart type"
f"geometry type {geometry_type} has no corresponding multipart type"
)


def get_geometry_type(geometry_type: Union[Type[Geometry], str]) -> Type[Geometry]:
def get_singlepart_type(
geometry_type: Union[Type[Geometry], str]
) -> SinglepartGeometry:
try:
return {
Point: Point,
LineString: LineString,
Polygon: Polygon,
MultiPoint: Point,
MultiLineString: LineString,
MultiPolygon: Polygon,
}[
get_geometry_type(geometry_type)
] # type: ignore
except KeyError: # pragma: no cover
raise GeometryTypeError(
f"geometry type {geometry_type} has no corresponding multipart type"
)


def get_geometry_type(
geometry_type: Union[Type[Geometry], str, dict, Geometry]
) -> Geometry:
if isinstance(geometry_type, str):
try:
return {
Expand All @@ -87,5 +111,5 @@ def get_geometry_type(geometry_type: Union[Type[Geometry], str]) -> Type[Geometr
f"geometry type cannot be determined from {geometry_type}"
)
elif issubclass(geometry_type, Geometry):
return geometry_type
return geometry_type # type: ignore
raise GeometryTypeError(f"geometry type cannot be determined from {geometry_type}")
4 changes: 2 additions & 2 deletions mapchete/io/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
segmentize_geometry,
to_shape,
)
from mapchete.geometry.types import get_geometry_type
from mapchete.geometry.types import get_geometry_type, get_singlepart_type
from mapchete.io import copy
from mapchete.path import MPath, fs_from_path
from mapchete.settings import IORetrySettings
Expand Down Expand Up @@ -323,7 +323,7 @@ def _get_reprojected_features(
clipped_geom = original_geom.intersection(dst_bbox)
for checked_geom in filter_by_geometry_type(
clipped_geom,
original_geom.geom_type,
get_singlepart_type(original_geom.geom_type),
):
# reproject each feature to tile CRS
reprojected_geom = reproject_geometry(
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ dependencies = [
"geojson-pydantic",
"importlib-metadata",
"importlib-resources",
"numpy>=1.16",
"numpy>=1.16,<=2.0.1",
"oyaml",
"pydantic>=2.3.0",
"pydantic_settings>=2.0.0",
Expand Down

0 comments on commit d077c0f

Please sign in to comment.