diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index fbf9e0ec..00000000 --- a/.coveragerc +++ /dev/null @@ -1,3 +0,0 @@ -[run] -omit = - */h3/unstable/* diff --git a/.github/workflows/coverage-lint.yml b/.github/workflows/coverage-lint.yml index e1d0bbdb..51102363 100644 --- a/.github/workflows/coverage-lint.yml +++ b/.github/workflows/coverage-lint.yml @@ -2,9 +2,9 @@ name: coverage-lint on: push: - branches: [master] + branches: [master, dev_v4] pull_request: - branches: [master] + branches: [master, dev_v4] jobs: tests: @@ -23,6 +23,7 @@ jobs: - name: Install from source run: | pip install --upgrade pip setuptools wheel + pip install git+https://github.com/ajfriend/h3fake2.git pip install .[all] - name: Lint diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f2c56827..0b9883dd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,9 +2,9 @@ name: tests on: push: - branches: [master] + branches: [master, dev_v4] pull_request: - branches: [master] + branches: [master, dev_v4] jobs: tests: @@ -44,6 +44,7 @@ jobs: - name: Install from source run: | pip install --upgrade pip setuptools wheel + pip install git+https://github.com/ajfriend/h3fake2.git pip install .[all] - name: Tests diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 5f75525d..71f47d5d 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -2,9 +2,9 @@ name: wheels on: push: - branches: [master] + branches: [master, dev_v4] pull_request: - branches: [master] + branches: [master, dev_v4] types: # Opened, synchronize, and reopened are the default types # We add ready_for_review to additionally trigger when converting from draft to non-draft @@ -18,33 +18,34 @@ on: - published jobs: - make_sdist: - name: SDist - if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.draft }} - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Make sdist - run: | - pipx run build --sdist - - - name: Install from sdist - run: | - pip install --upgrade pip setuptools wheel - cp dist/h3-*.tar.gz h3.tar.gz - pip install h3.tar.gz[all] - - - name: Test sdist - run: pytest --cov=h3 --full-trace - - - name: Upload artifacts to GitHub - uses: actions/upload-artifact@v3 - with: - path: ./dist + # make_sdist: + # name: SDist + # if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.draft }} + # runs-on: ubuntu-latest + + # steps: + # - uses: actions/checkout@v3 + # with: + # submodules: recursive + + # - name: Make sdist + # run: | + # pipx run build --sdist + + # - name: Install from sdist + # run: | + # pip install --upgrade pip setuptools wheel + # pip install git+https://github.com/ajfriend/h3fake2.git + # cp dist/h3-*.tar.gz h3.tar.gz + # pip install h3.tar.gz[all] + + # - name: Test sdist + # run: pytest --cov=h3 --full-trace + + # - name: Upload artifacts to GitHub + # uses: actions/upload-artifact@v3 + # with: + # path: ./dist make_cibw_v1_wheels: name: "cibuildwheel v1: ${{ matrix.name }}" @@ -74,6 +75,8 @@ jobs: CIBW_TEST_COMMAND: pytest {project}/tests CIBW_ARCHS_LINUX: auto aarch64 CIBW_BUILD: ${{ matrix.build }} + CIBW_BEFORE_BUILD: pip install git+https://github.com/ajfriend/h3fake2.git + CIBW_BEFORE_TEST: pip install git+https://github.com/ajfriend/h3fake2.git - name: Check with Twine run: | @@ -152,6 +155,8 @@ jobs: CIBW_ARCHS_MACOS: x86_64 arm64 CIBW_BUILD: ${{ matrix.build }} CIBW_SKIP: ${{ matrix.skip }} + CIBW_BEFORE_BUILD: pip install git+https://github.com/ajfriend/h3fake2.git + CIBW_BEFORE_TEST: pip install git+https://github.com/ajfriend/h3fake2.git - name: Check with Twine run: | diff --git a/makefile b/makefile index 6e44c02d..7c2845e4 100644 --- a/makefile +++ b/makefile @@ -12,6 +12,7 @@ init: purge git submodule update --init python -m venv env env/bin/pip install --upgrade pip wheel setuptools + env/bin/pip install git+https://github.com/ajfriend/h3fake2.git env/bin/pip install .[all] env/bin/pip install -r requirements.in @@ -23,6 +24,7 @@ clear: -@find . -type d -name '*.egg-info' | xargs rm -r -@find . -type f -name '*.pyc' | xargs rm -r -@find . -type d -name '*.ipynb_checkpoints' | xargs rm -r + -@find ./tests -type f -name '*.c' | xargs rm -r rebuild: clear env/bin/pip install .[all] diff --git a/src/h3/_cy/CMakeLists.txt b/src/h3/_cy/CMakeLists.txt index 0d7e78c9..386557c5 100644 --- a/src/h3/_cy/CMakeLists.txt +++ b/src/h3/_cy/CMakeLists.txt @@ -22,7 +22,6 @@ add_cython_file(geo) add_cython_file(cells) add_cython_file(edges) add_cython_file(to_multipoly) -add_cython_file(unstable_vect) # Include pyx and pxd files in distribution for use by Cython API install( diff --git a/src/h3/_cy/__init__.py b/src/h3/_cy/__init__.py index bb4dfc06..6b17b2d0 100644 --- a/src/h3/_cy/__init__.py +++ b/src/h3/_cy/__init__.py @@ -18,27 +18,44 @@ is_pentagon, get_base_cell, resolution, - parent, + is_res_class_iii, distance, disk, - ring, + parent, children, + center_child, compact, uncompact, num_hexagons, +) + +from h3fake2._cy import ( + # is_cell, + # is_pentagon, + # get_base_cell, + # resolution, + # parent, + # distance, + # disk, + ring, + # children, + # compact, + # uncompact, + # num_hexagons, mean_hex_area, cell_area, line, - is_res_class_iii, + # is_res_class_iii, get_pentagon_indexes, get_res0_indexes, - center_child, + # center_child, get_faces, experimental_h3_to_local_ij, experimental_local_ij_to_h3, ) -from .edges import ( +# from .edges import ( +from h3fake2._cy import ( are_neighbors, edge, is_edge, @@ -53,6 +70,17 @@ from .geo import ( geo_to_h3, h3_to_geo, + # polyfill_polygon, + # polyfill_geojson, + # polyfill, + # cell_boundary, + # edge_boundary, + # point_dist, +) + +from h3fake2._cy import ( + # geo_to_h3, + # h3_to_geo, polyfill_polygon, polyfill_geojson, polyfill, @@ -61,7 +89,8 @@ point_dist, ) -from .to_multipoly import ( +# from .to_multipoly import ( +from h3fake2._cy import ( h3_set_to_multi_polygon ) diff --git a/src/h3/_cy/cells.pxd b/src/h3/_cy/cells.pxd index 10719389..4e2d4411 100644 --- a/src/h3/_cy/cells.pxd +++ b/src/h3/_cy/cells.pxd @@ -6,20 +6,20 @@ cpdef int get_base_cell(H3int h) except -1 cpdef int resolution(H3int h) except -1 cpdef int distance(H3int h1, H3int h2) except -1 cpdef H3int[:] disk(H3int h, int k) -cpdef H3int[:] _ring_fallback(H3int h, int k) -cpdef H3int[:] ring(H3int h, int k) +# cpdef H3int[:] _ring_fallback(H3int h, int k) +# cpdef H3int[:] ring(H3int h, int k) cpdef H3int parent(H3int h, res=*) except 0 cpdef H3int[:] children(H3int h, res=*) cpdef H3int center_child(H3int h, res=*) except 0 cpdef H3int[:] compact(const H3int[:] hu) cpdef H3int[:] uncompact(const H3int[:] hc, int res) cpdef int64_t num_hexagons(int resolution) except -1 -cpdef double mean_hex_area(int resolution, unit=*) except -1 -cpdef double cell_area(H3int h, unit=*) except -1 -cpdef H3int[:] line(H3int start, H3int end) +# cpdef double mean_hex_area(int resolution, unit=*) except -1 +# cpdef double cell_area(H3int h, unit=*) except -1 +# cpdef H3int[:] line(H3int start, H3int end) cpdef bool is_res_class_iii(H3int h) -cpdef H3int[:] get_pentagon_indexes(int res) -cpdef H3int[:] get_res0_indexes() -cpdef get_faces(H3int h) -cpdef (int, int) experimental_h3_to_local_ij(H3int origin, H3int h) except * -cpdef H3int experimental_local_ij_to_h3(H3int origin, int i, int j) except 0 +# cpdef H3int[:] get_pentagon_indexes(int res) +# cpdef H3int[:] get_res0_indexes() +# cpdef get_faces(H3int h) +# cpdef (int, int) experimental_h3_to_local_ij(H3int origin, H3int h) except * +# cpdef H3int experimental_local_ij_to_h3(H3int origin, int i, int j) except 0 diff --git a/src/h3/_cy/cells.pyx b/src/h3/_cy/cells.pyx index 4206b81d..265e4c37 100644 --- a/src/h3/_cy/cells.pyx +++ b/src/h3/_cy/cells.pyx @@ -24,15 +24,17 @@ cpdef bool is_cell(H3int h): ------- boolean """ - return h3lib.h3IsValid(h) == 1 + return h3lib.isValidCell(h) == 1 + cpdef bool is_pentagon(H3int h): - return h3lib.h3IsPentagon(h) == 1 + return h3lib.isPentagon(h) == 1 + cpdef int get_base_cell(H3int h) except -1: check_cell(h) - return h3lib.h3GetBaseCell(h) + return h3lib.getBaseCellNumber(h) cpdef int resolution(H3int h) except -1: @@ -41,96 +43,109 @@ cpdef int resolution(H3int h) except -1: """ check_cell(h) - return h3lib.h3GetResolution(h) + return h3lib.getResolution(h) cpdef int distance(H3int h1, H3int h2) except -1: - """ compute the hex-distance between two hexagons + """ Compute the grid distance between two cells """ + cdef: + int64_t distance + h3lib.H3Error err + check_cell(h1) check_cell(h2) - d = h3lib.h3Distance(h1,h2) - - if d < 0: + err = h3lib.gridDistance(h1, h2, &distance) + if err: + # todo: do error handling later s = 'Cells are too far apart to compute distance: {} and {}' s = s.format(hex(h1), hex(h2)) raise H3ValueError(s) - return d + return distance cpdef H3int[:] disk(H3int h, int k): """ Return cells at grid distance `<= k` from `h`. """ + cdef: + int64_t n + h3lib.H3Error err + check_cell(h) check_distance(k) - n = h3lib.maxKringSize(k) + # ignoring error for now + err = h3lib.maxGridDiskSize(k, &n) ptr = create_ptr(n) # todo: return a "smart" pointer that knows its length? - h3lib.kRing(h, k, ptr) + err = h3lib.gridDisk(h, k, ptr) # ignoring error again! mv = create_mv(ptr, n) return mv -cpdef H3int[:] _ring_fallback(H3int h, int k): - """ - `ring` tries to call `h3lib.hexRing` first; if that fails, we call - this function, which relies on `h3lib.kRingDistances`. +# cpdef H3int[:] _ring_fallback(H3int h, int k): +# """ +# `ring` tries to call `h3lib.hexRing` first; if that fails, we call +# this function, which relies on `h3lib.kRingDistances`. - Failures for `h3lib.hexRing` happen when the algortihm runs into a pentagon. - """ - check_cell(h) - check_distance(k) +# Failures for `h3lib.hexRing` happen when the algortihm runs into a pentagon. +# """ +# check_cell(h) +# check_distance(k) - n = h3lib.maxKringSize(k) - # array of h3 cells - ptr = create_ptr(n) +# n = h3lib.maxKringSize(k) +# # array of h3 cells +# ptr = create_ptr(n) - # array of cell distances from `h` - dist_ptr = stdlib.calloc(n, sizeof(int)) - if dist_ptr is NULL: - raise MemoryError() +# # array of cell distances from `h` +# dist_ptr = stdlib.calloc(n, sizeof(int)) +# if dist_ptr is NULL: +# raise MemoryError() - h3lib.kRingDistances(h, k, ptr, dist_ptr) +# h3lib.kRingDistances(h, k, ptr, dist_ptr) - distances = dist_ptr - distances.callback_free_data = stdlib.free +# distances = dist_ptr +# distances.callback_free_data = stdlib.free - for i,v in enumerate(distances): - if v != k: - ptr[i] = 0 +# for i,v in enumerate(distances): +# if v != k: +# ptr[i] = 0 - mv = create_mv(ptr, n) +# mv = create_mv(ptr, n) - return mv +# return mv -cpdef H3int[:] 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) +# cpdef H3int[:] 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 - ptr = create_ptr(n) +# n = 6*k if k > 0 else 1 +# ptr = create_ptr(n) - flag = h3lib.hexRing(h, k, ptr) +# flag = h3lib.hexRing(h, k, ptr) - # if we drop into the failure state, we might be tempted to not create - # this mv, but creating the mv is exactly what guarantees that we'll free - # the memory. context manager would be better here, if we can figure out - # how to do that - mv = create_mv(ptr, n) +# # if we drop into the failure state, we might be tempted to not create +# # this mv, but creating the mv is exactly what guarantees that we'll free +# # the memory. context manager would be better here, if we can figure out +# # how to do that +# mv = create_mv(ptr, n) - if flag != 0: - mv = _ring_fallback(h, k) +# if flag != 0: +# mv = _ring_fallback(h, k) - return mv +# return mv cpdef H3int parent(H3int h, res=None) except 0: + cdef: + H3int parent + h3lib.H3Error err + check_cell(h) if res is None: @@ -141,10 +156,17 @@ cpdef H3int parent(H3int h, res=None) except 0: raise H3ResolutionError(msg) check_res(res) + err = h3lib.cellToParent(h, res, &parent) + + return parent - return h3lib.h3ToParent(h, res) cpdef H3int[:] children(H3int h, res=None): + cdef: + H3int child + h3lib.H3Error err + int64_t N + check_cell(h) if res is None: @@ -155,16 +177,20 @@ cpdef H3int[:] children(H3int h, res=None): raise H3ResolutionError(msg) check_res(res) + err = h3lib.cellToChildrenSize(h, res, &N) - n = h3lib.maxH3ToChildrenSize(h, res) - - ptr = create_ptr(n) - h3lib.h3ToChildren(h, res, ptr) - mv = create_mv(ptr, n) + ptr = create_ptr(N) + err = h3lib.cellToChildren(h, res, ptr) + mv = create_mv(ptr, N) return mv + cpdef H3int center_child(H3int h, res=None) except 0: + cdef: + H3int child + h3lib.H3Error err + check_cell(h) if res is None: @@ -175,8 +201,9 @@ cpdef H3int center_child(H3int h, res=None) except 0: raise H3ResolutionError(msg) check_res(res) + err = h3lib.cellToCenterChild(h, res, &child) - return h3lib.h3ToCenterChild(h, res) + return child @@ -186,6 +213,9 @@ cpdef H3int[:] compact(const H3int[:] hu): # `&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! + cdef: + h3lib.H3Error err + if len(hu) == 0: return empty_memory_view() @@ -193,14 +223,16 @@ cpdef H3int[:] compact(const H3int[:] hu): check_cell(h) ptr = create_ptr(len(hu)) - flag = h3lib.compact(&hu[0], ptr, len(hu)) + err = h3lib.compactCells(&hu[0], ptr, len(hu)) mv = create_mv(ptr, len(hu)) - if flag != 0: + if err: + # todo: additional error processing raise H3ValueError('Could not compact set of hexagons!') 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(const H3int[:] hc, int res): @@ -209,23 +241,30 @@ cpdef H3int[:] uncompact(const H3int[:] hc, int res): # `&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: + h3lib.H3Error err + int64_t N + if len(hc) == 0: return empty_memory_view() for h in hc: check_cell(h) - N = h3lib.maxUncompactSize(&hc[0], len(hc), res) + # ignoring error for now + err = h3lib.uncompactCellsSize(&hc[0], len(hc), res, &N) ptr = create_ptr(N) - flag = h3lib.uncompact( - &hc[0], len(hc), - ptr, N, + err = h3lib.uncompactCells( + &hc[0], + len(hc), + ptr, + N, res ) mv = create_mv(ptr, N) - if flag != 0: + if err: raise H3ValueError('Could not uncompact set of hexagons!') return mv @@ -233,143 +272,148 @@ cpdef H3int[:] uncompact(const H3int[:] hc, int res): cpdef int64_t num_hexagons(int resolution) except -1: check_res(resolution) + cdef: + h3lib.H3Error err + int64_t num_cells - return h3lib.numHexagons(resolution) + err = h3lib.getNumCells(resolution, &num_cells) + return num_cells -cpdef double mean_hex_area(int resolution, unit='km^2') except -1: - check_res(resolution) - area = h3lib.hexAreaKm2(resolution) +# cpdef double mean_hex_area(int resolution, unit='km^2') except -1: +# check_res(resolution) - # todo: multiple units - convert = { - 'km^2': 1.0, - 'm^2': 1000*1000.0 - } +# area = h3lib.hexAreaKm2(resolution) - try: - area *= convert[unit] - except: - raise H3ValueError('Unknown unit: {}'.format(unit)) +# # todo: multiple units +# convert = { +# 'km^2': 1.0, +# 'm^2': 1000*1000.0 +# } - return area +# try: +# area *= convert[unit] +# except: +# raise H3ValueError('Unknown unit: {}'.format(unit)) +# return area -cpdef double cell_area(H3int h, unit='km^2') except -1: - check_cell(h) - if unit == 'rads^2': - area = h3lib.cellAreaRads2(h) - elif unit == 'km^2': - area = h3lib.cellAreaKm2(h) - elif unit == 'm^2': - area = h3lib.cellAreaM2(h) - else: - raise H3ValueError('Unknown unit: {}'.format(unit)) +# cpdef double cell_area(H3int h, unit='km^2') except -1: +# check_cell(h) - return area +# if unit == 'rads^2': +# area = h3lib.cellAreaRads2(h) +# elif unit == 'km^2': +# area = h3lib.cellAreaKm2(h) +# elif unit == 'm^2': +# area = h3lib.cellAreaM2(h) +# else: +# raise H3ValueError('Unknown unit: {}'.format(unit)) +# return area -cpdef H3int[:] line(H3int start, H3int end): - check_cell(start) - check_cell(end) - n = h3lib.h3LineSize(start, end) +# cpdef H3int[:] line(H3int start, H3int end): +# check_cell(start) +# check_cell(end) - if n < 0: - s = "Couldn't find line between cells {} and {}" - s = s.format(hex(start), hex(end)) - raise H3ValueError(s) +# n = h3lib.h3LineSize(start, end) - ptr = create_ptr(n) - flag = h3lib.h3Line(start, end, ptr) - mv = create_mv(ptr, n) +# if n < 0: +# s = "Couldn't find line between cells {} and {}" +# s = s.format(hex(start), hex(end)) +# raise H3ValueError(s) - if flag != 0: - s = "Couldn't find line between cells {} and {}" - s = s.format(hex(start), hex(end)) - raise H3ValueError(s) +# ptr = create_ptr(n) +# flag = h3lib.h3Line(start, end, ptr) +# mv = create_mv(ptr, n) - return mv +# if flag != 0: +# s = "Couldn't find line between cells {} and {}" +# s = s.format(hex(start), hex(end)) +# raise H3ValueError(s) + +# return mv cpdef bool is_res_class_iii(H3int h): - return h3lib.h3IsResClassIII(h) == 1 + return h3lib.isResClassIII(h) == 1 -cpdef H3int[:] get_pentagon_indexes(int res): - check_res(res) +# cpdef H3int[:] get_pentagon_indexes(int res): +# check_res(res) - n = h3lib.pentagonIndexCount() +# n = h3lib.pentagonIndexCount() - ptr = create_ptr(n) - h3lib.getPentagonIndexes(res, ptr) - mv = create_mv(ptr, n) +# ptr = create_ptr(n) +# h3lib.getPentagonIndexes(res, ptr) +# mv = create_mv(ptr, n) - return mv +# return mv -cpdef H3int[:] get_res0_indexes(): - n = h3lib.res0IndexCount() +# cpdef H3int[:] get_res0_indexes(): +# n = h3lib.res0IndexCount() - ptr = create_ptr(n) - h3lib.getRes0Indexes(ptr) - mv = create_mv(ptr, n) +# ptr = create_ptr(n) +# h3lib.getRes0Indexes(ptr) +# mv = create_mv(ptr, n) - return mv +# return mv -cpdef get_faces(H3int h): - check_cell(h) +# cpdef get_faces(H3int h): +# check_cell(h) - n = h3lib.maxFaceCount(h) +# n = h3lib.maxFaceCount(h) - cdef int* ptr = stdlib.calloc(n, sizeof(int)) - if (n > 0) and (not ptr): - raise MemoryError() +# cdef int* ptr = stdlib.calloc(n, sizeof(int)) +# if (n > 0) and (not ptr): +# raise MemoryError() - h3lib.h3GetFaces(h, ptr) +# h3lib.h3GetFaces(h, ptr) - faces = ptr - faces = {f for f in faces if f >= 0} - stdlib.free(ptr) +# faces = ptr +# faces = {f for f in faces if f >= 0} +# stdlib.free(ptr) - return faces +# return faces -cpdef (int, int) experimental_h3_to_local_ij(H3int origin, H3int h) except *: - cdef: - int flag - h3lib.CoordIJ c +# cpdef (int, int) experimental_h3_to_local_ij(H3int origin, H3int h) except *: +# cdef: +# int flag +# h3lib.CoordIJ c - check_cell(origin) - check_cell(h) +# check_cell(origin) +# check_cell(h) - flag = h3lib.experimentalH3ToLocalIj(origin, h, &c) +# flag = h3lib.experimentalH3ToLocalIj(origin, h, &c) - if flag != 0: - s = "Couldn't find local (i,j) between cells {} and {}." - s = s.format(hex(origin), hex(h)) - raise H3ValueError(s) +# if flag != 0: +# s = "Couldn't find local (i,j) between cells {} and {}." +# s = s.format(hex(origin), hex(h)) +# raise H3ValueError(s) - return c.i, c.j +# return c.i, c.j -cpdef H3int experimental_local_ij_to_h3(H3int origin, int i, int j) except 0: - cdef: - int flag - h3lib.CoordIJ c - H3int out +# cpdef H3int experimental_local_ij_to_h3(H3int origin, int i, int j) except 0: +# cdef: +# int flag +# h3lib.CoordIJ c +# H3int out - check_cell(origin) +# check_cell(origin) - c.i, c.j = i, j +# c.i, c.j = i, j - flag = h3lib.experimentalLocalIjToH3(origin, &c, &out) +# flag = h3lib.experimentalLocalIjToH3(origin, &c, &out) - if flag != 0: - s = "Couldn't find cell at local ({},{}) from cell {}." - s = s.format(i, j, hex(origin)) - raise H3ValueError(s) +# if flag != 0: +# s = "Couldn't find cell at local ({},{}) from cell {}." +# s = s.format(i, j, hex(origin)) +# raise H3ValueError(s) - return out +# return out diff --git a/src/h3/_cy/edges.pxd b/src/h3/_cy/edges.pxd index 465b82d8..cdcf4d02 100644 --- a/src/h3/_cy/edges.pxd +++ b/src/h3/_cy/edges.pxd @@ -1,11 +1,11 @@ -from .h3lib cimport bool, H3int +# from .h3lib cimport bool, H3int -cpdef bool are_neighbors(H3int h1, H3int h2) -cpdef H3int edge(H3int origin, H3int destination) except 1 -cpdef bool is_edge(H3int e) -cpdef H3int edge_origin(H3int e) except 1 -cpdef H3int edge_destination(H3int e) except 1 -cpdef (H3int, H3int) edge_cells(H3int e) except * -cpdef H3int[:] edges_from_cell(H3int origin) -cpdef double mean_edge_length(int resolution, unit=*) except -1 -cpdef double edge_length(H3int e, unit=*) except -1 +# cpdef bool are_neighbors(H3int h1, H3int h2) +# cpdef H3int edge(H3int origin, H3int destination) except 1 +# cpdef bool is_edge(H3int e) +# cpdef H3int edge_origin(H3int e) except 1 +# cpdef H3int edge_destination(H3int e) except 1 +# cpdef (H3int, H3int) edge_cells(H3int e) except * +# cpdef H3int[:] edges_from_cell(H3int origin) +# cpdef double mean_edge_length(int resolution, unit=*) except -1 +# cpdef double edge_length(H3int e, unit=*) except -1 diff --git a/src/h3/_cy/edges.pyx b/src/h3/_cy/edges.pyx index 65600063..27397d95 100644 --- a/src/h3/_cy/edges.pyx +++ b/src/h3/_cy/edges.pyx @@ -1,100 +1,100 @@ -cimport h3lib -from .h3lib cimport bool, H3int +# cimport h3lib +# from .h3lib cimport bool, H3int -from .util cimport ( - check_cell, - check_edge, - check_res, - create_ptr, - create_mv, -) +# from .util cimport ( +# check_cell, +# check_edge, +# check_res, +# create_ptr, +# create_mv, +# ) -from .util import H3ValueError +# from .util import H3ValueError -cpdef bool are_neighbors(H3int h1, H3int h2): - check_cell(h1) - check_cell(h2) +# cpdef bool are_neighbors(H3int h1, H3int h2): +# check_cell(h1) +# check_cell(h2) - return h3lib.h3IndexesAreNeighbors(h1, h2) == 1 +# return h3lib.h3IndexesAreNeighbors(h1, h2) == 1 -cpdef H3int edge(H3int origin, H3int destination) except 1: - check_cell(origin) - check_cell(destination) +# cpdef H3int edge(H3int origin, H3int destination) except 1: +# check_cell(origin) +# check_cell(destination) - if h3lib.h3IndexesAreNeighbors(origin, destination) != 1: - s = 'Cells are not neighbors: {} and {}' - s = s.format(hex(origin), hex(destination)) - raise H3ValueError(s) +# if h3lib.h3IndexesAreNeighbors(origin, destination) != 1: +# s = 'Cells are not neighbors: {} and {}' +# s = s.format(hex(origin), hex(destination)) +# raise H3ValueError(s) - return h3lib.getH3UnidirectionalEdge(origin, destination) +# return h3lib.getH3UnidirectionalEdge(origin, destination) -cpdef bool is_edge(H3int e): - return h3lib.h3UnidirectionalEdgeIsValid(e) == 1 +# cpdef bool is_edge(H3int e): +# return h3lib.h3UnidirectionalEdgeIsValid(e) == 1 -cpdef H3int edge_origin(H3int e) except 1: - # without the check, with an invalid input, the function will just return 0 - check_edge(e) +# cpdef H3int edge_origin(H3int e) except 1: +# # without the check, with an invalid input, the function will just return 0 +# check_edge(e) - return h3lib.getOriginH3IndexFromUnidirectionalEdge(e) +# return h3lib.getOriginH3IndexFromUnidirectionalEdge(e) -cpdef H3int edge_destination(H3int e) except 1: - check_edge(e) +# cpdef H3int edge_destination(H3int e) except 1: +# check_edge(e) - return h3lib.getDestinationH3IndexFromUnidirectionalEdge(e) +# return h3lib.getDestinationH3IndexFromUnidirectionalEdge(e) -cpdef (H3int, H3int) edge_cells(H3int e) except *: - check_edge(e) +# cpdef (H3int, H3int) edge_cells(H3int e) except *: +# check_edge(e) - return edge_origin(e), edge_destination(e) +# return edge_origin(e), edge_destination(e) -cpdef H3int[:] edges_from_cell(H3int origin): - """ Returns the 6 (or 5 for pentagons) directed edges - for the given origin cell - """ - check_cell(origin) +# cpdef H3int[:] edges_from_cell(H3int origin): +# """ Returns the 6 (or 5 for pentagons) directed edges +# for the given origin cell +# """ +# check_cell(origin) - ptr = create_ptr(6) - h3lib.getH3UnidirectionalEdgesFromHexagon(origin, ptr) - mv = create_mv(ptr, 6) +# ptr = create_ptr(6) +# h3lib.getH3UnidirectionalEdgesFromHexagon(origin, ptr) +# mv = create_mv(ptr, 6) - return mv +# return mv -cpdef double mean_edge_length(int resolution, unit='km') except -1: - check_res(resolution) +# cpdef double mean_edge_length(int resolution, unit='km') except -1: +# check_res(resolution) - length = h3lib.edgeLengthKm(resolution) +# length = h3lib.edgeLengthKm(resolution) - # todo: multiple units - convert = { - 'km': 1.0, - 'm': 1000.0 - } +# # todo: multiple units +# convert = { +# 'km': 1.0, +# 'm': 1000.0 +# } - try: - length *= convert[unit] - except: - raise H3ValueError('Unknown unit: {}'.format(unit)) +# try: +# length *= convert[unit] +# except: +# raise H3ValueError('Unknown unit: {}'.format(unit)) - return length +# return length -cpdef double edge_length(H3int e, unit='km') except -1: - check_edge(e) +# cpdef double edge_length(H3int e, unit='km') except -1: +# check_edge(e) - # todo: maybe kick this logic up to the python level - # it might be a little cleaner, because we can do the "switch statement" - # with a dict, but would require exposing more C functions +# # todo: maybe kick this logic up to the python level +# # it might be a little cleaner, because we can do the "switch statement" +# # with a dict, but would require exposing more C functions - if unit == 'rads': - length = h3lib.exactEdgeLengthRads(e) - elif unit == 'km': - length = h3lib.exactEdgeLengthKm(e) - elif unit == 'm': - length = h3lib.exactEdgeLengthM(e) - else: - raise H3ValueError('Unknown unit: {}'.format(unit)) +# if unit == 'rads': +# length = h3lib.exactEdgeLengthRads(e) +# elif unit == 'km': +# length = h3lib.exactEdgeLengthKm(e) +# elif unit == 'm': +# length = h3lib.exactEdgeLengthM(e) +# else: +# raise H3ValueError('Unknown unit: {}'.format(unit)) - return length +# return length diff --git a/src/h3/_cy/geo.pxd b/src/h3/_cy/geo.pxd index 1a2c1c33..37e32a0b 100644 --- a/src/h3/_cy/geo.pxd +++ b/src/h3/_cy/geo.pxd @@ -2,6 +2,6 @@ from .h3lib cimport H3int cpdef H3int geo_to_h3(double lat, double lng, int res) except 1 cpdef (double, double) h3_to_geo(H3int h) except * -cpdef double point_dist( - double lat1, double lng1, - double lat2, double lng2, unit=*) except -1 +# cpdef double point_dist( +# double lat1, double lng1, +# double lat2, double lng2, unit=*) except -1 diff --git a/src/h3/_cy/geo.pyx b/src/h3/_cy/geo.pyx index dc2c1d7b..fe6c93b5 100644 --- a/src/h3/_cy/geo.pyx +++ b/src/h3/_cy/geo.pyx @@ -16,263 +16,268 @@ from .util import H3ValueError cpdef H3int geo_to_h3(double lat, double lng, int res) except 1: cdef: - h3lib.GeoCoord c + h3lib.LatLng c + H3int out + h3lib.H3Error err check_res(res) - c = deg2coord(lat, lng) - return h3lib.geoToH3(&c, res) + # todo: just ignoring the error for now, check in the future + err = h3lib.latLngToCell(&c, res, &out) + + return out cpdef (double, double) h3_to_geo(H3int h) except *: """Map an H3 cell into its centroid geo-coordinate (lat/lng)""" cdef: - h3lib.GeoCoord c + h3lib.LatLng c + h3lib.H3Error err check_cell(h) - h3lib.h3ToGeo(h, &c) + err = h3lib.cellToLatLng(h, &c) return coord2deg(c) -cdef h3lib.Geofence make_geofence(geos, bool lnglat_order=False) except *: - """ +# cdef h3lib.Geofence make_geofence(geos, bool lnglat_order=False) except *: +# """ - The returned `Geofence` must be freed with a call to `free_geofence`. +# The returned `Geofence` must be freed with a call to `free_geofence`. - Parameters - ---------- - geos : list or tuple - GeoFence: A sequence of >= 3 (lat, lng) pairs where the last - element may or may not be same as the first (to form a closed loop). - The order of the pairs may be either clockwise or counterclockwise. - lnglat_order : bool - If True, assume coordinate pairs like (lng, lat) - If False, assume coordinate pairs like (lat, lng) - """ - cdef: - h3lib.Geofence gf +# Parameters +# ---------- +# geos : list or tuple +# GeoFence: A sequence of >= 3 (lat, lng) pairs where the last +# element may or may not be same as the first (to form a closed loop). +# The order of the pairs may be either clockwise or counterclockwise. +# lnglat_order : bool +# If True, assume coordinate pairs like (lng, lat) +# If False, assume coordinate pairs like (lat, lng) +# """ +# cdef: +# h3lib.Geofence gf - gf.numVerts = len(geos) +# gf.numVerts = len(geos) - gf.verts = stdlib.calloc(gf.numVerts, sizeof(h3lib.GeoCoord)) +# gf.verts = stdlib.calloc(gf.numVerts, sizeof(h3lib.GeoCoord)) - if lnglat_order: - latlng = (g[::-1] for g in geos) - else: - latlng = geos +# if lnglat_order: +# latlng = (g[::-1] for g in geos) +# else: +# latlng = geos - for i, (lat, lng) in enumerate(latlng): - gf.verts[i] = deg2coord(lat, lng) +# for i, (lat, lng) in enumerate(latlng): +# gf.verts[i] = deg2coord(lat, lng) - return gf +# return gf -cdef free_geofence(h3lib.Geofence* gf): - stdlib.free(gf.verts) - gf.verts = NULL +# cdef free_geofence(h3lib.Geofence* gf): +# stdlib.free(gf.verts) +# gf.verts = NULL -cdef class GeoPolygon: - cdef: - h3lib.GeoPolygon gp - - def __cinit__(self, outer, holes=None, bool lnglat_order=False): - """ - - Parameters - ---------- - outer : list or tuple - GeoFence - A GeoFence is a sequence of >= 3 (lat, lng) pairs where the last - element may or may not be same as the first (to form a closed loop). - The order of the pairs may be either clockwise or counterclockwise. - holes : list or tuple - A sequence of GeoFences - lnglat_order : bool - If True, assume coordinate pairs like (lng, lat) - If False, assume coordinate pairs like (lat, lng) - - """ - if holes is None: - holes = [] - - self.gp.geofence = make_geofence(outer, lnglat_order) - self.gp.numHoles = len(holes) - self.gp.holes = NULL - - if len(holes) > 0: - self.gp.holes = stdlib.calloc(len(holes), sizeof(h3lib.Geofence)) - for i, hole in enumerate(holes): - self.gp.holes[i] = make_geofence(hole, lnglat_order) - - - def __dealloc__(self): - free_geofence(&self.gp.geofence) - - for i in range(self.gp.numHoles): - free_geofence(&self.gp.holes[i]) - - stdlib.free(self.gp.holes) - - -def polyfill_polygon(outer, int res, holes=None, bool lnglat_order=False): - """ Set of hexagons whose center is contained in a polygon. - - The polygon is defined as in the GeoJson standard, with an exterior - LinearRing `outer` and a list of LinearRings `holes`, which define any - holes in the polygon. - - Each LinearRing may be in clockwise or counter-clockwise order - (right-hand rule or not), and may or may not be a closed loop (where the last - element is equal to the first). - The GeoJSON spec requires the right-hand rule, and a closed loop, but - this function will work with any input format. - - Parameters - ---------- - outer : list or tuple - A LinearRing, a sequence of (lat/lng) or (lng/lat) pairs - res : int - The resolution of the output hexagons - holes : list or tuple - A collection of LinearRings, describing any holes in the polygon - lnglat_order : bool - If True, assume coordinate pairs like (lng, lat) - If False, assume coordinate pairs like (lat, lng) - """ +# cdef class GeoPolygon: +# cdef: +# h3lib.GeoPolygon gp - check_res(res) - gp = GeoPolygon(outer, holes=holes, lnglat_order=lnglat_order) +# def __cinit__(self, outer, holes=None, bool lnglat_order=False): +# """ - n = h3lib.maxPolyfillSize(&gp.gp, res) - ptr = create_ptr(n) +# Parameters +# ---------- +# outer : list or tuple +# GeoFence +# A GeoFence is a sequence of >= 3 (lat, lng) pairs where the last +# element may or may not be same as the first (to form a closed loop). +# The order of the pairs may be either clockwise or counterclockwise. +# holes : list or tuple +# A sequence of GeoFences +# lnglat_order : bool +# If True, assume coordinate pairs like (lng, lat) +# If False, assume coordinate pairs like (lat, lng) - h3lib.polyfill(&gp.gp, res, ptr) - mv = create_mv(ptr, n) +# """ +# if holes is None: +# holes = [] - return mv +# self.gp.geofence = make_geofence(outer, lnglat_order) +# self.gp.numHoles = len(holes) +# self.gp.holes = NULL +# if len(holes) > 0: +# self.gp.holes = stdlib.calloc(len(holes), sizeof(h3lib.Geofence)) +# for i, hole in enumerate(holes): +# self.gp.holes[i] = make_geofence(hole, lnglat_order) -def polyfill_geojson(geojson, int res): - """ Set of hexagons whose center is contained in a GeoJson Polygon object. - The polygon is defined exactly as in the GeoJson standard, so - `geojson` should be a dictionary like: - { - 'type': 'Polygon', - 'coordinates': [...] - } +# def __dealloc__(self): +# free_geofence(&self.gp.geofence) - 'coordinates' should be a list of LinearRings, where the first ring describes - the exterior boundary of the Polygon, and any subsequent LinearRings - describe holes in the polygon. +# for i in range(self.gp.numHoles): +# free_geofence(&self.gp.holes[i]) - Note that we don't provide an option for the order of the coordinates, - as the GeoJson standard requires them to be in lng/lat order. +# stdlib.free(self.gp.holes) - Parameters - ---------- - geojson : dict - res : int - The resolution of the output hexagons - """ - # todo: this one could handle multipolygons... +# def polyfill_polygon(outer, int res, holes=None, bool lnglat_order=False): +# """ Set of hexagons whose center is contained in a polygon. - if geojson['type'] != 'Polygon': - raise ValueError('Only Polygon GeoJSON supported') +# The polygon is defined as in the GeoJson standard, with an exterior +# LinearRing `outer` and a list of LinearRings `holes`, which define any +# holes in the polygon. - coords = geojson['coordinates'] +# Each LinearRing may be in clockwise or counter-clockwise order +# (right-hand rule or not), and may or may not be a closed loop (where the last +# element is equal to the first). +# The GeoJSON spec requires the right-hand rule, and a closed loop, but +# this function will work with any input format. - out = polyfill_polygon(coords[0], res, holes=coords[1:], lnglat_order=True) +# Parameters +# ---------- +# outer : list or tuple +# A LinearRing, a sequence of (lat/lng) or (lng/lat) pairs +# res : int +# The resolution of the output hexagons +# holes : list or tuple +# A collection of LinearRings, describing any holes in the polygon +# lnglat_order : bool +# If True, assume coordinate pairs like (lng, lat) +# If False, assume coordinate pairs like (lat, lng) +# """ - return out +# check_res(res) +# gp = GeoPolygon(outer, holes=holes, lnglat_order=lnglat_order) +# n = h3lib.maxPolyfillSize(&gp.gp, res) +# ptr = create_ptr(n) -def polyfill(dict geojson, int res, bool geo_json_conformant=False): - """ Light wrapper around `polyfill_geojson` to provide backward compatibility. - """ +# h3lib.polyfill(&gp.gp, res, ptr) +# mv = create_mv(ptr, n) - try: - gj_type = geojson['type'] - except KeyError: - raise KeyError("`geojson` dict must have key 'type'.") from None +# return mv - if gj_type != 'Polygon': - raise ValueError('Only Polygon GeoJSON supported') - if geo_json_conformant: - out = polyfill_geojson(geojson, res) - else: - coords = geojson['coordinates'] - out = polyfill_polygon(coords[0], res, holes=coords[1:], lnglat_order=False) +# def polyfill_geojson(geojson, int res): +# """ Set of hexagons whose center is contained in a GeoJson Polygon object. - return out +# The polygon is defined exactly as in the GeoJson standard, so +# `geojson` should be a dictionary like: +# { +# 'type': 'Polygon', +# 'coordinates': [...] +# } +# 'coordinates' should be a list of LinearRings, where the first ring describes +# the exterior boundary of the Polygon, and any subsequent LinearRings +# describe holes in the polygon. -def cell_boundary(H3int h, bool geo_json=False): - """Compose an array of geo-coordinates that outlines a hexagonal cell""" - cdef: - h3lib.GeoBoundary gb +# Note that we don't provide an option for the order of the coordinates, +# as the GeoJson standard requires them to be in lng/lat order. - check_cell(h) +# Parameters +# ---------- +# geojson : dict +# res : int +# The resolution of the output hexagons +# """ - h3lib.h3ToGeoBoundary(h, &gb) +# # todo: this one could handle multipolygons... - verts = tuple( - coord2deg(gb.verts[i]) - for i in range(gb.num_verts) - ) +# if geojson['type'] != 'Polygon': +# raise ValueError('Only Polygon GeoJSON supported') - if geo_json: - #lat/lng -> lng/lat and last point same as first - verts += (verts[0],) - verts = tuple(v[::-1] for v in verts) +# coords = geojson['coordinates'] - return verts +# out = polyfill_polygon(coords[0], res, holes=coords[1:], lnglat_order=True) +# return out -def edge_boundary(H3int edge, bool geo_json=False): - """ Returns the GeoBoundary containing the coordinates of the edge - """ - cdef: - h3lib.GeoBoundary gb - check_edge(edge) +# def polyfill(dict geojson, int res, bool geo_json_conformant=False): +# """ Light wrapper around `polyfill_geojson` to provide backward compatibility. +# """ + +# try: +# gj_type = geojson['type'] +# except KeyError: +# raise KeyError("`geojson` dict must have key 'type'.") from None + +# if gj_type != 'Polygon': +# raise ValueError('Only Polygon GeoJSON supported') + +# if geo_json_conformant: +# out = polyfill_geojson(geojson, res) +# else: +# coords = geojson['coordinates'] +# out = polyfill_polygon(coords[0], res, holes=coords[1:], lnglat_order=False) + +# return out + + +# def cell_boundary(H3int h, bool geo_json=False): +# """Compose an array of geo-coordinates that outlines a hexagonal cell""" +# cdef: +# h3lib.GeoBoundary gb + +# check_cell(h) + +# h3lib.h3ToGeoBoundary(h, &gb) + +# verts = tuple( +# coord2deg(gb.verts[i]) +# for i in range(gb.num_verts) +# ) + +# if geo_json: +# #lat/lng -> lng/lat and last point same as first +# verts += (verts[0],) +# verts = tuple(v[::-1] for v in verts) + +# return verts + + +# def edge_boundary(H3int edge, bool geo_json=False): +# """ Returns the GeoBoundary containing the coordinates of the edge +# """ +# cdef: +# h3lib.GeoBoundary gb + +# check_edge(edge) - h3lib.getH3UnidirectionalEdgeBoundary(edge, &gb) +# h3lib.getH3UnidirectionalEdgeBoundary(edge, &gb) - # todo: move this verts transform into the GeoBoundary object - verts = tuple( - coord2deg(gb.verts[i]) - for i in range(gb.num_verts) - ) +# # todo: move this verts transform into the GeoBoundary object +# verts = tuple( +# coord2deg(gb.verts[i]) +# for i in range(gb.num_verts) +# ) - if geo_json: - #lat/lng -> lng/lat and last point same as first - verts += (verts[0],) - verts = tuple(v[::-1] for v in verts) +# if geo_json: +# #lat/lng -> lng/lat and last point same as first +# verts += (verts[0],) +# verts = tuple(v[::-1] for v in verts) - return verts +# return verts -cpdef double point_dist( - double lat1, double lng1, - double lat2, double lng2, unit='km') except -1: +# cpdef double point_dist( +# double lat1, double lng1, +# double lat2, double lng2, unit='km') except -1: - a = deg2coord(lat1, lng1) - b = deg2coord(lat2, lng2) +# a = deg2coord(lat1, lng1) +# b = deg2coord(lat2, lng2) - if unit == 'rads': - d = h3lib.pointDistRads(&a, &b) - elif unit == 'km': - d = h3lib.pointDistKm(&a, &b) - elif unit == 'm': - d = h3lib.pointDistM(&a, &b) - else: - raise H3ValueError('Unknown unit: {}'.format(unit)) +# if unit == 'rads': +# d = h3lib.pointDistRads(&a, &b) +# elif unit == 'km': +# d = h3lib.pointDistKm(&a, &b) +# elif unit == 'm': +# d = h3lib.pointDistM(&a, &b) +# else: +# raise H3ValueError('Unknown unit: {}'.format(unit)) - return d +# return d diff --git a/src/h3/_cy/h3lib.pxd b/src/h3/_cy/h3lib.pxd index 5fa05cc0..633af812 100644 --- a/src/h3/_cy/h3lib.pxd +++ b/src/h3/_cy/h3lib.pxd @@ -3,6 +3,7 @@ from cpython cimport bool from libc.stdint cimport int64_t ctypedef stdint.uint64_t H3int +ctypedef stdint.uint32_t H3Error ctypedef basestring H3str cdef extern from "h3api.h": @@ -12,157 +13,160 @@ cdef extern from "h3api.h": ctypedef stdint.uint64_t H3Index - ctypedef struct GeoCoord: + ctypedef struct LatLng: double lat # in radians - double lng "lon" # in radians + double lng # in radians - ctypedef struct GeoBoundary: - int num_verts "numVerts" - GeoCoord verts[10] # MAX_CELL_BNDRY_VERTS + int isValidCell(H3Index h) nogil + int isPentagon(H3Index h) nogil + int isResClassIII(H3Index h) nogil + int isValidDirectedEdge(H3Index edge) nogil - ctypedef struct Geofence: - int numVerts - GeoCoord *verts - - ctypedef struct GeoPolygon: - Geofence geofence - int numHoles - Geofence *holes - - ctypedef struct GeoMultiPolygon: - int numPolygons - GeoPolygon *polygons - - ctypedef struct LinkedGeoCoord: - GeoCoord data "vertex" - LinkedGeoCoord *next - - # renaming these for clarity - ctypedef struct LinkedGeoLoop: - LinkedGeoCoord *data "first" - LinkedGeoCoord *_data_last "last" # not needed in Cython bindings - LinkedGeoLoop *next - - ctypedef struct LinkedGeoPolygon: - LinkedGeoLoop *data "first" - LinkedGeoLoop *_data_last "last" # not needed in Cython bindings - LinkedGeoPolygon *next - - ctypedef struct CoordIJ: - int i - int j - - H3Index geoToH3(const GeoCoord *g, int res) nogil - - void h3ToGeo(H3Index h3, GeoCoord *g) nogil - - void h3ToGeoBoundary(H3Index h3, GeoBoundary *gp) - - int maxKringSize(int k) - - int hexRange(H3Index origin, int k, H3Index *out) - - int hexRangeDistances(H3Index origin, int k, H3Index *out, int *distances) + double degsToRads(double degrees) nogil + double radsToDegs(double radians) nogil - int h3Distance(H3Index origin, H3Index h3) + int getResolution(H3Index h) nogil + int getBaseCellNumber(H3Index h) nogil - int hexRanges(H3Index *h3Set, int length, int k, H3Index *out) + H3Error latLngToCell(const LatLng *g, int res, H3Index *out) nogil + H3Error cellToLatLng(H3Index h, LatLng *) nogil + H3Error gridDistance(H3Index h1, H3Index h2, int64_t *distance) nogil - void kRing(H3Index origin, int k, H3Index *out) + H3Error maxGridDiskSize(int k, int64_t *out) nogil # num/out/N? + H3Error gridDisk(H3Index h, int k, H3Index *out) nogil - void kRingDistances(H3Index origin, int k, H3Index *out, int *distances) + H3Error cellToParent( H3Index h, int parentRes, H3Index *parent) nogil + H3Error cellToCenterChild(H3Index h, int childRes, H3Index *child) nogil - int hexRing(H3Index origin, int k, H3Index *out) + H3Error cellToChildrenSize(H3Index h, int childRes, int64_t *num) nogil # num/out/N? + H3Error cellToChildren( H3Index h, int childRes, H3Index *children) nogil - int maxPolyfillSize(const GeoPolygon *geoPolygon, int res) + H3Error compactCells( + const H3Index *cells_u, + H3Index *cells_c, + const int num_u + ) nogil + H3Error uncompactCellsSize( + const H3Index *cells_c, + const int64_t num_c, + const int res, + int64_t *num_u + ) nogil + H3Error uncompactCells( + const H3Index *cells_c, + const int num_c, + H3Index *cells_u, + const int num_u, + const int res + ) nogil - void polyfill(const GeoPolygon *geoPolygon, int res, H3Index *out) + H3Error getNumCells(int res, int64_t *out) - void h3SetToLinkedGeo(const H3Index *h3Set, const int numHexes, LinkedGeoPolygon *out) + # ctypedef struct GeoBoundary: + # int num_verts "numVerts" + # GeoCoord verts[10] # MAX_CELL_BNDRY_VERTS - void destroyLinkedPolygon(LinkedGeoPolygon *polygon) + # ctypedef struct Geofence: + # int numVerts + # GeoCoord *verts - double degsToRads(double degrees) nogil + # ctypedef struct GeoPolygon: + # Geofence geofence + # int numHoles + # Geofence *holes - double radsToDegs(double radians) nogil + # ctypedef struct GeoMultiPolygon: + # int numPolygons + # GeoPolygon *polygons - stdint.int64_t numHexagons(int res) + # ctypedef struct LinkedGeoCoord: + # GeoCoord data "vertex" + # LinkedGeoCoord *next - int h3GetResolution(H3Index h) nogil + # # renaming these for clarity + # ctypedef struct LinkedGeoLoop: + # LinkedGeoCoord *data "first" + # LinkedGeoCoord *_data_last "last" # not needed in Cython bindings + # LinkedGeoLoop *next - int h3GetBaseCell(H3Index h) + # ctypedef struct LinkedGeoPolygon: + # LinkedGeoLoop *data "first" + # LinkedGeoLoop *_data_last "last" # not needed in Cython bindings + # LinkedGeoPolygon *next - H3Index stringToH3(const char *str) + # ctypedef struct CoordIJ: + # int i + # int j - void h3ToString(H3Index h, char *str, size_t sz) + # void h3ToGeoBoundary(H3Index h3, GeoBoundary *gp) - int h3IsValid(H3Index h) + # int hexRange(H3Index origin, int k, H3Index *out) - H3Index h3ToParent(H3Index h, int parentRes) nogil + # int hexRangeDistances(H3Index origin, int k, H3Index *out, int *distances) - int maxH3ToChildrenSize(H3Index h, int childRes) + # int hexRanges(H3Index *h3Set, int length, int k, H3Index *out) - void h3ToChildren(H3Index h, int childRes, H3Index *children) + # void kRingDistances(H3Index origin, int k, H3Index *out, int *distances) - int compact(const H3Index *h3Set, H3Index *compactedSet, const int numHexes) + # int hexRing(H3Index origin, int k, H3Index *out) - int maxUncompactSize(const H3Index *compactedSet, const int numHexes, const int res) + # int maxPolyfillSize(const GeoPolygon *geoPolygon, int res) - int uncompact(const H3Index *compactedSet, const int numHexes, H3Index *h3Set, const int maxHexes, const int res) + # void polyfill(const GeoPolygon *geoPolygon, int res, H3Index *out) - int h3IsResClassIII(H3Index h) + # void h3SetToLinkedGeo(const H3Index *h3Set, const int numHexes, LinkedGeoPolygon *out) - int h3IsPentagon(H3Index h) + # void destroyLinkedPolygon(LinkedGeoPolygon *polygon) - int pentagonIndexCount() + # H3Index stringToH3(const char *str) - void getPentagonIndexes(int res, H3Index *out) + # void h3ToString(H3Index h, char *str, size_t sz) - int res0IndexCount() + # int pentagonIndexCount() - void getRes0Indexes(H3Index *out) + # void getPentagonIndexes(int res, H3Index *out) - H3Index h3ToCenterChild(H3Index h, int res) + # int res0IndexCount() - int h3IndexesAreNeighbors(H3Index origin, H3Index destination) + # void getRes0Indexes(H3Index *out) - H3Index getH3UnidirectionalEdge(H3Index origin, H3Index destination) + # int h3IndexesAreNeighbors(H3Index origin, H3Index destination) - int h3UnidirectionalEdgeIsValid(H3Index edge) + # H3Index getH3UnidirectionalEdge(H3Index origin, H3Index destination) - H3Index getOriginH3IndexFromUnidirectionalEdge(H3Index edge) + # H3Index getOriginH3IndexFromUnidirectionalEdge(H3Index edge) - H3Index getDestinationH3IndexFromUnidirectionalEdge(H3Index edge) + # H3Index getDestinationH3IndexFromUnidirectionalEdge(H3Index edge) - void getH3IndexesFromUnidirectionalEdge(H3Index edge, H3Index *originDestination) + # void getH3IndexesFromUnidirectionalEdge(H3Index edge, H3Index *originDestination) - void getH3UnidirectionalEdgesFromHexagon(H3Index origin, H3Index *edges) + # void getH3UnidirectionalEdgesFromHexagon(H3Index origin, H3Index *edges) - void getH3UnidirectionalEdgeBoundary(H3Index edge, GeoBoundary *gb) + # void getH3UnidirectionalEdgeBoundary(H3Index edge, GeoBoundary *gb) - int h3LineSize(H3Index start, H3Index end) - int h3Line(H3Index start, H3Index end, H3Index *out) + # int h3LineSize(H3Index start, H3Index end) + # int h3Line(H3Index start, H3Index end, H3Index *out) - int maxFaceCount(H3Index h3) - void h3GetFaces(H3Index h3, int *out) + # int maxFaceCount(H3Index h3) + # void h3GetFaces(H3Index h3, int *out) - int experimentalH3ToLocalIj(H3Index origin, H3Index h3, CoordIJ *out) - int experimentalLocalIjToH3(H3Index origin, const CoordIJ *ij, H3Index *out) + # int experimentalH3ToLocalIj(H3Index origin, H3Index h3, CoordIJ *out) + # int experimentalLocalIjToH3(H3Index origin, const CoordIJ *ij, H3Index *out) - double hexAreaKm2(int res) nogil - double hexAreaM2(int res) nogil + # double hexAreaKm2(int res) nogil + # double hexAreaM2(int res) nogil - double cellAreaRads2(H3Index h) nogil - double cellAreaKm2(H3Index h) nogil - double cellAreaM2(H3Index h) nogil + # double cellAreaRads2(H3Index h) nogil + # double cellAreaKm2(H3Index h) nogil + # double cellAreaM2(H3Index h) nogil - double edgeLengthKm(int res) nogil - double edgeLengthM(int res) nogil + # double edgeLengthKm(int res) nogil + # double edgeLengthM(int res) nogil - double exactEdgeLengthRads(H3Index edge) nogil - double exactEdgeLengthKm(H3Index edge) nogil - double exactEdgeLengthM(H3Index edge) nogil + # double exactEdgeLengthRads(H3Index edge) nogil + # double exactEdgeLengthKm(H3Index edge) nogil + # double exactEdgeLengthM(H3Index edge) nogil - double pointDistRads(const GeoCoord *a, const GeoCoord *b) nogil - double pointDistKm(const GeoCoord *a, const GeoCoord *b) nogil - double pointDistM(const GeoCoord *a, const GeoCoord *b) nogil + # double pointDistRads(const GeoCoord *a, const GeoCoord *b) nogil + # double pointDistKm(const GeoCoord *a, const GeoCoord *b) nogil + # double pointDistM(const GeoCoord *a, const GeoCoord *b) nogil diff --git a/src/h3/_cy/to_multipoly.pyx b/src/h3/_cy/to_multipoly.pyx index 8e5ca55b..296a932f 100644 --- a/src/h3/_cy/to_multipoly.pyx +++ b/src/h3/_cy/to_multipoly.pyx @@ -1,75 +1,75 @@ -cimport h3lib -from h3lib cimport H3int -from .util cimport check_cell, coord2deg +# cimport h3lib +# from h3lib cimport H3int +# from .util cimport check_cell, coord2deg -# todo: it's driving me crazy that these three functions are all essentially the same linked list walker... -# grumble: no way to do iterators in with cdef functions! -cdef walk_polys(const h3lib.LinkedGeoPolygon* L): - out = [] - while L: - out += [walk_loops(L.data)] - L = L.next +# # todo: it's driving me crazy that these three functions are all essentially the same linked list walker... +# # grumble: no way to do iterators in with cdef functions! +# cdef walk_polys(const h3lib.LinkedGeoPolygon* L): +# out = [] +# while L: +# out += [walk_loops(L.data)] +# L = L.next - return out +# return out -cdef walk_loops(const h3lib.LinkedGeoLoop* L): - out = [] - while L: - out += [walk_coords(L.data)] - L = L.next +# cdef walk_loops(const h3lib.LinkedGeoLoop* L): +# out = [] +# while L: +# out += [walk_coords(L.data)] +# L = L.next - return out +# return out -cdef walk_coords(const h3lib.LinkedGeoCoord* L): - out = [] - while L: - out += [coord2deg(L.data)] - L = L.next +# cdef walk_coords(const h3lib.LinkedGeoCoord* L): +# out = [] +# while L: +# out += [coord2deg(L.data)] +# L = L.next - return out +# return out -# todo: tuples instead of lists? -def _to_multi_polygon(const H3int[:] hexes): - cdef: - h3lib.LinkedGeoPolygon polygon +# # todo: tuples instead of lists? +# def _to_multi_polygon(const H3int[:] hexes): +# cdef: +# h3lib.LinkedGeoPolygon polygon - for h in hexes: - check_cell(h) +# for h in hexes: +# check_cell(h) - h3lib.h3SetToLinkedGeo(&hexes[0], len(hexes), &polygon) +# h3lib.h3SetToLinkedGeo(&hexes[0], len(hexes), &polygon) - out = walk_polys(&polygon) +# out = walk_polys(&polygon) - # we're still responsible for cleaning up the passed in `polygon`, - # but not a problem here, since it is stack allocated - h3lib.destroyLinkedPolygon(&polygon) +# # we're still responsible for cleaning up the passed in `polygon`, +# # but not a problem here, since it is stack allocated +# h3lib.destroyLinkedPolygon(&polygon) - return out +# return out -def _geojson_loop(loop): - """ Swap lat/lng order and close loop. - """ - loop = [e[::-1] for e in loop] - loop += [loop[0]] +# def _geojson_loop(loop): +# """ Swap lat/lng order and close loop. +# """ +# loop = [e[::-1] for e in loop] +# loop += [loop[0]] - return loop +# return loop -def h3_set_to_multi_polygon(const H3int[:] hexes, geo_json=False): - # todo: gotta be a more elegant way to handle these... - if len(hexes) == 0: - return [] +# def h3_set_to_multi_polygon(const H3int[:] hexes, geo_json=False): +# # todo: gotta be a more elegant way to handle these... +# if len(hexes) == 0: +# return [] - multipoly = _to_multi_polygon(hexes) +# multipoly = _to_multi_polygon(hexes) - if geo_json: - multipoly = [ - [_geojson_loop(loop) for loop in poly] - for poly in multipoly - ] +# if geo_json: +# multipoly = [ +# [_geojson_loop(loop) for loop in poly] +# for poly in multipoly +# ] - return multipoly +# return multipoly diff --git a/src/h3/_cy/unstable_vect.pyx b/src/h3/_cy/unstable_vect.pyx deleted file mode 100644 index 316ac12c..00000000 --- a/src/h3/_cy/unstable_vect.pyx +++ /dev/null @@ -1,89 +0,0 @@ -cimport h3lib -from h3lib cimport H3int -from .util cimport deg2coord - -from cython cimport boundscheck, wraparound -from libc.math cimport sqrt, sin, cos, asin - -cdef double haversineDistance(double th1, double ph1, double th2, double ph2) nogil: - cdef: - double dx, dy, dz - double R = 6371.0088 - - ph1 -= ph2 - - dz = sin(th1) - sin(th2) - dx = cos(ph1) * cos(th1) - cos(th2) - dy = sin(ph1) * cos(th1) - - return asin(sqrt(dx*dx + dy*dy + dz*dz) / 2)*2*R - - -@boundscheck(False) -@wraparound(False) -cpdef void haversine_vect( - const H3int[:] a, - const H3int[:] b, - double[:] out -) nogil: - - cdef h3lib.GeoCoord p1, p2 - - with nogil: - # todo: add these back in when cython 3.0 comes out - #assert len(a) == len(b) - #assert len(a) <= len(out) - - for i in range(len(a)): - h3lib.h3ToGeo(a[i], &p1) - h3lib.h3ToGeo(b[i], &p2) - out[i] = haversineDistance( - p1.lat, p1.lng, - p2.lat, p2.lng - ) - - -@boundscheck(False) -@wraparound(False) -cpdef void geo_to_h3_vect( - const double[:] lat, - const double[:] lng, - int res, - H3int[:] out -) nogil: - - cdef h3lib.GeoCoord c - - with nogil: - for i in range(len(lat)): - c = deg2coord(lat[i], lng[i]) - out[i] = h3lib.geoToH3(&c, res) - - -@boundscheck(False) -@wraparound(False) -cpdef void h3_to_parent_vect( - const H3int[:] h, - int[:] res, - H3int[:] out -) nogil: - - cdef Py_ssize_t i - - with nogil: - for i in range(len(h)): - out[i] = h3lib.h3ToParent(h[i], res[i]) - - -@boundscheck(False) -@wraparound(False) -cpdef void h3_get_resolution_vect( - const H3int[:] h, - int[:] out, -) nogil: - - cdef Py_ssize_t i - - with nogil: - for i in range(len(h)): - out[i] = h3lib.h3GetResolution(h[i]) diff --git a/src/h3/_cy/util.pxd b/src/h3/_cy/util.pxd index 06cc26a7..6a34b751 100644 --- a/src/h3/_cy/util.pxd +++ b/src/h3/_cy/util.pxd @@ -1,7 +1,7 @@ -from .h3lib cimport H3int, H3str, GeoCoord +from .h3lib cimport H3int, H3str, LatLng -cdef GeoCoord deg2coord(double lat, double lng) nogil -cdef (double, double) coord2deg(GeoCoord c) nogil +cdef LatLng deg2coord(double lat, double lng) nogil +cdef (double, double) coord2deg(LatLng c) nogil cpdef H3int hex2int(H3str h) except? 0 cpdef H3str int2hex(H3int x) diff --git a/src/h3/_cy/util.pyx b/src/h3/_cy/util.pyx index dbf17daa..bbb63e4f 100644 --- a/src/h3/_cy/util.pyx +++ b/src/h3/_cy/util.pyx @@ -1,13 +1,13 @@ from libc cimport stdlib from cython.view cimport array -from .h3lib cimport H3int, H3str, h3IsValid, h3UnidirectionalEdgeIsValid +from .h3lib cimport H3int, H3str, isValidCell, isValidDirectedEdge cimport h3lib -cdef h3lib.GeoCoord deg2coord(double lat, double lng) nogil: +cdef h3lib.LatLng deg2coord(double lat, double lng) nogil: cdef: - h3lib.GeoCoord c + h3lib.LatLng c c.lat = h3lib.degsToRads(lat) c.lng = h3lib.degsToRads(lng) @@ -15,7 +15,7 @@ cdef h3lib.GeoCoord deg2coord(double lat, double lng) nogil: return c -cdef (double, double) coord2deg(h3lib.GeoCoord c) nogil: +cdef (double, double) coord2deg(h3lib.LatLng c) nogil: return ( h3lib.radsToDegs(c.lat), h3lib.radsToDegs(c.lng) @@ -77,11 +77,11 @@ cdef check_cell(H3int h): is incorrect, but in a format that is easily compared to `str` inputs. """ - if h3IsValid(h) == 0: + if isValidCell(h) == 0: raise H3CellError('Integer is not a valid H3 cell: {}'.format(hex(h))) cdef check_edge(H3int e): - if h3UnidirectionalEdgeIsValid(e) == 0: + if isValidDirectedEdge(e) == 0: raise H3EdgeError('Integer is not a valid H3 edge: {}'.format(hex(e))) cdef check_res(int res): diff --git a/src/h3/_version.py b/src/h3/_version.py index eb5cae01..41540d5e 100644 --- a/src/h3/_version.py +++ b/src/h3/_version.py @@ -1,24 +1,25 @@ -__version__ = '3.7.4' +__version__ = '4.0.0' __description__ = 'Hierarchical hexagonal geospatial indexing system' __url__ = 'https://github.com/uber/h3-py' -__license__ = "Apache 2.0 License" +__license__ = 'Apache 2.0 License' __author__ = 'Uber Technologies' __author_email__ = 'AJ Friend ' __classifiers__ = [ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "Intended Audience :: Science/Research", - "License :: OSI Approved :: Apache Software License", - "Programming Language :: C", - "Programming Language :: Cython", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Operating System :: MacOS :: MacOS X", - "Operating System :: POSIX :: Linux", - "Operating System :: Microsoft :: Windows", - "Topic :: Scientific/Engineering :: GIS", + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'Intended Audience :: Science/Research', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: C', + 'Programming Language :: Cython', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: POSIX :: Linux', + 'Operating System :: Microsoft :: Windows', + 'Topic :: Scientific/Engineering :: GIS', ] diff --git a/src/h3/unstable/__init__.py b/src/h3/unstable/__init__.py deleted file mode 100644 index 5c1839c4..00000000 --- a/src/h3/unstable/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# flake8: noqa - -import warnings -warnings.warn( - 'Modules under `h3.unstable` are experimental, and may change at any time.' -) - -from . import v4 -from . import vect diff --git a/src/h3/unstable/v4.py b/src/h3/unstable/v4.py deleted file mode 100644 index 833cd6e1..00000000 --- a/src/h3/unstable/v4.py +++ /dev/null @@ -1,125 +0,0 @@ -# flake8: noqa - -from ..api.basic_str import ( - # todo: implement is_valid_index - - compact as - compact_cells, - - edge_length as - get_hexagon_edge_length_avg, - - geo_to_h3 as - point_to_cell, - - get_destination_h3_index_from_unidirectional_edge as - get_directed_edge_destination, - - get_h3_indexes_from_unidirectional_edge as - directed_edge_to_cells, - - get_h3_unidirectional_edge as - cells_to_directed_edge, - - get_h3_unidirectional_edge_boundary as - directed_edge_to_boundary, - - get_h3_unidirectional_edges_from_hexagon as - origin_to_directed_edges, - - get_origin_h3_index_from_unidirectional_edge as - get_directed_edge_origin, - - get_pentagon_indexes as - get_pentagons, - - get_res0_indexes as - get_res0_cells, - - h3_distance as - grid_distance, - - h3_get_base_cell as - get_base_cell_number, - - h3_get_faces as - get_icosahedron_faces, - - h3_get_resolution as - get_resolution, - - h3_indexes_are_neighbors as - are_neighbor_cells, - - h3_is_pentagon as - is_pentagon, - - h3_is_res_class_III as - is_res_class_III, - - h3_is_valid as - is_valid_cell, - - h3_line as - grid_path_cells, - - h3_set_to_multi_polygon as - cells_to_multipolygon, - - h3_to_center_child as - cell_to_center_child, - - h3_to_children as - cell_to_children, - - h3_to_geo as - cell_to_point, - - h3_to_geo_boundary as - cell_to_boundary, - - h3_to_parent as - cell_to_parent, - - h3_to_string as - int_to_str, - - h3_unidirectional_edge_is_valid as - is_valid_directed_edge, - - hex_area as - get_hexagon_area_avg, - - # hex_range as _, - # hex_range_distances as _, # not sure we want this one; easy for user to implement - # hex_ranges as _, - - hex_ring as - grid_ring, - - k_ring as - grid_disk, - - k_ring_distances as - grid_disk_distances, - - num_hexagons as - get_num_cells, - - polyfill as - polygon_to_cells, - - polyfill_geojson as - geojson_to_cells, # still need to figure out the final polyfill interface - - #polyfill_polygon as _, - - string_to_h3 as - str_to_int, - - uncompact as - uncompact_cells, - - versions as - versions, -) diff --git a/src/h3/unstable/vect.py b/src/h3/unstable/vect.py deleted file mode 100644 index 122c07d7..00000000 --- a/src/h3/unstable/vect.py +++ /dev/null @@ -1,105 +0,0 @@ -from .._cy import unstable_vect as _vect - -import numpy as np - - -def h3_to_parent(h, res=None): - """ - Get parent of arrays of cells. - - Parameters - ---------- - h : array of H3Cells - res: int or None, optional - The resolution for the parent - If `None`, then `res = resolution(h) - 1` - - Returns - ------- - array of H3Cells - """ - h = np.array(h, dtype=np.uint64) - out = np.zeros(len(h), dtype=np.uint64) - - if res is None: - res = h3_get_resolution(h) - 1 - else: - res = np.full(h.shape, res, dtype=np.intc) - - _vect.h3_to_parent_vect(h, res, out) - - return out - - -def h3_get_resolution(h): - """ - Return the resolution of an array of H3 cells. - - Parameters - ---------- - h : H3Cell - - Returns - ------- - array of int - """ - h = np.array(h, dtype=np.uint64) - out = np.zeros(len(h), dtype=np.intc) - - _vect.h3_get_resolution_vect(h, out) - - return out - - -def geo_to_h3(lats, lngs, res): - """ - Convert arrays describing lat/lng pairs to cells. - - Parameters - ---------- - lats, lngs : arrays of floats - - res: int - Resolution for output cells. - - Returns - ------- - array of H3Cells - """ - lats = np.array(lats, dtype=np.float64) - lngs = np.array(lngs, dtype=np.float64) - - assert len(lats) == len(lngs) - - out = np.zeros(len(lats), dtype='uint64') - - _vect.geo_to_h3_vect(lats, lngs, res, out) - - return out - - -def cell_haversine(a, b): - """ - Compute haversine distance between the centers of cells given in - arrays a and b. - - - Parameters - ---------- - a, b : arrays of H3Cell - - Returns - ------- - float - Haversine distance in kilometers - """ - a = np.array(a, dtype=np.uint64) - b = np.array(b, dtype=np.uint64) - - assert len(a) == len(b) - - out = np.zeros(len(a), dtype='double') - - _vect.haversine_vect(a, b, out) - - return out diff --git a/src/h3lib b/src/h3lib index ee292a96..294c20ed 160000 --- a/src/h3lib +++ b/src/h3lib @@ -1 +1 @@ -Subproject commit ee292a96906767162f2f530b46fc2428b2555748 +Subproject commit 294c20ed7d447122d5ab37260896d4ef7579ebfb diff --git a/tests/cython_example.pyx b/tests/cython_example.pyx index e8d28594..c19fe21e 100644 --- a/tests/cython_example.pyx +++ b/tests/cython_example.pyx @@ -1,18 +1,18 @@ -from cython cimport boundscheck, wraparound +# from cython cimport boundscheck, wraparound -from h3._cy.h3lib cimport H3int -from h3._cy.geo cimport geo_to_h3 +# from h3._cy.h3lib cimport H3int +# from h3._cy.geo cimport geo_to_h3 -@boundscheck(False) -@wraparound(False) -cpdef void geo_to_h3_vect( - const double[:] lat, - const double[:] lng, - int res, - H3int[:] out -): +# @boundscheck(False) +# @wraparound(False) +# cpdef void geo_to_h3_vect( +# const double[:] lat, +# const double[:] lng, +# int res, +# H3int[:] out +# ): - cdef Py_ssize_t i +# cdef Py_ssize_t i - for i in range(len(lat)): - out[i] = geo_to_h3(lat[i], lng[i], res) +# for i in range(len(lat)): +# out[i] = geo_to_h3(lat[i], lng[i], res) diff --git a/tests/h3fake2_errors.py b/tests/h3fake2_errors.py new file mode 100644 index 00000000..da568873 --- /dev/null +++ b/tests/h3fake2_errors.py @@ -0,0 +1,15 @@ +# flake8: noqa +""" +Some of the tests expect to catch certain errors. +As we transition, we'll have errors of each type. +pytest functions can accept tuples of errors to check for either one. +""" + +import h3 +import h3fake2 + +H3ValueError = (h3.H3ValueError, h3fake2.H3ValueError) +H3CellError = (h3.H3CellError, h3fake2.H3CellError) +H3ResolutionError = (h3.H3ResolutionError, h3fake2.H3ResolutionError) +H3EdgeError = (h3.H3EdgeError, h3fake2.H3EdgeError) +H3DistanceError = (h3.H3DistanceError, h3fake2.H3DistanceError) diff --git a/tests/test_cells_and_edges.py b/tests/test_cells_and_edges.py index 173033ac..014f5193 100644 --- a/tests/test_cells_and_edges.py +++ b/tests/test_cells_and_edges.py @@ -1,7 +1,7 @@ import h3 import pytest -from h3 import ( +from .h3fake2_errors import ( H3ValueError, H3CellError, H3ResolutionError, diff --git a/tests/test_cython.py b/tests/test_cython.py index 30566b18..0acf3073 100644 --- a/tests/test_cython.py +++ b/tests/test_cython.py @@ -1,28 +1,28 @@ -import numpy as np +# import numpy as np -# Avoid checking for import-error here because cython_example may not have -# been compiled yet. -try: - from .cython_example import geo_to_h3_vect # pylint: disable=import-error -except ImportError: - geo_to_h3_vect = None +# # Avoid checking for import-error here because cython_example may not have +# # been compiled yet. +# try: +# from .cython_example import geo_to_h3_vect # pylint: disable=import-error +# except ImportError: +# geo_to_h3_vect = None -np.random.seed(0) +# np.random.seed(0) -def test_cython_api(): - if geo_to_h3_vect is None: - print("Not running Cython test because cython example was not compiled") - return +# def test_cython_api(): +# if geo_to_h3_vect is None: +# print("Not running Cython test because cython example was not compiled") +# return - N = 100000 +# N = 100000 - lats, lngs = np.random.uniform(0, 90, N), np.random.uniform(0, 90, N) - res = 9 +# lats, lngs = np.random.uniform(0, 90, N), np.random.uniform(0, 90, N) +# res = 9 - lats = np.array(lats, dtype=np.float64) - lngs = np.array(lngs, dtype=np.float64) - out = np.zeros(len(lats), dtype="uint64") - geo_to_h3_vect(lats, lngs, res, out) +# lats = np.array(lats, dtype=np.float64) +# lngs = np.array(lngs, dtype=np.float64) +# out = np.zeros(len(lats), dtype="uint64") +# geo_to_h3_vect(lats, lngs, res, out) - assert out[0] == 617284541015654399 +# assert out[0] == 617284541015654399 diff --git a/tests/test_length_area.py b/tests/test_length_area.py index 8236199d..2a1b5d7d 100644 --- a/tests/test_length_area.py +++ b/tests/test_length_area.py @@ -1,7 +1,7 @@ import h3 import pytest -from h3 import H3ValueError +from .h3fake2_errors import H3ValueError def approx2(a, b): diff --git a/tests/test_polyfill.py b/tests/test_polyfill.py index 4739009b..a02560d1 100644 --- a/tests/test_polyfill.py +++ b/tests/test_polyfill.py @@ -2,6 +2,8 @@ import itertools import pytest +from .h3fake2_errors import H3ResolutionError + def reverse(loop): return list(reversed(loop)) @@ -209,10 +211,10 @@ def test_resolution(): 'coordinates': [[]], } - with pytest.raises(h3.H3ResolutionError): + with pytest.raises(H3ResolutionError): h3.polyfill(d, -1) - with pytest.raises(h3.H3ResolutionError): + with pytest.raises(H3ResolutionError): h3.polyfill(d, 16) diff --git a/tests/test_unstable_vect.py b/tests/test_unstable_vect.py deleted file mode 100644 index a556aae6..00000000 --- a/tests/test_unstable_vect.py +++ /dev/null @@ -1,69 +0,0 @@ -import h3.api.numpy_int as h3 -import numpy as np # only run this test suite if numpy is installed -import pytest - -with pytest.warns( - UserWarning, - match = 'Modules under `h3.unstable` are experimental', -): - import h3.unstable.vect as h3_vect - - -def test_h3_to_parent(): - # At res 9 - h = np.array([617700169958555647, 617700169958555647], np.uint64) - - # Default to res - 1 - arr1 = h3_vect.h3_to_parent(h) - arr2 = h3_vect.h3_to_parent(h, 8) - assert np.array_equal(arr1, arr2) - - # Same as other h3 bindings - arr1 = h3_vect.h3_to_parent(h) - arr2 = np.array(list(map(h3.h3_to_parent, h)), dtype=np.uint64) - assert np.array_equal(arr1, arr2) - - # Test with a number passed to res - arr1 = h3_vect.h3_to_parent(h, 7) - arr2 = np.array([h3.h3_to_parent(c, 7) for c in h], dtype=np.uint64) - assert np.array_equal(arr1, arr2) - - # Test with an array passed to res - arr1 = h3_vect.h3_to_parent(h, np.array([7, 5], np.intc)) - arr2 = h3_vect.h3_to_parent(h, [7, 5]) - - arr3 = [] - for c, res in zip(h, [7, 5]): - arr3.append(h3.h3_to_parent(c, res)) - - assert np.array_equal(arr1, arr2) - assert np.array_equal(arr1, np.array(arr3, dtype=np.uint64)) - - # Test with array-like (but not np.array) cell input - arr1 = h3_vect.h3_to_parent(list(h)) - arr2 = np.array(list(map(h3.h3_to_parent, h)), dtype=np.uint64) - assert np.array_equal(arr1, arr2) - - -def test_h3_to_parent_multiple_res(): - h = np.array([617700169958555647, 613196570331971583], np.uint64) - - # Cells at res 9, 8 - assert list(h3_vect.h3_get_resolution(h)) == [9, 8] - - # Same as other h3 bindings - arr1 = h3_vect.h3_to_parent(h) - arr2 = np.array(list(map(h3.h3_to_parent, h)), dtype=np.uint64) - assert np.array_equal(arr1, arr2) - - # Parent cells are 8, 7 - parents = h3_vect.h3_to_parent(h) - assert list(h3_vect.h3_get_resolution(parents)) == [8, 7] - - -def test_h3_get_resolution(): - h = np.array([617700169958555647], np.uint64) - - arr1 = h3_vect.h3_get_resolution(h) - arr2 = np.array(list(map(h3.h3_get_resolution, h)), dtype=np.intc) - assert np.array_equal(arr1, arr2)