Skip to content

Commit

Permalink
Added Bindings for Vertex-Related Functions (#388)
Browse files Browse the repository at this point in the history
* Update CHANGELOG.md

* Update h3lib.pxd

* Update latlng.pxd

* Update latlng.pyx

* Update util.pxd

* Update util.pyx

* Update _version.py

* Update __init__.py

* Update __init__.py

* Update test_h3.py

* Update __init__.py

* Update test_h3.py

* Update CHANGELOG.md

* Update __init__.py

* Update test_h3.py

* Update _version.py

* Update test_h3.py

* Update __init__.py

* Update CMakeLists.txt

* Update latlng.pxd

* Update latlng.pyx

* Create vertex.pxd

* Create vertex.pyx

* Update __init__.py

* Update test_h3.py

* Update __init__.py

* Update api_quick.md
  • Loading branch information
Shiran-Yuan authored and ajfriend committed Sep 4, 2024
1 parent f9d4f24 commit 9a7442b
Show file tree
Hide file tree
Showing 12 changed files with 218 additions and 4 deletions.
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
2 changes: 2 additions & 0 deletions src/h3/_cy/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ install(
memory.pyx
util.pxd
util.pyx
vertex.pxd
vertex.pyx
DESTINATION
${SKBUILD_PROJECT_NAME}/_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 @@ -391,3 +391,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')

0 comments on commit 9a7442b

Please sign in to comment.