mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-20 15:54:13 +08:00
420 lines
9.3 KiB
Cython
420 lines
9.3 KiB
Cython
cimport h3lib
|
|
from .h3lib cimport bool, int64_t, H3int, H3ErrorCodes
|
|
|
|
from .util cimport (
|
|
check_cell,
|
|
check_res, # we don't use?
|
|
check_distance,
|
|
)
|
|
|
|
from .error_system cimport (
|
|
check_for_error,
|
|
check_for_error_msg,
|
|
)
|
|
|
|
from .memory cimport (
|
|
H3MemoryManager,
|
|
int_mv,
|
|
)
|
|
|
|
# todo: add notes about Cython exception handling
|
|
|
|
|
|
# bool is a python type, so we don't need the except clause
|
|
cpdef bool is_valid_cell(H3int h):
|
|
"""Validates an H3 cell (hexagon or pentagon)
|
|
|
|
Returns
|
|
-------
|
|
boolean
|
|
"""
|
|
return h3lib.isValidCell(h) == 1
|
|
|
|
|
|
cpdef bool is_pentagon(H3int h):
|
|
return h3lib.isPentagon(h) == 1
|
|
|
|
|
|
cpdef int get_base_cell_number(H3int h) except -1:
|
|
check_cell(h)
|
|
|
|
return h3lib.getBaseCellNumber(h)
|
|
|
|
|
|
cpdef int get_resolution(H3int h) except -1:
|
|
"""Returns the resolution of an H3 Index
|
|
0--15
|
|
"""
|
|
check_cell(h)
|
|
|
|
return h3lib.getResolution(h)
|
|
|
|
|
|
cpdef int grid_distance(H3int h1, H3int h2) except -1:
|
|
""" Compute the grid distance between two cells
|
|
"""
|
|
cdef:
|
|
int64_t distance
|
|
|
|
check_cell(h1)
|
|
check_cell(h2)
|
|
|
|
check_for_error(
|
|
h3lib.gridDistance(h1, h2, &distance)
|
|
)
|
|
|
|
return distance
|
|
|
|
cpdef H3int[:] grid_disk(H3int h, int k):
|
|
""" Return cells at grid distance `<= k` from `h`.
|
|
"""
|
|
cdef:
|
|
int64_t n
|
|
|
|
check_cell(h)
|
|
check_distance(k)
|
|
|
|
check_for_error(
|
|
h3lib.maxGridDiskSize(k, &n)
|
|
)
|
|
|
|
hmm = H3MemoryManager(n)
|
|
check_for_error(
|
|
h3lib.gridDisk(h, k, hmm.ptr)
|
|
)
|
|
mv = hmm.to_mv()
|
|
|
|
return mv
|
|
|
|
|
|
cpdef H3int[:] grid_ring(H3int h, int k):
|
|
""" Return cells at grid distance `== k` from `h`.
|
|
Collection is "hollow" for k >= 1.
|
|
"""
|
|
check_cell(h)
|
|
check_distance(k)
|
|
|
|
n = 6*k if k > 0 else 1
|
|
hmm = H3MemoryManager(n)
|
|
check_for_error(
|
|
h3lib.gridRing(h, k, hmm.ptr)
|
|
)
|
|
mv = hmm.to_mv()
|
|
|
|
return mv
|
|
|
|
|
|
cpdef H3int cell_to_parent(H3int h, res=None) except 0:
|
|
cdef:
|
|
H3int parent
|
|
|
|
check_cell(h)
|
|
if res is None:
|
|
res = get_resolution(h) - 1
|
|
|
|
err = h3lib.cellToParent(h, res, &parent)
|
|
if err:
|
|
msg = 'Invalid parent resolution {} for cell {}.'
|
|
msg = msg.format(res, hex(h))
|
|
check_for_error_msg(err, msg)
|
|
|
|
return parent
|
|
|
|
|
|
cpdef int64_t cell_to_children_size(H3int h, res=None) except -1:
|
|
cdef:
|
|
int64_t n
|
|
|
|
check_cell(h)
|
|
if res is None:
|
|
res = get_resolution(h) + 1
|
|
|
|
err = h3lib.cellToChildrenSize(h, res, &n)
|
|
if err:
|
|
msg = 'Invalid child resolution {} for cell {}.'
|
|
msg = msg.format(res, hex(h))
|
|
check_for_error_msg(err, msg)
|
|
|
|
return n
|
|
|
|
|
|
cpdef H3int[:] cell_to_children(H3int h, res=None):
|
|
check_cell(h)
|
|
if res is None:
|
|
res = get_resolution(h) + 1
|
|
|
|
n = cell_to_children_size(h, res)
|
|
|
|
hmm = H3MemoryManager(n)
|
|
check_for_error(
|
|
h3lib.cellToChildren(h, res, hmm.ptr)
|
|
)
|
|
mv = hmm.to_mv()
|
|
|
|
return mv
|
|
|
|
|
|
|
|
cpdef H3int cell_to_center_child(H3int h, res=None) except 0:
|
|
cdef:
|
|
H3int child
|
|
|
|
check_cell(h)
|
|
if res is None:
|
|
res = get_resolution(h) + 1
|
|
|
|
err = h3lib.cellToCenterChild(h, res, &child)
|
|
if err:
|
|
msg = 'Invalid child resolution {} for cell {}.'
|
|
msg = msg.format(res, hex(h))
|
|
check_for_error_msg(err, msg)
|
|
|
|
return child
|
|
|
|
|
|
cpdef int64_t cell_to_child_pos(H3int child, int parent_res) except -1:
|
|
cdef:
|
|
int64_t child_pos
|
|
|
|
check_cell(child)
|
|
err = h3lib.cellToChildPos(child, parent_res, &child_pos)
|
|
if err:
|
|
msg = "Couldn't find child pos of cell {} at res {}."
|
|
msg = msg.format(hex(child), parent_res)
|
|
check_for_error_msg(err, msg)
|
|
|
|
return child_pos
|
|
|
|
|
|
cpdef H3int child_pos_to_cell(H3int parent, int child_res, int64_t child_pos) except 0:
|
|
cdef:
|
|
H3int child
|
|
|
|
check_cell(parent)
|
|
err = h3lib.childPosToCell(child_pos, parent, child_res, &child)
|
|
if err:
|
|
msg = "Couldn't find child with pos {} at res {} from parent {}."
|
|
msg = msg.format(child_pos, child_res, hex(parent))
|
|
check_for_error_msg(err, msg)
|
|
|
|
return child
|
|
|
|
|
|
cpdef H3int[:] compact_cells(const H3int[:] hu):
|
|
# todo: fix this with my own Cython object "wrapper" class?
|
|
# everything has a .ptr interface?
|
|
# todo: the Clib can handle 0-len arrays because it **avoids**
|
|
# dereferencing the pointer, but Cython's syntax of
|
|
# `&hu[0]` **requires** a dereference. For Cython, checking for array
|
|
# length of zero and returning early seems like the easiest solution.
|
|
# note: open to better ideas!
|
|
|
|
if len(hu) == 0:
|
|
return H3MemoryManager(0).to_mv()
|
|
|
|
for h in hu: ## todo: should we have an array version? would that be faster?
|
|
check_cell(h)
|
|
|
|
cdef size_t n = len(hu)
|
|
hmm = H3MemoryManager(n)
|
|
check_for_error(
|
|
h3lib.compactCells(&hu[0], hmm.ptr, n)
|
|
)
|
|
mv = hmm.to_mv()
|
|
|
|
return mv
|
|
|
|
|
|
# todo: https://stackoverflow.com/questions/50684977/cython-exception-type-for-a-function-returning-a-typed-memoryview
|
|
# apparently, memoryviews are python objects, so we don't need to do the except clause
|
|
cpdef H3int[:] uncompact_cells(const H3int[:] hc, int res):
|
|
# todo: the Clib can handle 0-len arrays because it **avoids**
|
|
# dereferencing the pointer, but Cython's syntax of
|
|
# `&hc[0]` **requires** a dereference. For Cython, checking for array
|
|
# length of zero and returning early seems like the easiest solution.
|
|
# note: open to better ideas!
|
|
cdef:
|
|
int64_t n
|
|
|
|
|
|
if len(hc) == 0:
|
|
return H3MemoryManager(0).to_mv()
|
|
|
|
for h in hc:
|
|
check_cell(h)
|
|
|
|
check_for_error(
|
|
h3lib.uncompactCellsSize(&hc[0], len(hc), res, &n)
|
|
)
|
|
|
|
hmm = H3MemoryManager(n)
|
|
check_for_error(
|
|
h3lib.uncompactCells(
|
|
&hc[0], # todo: symmetry here with the wrapper object might be nice. hc.ptr / hc.n
|
|
len(hc),
|
|
hmm.ptr,
|
|
hmm.n,
|
|
res
|
|
)
|
|
)
|
|
|
|
mv = hmm.to_mv()
|
|
|
|
return mv
|
|
|
|
|
|
cpdef int64_t get_num_cells(int resolution) except -1:
|
|
cdef:
|
|
int64_t num_cells
|
|
|
|
check_for_error(
|
|
h3lib.getNumCells(resolution, &num_cells)
|
|
)
|
|
|
|
return num_cells
|
|
|
|
|
|
cpdef double average_hexagon_area(int resolution, unit='km^2') except -1:
|
|
cdef:
|
|
double area
|
|
|
|
check_for_error(
|
|
h3lib.getHexagonAreaAvgKm2(resolution, &area)
|
|
)
|
|
|
|
# todo: multiple units
|
|
convert = {
|
|
'km^2': 1.0,
|
|
'm^2': 1000*1000.0
|
|
}
|
|
|
|
try:
|
|
area *= convert[unit]
|
|
except:
|
|
raise ValueError('Unknown unit: {}'.format(unit))
|
|
|
|
return area
|
|
|
|
|
|
cpdef double cell_area(H3int h, unit='km^2') except -1:
|
|
cdef:
|
|
double area
|
|
|
|
if unit == 'rads^2':
|
|
err = h3lib.cellAreaRads2(h, &area)
|
|
elif unit == 'km^2':
|
|
err = h3lib.cellAreaKm2(h, &area)
|
|
elif unit == 'm^2':
|
|
err = h3lib.cellAreaM2(h, &area)
|
|
else:
|
|
raise ValueError('Unknown unit: {}'.format(unit))
|
|
|
|
check_for_error(err)
|
|
|
|
return area
|
|
|
|
|
|
cdef _could_not_find_line(err, start, end):
|
|
msg = "Couldn't find line between cells {} and {}"
|
|
msg = msg.format(hex(start), hex(end))
|
|
|
|
check_for_error_msg(err, msg)
|
|
|
|
cpdef H3int[:] grid_path_cells(H3int start, H3int end):
|
|
cdef:
|
|
int64_t n
|
|
|
|
# todo: can we segfault here with invalid inputs?
|
|
# Can we trust the c library to validate the start/end cells?
|
|
# probably applies to all size/work pairs of functions...
|
|
err = h3lib.gridPathCellsSize(start, end, &n)
|
|
|
|
_could_not_find_line(err, start, end)
|
|
|
|
hmm = H3MemoryManager(n)
|
|
err = h3lib.gridPathCells(start, end, hmm.ptr)
|
|
|
|
_could_not_find_line(err, start, end)
|
|
|
|
# todo: probably here too?
|
|
mv = hmm.to_mv()
|
|
|
|
return mv
|
|
|
|
cpdef bool is_res_class_iii(H3int h):
|
|
return h3lib.isResClassIII(h) == 1
|
|
|
|
|
|
cpdef H3int[:] get_pentagons(int res):
|
|
n = h3lib.pentagonCount()
|
|
|
|
hmm = H3MemoryManager(n)
|
|
check_for_error(
|
|
h3lib.getPentagons(res, hmm.ptr)
|
|
)
|
|
mv = hmm.to_mv()
|
|
|
|
return mv
|
|
|
|
|
|
cpdef H3int[:] get_res0_cells():
|
|
n = h3lib.res0CellCount()
|
|
|
|
hmm = H3MemoryManager(n)
|
|
check_for_error(
|
|
h3lib.getRes0Cells(hmm.ptr)
|
|
)
|
|
mv = hmm.to_mv()
|
|
|
|
return mv
|
|
|
|
# oh, this is returning a set??
|
|
# todo: convert to int[:]?
|
|
cpdef get_icosahedron_faces(H3int h):
|
|
cdef:
|
|
int n
|
|
int[:] faces ## todo: weird, this needs to be specified to avoid errors. cython bug?
|
|
|
|
check_for_error(
|
|
h3lib.maxFaceCount(h, &n)
|
|
)
|
|
|
|
faces = int_mv(n)
|
|
check_for_error(
|
|
h3lib.getIcosahedronFaces(h, &faces[0])
|
|
)
|
|
|
|
# todo: wait? do faces start from 0 or 1?
|
|
# we could do this check/processing in the int_mv object
|
|
out = [f for f in faces if f >= 0]
|
|
|
|
return out
|
|
|
|
|
|
cpdef (int, int) cell_to_local_ij(H3int origin, H3int h) except *:
|
|
cdef:
|
|
h3lib.CoordIJ c
|
|
|
|
err = h3lib.cellToLocalIj(origin, h, 0, &c)
|
|
if err:
|
|
msg = "Couldn't find local (i,j) between cells {} and {}."
|
|
msg = msg.format(hex(origin), hex(h))
|
|
check_for_error_msg(err, msg)
|
|
|
|
return c.i, c.j
|
|
|
|
cpdef H3int local_ij_to_cell(H3int origin, int i, int j) except 0:
|
|
cdef:
|
|
h3lib.CoordIJ c
|
|
H3int out
|
|
|
|
c.i, c.j = i, j
|
|
|
|
err = h3lib.localIjToCell(origin, &c, 0, &out)
|
|
if err:
|
|
msg = "Couldn't find cell at local ({},{}) from cell {}."
|
|
msg = msg.format(i, j, hex(origin))
|
|
check_for_error_msg(err, msg)
|
|
|
|
return out
|