Skip to content

Commit

Permalink
Add get_x and get_y vectorized functions (Point-only) (#37)
Browse files Browse the repository at this point in the history
Co-authored-by: Benoit Bovy <benbovy@gmail.com>
  • Loading branch information
jorisvandenbossche and benbovy authored Nov 27, 2024
1 parent 8e2d481 commit da0d66d
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 0 deletions.
2 changes: 2 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Geography properties
is_geography
get_dimensions
get_type_id
get_x
get_y

.. _api_creation:

Expand Down
42 changes: 42 additions & 0 deletions src/accessors-geog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,22 @@ PyObjectGeography convex_hull(PyObjectGeography a) {
return make_py_geography(s2geog::s2_convex_hull(a_ptr));
}

double get_x(PyObjectGeography a) {
auto geog = a.as_geog_ptr();
if (geog->geog_type() != GeographyType::Point) {
throw py::value_error("Only Point geometries supported");
}
return s2geog::s2_x(geog->geog());
}

double get_y(PyObjectGeography a) {
auto geog = a.as_geog_ptr();
if (geog->geog_type() != GeographyType::Point) {
throw py::value_error("Only Point geometries supported");
}
return s2geog::s2_y(geog->geog());
}

double distance(PyObjectGeography a, PyObjectGeography b, double radius = EARTH_RADIUS_METERS) {
const auto& a_index = a.as_geog_ptr()->geog_index();
const auto& b_index = b.as_geog_ptr()->geog_index();
Expand Down Expand Up @@ -86,6 +102,32 @@ void init_accessors(py::module& m) {
)pbdoc");

m.def("get_x",
py::vectorize(&get_x),
py::arg("a"),
R"pbdoc(
Returns the longitude value of the Point (in degrees).
Parameters
----------
a: :py:class:`Geography` or array_like
Geography object(s).
)pbdoc");

m.def("get_y",
py::vectorize(&get_y),
py::arg("a"),
R"pbdoc(
Returns the latitude value of the Point (in degrees).
Parameters
----------
a: :py:class:`Geography` or array_like
Geography object(s).
)pbdoc");

m.def("distance",
py::vectorize(&distance),
py::arg("a"),
Expand Down
5 changes: 5 additions & 0 deletions src/spherely.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ touches: _VFunc_Nin2_Nout1[Literal["touches"], bool, bool]
covers: _VFunc_Nin2_Nout1[Literal["covers"], bool, bool]
covered_by: _VFunc_Nin2_Nout1[Literal["covered_by"], bool, bool]

# coords

get_x: _VFunc_Nin1_Nout1[Literal["get_x"], float, np.float64]
get_y: _VFunc_Nin1_Nout1[Literal["get_y"], float, np.float64]

# geography accessors

centroid: _VFunc_Nin1_Nout1[Literal["centroid"], PointGeography, PointGeography]
Expand Down
25 changes: 25 additions & 0 deletions tests/test_accessors.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,31 @@ def test_convex_hull(geog, expected) -> None:
assert spherely.equals(actual, expected)


def test_get_x_y() -> None:
# scalar
a = spherely.point(1.5, 2.6)
assert spherely.get_x(a) == pytest.approx(1.5, abs=1e-14)
assert spherely.get_y(a) == pytest.approx(2.6, abs=1e-14)

# array
arr = np.array([spherely.point(0, 1), spherely.point(1, 2), spherely.point(2, 3)])

actual = spherely.get_x(arr)
expected = np.array([0, 1, 2], dtype="float64")
np.testing.assert_allclose(actual, expected)

actual = spherely.get_y(arr)
expected = np.array([1, 2, 3], dtype="float64")
np.testing.assert_allclose(actual, expected)

# only points are supported
with pytest.raises(ValueError):
spherely.get_x(spherely.linestring([(0, 1), (1, 2)]))

with pytest.raises(ValueError):
spherely.get_y(spherely.linestring([(0, 1), (1, 2)]))


@pytest.mark.parametrize(
"geog_a, geog_b, expected",
[
Expand Down

0 comments on commit da0d66d

Please sign in to comment.