From 4b6106ae194a526c6822bfbf0530b86f2b05d2de Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 11:05:20 -0700 Subject: [PATCH 01/18] cell_to_child_pos and child_pos_to_cell --- src/h3/_cy/__init__.py | 2 ++ src/h3/_cy/cells.pxd | 2 ++ src/h3/_cy/cells.pyx | 29 ++++++++++++++++++++++++++++- src/h3/_cy/h3lib.pxd | 2 ++ src/h3/api/basic_int/__init__.py | 13 +++++++++++++ 5 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/h3/_cy/__init__.py b/src/h3/_cy/__init__.py index c739bc04..b7737407 100644 --- a/src/h3/_cy/__init__.py +++ b/src/h3/_cy/__init__.py @@ -23,6 +23,8 @@ grid_disk, grid_ring, cell_to_children, + cell_to_child_pos, + child_pos_to_cell, compact_cells, uncompact_cells, get_num_cells, diff --git a/src/h3/_cy/cells.pxd b/src/h3/_cy/cells.pxd index b6a36d18..246be0cd 100644 --- a/src/h3/_cy/cells.pxd +++ b/src/h3/_cy/cells.pxd @@ -10,6 +10,8 @@ cpdef H3int[:] grid_ring(H3int h, int k) cpdef H3int cell_to_parent(H3int h, res=*) except 0 cpdef H3int[:] cell_to_children(H3int h, res=*) cpdef H3int cell_to_center_child(H3int h, res=*) except 0 +cpdef int64_t cell_to_child_pos(int parent_res, H3int child) except -1 +cpdef H3int child_pos_to_cell(H3int parent, int child_res, int64_t child_pos) except 0 cpdef H3int[:] compact_cells(const H3int[:] hu) cpdef H3int[:] uncompact_cells(const H3int[:] hc, int res) cpdef int64_t get_num_cells(int resolution) except -1 diff --git a/src/h3/_cy/cells.pyx b/src/h3/_cy/cells.pyx index 64be807e..e5c18bc0 100644 --- a/src/h3/_cy/cells.pyx +++ b/src/h3/_cy/cells.pyx @@ -3,7 +3,7 @@ from .h3lib cimport bool, int64_t, H3int, H3ErrorCodes from .util cimport ( check_cell, - check_res, + check_res, # we don't use? check_distance, ) @@ -199,6 +199,33 @@ cpdef H3int cell_to_center_child(H3int h, res=None) except 0: return child +cpdef int64_t cell_to_child_pos(int parent_res, H3int child) except -1: + cdef: + int64_t child_pos + + check_cell(child) + err = h3lib.cellToChildPos(child, parent_res, &child_pos) + if err: + msg = 'Invalid!' # TODO + # msg = msg.format(parent_res, hex(child)) + 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 = 'Invalid!' # TODO + # msg = msg.format(parent_res, hex(child)) + 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? diff --git a/src/h3/_cy/h3lib.pxd b/src/h3/_cy/h3lib.pxd index eae1b853..a578a8fe 100644 --- a/src/h3/_cy/h3lib.pxd +++ b/src/h3/_cy/h3lib.pxd @@ -90,6 +90,8 @@ cdef extern from 'h3api.h': H3Error cellToParent( H3int h, int parentRes, H3int *parent) nogil H3Error cellToCenterChild(H3int h, int childRes, H3int *child) nogil + H3Error cellToChildPos(H3int child, int parentRes, int64_t *out) nogil + H3Error childPosToCell(int64_t childPos, H3int parent, int childRes, H3int *child) nogil H3Error cellToChildrenSize(H3int h, int childRes, int64_t *num) nogil # num/out/N? H3Error cellToChildren( H3int h, int childRes, H3int *children) nogil diff --git a/src/h3/api/basic_int/__init__.py b/src/h3/api/basic_int/__init__.py index d292ed69..e0aba3ef 100644 --- a/src/h3/api/basic_int/__init__.py +++ b/src/h3/api/basic_int/__init__.py @@ -341,6 +341,19 @@ def cell_to_children(h, res=None): return _out_collection(mv) +def cell_to_child_pos(res, child): + child = _in_scalar(child) + return _cy.cell_to_child_pos(res, child) + + +def child_pos_to_cell(parent, child_res, child_pos): + parent = _in_scalar(parent) + child = _cy.child_pos_to_cell(parent, child_res, child_pos) + child = _out_scalar(child) + + return child + + # todo: nogil for expensive C operation? def compact_cells(cells): """ From e2c387ef1ebd0cbe9ee8ad60f66f5107e9fbcf04 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 11:29:54 -0700 Subject: [PATCH 02/18] start exposing cell_to_children_size --- src/h3/_cy/cells.pyx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/h3/_cy/cells.pyx b/src/h3/_cy/cells.pyx index e5c18bc0..688bfca1 100644 --- a/src/h3/_cy/cells.pyx +++ b/src/h3/_cy/cells.pyx @@ -157,7 +157,7 @@ cpdef H3int cell_to_parent(H3int h, res=None) except 0: return parent -cpdef H3int[:] cell_to_children(H3int h, res=None): +cpdef int64_t cell_to_children_size(H3int h, res=None) except -1: cdef: int64_t n @@ -172,6 +172,14 @@ cpdef H3int[:] cell_to_children(H3int h, res=None): msg = msg.format(res, hex(h)) check_for_error_msg(err, msg) + return n + + +cpdef H3int[:] cell_to_children(H3int h, res=None): + 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) @@ -181,6 +189,7 @@ cpdef H3int[:] cell_to_children(H3int h, res=None): return mv + cpdef H3int cell_to_center_child(H3int h, res=None) except 0: cdef: H3int child From bed476dc3fdac7bb129c070a1568eb8d53c85a5d Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 11:48:53 -0700 Subject: [PATCH 03/18] checking resolutions --- src/h3/_cy/cells.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/h3/_cy/cells.pyx b/src/h3/_cy/cells.pyx index 688bfca1..492eb146 100644 --- a/src/h3/_cy/cells.pyx +++ b/src/h3/_cy/cells.pyx @@ -139,12 +139,12 @@ cpdef H3int[:] grid_ring(H3int h, int k): return mv + cpdef H3int cell_to_parent(H3int h, res=None) except 0: cdef: H3int parent - check_cell(h) # todo: do we want to check for validity here? or leave correctness to the user? - + check_cell(h) if res is None: res = get_resolution(h) - 1 @@ -162,7 +162,6 @@ cpdef int64_t cell_to_children_size(H3int h, res=None) except -1: int64_t n check_cell(h) - if res is None: res = get_resolution(h) + 1 @@ -176,8 +175,10 @@ cpdef int64_t cell_to_children_size(H3int h, res=None) except -1: 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) @@ -195,7 +196,6 @@ cpdef H3int cell_to_center_child(H3int h, res=None) except 0: H3int child check_cell(h) - if res is None: res = get_resolution(h) + 1 From 4dc8dd188b5cbbf73f25362fdac4b08dcd5ecf33 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 11:55:55 -0700 Subject: [PATCH 04/18] cell_to_children_size python function --- src/h3/_cy/__init__.py | 1 + src/h3/_cy/cells.pxd | 1 + src/h3/api/basic_int/__init__.py | 23 +++++++++++++++++------ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/h3/_cy/__init__.py b/src/h3/_cy/__init__.py index b7737407..804b6b0e 100644 --- a/src/h3/_cy/__init__.py +++ b/src/h3/_cy/__init__.py @@ -22,6 +22,7 @@ grid_distance, grid_disk, grid_ring, + cell_to_children_size, cell_to_children, cell_to_child_pos, child_pos_to_cell, diff --git a/src/h3/_cy/cells.pxd b/src/h3/_cy/cells.pxd index 246be0cd..8fa2a596 100644 --- a/src/h3/_cy/cells.pxd +++ b/src/h3/_cy/cells.pxd @@ -8,6 +8,7 @@ cpdef int grid_distance(H3int h1, H3int h2) except -1 cpdef H3int[:] grid_disk(H3int h, int k) cpdef H3int[:] grid_ring(H3int h, int k) cpdef H3int cell_to_parent(H3int h, res=*) except 0 +cpdef int64_t cell_to_children_size(H3int h, res=*) except -1 cpdef H3int[:] cell_to_children(H3int h, res=*) cpdef H3int cell_to_center_child(H3int h, res=*) except 0 cpdef int64_t cell_to_child_pos(int parent_res, H3int child) except -1 diff --git a/src/h3/api/basic_int/__init__.py b/src/h3/api/basic_int/__init__.py index e0aba3ef..bbd8e30a 100644 --- a/src/h3/api/basic_int/__init__.py +++ b/src/h3/api/basic_int/__init__.py @@ -185,7 +185,8 @@ def cell_to_latlng(h): lng : float Longitude """ - return _cy.cell_to_latlng(_in_scalar(h)) + h = _in_scalar(h) + return _cy.cell_to_latlng(h) def get_resolution(h): @@ -201,7 +202,8 @@ def get_resolution(h): int """ # todo: could also work for edges - return _cy.get_resolution(_in_scalar(h)) + h = _in_scalar(h) + return _cy.get_resolution(h) def cell_to_parent(h, res=None): @@ -266,7 +268,8 @@ def cell_to_boundary(h): ------- tuple of (lat, lng) tuples """ - return _cy.cell_to_boundary(_in_scalar(h)) + h = _in_scalar(h) + return _cy.cell_to_boundary(h) def grid_disk(h, k=1): @@ -288,7 +291,8 @@ def grid_disk(h, k=1): ----- There is currently no guaranteed order of the output cells. """ - mv = _cy.grid_disk(_in_scalar(h), k) + h = _in_scalar(h) + mv = _cy.grid_disk(h, k) return _out_collection(mv) @@ -312,11 +316,17 @@ def grid_ring(h, k=1): ----- There is currently no guaranteed order of the output cells. """ - mv = _cy.grid_ring(_in_scalar(h), k) + h = _in_scalar(h) + mv = _cy.grid_ring(h, k) return _out_collection(mv) +def cell_to_children_size(h, res=None): + h = _in_scalar(h) + return _cy.cell_to_children_size(h, res) + + def cell_to_children(h, res=None): """ Children of a cell as an unordered collection. @@ -336,7 +346,8 @@ def cell_to_children(h, res=None): ----- There is currently no guaranteed order of the output cells. """ - mv = _cy.cell_to_children(_in_scalar(h), res) + h = _in_scalar(h) + mv = _cy.cell_to_children(h, res) return _out_collection(mv) From 711d922f5e255e907187aec4c04dac8d9b79ee04 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 12:09:00 -0700 Subject: [PATCH 05/18] adding tests from https://github.com/uber/h3-py/pull/404 --- tests/test_h3.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/test_h3.py b/tests/test_h3.py index 051480a6..8e11a41d 100644 --- a/tests/test_h3.py +++ b/tests/test_h3.py @@ -446,3 +446,34 @@ 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') + + +def test_child_pos(): + assert h3.cell_to_child_pos(8, '88283080ddfffff') == 0 + assert h3.cell_to_child_pos(7, '88283080ddfffff') == 6 + assert h3.cell_to_child_pos(6, '88283080ddfffff') == 41 + + with pytest.raises(h3.H3BaseException): + h3.cell_to_child_pos(9, '88283080ddfffff') + + with pytest.raises(h3.H3BaseException): + h3.cell_to_child_pos(9, '88283080ddfffff') + + assert h3.child_pos_to_cell('88283080ddfffff', 8, 0) == '88283080ddfffff' + + assert '88283080ddfffff' == h3.child_pos_to_cell( + h3.cell_to_parent('88283080ddfffff', 7), + 8, + 6, + ) + assert '88283080ddfffff' == h3.child_pos_to_cell( + h3.cell_to_parent('88283080ddfffff', 6), + 8, + 41, + ) + + with pytest.raises(h3.H3BaseException): + h3.child_pos_to_cell('88283080ddfffff', 9, -1) + + with pytest.raises(h3.H3BaseException): + h3.child_pos_to_cell('88283080ddfffff', 9, 10000) From 2d79be02c738a911368a6fadfaf7640037df6f12 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 12:19:13 -0700 Subject: [PATCH 06/18] fiddling --- tests/test_h3.py | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/tests/test_h3.py b/tests/test_h3.py index 8e11a41d..37fb7049 100644 --- a/tests/test_h3.py +++ b/tests/test_h3.py @@ -449,31 +449,45 @@ def test_is_valid_vertex(): def test_child_pos(): - assert h3.cell_to_child_pos(8, '88283080ddfffff') == 0 - assert h3.cell_to_child_pos(7, '88283080ddfffff') == 6 - assert h3.cell_to_child_pos(6, '88283080ddfffff') == 41 + cell = '88283080ddfffff' + + assert h3.cell_to_child_pos(8, cell) == 0 + assert h3.cell_to_child_pos(7, cell) == 6 + assert h3.cell_to_child_pos(6, cell) == 41 + + with pytest.raises(h3.H3BaseException): + h3.cell_to_child_pos(9, cell) + + with pytest.raises(h3.H3BaseException): + h3.cell_to_child_pos(9, cell) with pytest.raises(h3.H3BaseException): - h3.cell_to_child_pos(9, '88283080ddfffff') + h3.child_pos_to_cell(cell, 9, -1) with pytest.raises(h3.H3BaseException): - h3.cell_to_child_pos(9, '88283080ddfffff') + h3.child_pos_to_cell(cell, 9, 10000) - assert h3.child_pos_to_cell('88283080ddfffff', 8, 0) == '88283080ddfffff' - assert '88283080ddfffff' == h3.child_pos_to_cell( - h3.cell_to_parent('88283080ddfffff', 7), +def test_child_pos2(): + cell = '88283080ddfffff' + assert cell == h3.child_pos_to_cell( + cell, + 8, + 0, + ) + + assert cell == h3.child_pos_to_cell( + h3.cell_to_parent(cell, 7), 8, 6, ) - assert '88283080ddfffff' == h3.child_pos_to_cell( - h3.cell_to_parent('88283080ddfffff', 6), + assert cell == h3.child_pos_to_cell( + h3.cell_to_parent(cell, 6), 8, 41, ) - with pytest.raises(h3.H3BaseException): - h3.child_pos_to_cell('88283080ddfffff', 9, -1) +def test_cell_to_children_size(): + assert h3.cell_to_children_size('88283080ddfffff') == 7 + # for r in range(16): - with pytest.raises(h3.H3BaseException): - h3.child_pos_to_cell('88283080ddfffff', 9, 10000) From 35d78072c0973a1f991ce4d415c2eab9a7c16449 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 12:20:48 -0700 Subject: [PATCH 07/18] fix test_cell_to_vertex; helps coverage by removing untouched branch --- tests/test_h3.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/test_h3.py b/tests/test_h3.py index 37fb7049..9b5bae52 100644 --- a/tests/test_h3.py +++ b/tests/test_h3.py @@ -400,12 +400,9 @@ def test_cell_to_vertex(): 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: + + with pytest.raises(h3.H3DomainError): 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' From eb162d6be3637c2b55704c0c614dde5cc906b51c Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 12:30:26 -0700 Subject: [PATCH 08/18] more tests --- tests/test_h3.py | 52 ++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/tests/test_h3.py b/tests/test_h3.py index 9b5bae52..23e1e1e3 100644 --- a/tests/test_h3.py +++ b/tests/test_h3.py @@ -446,45 +446,45 @@ def test_is_valid_vertex(): def test_child_pos(): - cell = '88283080ddfffff' + h = '88283080ddfffff' - assert h3.cell_to_child_pos(8, cell) == 0 - assert h3.cell_to_child_pos(7, cell) == 6 - assert h3.cell_to_child_pos(6, cell) == 41 + assert h3.cell_to_child_pos(8, h) == 0 + assert h3.cell_to_child_pos(7, h) == 6 + assert h3.cell_to_child_pos(6, h) == 41 with pytest.raises(h3.H3BaseException): - h3.cell_to_child_pos(9, cell) + h3.cell_to_child_pos(9, h) with pytest.raises(h3.H3BaseException): - h3.cell_to_child_pos(9, cell) + h3.cell_to_child_pos(9, h) with pytest.raises(h3.H3BaseException): - h3.child_pos_to_cell(cell, 9, -1) + h3.child_pos_to_cell(h, 9, -1) with pytest.raises(h3.H3BaseException): - h3.child_pos_to_cell(cell, 9, 10000) + h3.child_pos_to_cell(h, 9, 10000) def test_child_pos2(): - cell = '88283080ddfffff' - assert cell == h3.child_pos_to_cell( - cell, - 8, - 0, - ) + h = '88283080ddfffff' + assert h == h3.child_pos_to_cell(h, 8, 0) + assert h == h3.child_pos_to_cell(h3.cell_to_parent(h, 7), 8, 6) + assert h == h3.child_pos_to_cell(h3.cell_to_parent(h, 6), 8, 41) - assert cell == h3.child_pos_to_cell( - h3.cell_to_parent(cell, 7), - 8, - 6, - ) - assert cell == h3.child_pos_to_cell( - h3.cell_to_parent(cell, 6), - 8, - 41, - ) def test_cell_to_children_size(): - assert h3.cell_to_children_size('88283080ddfffff') == 7 - # for r in range(16): + h = '8053fffffffffff' # hexagon + for r in range(16): + assert h3.cell_to_children_size(h, r) == 7**r + +def test_cell_to_children_size2(): + cells = h3.get_res0_cells() + + for r in range(16): + total_cells = 120*(7**r) + 2 + + assert total_cells == sum( + h3.cell_to_children_size(h, r) + for h in cells + ) From 7cb8f12fe097fe6f37558ef14cd25f098e4613aa Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 12:31:24 -0700 Subject: [PATCH 09/18] lint --- tests/test_h3.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_h3.py b/tests/test_h3.py index 23e1e1e3..c5cddf16 100644 --- a/tests/test_h3.py +++ b/tests/test_h3.py @@ -473,18 +473,18 @@ def test_child_pos2(): def test_cell_to_children_size(): - h = '8053fffffffffff' # hexagon + h = '8053fffffffffff' # hexagon for r in range(16): assert h3.cell_to_children_size(h, r) == 7**r + def test_cell_to_children_size2(): cells = h3.get_res0_cells() for r in range(16): - total_cells = 120*(7**r) + 2 + total_cells = 120 * (7**r) + 2 assert total_cells == sum( h3.cell_to_children_size(h, r) for h in cells ) - From 07955b5f1ff9f9d4475f3d180eeefd8f3cbb036a Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 12:56:19 -0700 Subject: [PATCH 10/18] exhaustive test for a few resolution pairs --- tests/test_h3.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_h3.py b/tests/test_h3.py index c5cddf16..5026ac04 100644 --- a/tests/test_h3.py +++ b/tests/test_h3.py @@ -488,3 +488,21 @@ def test_cell_to_children_size2(): h3.cell_to_children_size(h, r) for h in cells ) + +def test_child_pos3(): + def cells_at_res(res): + cells = h3.get_res0_cells() + for parent in cells: + yield from h3.cell_to_children(parent, res) + + def roundtrip(children, res_parent): + for c in children: + parent = h3.cell_to_parent(c, res_parent) + pos = h3.cell_to_child_pos(res_parent, c) + yield h3.child_pos_to_cell(parent, res_child, pos) + + for res_parent in [0,1]: + for res_child in [0,1,2,3]: + if res_parent <= res_child: + children = set(cells_at_res(res_child)) + assert set(roundtrip(children, res_parent)) == children From 1be36fa570494016bb63c00d55c23c3882803858 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 13:08:52 -0700 Subject: [PATCH 11/18] lint --- tests/test_h3.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_h3.py b/tests/test_h3.py index 5026ac04..f9f8dcd4 100644 --- a/tests/test_h3.py +++ b/tests/test_h3.py @@ -489,6 +489,7 @@ def test_cell_to_children_size2(): for h in cells ) + def test_child_pos3(): def cells_at_res(res): cells = h3.get_res0_cells() @@ -501,8 +502,8 @@ def roundtrip(children, res_parent): pos = h3.cell_to_child_pos(res_parent, c) yield h3.child_pos_to_cell(parent, res_child, pos) - for res_parent in [0,1]: - for res_child in [0,1,2,3]: + for res_parent in [0, 1]: + for res_child in [0, 1, 2, 3]: if res_parent <= res_child: children = set(cells_at_res(res_child)) assert set(roundtrip(children, res_parent)) == children From 2dbb990fb594b1b908316b12d9bd5b8b4dcc8700 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 13:32:59 -0700 Subject: [PATCH 12/18] doc strings --- docs/api_quick.md | 3 ++ src/h3/api/basic_int/__init__.py | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/docs/api_quick.md b/docs/api_quick.md index a8c585c3..5503baef 100644 --- a/docs/api_quick.md +++ b/docs/api_quick.md @@ -79,6 +79,9 @@ Functions relating H3 objects to geographic (lat/lng) coordinates. cell_to_parent cell_to_children cell_to_center_child + cell_to_children_size + cell_to_child_pos + child_pos_to_cell compact_cells uncompact_cells ``` diff --git a/src/h3/api/basic_int/__init__.py b/src/h3/api/basic_int/__init__.py index bbd8e30a..3da07a87 100644 --- a/src/h3/api/basic_int/__init__.py +++ b/src/h3/api/basic_int/__init__.py @@ -323,6 +323,21 @@ def grid_ring(h, k=1): def cell_to_children_size(h, res=None): + """ + Number of children at resolution ``res`` of given cell. + + Parameters + ---------- + h : H3Cell + res : int or None, optional + The resolution for the children. + If ``None``, then ``res = resolution(h) + 1`` + + Returns + ------- + int + Count of children + """ h = _in_scalar(h) return _cy.cell_to_children_size(h, res) @@ -353,11 +368,45 @@ def cell_to_children(h, res=None): def cell_to_child_pos(res, child): + """ + Child position index of given cell, with respect to parent of resolution ``res``. + + The reverse operation can be done with ``child_pos_to_cell``. + + Parameters + ---------- + res : int + Parent resolution + child : H3Cell + + Returns + ------- + int + Integer index of the child with respect to parent cell. + """ child = _in_scalar(child) return _cy.cell_to_child_pos(res, child) def child_pos_to_cell(parent, child_res, child_pos): + """ + Get child H3 cell from a parent cell, child resolution, and child position index. + + The reverse operation can be done with ``cell_to_child_pos``. + + Parameters + ---------- + parent : H3Cell + child_res : int + Child cell resolution + child_pos : int + Integer position of child cell, releative to parent. + + + Returns + ------- + H3Cell + """ parent = _in_scalar(parent) child = _cy.child_pos_to_cell(parent, child_res, child_pos) child = _out_scalar(child) From e167099772be4949a4273bec95bffc2ef648f0c2 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 13:43:31 -0700 Subject: [PATCH 13/18] error messages --- src/h3/_cy/cells.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/h3/_cy/cells.pyx b/src/h3/_cy/cells.pyx index 492eb146..5d0f21fe 100644 --- a/src/h3/_cy/cells.pyx +++ b/src/h3/_cy/cells.pyx @@ -215,8 +215,8 @@ cpdef int64_t cell_to_child_pos(int parent_res, H3int child) except -1: check_cell(child) err = h3lib.cellToChildPos(child, parent_res, &child_pos) if err: - msg = 'Invalid!' # TODO - # msg = msg.format(parent_res, hex(child)) + 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 @@ -229,8 +229,8 @@ cpdef H3int child_pos_to_cell(H3int parent, int child_res, int64_t child_pos) ex check_cell(parent) err = h3lib.childPosToCell(child_pos, parent, child_res, &child) if err: - msg = 'Invalid!' # TODO - # msg = msg.format(parent_res, hex(child)) + 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 From bde9adf94918c95c04edaf95be212e1cab196114 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 13:59:00 -0700 Subject: [PATCH 14/18] stricter test --- tests/test_h3.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/tests/test_h3.py b/tests/test_h3.py index f9f8dcd4..8ac307d2 100644 --- a/tests/test_h3.py +++ b/tests/test_h3.py @@ -496,14 +496,19 @@ def cells_at_res(res): for parent in cells: yield from h3.cell_to_children(parent, res) - def roundtrip(children, res_parent): - for c in children: - parent = h3.cell_to_parent(c, res_parent) - pos = h3.cell_to_child_pos(res_parent, c) - yield h3.child_pos_to_cell(parent, res_child, pos) - - for res_parent in [0, 1]: - for res_child in [0, 1, 2, 3]: - if res_parent <= res_child: - children = set(cells_at_res(res_child)) - assert set(roundtrip(children, res_parent)) == children + def roundtrip(child, res_parent): + res_child = h3.get_resolution(child) + parent = h3.cell_to_parent(child, res_parent) + pos = h3.cell_to_child_pos(res_parent, child) + return h3.child_pos_to_cell(parent, res_child, pos) + + res_pairs = [ + (res_parent, res_child) + for res_parent in [0, 1] + for res_child in [0, 1, 2, 3] + if res_parent <= res_child + ] + + for res_parent, res_child in res_pairs: + for child in cells_at_res(res_child): + assert child == roundtrip(child, res_parent) From 8765798290ecac45cb43b18a315f40cb65f4c3aa Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 14:07:52 -0700 Subject: [PATCH 15/18] playing with argument order. example code in tests looks nicer --- src/h3/_cy/cells.pxd | 2 +- src/h3/_cy/cells.pyx | 2 +- src/h3/api/basic_int/__init__.py | 6 +++--- tests/test_h3.py | 13 +++++-------- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/h3/_cy/cells.pxd b/src/h3/_cy/cells.pxd index 8fa2a596..06b35ee6 100644 --- a/src/h3/_cy/cells.pxd +++ b/src/h3/_cy/cells.pxd @@ -11,7 +11,7 @@ cpdef H3int cell_to_parent(H3int h, res=*) except 0 cpdef int64_t cell_to_children_size(H3int h, res=*) except -1 cpdef H3int[:] cell_to_children(H3int h, res=*) cpdef H3int cell_to_center_child(H3int h, res=*) except 0 -cpdef int64_t cell_to_child_pos(int parent_res, H3int child) except -1 +cpdef int64_t cell_to_child_pos(H3int child, int parent_res) except -1 cpdef H3int child_pos_to_cell(H3int parent, int child_res, int64_t child_pos) except 0 cpdef H3int[:] compact_cells(const H3int[:] hu) cpdef H3int[:] uncompact_cells(const H3int[:] hc, int res) diff --git a/src/h3/_cy/cells.pyx b/src/h3/_cy/cells.pyx index 5d0f21fe..2b57af27 100644 --- a/src/h3/_cy/cells.pyx +++ b/src/h3/_cy/cells.pyx @@ -208,7 +208,7 @@ cpdef H3int cell_to_center_child(H3int h, res=None) except 0: return child -cpdef int64_t cell_to_child_pos(int parent_res, H3int child) except -1: +cpdef int64_t cell_to_child_pos(H3int child, int parent_res) except -1: cdef: int64_t child_pos diff --git a/src/h3/api/basic_int/__init__.py b/src/h3/api/basic_int/__init__.py index 3da07a87..a1989fd3 100644 --- a/src/h3/api/basic_int/__init__.py +++ b/src/h3/api/basic_int/__init__.py @@ -367,7 +367,7 @@ def cell_to_children(h, res=None): return _out_collection(mv) -def cell_to_child_pos(res, child): +def cell_to_child_pos(child, res): """ Child position index of given cell, with respect to parent of resolution ``res``. @@ -375,9 +375,9 @@ def cell_to_child_pos(res, child): Parameters ---------- + child : H3Cell res : int Parent resolution - child : H3Cell Returns ------- @@ -385,7 +385,7 @@ def cell_to_child_pos(res, child): Integer index of the child with respect to parent cell. """ child = _in_scalar(child) - return _cy.cell_to_child_pos(res, child) + return _cy.cell_to_child_pos(child, res) def child_pos_to_cell(parent, child_res, child_pos): diff --git a/tests/test_h3.py b/tests/test_h3.py index 8ac307d2..760c3fab 100644 --- a/tests/test_h3.py +++ b/tests/test_h3.py @@ -448,15 +448,12 @@ def test_is_valid_vertex(): def test_child_pos(): h = '88283080ddfffff' - assert h3.cell_to_child_pos(8, h) == 0 - assert h3.cell_to_child_pos(7, h) == 6 - assert h3.cell_to_child_pos(6, h) == 41 + assert h3.cell_to_child_pos(h, 8) == 0 + assert h3.cell_to_child_pos(h, 7) == 6 + assert h3.cell_to_child_pos(h, 6) == 41 with pytest.raises(h3.H3BaseException): - h3.cell_to_child_pos(9, h) - - with pytest.raises(h3.H3BaseException): - h3.cell_to_child_pos(9, h) + h3.cell_to_child_pos(h, 9) with pytest.raises(h3.H3BaseException): h3.child_pos_to_cell(h, 9, -1) @@ -499,7 +496,7 @@ def cells_at_res(res): def roundtrip(child, res_parent): res_child = h3.get_resolution(child) parent = h3.cell_to_parent(child, res_parent) - pos = h3.cell_to_child_pos(res_parent, child) + pos = h3.cell_to_child_pos(child, res_parent) return h3.child_pos_to_cell(parent, res_child, pos) res_pairs = [ From 4e9dd1634e79ec1b1e30f4110788a9e94a9ebe60 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 14:11:39 -0700 Subject: [PATCH 16/18] use parent_res instead of res --- src/h3/api/basic_int/__init__.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/h3/api/basic_int/__init__.py b/src/h3/api/basic_int/__init__.py index a1989fd3..604e7736 100644 --- a/src/h3/api/basic_int/__init__.py +++ b/src/h3/api/basic_int/__init__.py @@ -367,17 +367,16 @@ def cell_to_children(h, res=None): return _out_collection(mv) -def cell_to_child_pos(child, res): +def cell_to_child_pos(child, parent_res): """ - Child position index of given cell, with respect to parent of resolution ``res``. + Child position index of given cell, with respect to its parent at ``parent_res``. The reverse operation can be done with ``child_pos_to_cell``. Parameters ---------- child : H3Cell - res : int - Parent resolution + parent_res : int Returns ------- @@ -385,7 +384,7 @@ def cell_to_child_pos(child, res): Integer index of the child with respect to parent cell. """ child = _in_scalar(child) - return _cy.cell_to_child_pos(child, res) + return _cy.cell_to_child_pos(child, parent_res) def child_pos_to_cell(parent, child_res, child_pos): From f17b76c88b49cc00a667d7e4db2c62271fa7edce Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 14:21:58 -0700 Subject: [PATCH 17/18] Use `res_*` instead of `*_res` --- src/h3/api/basic_int/__init__.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/h3/api/basic_int/__init__.py b/src/h3/api/basic_int/__init__.py index 604e7736..60359662 100644 --- a/src/h3/api/basic_int/__init__.py +++ b/src/h3/api/basic_int/__init__.py @@ -367,16 +367,16 @@ def cell_to_children(h, res=None): return _out_collection(mv) -def cell_to_child_pos(child, parent_res): +def cell_to_child_pos(child, res_parent): """ - Child position index of given cell, with respect to its parent at ``parent_res``. + Child position index of given cell, with respect to its parent at ``res_parent``. The reverse operation can be done with ``child_pos_to_cell``. Parameters ---------- child : H3Cell - parent_res : int + res_parent : int Returns ------- @@ -384,10 +384,10 @@ def cell_to_child_pos(child, parent_res): Integer index of the child with respect to parent cell. """ child = _in_scalar(child) - return _cy.cell_to_child_pos(child, parent_res) + return _cy.cell_to_child_pos(child, res_parent) -def child_pos_to_cell(parent, child_res, child_pos): +def child_pos_to_cell(parent, res_child, child_pos): """ Get child H3 cell from a parent cell, child resolution, and child position index. @@ -396,7 +396,7 @@ def child_pos_to_cell(parent, child_res, child_pos): Parameters ---------- parent : H3Cell - child_res : int + res_child : int Child cell resolution child_pos : int Integer position of child cell, releative to parent. @@ -407,7 +407,7 @@ def child_pos_to_cell(parent, child_res, child_pos): H3Cell """ parent = _in_scalar(parent) - child = _cy.child_pos_to_cell(parent, child_res, child_pos) + child = _cy.child_pos_to_cell(parent, res_child, child_pos) child = _out_scalar(child) return child From fdd6fa50dd2741c3e8652d63a24e0d8dced416da Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Fri, 27 Sep 2024 14:39:46 -0700 Subject: [PATCH 18/18] fix up changelog --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51cbf64e..69d74874 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,11 +16,12 @@ avoid adding features or APIs which do not map onto the ## Unreleased -None. +- Add `cell_to_child_pos`, `child_pos_to_cell`, `cell_to_children_size` (#405) ## [4.1.0b1] - 2024-09-26 - Bump h3lib to v4.1.0 (#402) +- Add `polygon_to_cells` alias (#399) ## [4.0.0b7] - 2024-09-04 @@ -28,7 +29,7 @@ None. ## [4.0.0b6] - 2024-09-03 -- Added bindings for `cellToVertex`, `cellToVertexes`, `vertexToLatLng`, and `isValidVertex` (#323) +- Added bindings for `cellToVertex`, `cellToVertexes`, `vertexToLatLng`, and `isValidVertex` (#388) ## [4.0.0b5] - 2024-05-19