Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Bindings for Vertex-Related Functions #388

Merged
merged 27 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ Because H3-Py is versioned in lockstep with the H3 core library, please
avoid adding features or APIs which do not map onto the
[H3 core API](https://uber.github.io/h3/#/documentation/api-reference/).

## Unreleased
## Unreleased - 2024-08-23

- None
- Added bindings for `cellToVertex`, `cellToVertexes`, `vertexToLatLng`, and `isValidVertex` (#323)

## [4.0.0b5] - 2024-05-19

Expand Down
12 changes: 12 additions & 0 deletions docs/api_quick.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ and should be generally aligned with the
is_pentagon
is_res_class_III
is_valid_directed_edge
is_valid_vertex
```

## Index representation
Expand Down Expand Up @@ -108,6 +109,17 @@ Functions relating H3 objects to geographic (lat/lng) coordinates.
get_directed_edge_origin
```

## Vertexes

```{eval-rst}
.. currentmodule:: h3

.. autosummary::
cell_to_vertex
cell_to_vertexes
vertex_to_latlng
```

## Polygon interface

The ``LatLngPoly`` and ``LatLngMultiPoly`` objects and their related functions allow users to represent (multi)polygons of lat/lng points and convert back and forth between H3 cells.
Expand Down
3 changes: 3 additions & 0 deletions src/h3/_cy/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ add_cython_file(edges)
add_cython_file(to_multipoly)
add_cython_file(error_system)
add_cython_file(memory)
add_cython_file(vertex)


# Include pyx and pxd files in distribution for use by Cython API
Expand All @@ -41,6 +42,8 @@ install(
error_system.pyx
memory.pxd
memory.pyx
vertex.pxd
vertex.pyx
DESTINATION
src/h3/_cy
)
7 changes: 7 additions & 0 deletions src/h3/_cy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@
great_circle_distance,
)

from .vertex import (
cell_to_vertex,
cell_to_vertexes,
vertex_to_latlng,
is_valid_vertex,
)

from .to_multipoly import (
cells_to_multi_polygon
)
Expand Down
5 changes: 5 additions & 0 deletions src/h3/_cy/h3lib.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ cdef extern from 'h3api.h':
int isPentagon(H3int h) nogil
int isResClassIII(H3int h) nogil
int isValidDirectedEdge(H3int edge) nogil
int isValidVertex(H3int v) nogil

double degsToRads(double degrees) nogil
double radsToDegs(double radians) nogil
Expand All @@ -80,6 +81,10 @@ cdef extern from 'h3api.h':
H3Error cellToLatLng(H3int h, LatLng *) nogil
H3Error gridDistance(H3int h1, H3int h2, int64_t *distance) nogil

H3Error cellToVertex(H3int cell, int vertexNum, H3int *out) nogil
H3Error cellToVertexes(H3int cell, H3int *vertexes) nogil
H3Error vertexToLatLng(H3int vertex, LatLng *coord) nogil

H3Error maxGridDiskSize(int k, int64_t *out) nogil # num/out/N?
H3Error gridDisk(H3int h, int k, H3int *out) nogil

Expand Down
2 changes: 1 addition & 1 deletion src/h3/_cy/latlng.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ from .util cimport (
check_edge,
check_res,
deg2coord,
coord2deg,
coord2deg
)

from .error_system cimport check_for_error
Expand Down
1 change: 1 addition & 0 deletions src/h3/_cy/util.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ cpdef H3str int_to_str(H3int x)
cdef check_cell(H3int h)
cdef check_edge(H3int e)
cdef check_res(int res)
cdef check_vertex(H3int v)
cdef check_distance(int k)
7 changes: 6 additions & 1 deletion src/h3/_cy/util.pyx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .h3lib cimport H3int, H3str, isValidCell, isValidDirectedEdge
from .h3lib cimport H3int, H3str, isValidCell, isValidDirectedEdge, isValidVertex

cimport h3lib

Expand All @@ -7,6 +7,7 @@ from .error_system import (
H3DomainError,
H3DirEdgeInvalidError,
H3CellInvalidError,
H3VertexInvalidError
)

cdef h3lib.LatLng deg2coord(double lat, double lng) nogil:
Expand Down Expand Up @@ -73,6 +74,10 @@ cdef check_edge(H3int e):
if isValidDirectedEdge(e) == 0:
raise H3DirEdgeInvalidError('Integer is not a valid H3 edge: {}'.format(hex(e)))

cdef check_vertex(H3int v):
if isValidVertex(v) == 0:
raise H3VertexInvalidError('Integer is not a valid H3 vertex: {}'.format(hex(v)))

cdef check_res(int res):
if (res < 0) or (res > 15):
raise H3ResDomainError(res)
Expand Down
6 changes: 6 additions & 0 deletions src/h3/_cy/vertex.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from .h3lib cimport bool, H3int

cpdef H3int cell_to_vertex(H3int h, int vertex_num) except 1
cpdef H3int[:] cell_to_vertexes(H3int h)
cpdef (double, double) vertex_to_latlng(H3int v) except *
cpdef bool is_valid_vertex(H3int v)
54 changes: 54 additions & 0 deletions src/h3/_cy/vertex.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
cimport h3lib
from h3lib cimport bool, H3int

from .util cimport (
check_cell,
check_vertex,
coord2deg
)

from .error_system cimport check_for_error

from .memory cimport H3MemoryManager


cpdef H3int cell_to_vertex(H3int h, int vertex_num) except 1:
cdef:
H3int out

check_cell(h)

check_for_error(
h3lib.cellToVertex(h, vertex_num, &out)
)

return out

cpdef H3int[:] cell_to_vertexes(H3int h):
cdef:
H3int out

check_cell(h)

hmm = H3MemoryManager(6)
check_for_error(
h3lib.cellToVertexes(h, hmm.ptr)
)
mv = hmm.to_mv()

return mv

cpdef (double, double) vertex_to_latlng(H3int v) except *:
cdef:
h3lib.LatLng c

check_vertex(v)

check_for_error(
h3lib.vertexToLatLng(v, &c)
)

return coord2deg(c)

cpdef bool is_valid_vertex(H3int v):
return h3lib.isValidVertex(v) == 1
67 changes: 67 additions & 0 deletions src/h3/api/basic_int/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -996,3 +996,70 @@ def great_circle_distance(latlng1, latlng2, unit='km'):
lat2, lng2,
unit = unit
)


def cell_to_vertex(h, vertex_num):
"""
Return a (specified) vertex of an H3 cell.

Parameters
----------
h : H3Cell
vertex_num : int
Vertex number (0-5)

Returns
-------
The vertex
"""
h = _in_scalar(h)
h = _cy.cell_to_vertex(h, vertex_num)
return _out_scalar(h)


def cell_to_vertexes(h):
"""
Return a list of vertexes of an H3 cell.
The list will be of length 5 for pentagons and 6 for hexagons.

Parameters
----------
h : H3Cell

Returns
-------
A list of vertexes
"""
h = _in_scalar(h)
mv = _cy.cell_to_vertexes(h)
return _out_collection(mv)


def vertex_to_latlng(v):
"""
Return latitude and longitude of a vertex.

Returns
-------
lat : float
Latitude
lng : float
Longitude
"""
v = _in_scalar(v)
return _cy.vertex_to_latlng(v)


def is_valid_vertex(v):
"""
Validates an H3 vertex.

Returns
-------
bool
"""
try:
v = _in_scalar(v)
return _cy.is_valid_vertex(v)
except (ValueError, TypeError):
return False
55 changes: 55 additions & 0 deletions tests/test_h3.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,3 +387,58 @@ def test_grid_path_cells():

with pytest.raises(h3.H3ResMismatchError):
h3.grid_path_cells(h1, '8001fffffffffff')


def test_cell_to_vertex():
# pentagon
assert h3.cell_to_vertex('814c3ffffffffff', 0) == '2014c3ffffffffff'
assert h3.cell_to_vertex('814c3ffffffffff', 1) == '2114c3ffffffffff'
assert h3.cell_to_vertex('814c3ffffffffff', 2) == '2214c3ffffffffff'
assert h3.cell_to_vertex('814c3ffffffffff', 3) == '2314c3ffffffffff'
assert h3.cell_to_vertex('814c3ffffffffff', 4) == '2414c3ffffffffff'
try:
h3.cell_to_vertex('814c3ffffffffff', 5)
except h3._cy.error_system.H3DomainError:
pass
else:
assert False

# hexagon
assert h3.cell_to_vertex('814d7ffffffffff', 0) == '2014d7ffffffffff'
assert h3.cell_to_vertex('814d7ffffffffff', 1) == '2213abffffffffff'
assert h3.cell_to_vertex('814d7ffffffffff', 2) == '2113abffffffffff'
assert h3.cell_to_vertex('814d7ffffffffff', 3) == '2414c3ffffffffff'
assert h3.cell_to_vertex('814d7ffffffffff', 4) == '2314c3ffffffffff'
assert h3.cell_to_vertex('814d7ffffffffff', 5) == '2414cfffffffffff'


def test_cell_to_vertexes():
# pentagon
assert h3.cell_to_vertexes('814c3ffffffffff') == [
'2014c3ffffffffff',
'2114c3ffffffffff',
'2214c3ffffffffff',
'2314c3ffffffffff',
'2414c3ffffffffff',
]

# hexagon
assert h3.cell_to_vertexes('814d7ffffffffff') == [
'2014d7ffffffffff',
'2213abffffffffff',
'2113abffffffffff',
'2414c3ffffffffff',
'2314c3ffffffffff',
'2414cfffffffffff',
]


def test_vertex_to_latlng():
latlng = h3.vertex_to_latlng('2114c3ffffffffff')
assert latlng == approx((24.945215618732814, -70.33904370008679))


def test_is_valid_vertex():
assert h3.is_valid_vertex('2114c3ffffffffff')
assert not h3.is_valid_vertex(2455495337847029759)
assert not h3.is_valid_vertex('foobar')
Loading