diff --git a/pygeoif/factories.py b/pygeoif/factories.py index 09dfc61a..a612bae2 100644 --- a/pygeoif/factories.py +++ b/pygeoif/factories.py @@ -91,6 +91,40 @@ def force_2d( return shape(geometry) +def force_3d( + context: Union[GeoType, GeoCollectionType], + z: float = 0, +) -> Union[Geometry, GeometryCollection]: + """ + Force the dimensionality of a geometry to 3D. + + >>> force_3d(Point(0, 0)) + Point(0, 0, 0) + >>> force_3d(Point(0, 0), 1) + Point(0, 0, 1) + >>> force_3d(Point(0, 0, 0)) + Point(0, 0, 0) + >>> force_3d(LineString([(0, 0), (0, 1), (1, 1)])) + LineString(((0, 0, 0), (0, 1, 0), (1, 1, 0))) + """ + geometry = context if isinstance(context, dict) else mapping(context) + if not geometry: + msg = "Object does not implement __geo_interface__" + raise TypeError(msg) + if geometry["type"] == "GeometryCollection": + return GeometryCollection( + force_3d(g, z) # type: ignore [arg-type] + for g in geometry["geometries"] # type: ignore [typeddict-item] + ) + + geometry["coordinates"] = move_coordinates( # type: ignore [typeddict-unknown-key] + geometry["coordinates"], # type: ignore [typeddict-item] + (0, 0, 0), + z, + ) + return shape(geometry) + + def get_oriented_ring(ring: LineType, ccw: bool) -> LineType: # noqa: FBT001 s = 1.0 if ccw else -1.0 return ring if signed_area(ring) / s >= 0 else ring[::-1] @@ -363,6 +397,7 @@ def mapping( __all__ = [ + "force_3d", "box", "from_wkt", "mapping", diff --git a/tests/test_factories.py b/tests/test_factories.py index e3b5b475..b6c51876 100644 --- a/tests/test_factories.py +++ b/tests/test_factories.py @@ -139,6 +139,33 @@ def test_force2d_collection() -> None: assert list(gc2d.geoms) == [geometry.Point(-1, 1), geometry.Point(-2, 2)] +def test_force_3d_point() -> None: + p = geometry.Point(0, 0) + p3d = factories.force_3d(p) + assert p3d.x == 0 + assert p3d.y == 0 + assert p3d.z == 0 + assert p3d.has_z + + +def test_force_3d_point_with_z() -> None: + p = geometry.Point(0, 0, 1) + p3d = factories.force_3d(p) + assert p3d.x == 0 + assert p3d.y == 0 + assert p3d.z == 1 + assert p3d.has_z + + +def test_force_3d_point_3d() -> None: + p = geometry.Point(1, 2, 3) + p3d = factories.force_3d(p) + assert p3d.x == 1 + assert p3d.y == 2 + assert p3d.z == 3 + assert p3d.has_z + + def test_orient_true() -> None: ext = [(0, 0), (0, 2), (2, 2), (2, 0), (0, 0)] int_1 = [(0.5, 0.25), (1.5, 0.25), (1.5, 1.25), (0.5, 1.25), (0.5, 0.25)]