diff --git a/src/geoarrow.cpp b/src/geoarrow.cpp index 8d56b56..bfa6266 100644 --- a/src/geoarrow.cpp +++ b/src/geoarrow.cpp @@ -15,6 +15,7 @@ py::array_t from_geoarrow(py::object input, bool oriented, bool planar, float tessellate_tolerance, + Projection projection, py::object geometry_encoding) { if (!py::hasattr(input, "__arrow_c_array__")) { throw std::invalid_argument( @@ -33,6 +34,7 @@ py::array_t from_geoarrow(py::object input, s2geog::geoarrow::ImportOptions options; options.set_oriented(oriented); + options.set_projection(projection.s2_projection()); if (planar) { auto tol = S1Angle::Radians(tessellate_tolerance / EARTH_RADIUS_METERS); options.set_tessellate_tolerance(tol); @@ -264,6 +266,7 @@ void init_geoarrow(py::module& m) { py::arg("oriented") = false, py::arg("planar") = false, py::arg("tessellate_tolerance") = 100.0, + py::arg("projection") = Projection::lnglat(), py::arg("geometry_encoding") = py::none(), R"pbdoc( Create an array of geographies from an Arrow array object with a GeoArrow @@ -301,6 +304,10 @@ void init_geoarrow(py::module& m) { The maximum distance in meters that a point must be moved to satisfy the planar edge constraint. This is only used if `planar` is set to True. + projection : spherely.Projection, default Projection.lnglat() + The projection of the input coordinates. By default, it assumes + longitude/latitude coordinates, but this option allows to convert + from coordinates in pseudo-mercator or orthographic projection as well. geometry_encoding : str, default None By default, the encoding is inferred from the GeoArrow extension type of the input array. diff --git a/src/spherely.pyi b/src/spherely.pyi index d2382c8..ea90227 100644 --- a/src/spherely.pyi +++ b/src/spherely.pyi @@ -60,6 +60,16 @@ MultiLineStringGeography = Annotated[Geography, GeographyType.MULTILINESTRING] MultiPolygonGeography = Annotated[Geography, GeographyType.MULTIPOLYGON] GeometryCollection = Annotated[Geography, GeographyType.GEOMETRYCOLLECTION] +# Projection class + +class Projection: + @staticmethod + def lnglat() -> Projection: ... + @staticmethod + def speudo_mercator() -> Projection: ... + @staticmethod + def orthographic(longitude: float, latitude: float) -> Projection: ... + # Numpy-like vectorized (universal) functions _NameType = TypeVar("_NameType", bound=str) @@ -222,11 +232,24 @@ def from_wkb( tessellate_tolerance: float = 100.0, ) -> npt.NDArray[Any]: ... +class ArrowSchemaExportable(Protocol): + def __arrow_c_schema__(self) -> object: ... + class ArrowArrayExportable(Protocol): def __arrow_c_array__( self, requested_schema: object | None = None ) -> Tuple[object, object]: ... +def to_geoarrow( + input: npt.ArrayLike, + /, + *, + output_schema: ArrowSchemaExportable | None = None, + projection: Projection = Projection.lnglat(), + planar: bool = False, + tessellate_tolerance: float = 100.0, + precision: int = 6, +) -> ArrowArrayExportable: ... def from_geoarrow( input: ArrowArrayExportable, /, @@ -234,5 +257,6 @@ def from_geoarrow( oriented: bool = False, planar: bool = False, tessellate_tolerance: float = 100.0, + projection: Projection = Projection.lnglat(), geometry_encoding: str | None = None, ) -> npt.NDArray[Any]: ... diff --git a/tests/test_geoarrow.py b/tests/test_geoarrow.py index d7706b4..6bb9279 100644 --- a/tests/test_geoarrow.py +++ b/tests/test_geoarrow.py @@ -92,6 +92,18 @@ def test_from_wkt_planar(): assert spherely.distance(result, spherely.point(-30.1, 45)) < 10 +def test_from_geoarrow_projection(): + arr = ga.as_wkt(["POINT (1 0)", "POINT(0 1)"]) + + result = spherely.from_geoarrow( + arr, projection=spherely.Projection.orthographic(0, 0) + ) + expected = spherely.points([90, 0], [0, 90]) + # TODO use equality when we support precision / snapping + # assert spherely.equals(result, expected).all() + assert (spherely.to_wkt(result) == spherely.to_wkt(expected)).all() + + def test_from_geoarrow_no_extension_type(): arr = pa.array(["POINT (1 1)", "POINT(2 2)", "POINT(3 3)"])