From ef6103735be702e757735f32ea5ffdf80de38c80 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Sat, 30 Dec 2023 16:02:29 -0800 Subject: [PATCH 01/15] starting refactor --- src/h3/api/basic_int/__init__.py | 39 +++++++++++++ src/h3/api/{ => basic_int}/_api_template.py | 0 src/h3/api/basic_int/_binding.py | 36 ------------ src/h3/api/basic_int/_public_api.py | 63 --------------------- src/h3/api/basic_str/__init__.py | 45 +++++++++++++++ src/h3/api/basic_str/_api_template.py | 1 + src/h3/api/basic_str/_binding.py | 43 -------------- src/h3/api/basic_str/_public_api.py | 1 - src/h3/api/memview_int/__init__.py | 33 +++++++++++ src/h3/api/memview_int/_api_template.py | 1 + src/h3/api/memview_int/_binding.py | 31 ---------- src/h3/api/memview_int/_public_api.py | 1 - src/h3/api/numpy_int/__init__.py | 45 +++++++++++++++ src/h3/api/numpy_int/_api_template.py | 1 + src/h3/api/numpy_int/_binding.py | 43 -------------- src/h3/api/numpy_int/_public_api.py | 1 - 16 files changed, 165 insertions(+), 219 deletions(-) rename src/h3/api/{ => basic_int}/_api_template.py (100%) delete mode 100644 src/h3/api/basic_int/_binding.py delete mode 100644 src/h3/api/basic_int/_public_api.py create mode 120000 src/h3/api/basic_str/_api_template.py delete mode 100644 src/h3/api/basic_str/_binding.py delete mode 120000 src/h3/api/basic_str/_public_api.py create mode 120000 src/h3/api/memview_int/_api_template.py delete mode 100644 src/h3/api/memview_int/_binding.py delete mode 120000 src/h3/api/memview_int/_public_api.py create mode 120000 src/h3/api/numpy_int/_api_template.py delete mode 100644 src/h3/api/numpy_int/_binding.py delete mode 120000 src/h3/api/numpy_int/_public_api.py diff --git a/src/h3/api/basic_int/__init__.py b/src/h3/api/basic_int/__init__.py index a2974611..7eb1b36e 100644 --- a/src/h3/api/basic_int/__init__.py +++ b/src/h3/api/basic_int/__init__.py @@ -7,3 +7,42 @@ geo_to_h3shape, h3shape_to_geo, ) + + + +""" +This API handles H3 Indexes of type `int`, using +basic Python collections (`set`, `list`, `tuple`). +`h3` will interpret these Indexes as unsigned 64-bit integers. + +Input collections: + +- `Iterable[int]` + +Output collections: + +- `Set[int]` for unordered +- `List[int]` for ordered +""" + +from ... import _cy +from .._api_template import _API_FUNCTIONS + + +def _id(x): + return x + + +def _in_collection(cells): + it = list(cells) + + return _cy.iter_to_mv(it) + + +_binding = _API_FUNCTIONS( + _in_scalar=_id, + _out_scalar=_id, + _in_collection=_in_collection, + _out_unordered=set, # todo: should this be an (immutable) frozenset? + _out_ordered=list, # todo: should this be an (immutable) tuple? +) diff --git a/src/h3/api/_api_template.py b/src/h3/api/basic_int/_api_template.py similarity index 100% rename from src/h3/api/_api_template.py rename to src/h3/api/basic_int/_api_template.py diff --git a/src/h3/api/basic_int/_binding.py b/src/h3/api/basic_int/_binding.py deleted file mode 100644 index 28002dd9..00000000 --- a/src/h3/api/basic_int/_binding.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -This API handles H3 Indexes of type `int`, using -basic Python collections (`set`, `list`, `tuple`). -`h3` will interpret these Indexes as unsigned 64-bit integers. - -Input collections: - -- `Iterable[int]` - -Output collections: - -- `Set[int]` for unordered -- `List[int]` for ordered -""" - -from ... import _cy -from .._api_template import _API_FUNCTIONS - - -def _id(x): - return x - - -def _in_collection(cells): - it = list(cells) - - return _cy.iter_to_mv(it) - - -_binding = _API_FUNCTIONS( - _in_scalar=_id, - _out_scalar=_id, - _in_collection=_in_collection, - _out_unordered=set, # todo: should this be an (immutable) frozenset? - _out_ordered=list, # todo: should this be an (immutable) tuple? -) diff --git a/src/h3/api/basic_int/_public_api.py b/src/h3/api/basic_int/_public_api.py deleted file mode 100644 index 8e87df1b..00000000 --- a/src/h3/api/basic_int/_public_api.py +++ /dev/null @@ -1,63 +0,0 @@ -"""Binding of public API names to module scope - -Prior to binding the API to module scope explicitly, we dynamically modified the -`globals` object when `h3` was imported, which caused problems with static tooling not -being able to understand the H3 API. - -This file exists to avoid dynamically modifying `globals` and support static tooling. -""" -from ._binding import _binding as _b - - -is_valid_cell = _b.is_valid_cell -is_pentagon = _b.is_pentagon -is_valid_directed_edge = _b.is_valid_directed_edge -is_res_class_III = _b.is_res_class_III - -int_to_str = _b.int_to_str -str_to_int = _b.str_to_int - -cell_area = _b.cell_area -edge_length = _b.edge_length -great_circle_distance = _b.great_circle_distance -average_hexagon_area = _b.average_hexagon_area -average_hexagon_edge_length = _b.average_hexagon_edge_length - -latlng_to_cell = _b.latlng_to_cell -cell_to_latlng = _b.cell_to_latlng -cell_to_boundary = _b.cell_to_boundary -cell_to_local_ij = _b.cell_to_local_ij -local_ij_to_cell = _b.local_ij_to_cell - -grid_ring = _b.grid_ring -grid_disk = _b.grid_disk -grid_distance = _b.grid_distance -grid_path_cells = _b.grid_path_cells - -get_num_cells = _b.get_num_cells -get_pentagons = _b.get_pentagons -get_res0_cells = _b.get_res0_cells -get_resolution = _b.get_resolution -get_base_cell_number = _b.get_base_cell_number -get_icosahedron_faces = _b.get_icosahedron_faces - -cell_to_parent = _b.cell_to_parent -cell_to_children = _b.cell_to_children -cell_to_center_child = _b.cell_to_center_child -compact_cells = _b.compact_cells -uncompact_cells = _b.uncompact_cells - -h3shape_to_cells = _b.h3shape_to_cells -cells_to_h3shape = _b.cells_to_h3shape -cells_to_geo = _b.cells_to_geo -geo_to_cells = _b.geo_to_cells - -are_neighbor_cells = _b.are_neighbor_cells -cells_to_directed_edge = _b.cells_to_directed_edge -directed_edge_to_cells = _b.directed_edge_to_cells -origin_to_directed_edges = _b.origin_to_directed_edges -get_directed_edge_origin = _b.get_directed_edge_origin -get_directed_edge_destination = _b.get_directed_edge_destination -directed_edge_to_boundary = _b.directed_edge_to_boundary - -versions = _b.versions diff --git a/src/h3/api/basic_str/__init__.py b/src/h3/api/basic_str/__init__.py index a2974611..a64b019a 100644 --- a/src/h3/api/basic_str/__init__.py +++ b/src/h3/api/basic_str/__init__.py @@ -7,3 +7,48 @@ geo_to_h3shape, h3shape_to_geo, ) + + +""" +This API handles H3 Indexes of type `str`, using +basic Python collections (`set`, `list`, `tuple`). +`h3` will interpret these Indexes as hexadecimal +representations of unsigned 64-bit integers. + +Input collections: + +- `Iterable[str]` + +Output collections: + +- `Set[str]` for unordered +- `List[str]` for ordered +""" + +from ... import _cy +from .._api_template import _API_FUNCTIONS + + +def _in_collection(cells): + it = [_cy.str_to_int(h) for h in cells] + + return _cy.iter_to_mv(it) + + +def _out_unordered(mv): + # todo: should this be an (immutable) frozenset? + return set(_cy.int_to_str(h) for h in mv) + + +def _out_ordered(mv): + # todo: should this be an (immutable) tuple? + return list(_cy.int_to_str(h) for h in mv) + + +_binding = _API_FUNCTIONS( + _in_scalar = _cy.str_to_int, + _out_scalar = _cy.int_to_str, + _in_collection = _in_collection, + _out_unordered = _out_unordered, + _out_ordered = _out_ordered, +) diff --git a/src/h3/api/basic_str/_api_template.py b/src/h3/api/basic_str/_api_template.py new file mode 120000 index 00000000..5fefb9d0 --- /dev/null +++ b/src/h3/api/basic_str/_api_template.py @@ -0,0 +1 @@ +../basic_int/_api_template.py \ No newline at end of file diff --git a/src/h3/api/basic_str/_binding.py b/src/h3/api/basic_str/_binding.py deleted file mode 100644 index 4ebd5fb1..00000000 --- a/src/h3/api/basic_str/_binding.py +++ /dev/null @@ -1,43 +0,0 @@ -""" -This API handles H3 Indexes of type `str`, using -basic Python collections (`set`, `list`, `tuple`). -`h3` will interpret these Indexes as hexadecimal -representations of unsigned 64-bit integers. - -Input collections: - -- `Iterable[str]` - -Output collections: - -- `Set[str]` for unordered -- `List[str]` for ordered -""" - -from ... import _cy -from .._api_template import _API_FUNCTIONS - - -def _in_collection(cells): - it = [_cy.str_to_int(h) for h in cells] - - return _cy.iter_to_mv(it) - - -def _out_unordered(mv): - # todo: should this be an (immutable) frozenset? - return set(_cy.int_to_str(h) for h in mv) - - -def _out_ordered(mv): - # todo: should this be an (immutable) tuple? - return list(_cy.int_to_str(h) for h in mv) - - -_binding = _API_FUNCTIONS( - _in_scalar = _cy.str_to_int, - _out_scalar = _cy.int_to_str, - _in_collection = _in_collection, - _out_unordered = _out_unordered, - _out_ordered = _out_ordered, -) diff --git a/src/h3/api/basic_str/_public_api.py b/src/h3/api/basic_str/_public_api.py deleted file mode 120000 index c4a6ebd8..00000000 --- a/src/h3/api/basic_str/_public_api.py +++ /dev/null @@ -1 +0,0 @@ -../basic_int/_public_api.py \ No newline at end of file diff --git a/src/h3/api/memview_int/__init__.py b/src/h3/api/memview_int/__init__.py index a2974611..90028fac 100644 --- a/src/h3/api/memview_int/__init__.py +++ b/src/h3/api/memview_int/__init__.py @@ -7,3 +7,36 @@ geo_to_h3shape, h3shape_to_geo, ) + + +""" +This API handles H3 Indexes of type `int` (specifically, `uint64`), +using Python `memoryview` objects for collections. +`h3` will interpret these Indexes as unsigned 64-bit integers. + +Input collections: + +- `memoryview[uint64]`, i.e., anything that supports the buffer protocol + - `dtype` must be `uint64`. for example, `long` will raise an error + - `list` or `set` inputs will not be accepted + +Output collections: + +- `memoryview[uint64]` for unordered +- `memoryview[uint64]` for ordered +""" + +from .._api_template import _API_FUNCTIONS + + +def _id(x): + return x + + +_binding = _API_FUNCTIONS( + _in_scalar = _id, + _out_scalar = _id, + _in_collection = _id, + _out_unordered = _id, + _out_ordered = _id, +) diff --git a/src/h3/api/memview_int/_api_template.py b/src/h3/api/memview_int/_api_template.py new file mode 120000 index 00000000..5fefb9d0 --- /dev/null +++ b/src/h3/api/memview_int/_api_template.py @@ -0,0 +1 @@ +../basic_int/_api_template.py \ No newline at end of file diff --git a/src/h3/api/memview_int/_binding.py b/src/h3/api/memview_int/_binding.py deleted file mode 100644 index 96efb77d..00000000 --- a/src/h3/api/memview_int/_binding.py +++ /dev/null @@ -1,31 +0,0 @@ -""" -This API handles H3 Indexes of type `int` (specifically, `uint64`), -using Python `memoryview` objects for collections. -`h3` will interpret these Indexes as unsigned 64-bit integers. - -Input collections: - -- `memoryview[uint64]`, i.e., anything that supports the buffer protocol - - `dtype` must be `uint64`. for example, `long` will raise an error - - `list` or `set` inputs will not be accepted - -Output collections: - -- `memoryview[uint64]` for unordered -- `memoryview[uint64]` for ordered -""" - -from .._api_template import _API_FUNCTIONS - - -def _id(x): - return x - - -_binding = _API_FUNCTIONS( - _in_scalar = _id, - _out_scalar = _id, - _in_collection = _id, - _out_unordered = _id, - _out_ordered = _id, -) diff --git a/src/h3/api/memview_int/_public_api.py b/src/h3/api/memview_int/_public_api.py deleted file mode 120000 index c4a6ebd8..00000000 --- a/src/h3/api/memview_int/_public_api.py +++ /dev/null @@ -1 +0,0 @@ -../basic_int/_public_api.py \ No newline at end of file diff --git a/src/h3/api/numpy_int/__init__.py b/src/h3/api/numpy_int/__init__.py index a2974611..225689dd 100644 --- a/src/h3/api/numpy_int/__init__.py +++ b/src/h3/api/numpy_int/__init__.py @@ -7,3 +7,48 @@ geo_to_h3shape, h3shape_to_geo, ) + + +""" +This API handles H3 Indexes of type `int` (specifically, `uint64`), +using `numpy.array` objects for collections. +`h3` will interpret these Indexes as unsigned 64-bit integers. + +This API is **optional**, and will only work if the +user has `numpy` installed. + +Input collections: + +- `Iterable[int]` + - works for `lists`, but not `sets` + - will attempt to convert `int` to `uint64` + - no memory copy is made if input dtype is `uint64` + +Output collections: + +- `np.ndarray[np.uint64]` for unordered +- `np.ndarray[np.uint64]` for ordered +""" + +import numpy as np + +from .._api_template import _API_FUNCTIONS + + +def _id(x): + return x + + +def _in_collection(x): + # array is copied only if dtype does not match + # `list`s should work, but not `set`s of integers + return np.asarray(x, dtype='uint64') + + +_binding = _API_FUNCTIONS( + _in_scalar = _id, + _out_scalar = _id, + _in_collection = _in_collection, + _out_unordered = np.asarray, + _out_ordered = np.asarray, +) diff --git a/src/h3/api/numpy_int/_api_template.py b/src/h3/api/numpy_int/_api_template.py new file mode 120000 index 00000000..5fefb9d0 --- /dev/null +++ b/src/h3/api/numpy_int/_api_template.py @@ -0,0 +1 @@ +../basic_int/_api_template.py \ No newline at end of file diff --git a/src/h3/api/numpy_int/_binding.py b/src/h3/api/numpy_int/_binding.py deleted file mode 100644 index c00275eb..00000000 --- a/src/h3/api/numpy_int/_binding.py +++ /dev/null @@ -1,43 +0,0 @@ -""" -This API handles H3 Indexes of type `int` (specifically, `uint64`), -using `numpy.array` objects for collections. -`h3` will interpret these Indexes as unsigned 64-bit integers. - -This API is **optional**, and will only work if the -user has `numpy` installed. - -Input collections: - -- `Iterable[int]` - - works for `lists`, but not `sets` - - will attempt to convert `int` to `uint64` - - no memory copy is made if input dtype is `uint64` - -Output collections: - -- `np.ndarray[np.uint64]` for unordered -- `np.ndarray[np.uint64]` for ordered -""" - -import numpy as np - -from .._api_template import _API_FUNCTIONS - - -def _id(x): - return x - - -def _in_collection(x): - # array is copied only if dtype does not match - # `list`s should work, but not `set`s of integers - return np.asarray(x, dtype='uint64') - - -_binding = _API_FUNCTIONS( - _in_scalar = _id, - _out_scalar = _id, - _in_collection = _in_collection, - _out_unordered = np.asarray, - _out_ordered = np.asarray, -) diff --git a/src/h3/api/numpy_int/_public_api.py b/src/h3/api/numpy_int/_public_api.py deleted file mode 120000 index c4a6ebd8..00000000 --- a/src/h3/api/numpy_int/_public_api.py +++ /dev/null @@ -1 +0,0 @@ -../basic_int/_public_api.py \ No newline at end of file From 190af8a3a0556827d15a87746b4cc34a9d18f445 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Sat, 30 Dec 2023 16:10:23 -0800 Subject: [PATCH 02/15] remove stuff --- src/h3/api/basic_int/_api_template.py | 55 --------------------------- 1 file changed, 55 deletions(-) diff --git a/src/h3/api/basic_int/_api_template.py b/src/h3/api/basic_int/_api_template.py index 1b7fd0d8..cfae570b 100644 --- a/src/h3/api/basic_int/_api_template.py +++ b/src/h3/api/basic_int/_api_template.py @@ -1,42 +1,3 @@ -""" -Module to DRY-up code which is repeated across API modules. - -Definitions of types --------------------- -H3Index: - An unsigned 64-bit integer representing a valid H3 cell or - unidirectional edge. - Depending on the API, an H3Index may be represented as an - unsigned integer type, or as a hexadecimal string. - -H3 cell: - A pentagon or hexagon that can be represented by an H3Index. - -H3Cell: - H3Index representation of an H3 cell. - -H3Edge: - H3Index representation of an H3 unidirectional edge. - - -Definitions of collections --------------------------- -Collection types vary between APIs. We'll use the following terms: - -unordered collection: - Inputs and outputs are interpreted as *unordered* collections. - Examples: `set`, `numpy.ndarray`. - -ordered collection: - Inputs and outputs are interpreted as *ordered* collections. - Examples: `list`, `numpy.ndarray`. - -Notes --------------------- -todo: how do we lint these functions and docstrings? it seems to currently -be skipped due to it being inside the `_api_functions` function. -""" - from .. import _cy from .._h3shape import ( H3Poly, @@ -45,22 +6,6 @@ h3shape_to_geo, ) - -class _API_FUNCTIONS(object): - def __init__( - self, - _in_scalar, - _out_scalar, - _in_collection, - _out_unordered, - _out_ordered, - ): - self._in_scalar = _in_scalar - self._out_scalar = _out_scalar - self._in_collection = _in_collection - self._out_unordered = _out_unordered - self._out_ordered = _out_ordered - @staticmethod def versions(): """ From 20881e7be976800208ef00da7af0fe6fb292ca9a Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Sat, 30 Dec 2023 16:11:13 -0800 Subject: [PATCH 03/15] unindent --- src/h3/api/basic_int/_api_template.py | 1692 ++++++++++++------------- 1 file changed, 846 insertions(+), 846 deletions(-) diff --git a/src/h3/api/basic_int/_api_template.py b/src/h3/api/basic_int/_api_template.py index cfae570b..1da75934 100644 --- a/src/h3/api/basic_int/_api_template.py +++ b/src/h3/api/basic_int/_api_template.py @@ -6,885 +6,885 @@ h3shape_to_geo, ) - @staticmethod - def versions(): - """ - Version numbers for the Python (wrapper) and C (wrapped) libraries. - - Versions are output as strings of the form ``'X.Y.Z'``. - C and Python should match on ``X`` (major) and ``Y`` (minor), - but may differ on ``Z`` (patch). - - Returns - ------- - dict like ``{'c': 'X.Y.Z', 'python': 'A.B.C'}`` - """ - from .._version import __version__ - - v = { - 'c': _cy.c_version(), - 'python': __version__, - } - - return v - - @staticmethod - def str_to_int(h): - """ - Converts a hexadecimal string to an H3 64-bit integer index. - - Parameters - ---------- - h : str - Hexadecimal string like ``'89754e64993ffff'`` - - Returns - ------- - int - Unsigned 64-bit integer - """ - return _cy.str_to_int(h) - - @staticmethod - def int_to_str(x): - """ - Converts an H3 64-bit integer index to a hexadecimal string. - - Parameters - ---------- - x : int - Unsigned 64-bit integer - - Returns - ------- - str - Hexadecimal string like ``'89754e64993ffff'`` - """ - return _cy.int_to_str(x) - - @staticmethod - def get_num_cells(res): - """ - Return the total number of *cells* (hexagons and pentagons) - for the given resolution. - - Returns - ------- - int - """ - return _cy.get_num_cells(res) - - @staticmethod - def average_hexagon_area(res, unit='km^2'): - """ - Return the average area of an H3 *hexagon* - for the given resolution. - - This average *excludes* pentagons. - - Returns - ------- - float - """ - return _cy.average_hexagon_area(res, unit) - - @staticmethod - def average_hexagon_edge_length(res, unit='km'): - """ - Return the average *hexagon* edge length - for the given resolution. - - This average *excludes* pentagons. - - Returns - ------- - float - """ - return _cy.average_hexagon_edge_length(res, unit) - - def is_valid_cell(self, h): - """ - Validates an H3 cell (hexagon or pentagon). - - Returns - ------- - bool - """ - try: - h = self._in_scalar(h) - return _cy.is_valid_cell(h) - except (ValueError, TypeError): - return False - - def is_valid_directed_edge(self, edge): - """ - Validates an H3 unidirectional edge. - - Returns - ------- - bool - """ - try: - e = self._in_scalar(edge) - return _cy.is_valid_directed_edge(e) - except (ValueError, TypeError): - return False - - def latlng_to_cell(self, lat, lng, res): - """ - Return the cell containing the (lat, lng) point - for a given resolution. - - Returns - ------- - H3Cell - - """ - return self._out_scalar(_cy.latlng_to_cell(lat, lng, res)) - - def cell_to_latlng(self, h): - """ - Return the center point of an H3 cell as a lat/lng pair. - - Parameters - ---------- - h : H3Cell - - Returns - ------- - lat : float - Latitude - lng : float - Longitude - """ - return _cy.cell_to_latlng(self._in_scalar(h)) - - def get_resolution(self, h): - """ - Return the resolution of an H3 cell. - - Parameters - ---------- - h : H3Cell - - Returns - ------- - int - """ - # todo: could also work for edges - return _cy.get_resolution(self._in_scalar(h)) - - def cell_to_parent(self, h, res=None): - """ - Get the parent of a cell. - - Parameters - ---------- - h : H3Cell - res : int or None, optional - The resolution for the parent - If ``None``, then ``res = resolution(h) - 1`` - - Returns - ------- - H3Cell - """ +@staticmethod +def versions(): + """ + Version numbers for the Python (wrapper) and C (wrapped) libraries. + + Versions are output as strings of the form ``'X.Y.Z'``. + C and Python should match on ``X`` (major) and ``Y`` (minor), + but may differ on ``Z`` (patch). + + Returns + ------- + dict like ``{'c': 'X.Y.Z', 'python': 'A.B.C'}`` + """ + from .._version import __version__ + + v = { + 'c': _cy.c_version(), + 'python': __version__, + } + + return v + +@staticmethod +def str_to_int(h): + """ + Converts a hexadecimal string to an H3 64-bit integer index. + + Parameters + ---------- + h : str + Hexadecimal string like ``'89754e64993ffff'`` + + Returns + ------- + int + Unsigned 64-bit integer + """ + return _cy.str_to_int(h) + +@staticmethod +def int_to_str(x): + """ + Converts an H3 64-bit integer index to a hexadecimal string. + + Parameters + ---------- + x : int + Unsigned 64-bit integer + + Returns + ------- + str + Hexadecimal string like ``'89754e64993ffff'`` + """ + return _cy.int_to_str(x) + +@staticmethod +def get_num_cells(res): + """ + Return the total number of *cells* (hexagons and pentagons) + for the given resolution. + + Returns + ------- + int + """ + return _cy.get_num_cells(res) + +@staticmethod +def average_hexagon_area(res, unit='km^2'): + """ + Return the average area of an H3 *hexagon* + for the given resolution. + + This average *excludes* pentagons. + + Returns + ------- + float + """ + return _cy.average_hexagon_area(res, unit) + +@staticmethod +def average_hexagon_edge_length(res, unit='km'): + """ + Return the average *hexagon* edge length + for the given resolution. + + This average *excludes* pentagons. + + Returns + ------- + float + """ + return _cy.average_hexagon_edge_length(res, unit) + +def is_valid_cell(self, h): + """ + Validates an H3 cell (hexagon or pentagon). + + Returns + ------- + bool + """ + try: h = self._in_scalar(h) - p = _cy.cell_to_parent(h, res) - p = self._out_scalar(p) - - return p - - def grid_distance(self, h1, h2): - """ - Compute the H3 distance between two cells. - - The H3 distance is defined as the length of the shortest - path between the cells in the graph formed by connecting - adjacent cells. - - This function will raise an exception if the - cells are too far apart to compute the distance. - - Parameters - ---------- - h1 : H3Cell - h2 : H3Cell - - Returns - ------- - int - """ - h1 = self._in_scalar(h1) - h2 = self._in_scalar(h2) - - d = _cy.grid_distance(h1, h2) - - return d - - def cell_to_boundary(self, h): - """ - Return tuple of lat/lng pairs describing the cell boundary. - - Parameters - ---------- - h : H3Cell - - Returns - ------- - tuple of (lat, lng) tuples - """ - return _cy.cell_to_boundary(self._in_scalar(h)) - - def grid_disk(self, h, k=1): - """ - Return unordered set of cells with H3 distance ``<= k`` from ``h``. - That is, the "filled-in" disk. - - Parameters - ---------- - h : H3Cell - k : int - Size of disk. - - Returns - ------- - unordered collection of H3Cell - """ - mv = _cy.grid_disk(self._in_scalar(h), k) - - return self._out_unordered(mv) - - def grid_ring(self, h, k=1): - """ - Return unordered set of cells with H3 distance ``== k`` from ``h``. - That is, the "hollow" ring. - - Parameters - ---------- - h : H3Cell - k : int - Size of ring. - - Returns - ------- - unordered collection of H3Cell - """ - mv = _cy.grid_ring(self._in_scalar(h), k) - - return self._out_unordered(mv) - - def cell_to_children(self, h, res=None): - """ - Children of a cell. - - Parameters - ---------- - h : H3Cell - res : int or None, optional - The resolution for the children. - If ``None``, then ``res = resolution(h) + 1`` - - Returns - ------- - unordered collection of H3Cell - """ - mv = _cy.cell_to_children(self._in_scalar(h), res) - - return self._out_unordered(mv) - - # todo: nogil for expensive C operation? - def compact_cells(self, cells): - """ - Compact a collection of H3 cells by combining - smaller cells into larger cells, if all child cells - are present. Input cells must all share the same resolution. - - Parameters - ---------- - cells : iterable of H3 Cells - - Returns - ------- - unordered collection of H3Cell - """ - # todo: does compact_cells work on mixed-resolution collections? - hu = self._in_collection(cells) - hc = _cy.compact_cells(hu) - - return self._out_unordered(hc) - - def uncompact_cells(self, cells, res): - """ - Reverse the `compact_cells` operation. - - Return a collection of H3 cells, all of resolution ``res``. - - Parameters - ---------- - cells : iterable of H3Cell - res : int - Resolution of desired output cells. - - Returns - ------- - unordered collection of H3Cell - - Raises - ------ - todo: add test to make sure an error is returned when input - contains cell smaller than output res. - https://github.com/uber/h3/blob/master/src/h3lib/lib/h3Index.c#L425 - """ - hc = self._in_collection(cells) - hu = _cy.uncompact_cells(hc, res) - - return self._out_unordered(hu) - - def h3shape_to_cells(self, h3shape, res): - """ - Return the set of H3 cells at a given resolution whose center points - are contained within an `H3Poly` or `H3MultiPoly`. - - Parameters - ---------- - h3shape : H3shape - res : int - Resolution of the output cells - - Returns - ------- - unordered collection of H3Cell - - Examples - -------- - - >>> poly = H3Poly( - ... [(37.68, -122.54), (37.68, -122.34), (37.82, -122.34), - ... (37.82, -122.54)], - ... ) - >>> h3.h3shape_to_cells(poly, 6) - {'862830807ffffff', - '862830827ffffff', - '86283082fffffff', - '862830877ffffff', - '862830947ffffff', - '862830957ffffff', - '86283095fffffff'} - """ - - # todo: not sure if i want this dispatch logic here. maybe in the objects? - if isinstance(h3shape, H3Poly): - poly = h3shape - mv = _cy.polygon_to_cells(poly.outer, res, holes=poly.holes) - elif isinstance(h3shape, H3MultiPoly): - mpoly = h3shape - mv = _cy.polygons_to_cells(mpoly.polys, res) - else: - raise ValueError('Unrecognized type: ' + str(type(h3shape))) - - return self._out_unordered(mv) - - def cells_to_h3shape(self, cells, tight=True): - """ - Return a H3MultiPoly describing the area covered by a set of H3 cells. - - Parameters - ---------- - cells : iterable of H3 cells - tight : bool - If True, return H3Poly if possible. If False, always return H3MultiPoly - - Returns - ------- - H3Poly | H3MultiPoly - - Examples - -------- - - >>> cells = ['8428309ffffffff', '842830dffffffff'] - >>> h3.cells_to_h3shape(cells, tight=True) - - >>> h3.cells_to_h3shape(cells, tight=False) - - """ - cells = self._in_collection(cells) - mpoly = _cy.cells_to_multi_polygon(cells) - - polys = [H3Poly(*poly) for poly in mpoly] - out = H3MultiPoly(*polys) - - if tight and len(out) == 1: - out = out[0] - - return out - - def geo_to_cells(self, geo, res): - """ Convert from __geo_interface__ to cells. - - Parameters - ---------- - geo : an object implementing `__geo_interface__` or a dictionary in that format. - Both H3Poly and H3MultiPoly implement the interface. - res : int - Resolution of desired output cells. - """ - h3shape = geo_to_h3shape(geo) - return self.h3shape_to_cells(h3shape, res) - - def cells_to_geo(self, cells, tight=True): - """ - Parameters - ---------- - cells : iterable of H3 Cells - - Returns - ------- - dict - in `__geo_interface__` format - """ - h3shape = self.cells_to_h3shape(cells, tight=tight) - return h3shape_to_geo(h3shape) - - def is_pentagon(self, h): - """ - Identify if an H3 cell is a pentagon. - - Parameters - ---------- - h : H3Index - - Returns - ------- - bool - ``True`` if input is a valid H3 cell which is a pentagon. - - Notes - ----- - A pentagon should *also* pass ``is_valid_cell()``. - Will return ``False`` for valid H3Edge. - """ - return _cy.is_pentagon(self._in_scalar(h)) - - def get_base_cell_number(self, h): - """ - Return the base cell *number* (``0`` to ``121``) of the given cell. - - The base cell *number* and the H3Index are two different representations - of the same cell: the parent cell of resolution ``0``. - - The base cell *number* is encoded within the corresponding - H3Index. - - todo: could work with edges - - Parameters - ---------- - h : H3Cell - - Returns - ------- - int - """ - return _cy.get_base_cell_number(self._in_scalar(h)) - - def are_neighbor_cells(self, h1, h2): - """ - Returns ``True`` if ``h1`` and ``h2`` are neighboring cells. - - Parameters - ---------- - h1 : H3Cell - h2 : H3Cell - - Returns - ------- - bool - """ - h1 = self._in_scalar(h1) - h2 = self._in_scalar(h2) - - return _cy.are_neighbor_cells(h1, h2) - - def cells_to_directed_edge(self, origin, destination): - """ - Create an H3 Index denoting a unidirectional edge. - - The edge is constructed from neighboring cells ``origin`` and - ``destination``. - - Parameters - ---------- - origin : H3Cell - destination : H3Cell - - Raises - ------ - ValueError - When cells are not adjacent. - - Returns - ------- - H3Edge - """ - o = self._in_scalar(origin) - d = self._in_scalar(destination) - e = _cy.cells_to_directed_edge(o, d) - e = self._out_scalar(e) - - return e - - def get_directed_edge_origin(self, e): - """ - Origin cell from an H3 directed edge. - - Parameters - ---------- - e : H3Edge - - Returns - ------- - H3Cell - """ - e = self._in_scalar(e) - o = _cy.get_directed_edge_origin(e) - o = self._out_scalar(o) - - return o - - def get_directed_edge_destination(self, e): - """ - Destination cell from an H3 directed edge. - - Parameters - ---------- - e : H3Edge - - Returns - ------- - H3Cell - """ - e = self._in_scalar(e) - d = _cy.get_directed_edge_destination(e) - d = self._out_scalar(d) - - return d - - def directed_edge_to_cells(self, e): - """ - Return (origin, destination) tuple from H3 directed edge - - Parameters - ---------- - e : H3Edge - - Returns - ------- - H3Cell - Origin cell of edge - H3Cell - Destination cell of edge - """ - e = self._in_scalar(e) - o, d = _cy.directed_edge_to_cells(e) - o, d = self._out_scalar(o), self._out_scalar(d) - - return o, d - - def origin_to_directed_edges(self, origin): - """ - Return all directed edges starting from ``origin`` cell. - - Parameters - ---------- - origin : H3Cell - - Returns - ------- - unordered collection of H3Edge - """ - mv = _cy.origin_to_directed_edges(self._in_scalar(origin)) - - return self._out_unordered(mv) - - def directed_edge_to_boundary(self, edge): - return _cy.directed_edge_to_boundary(self._in_scalar(edge)) - - def grid_path_cells(self, start, end): - """ - Returns the ordered collection of cells denoting a - minimum-length non-unique path between cells. - - Parameters - ---------- - start : H3Cell - end : H3Cell - - Returns - ------- - ordered collection of H3Cell - Starting with ``start``, and ending with ``end``. - """ - mv = _cy.grid_path_cells(self._in_scalar(start), self._in_scalar(end)) - - return self._out_ordered(mv) - - def is_res_class_III(self, h): - """ - Determine if cell has orientation "Class II" or "Class III". - - The orientation of pentagons/hexagons on the icosahedron can be one - of two types: "Class II" or "Class III". - - All cells within a resolution have the same type, and the type - alternates between resolutions. - - "Class II" cells have resolutions: 0,2,4,6,8,10,12,14 - "Class III" cells have resolutions: 1,3,5,7,9,11,13,15 - - Parameters - ---------- - h : H3Cell - - Returns - ------- - bool - ``True`` if ``h`` is "Class III". - ``False`` if ``h`` is "Class II". - - References - ---------- - 1. https://uber.github.io/h3/#/documentation/core-library/coordinate-systems - """ - return _cy.is_res_class_iii(self._in_scalar(h)) - - def get_pentagons(self, res): - """ - Return all pentagons at a given resolution. - - Parameters - ---------- - res : int - Resolution of the pentagons - - Returns - ------- - unordered collection of H3Cell - """ - mv = _cy.get_pentagons(res) - - return self._out_unordered(mv) - - def get_res0_cells(self): - """ - Return all cells at resolution 0. - - Parameters - ---------- - None - - Returns - ------- - unordered collection of H3Cell - """ - mv = _cy.get_res0_cells() - - return self._out_unordered(mv) - - def cell_to_center_child(self, h, res=None): - """ - Get the center child of a cell at some finer resolution. - - Parameters - ---------- - h : H3Cell - res : int or None, optional - The resolution for the child cell - If ``None``, then ``res = resolution(h) + 1`` - - Returns - ------- - H3Cell - """ - h = self._in_scalar(h) - p = _cy.cell_to_center_child(h, res) - p = self._out_scalar(p) + return _cy.is_valid_cell(h) + except (ValueError, TypeError): + return False + +def is_valid_directed_edge(self, edge): + """ + Validates an H3 unidirectional edge. + + Returns + ------- + bool + """ + try: + e = self._in_scalar(edge) + return _cy.is_valid_directed_edge(e) + except (ValueError, TypeError): + return False + +def latlng_to_cell(self, lat, lng, res): + """ + Return the cell containing the (lat, lng) point + for a given resolution. + + Returns + ------- + H3Cell + + """ + return self._out_scalar(_cy.latlng_to_cell(lat, lng, res)) + +def cell_to_latlng(self, h): + """ + Return the center point of an H3 cell as a lat/lng pair. + + Parameters + ---------- + h : H3Cell + + Returns + ------- + lat : float + Latitude + lng : float + Longitude + """ + return _cy.cell_to_latlng(self._in_scalar(h)) + +def get_resolution(self, h): + """ + Return the resolution of an H3 cell. + + Parameters + ---------- + h : H3Cell + + Returns + ------- + int + """ + # todo: could also work for edges + return _cy.get_resolution(self._in_scalar(h)) + +def cell_to_parent(self, h, res=None): + """ + Get the parent of a cell. + + Parameters + ---------- + h : H3Cell + res : int or None, optional + The resolution for the parent + If ``None``, then ``res = resolution(h) - 1`` + + Returns + ------- + H3Cell + """ + h = self._in_scalar(h) + p = _cy.cell_to_parent(h, res) + p = self._out_scalar(p) + + return p + +def grid_distance(self, h1, h2): + """ + Compute the H3 distance between two cells. + + The H3 distance is defined as the length of the shortest + path between the cells in the graph formed by connecting + adjacent cells. + + This function will raise an exception if the + cells are too far apart to compute the distance. + + Parameters + ---------- + h1 : H3Cell + h2 : H3Cell + + Returns + ------- + int + """ + h1 = self._in_scalar(h1) + h2 = self._in_scalar(h2) + + d = _cy.grid_distance(h1, h2) + + return d + +def cell_to_boundary(self, h): + """ + Return tuple of lat/lng pairs describing the cell boundary. + + Parameters + ---------- + h : H3Cell + + Returns + ------- + tuple of (lat, lng) tuples + """ + return _cy.cell_to_boundary(self._in_scalar(h)) + +def grid_disk(self, h, k=1): + """ + Return unordered set of cells with H3 distance ``<= k`` from ``h``. + That is, the "filled-in" disk. + + Parameters + ---------- + h : H3Cell + k : int + Size of disk. + + Returns + ------- + unordered collection of H3Cell + """ + mv = _cy.grid_disk(self._in_scalar(h), k) + + return self._out_unordered(mv) + +def grid_ring(self, h, k=1): + """ + Return unordered set of cells with H3 distance ``== k`` from ``h``. + That is, the "hollow" ring. + + Parameters + ---------- + h : H3Cell + k : int + Size of ring. + + Returns + ------- + unordered collection of H3Cell + """ + mv = _cy.grid_ring(self._in_scalar(h), k) + + return self._out_unordered(mv) + +def cell_to_children(self, h, res=None): + """ + Children of a cell. + + Parameters + ---------- + h : H3Cell + res : int or None, optional + The resolution for the children. + If ``None``, then ``res = resolution(h) + 1`` + + Returns + ------- + unordered collection of H3Cell + """ + mv = _cy.cell_to_children(self._in_scalar(h), res) + + return self._out_unordered(mv) + +# todo: nogil for expensive C operation? +def compact_cells(self, cells): + """ + Compact a collection of H3 cells by combining + smaller cells into larger cells, if all child cells + are present. Input cells must all share the same resolution. + + Parameters + ---------- + cells : iterable of H3 Cells + + Returns + ------- + unordered collection of H3Cell + """ + # todo: does compact_cells work on mixed-resolution collections? + hu = self._in_collection(cells) + hc = _cy.compact_cells(hu) + + return self._out_unordered(hc) + +def uncompact_cells(self, cells, res): + """ + Reverse the `compact_cells` operation. + + Return a collection of H3 cells, all of resolution ``res``. + + Parameters + ---------- + cells : iterable of H3Cell + res : int + Resolution of desired output cells. + + Returns + ------- + unordered collection of H3Cell + + Raises + ------ + todo: add test to make sure an error is returned when input + contains cell smaller than output res. + https://github.com/uber/h3/blob/master/src/h3lib/lib/h3Index.c#L425 + """ + hc = self._in_collection(cells) + hu = _cy.uncompact_cells(hc, res) + + return self._out_unordered(hu) + +def h3shape_to_cells(self, h3shape, res): + """ + Return the set of H3 cells at a given resolution whose center points + are contained within an `H3Poly` or `H3MultiPoly`. + + Parameters + ---------- + h3shape : H3shape + res : int + Resolution of the output cells + + Returns + ------- + unordered collection of H3Cell + + Examples + -------- + + >>> poly = H3Poly( + ... [(37.68, -122.54), (37.68, -122.34), (37.82, -122.34), + ... (37.82, -122.54)], + ... ) + >>> h3.h3shape_to_cells(poly, 6) + {'862830807ffffff', + '862830827ffffff', + '86283082fffffff', + '862830877ffffff', + '862830947ffffff', + '862830957ffffff', + '86283095fffffff'} + """ + + # todo: not sure if i want this dispatch logic here. maybe in the objects? + if isinstance(h3shape, H3Poly): + poly = h3shape + mv = _cy.polygon_to_cells(poly.outer, res, holes=poly.holes) + elif isinstance(h3shape, H3MultiPoly): + mpoly = h3shape + mv = _cy.polygons_to_cells(mpoly.polys, res) + else: + raise ValueError('Unrecognized type: ' + str(type(h3shape))) + + return self._out_unordered(mv) + +def cells_to_h3shape(self, cells, tight=True): + """ + Return a H3MultiPoly describing the area covered by a set of H3 cells. + + Parameters + ---------- + cells : iterable of H3 cells + tight : bool + If True, return H3Poly if possible. If False, always return H3MultiPoly + + Returns + ------- + H3Poly | H3MultiPoly + + Examples + -------- + + >>> cells = ['8428309ffffffff', '842830dffffffff'] + >>> h3.cells_to_h3shape(cells, tight=True) + + >>> h3.cells_to_h3shape(cells, tight=False) + + """ + cells = self._in_collection(cells) + mpoly = _cy.cells_to_multi_polygon(cells) + + polys = [H3Poly(*poly) for poly in mpoly] + out = H3MultiPoly(*polys) + + if tight and len(out) == 1: + out = out[0] + + return out + +def geo_to_cells(self, geo, res): + """ Convert from __geo_interface__ to cells. + + Parameters + ---------- + geo : an object implementing `__geo_interface__` or a dictionary in that format. + Both H3Poly and H3MultiPoly implement the interface. + res : int + Resolution of desired output cells. + """ + h3shape = geo_to_h3shape(geo) + return self.h3shape_to_cells(h3shape, res) + +def cells_to_geo(self, cells, tight=True): + """ + Parameters + ---------- + cells : iterable of H3 Cells + + Returns + ------- + dict + in `__geo_interface__` format + """ + h3shape = self.cells_to_h3shape(cells, tight=tight) + return h3shape_to_geo(h3shape) + +def is_pentagon(self, h): + """ + Identify if an H3 cell is a pentagon. + + Parameters + ---------- + h : H3Index + + Returns + ------- + bool + ``True`` if input is a valid H3 cell which is a pentagon. + + Notes + ----- + A pentagon should *also* pass ``is_valid_cell()``. + Will return ``False`` for valid H3Edge. + """ + return _cy.is_pentagon(self._in_scalar(h)) + +def get_base_cell_number(self, h): + """ + Return the base cell *number* (``0`` to ``121``) of the given cell. + + The base cell *number* and the H3Index are two different representations + of the same cell: the parent cell of resolution ``0``. + + The base cell *number* is encoded within the corresponding + H3Index. + + todo: could work with edges + + Parameters + ---------- + h : H3Cell + + Returns + ------- + int + """ + return _cy.get_base_cell_number(self._in_scalar(h)) + +def are_neighbor_cells(self, h1, h2): + """ + Returns ``True`` if ``h1`` and ``h2`` are neighboring cells. + + Parameters + ---------- + h1 : H3Cell + h2 : H3Cell + + Returns + ------- + bool + """ + h1 = self._in_scalar(h1) + h2 = self._in_scalar(h2) + + return _cy.are_neighbor_cells(h1, h2) + +def cells_to_directed_edge(self, origin, destination): + """ + Create an H3 Index denoting a unidirectional edge. + + The edge is constructed from neighboring cells ``origin`` and + ``destination``. + + Parameters + ---------- + origin : H3Cell + destination : H3Cell + + Raises + ------ + ValueError + When cells are not adjacent. + + Returns + ------- + H3Edge + """ + o = self._in_scalar(origin) + d = self._in_scalar(destination) + e = _cy.cells_to_directed_edge(o, d) + e = self._out_scalar(e) + + return e + +def get_directed_edge_origin(self, e): + """ + Origin cell from an H3 directed edge. + + Parameters + ---------- + e : H3Edge + + Returns + ------- + H3Cell + """ + e = self._in_scalar(e) + o = _cy.get_directed_edge_origin(e) + o = self._out_scalar(o) + + return o + +def get_directed_edge_destination(self, e): + """ + Destination cell from an H3 directed edge. + + Parameters + ---------- + e : H3Edge + + Returns + ------- + H3Cell + """ + e = self._in_scalar(e) + d = _cy.get_directed_edge_destination(e) + d = self._out_scalar(d) + + return d + +def directed_edge_to_cells(self, e): + """ + Return (origin, destination) tuple from H3 directed edge + + Parameters + ---------- + e : H3Edge + + Returns + ------- + H3Cell + Origin cell of edge + H3Cell + Destination cell of edge + """ + e = self._in_scalar(e) + o, d = _cy.directed_edge_to_cells(e) + o, d = self._out_scalar(o), self._out_scalar(d) + + return o, d + +def origin_to_directed_edges(self, origin): + """ + Return all directed edges starting from ``origin`` cell. + + Parameters + ---------- + origin : H3Cell + + Returns + ------- + unordered collection of H3Edge + """ + mv = _cy.origin_to_directed_edges(self._in_scalar(origin)) + + return self._out_unordered(mv) + +def directed_edge_to_boundary(self, edge): + return _cy.directed_edge_to_boundary(self._in_scalar(edge)) + +def grid_path_cells(self, start, end): + """ + Returns the ordered collection of cells denoting a + minimum-length non-unique path between cells. + + Parameters + ---------- + start : H3Cell + end : H3Cell + + Returns + ------- + ordered collection of H3Cell + Starting with ``start``, and ending with ``end``. + """ + mv = _cy.grid_path_cells(self._in_scalar(start), self._in_scalar(end)) + + return self._out_ordered(mv) + +def is_res_class_III(self, h): + """ + Determine if cell has orientation "Class II" or "Class III". + + The orientation of pentagons/hexagons on the icosahedron can be one + of two types: "Class II" or "Class III". + + All cells within a resolution have the same type, and the type + alternates between resolutions. + + "Class II" cells have resolutions: 0,2,4,6,8,10,12,14 + "Class III" cells have resolutions: 1,3,5,7,9,11,13,15 + + Parameters + ---------- + h : H3Cell + + Returns + ------- + bool + ``True`` if ``h`` is "Class III". + ``False`` if ``h`` is "Class II". + + References + ---------- + 1. https://uber.github.io/h3/#/documentation/core-library/coordinate-systems + """ + return _cy.is_res_class_iii(self._in_scalar(h)) + +def get_pentagons(self, res): + """ + Return all pentagons at a given resolution. + + Parameters + ---------- + res : int + Resolution of the pentagons + + Returns + ------- + unordered collection of H3Cell + """ + mv = _cy.get_pentagons(res) + + return self._out_unordered(mv) + +def get_res0_cells(self): + """ + Return all cells at resolution 0. + + Parameters + ---------- + None - return p + Returns + ------- + unordered collection of H3Cell + """ + mv = _cy.get_res0_cells() - def get_icosahedron_faces(self, h): - """ - Return icosahedron faces intersecting a given H3 cell. + return self._out_unordered(mv) - There are twenty possible faces, ranging from 0--19. +def cell_to_center_child(self, h, res=None): + """ + Get the center child of a cell at some finer resolution. - Note: Every interface returns a Python ``set`` of ``int``. + Parameters + ---------- + h : H3Cell + res : int or None, optional + The resolution for the child cell + If ``None``, then ``res = resolution(h) + 1`` - Parameters - ---------- - h : H3Cell + Returns + ------- + H3Cell + """ + h = self._in_scalar(h) + p = _cy.cell_to_center_child(h, res) + p = self._out_scalar(p) - Returns - ------- - Python ``set`` of ``int`` - """ - h = self._in_scalar(h) - faces = _cy.get_icosahedron_faces(h) + return p - return faces +def get_icosahedron_faces(self, h): + """ + Return icosahedron faces intersecting a given H3 cell. - def cell_to_local_ij(self, origin, h): - """ - Return local (i,j) coordinates of cell ``h`` in relation to ``origin`` cell + There are twenty possible faces, ranging from 0--19. + Note: Every interface returns a Python ``set`` of ``int``. - Parameters - ---------- - origin : H3Cell - Origin/central cell for defining i,j coordinates. - h: H3Cell - Destination cell whose i,j coordinates we'd like, based off - of the origin cell. + Parameters + ---------- + h : H3Cell + Returns + ------- + Python ``set`` of ``int`` + """ + h = self._in_scalar(h) + faces = _cy.get_icosahedron_faces(h) - Returns - ------- - Tuple (i, j) of integer local coordinates of cell ``h`` + return faces +def cell_to_local_ij(self, origin, h): + """ + Return local (i,j) coordinates of cell ``h`` in relation to ``origin`` cell - Notes - ----- - The ``origin`` cell does not define (0, 0) for the IJ coordinate space. - (0, 0) refers to the center of the base cell containing origin at the - resolution of `origin`. - Subtracting the IJ coordinates of ``origin`` from every cell would get - you the property of (0, 0) being the ``origin``. + Parameters + ---------- + origin : H3Cell + Origin/central cell for defining i,j coordinates. + h: H3Cell + Destination cell whose i,j coordinates we'd like, based off + of the origin cell. - This is done so we don't need to keep recomputing the coordinates of - ``origin`` if not needed. - """ - origin = self._in_scalar(origin) - h = self._in_scalar(h) - i, j = _cy.cell_to_local_ij(origin, h) + Returns + ------- + Tuple (i, j) of integer local coordinates of cell ``h`` - return i, j - def local_ij_to_cell(self, origin, i, j): - """ - Return cell at local (i,j) position relative to the ``origin`` cell. + Notes + ----- - Parameters - ---------- - origin : H3Cell - Origin/central cell for defining i,j coordinates. - i, j: int - Integer coordinates with respect to ``origin`` cell. + The ``origin`` cell does not define (0, 0) for the IJ coordinate space. + (0, 0) refers to the center of the base cell containing origin at the + resolution of `origin`. + Subtracting the IJ coordinates of ``origin`` from every cell would get + you the property of (0, 0) being the ``origin``. + This is done so we don't need to keep recomputing the coordinates of + ``origin`` if not needed. + """ + origin = self._in_scalar(origin) + h = self._in_scalar(h) - Returns - ------- - H3Cell at local (i,j) position relative to the ``origin`` cell + i, j = _cy.cell_to_local_ij(origin, h) + return i, j - Notes - ----- +def local_ij_to_cell(self, origin, i, j): + """ + Return cell at local (i,j) position relative to the ``origin`` cell. - The ``origin`` cell does not define (0, 0) for the IJ coordinate space. - (0, 0) refers to the center of the base cell containing origin at the - resolution of ``origin``. - Subtracting the IJ coordinates of ``origin`` from every cell would get - you the property of (0, 0) being the ``origin``. + Parameters + ---------- + origin : H3Cell + Origin/central cell for defining i,j coordinates. + i, j: int + Integer coordinates with respect to ``origin`` cell. - This is done so we don't need to keep recomputing the coordinates of - ``origin`` if not needed. - """ - origin = self._in_scalar(origin) - h = _cy.local_ij_to_cell(origin, i, j) - h = self._out_scalar(h) + Returns + ------- + H3Cell at local (i,j) position relative to the ``origin`` cell - return h - def cell_area(self, h, unit='km^2'): - """ - Compute the spherical surface area of a specific H3 cell. + Notes + ----- - Parameters - ---------- - h : H3Cell - unit: str - Unit for area result (``'km^2'``, 'm^2', or 'rads^2') + The ``origin`` cell does not define (0, 0) for the IJ coordinate space. + (0, 0) refers to the center of the base cell containing origin at the + resolution of ``origin``. + Subtracting the IJ coordinates of ``origin`` from every cell would get + you the property of (0, 0) being the ``origin``. + This is done so we don't need to keep recomputing the coordinates of + ``origin`` if not needed. + """ + origin = self._in_scalar(origin) - Returns - ------- - The area of the H3 cell in the given units + h = _cy.local_ij_to_cell(origin, i, j) + h = self._out_scalar(h) + return h - Notes - ----- - This function breaks the cell into spherical triangles, and computes - their spherical area. - The function uses the spherical distance calculation given by - `great_circle_distance`. - """ - h = self._in_scalar(h) +def cell_area(self, h, unit='km^2'): + """ + Compute the spherical surface area of a specific H3 cell. + + Parameters + ---------- + h : H3Cell + unit: str + Unit for area result (``'km^2'``, 'm^2', or 'rads^2') + + + Returns + ------- + The area of the H3 cell in the given units + + + Notes + ----- + This function breaks the cell into spherical triangles, and computes + their spherical area. + The function uses the spherical distance calculation given by + `great_circle_distance`. + """ + h = self._in_scalar(h) + + return _cy.cell_area(h, unit=unit) + +def edge_length(self, e, unit='km'): + """ + Compute the spherical length of a specific H3 edge. + + Parameters + ---------- + h : H3Cell + unit: str + Unit for length result ('km', 'm', or 'rads') + + + Returns + ------- + The length of the edge in the given units + + + Notes + ----- + This function uses the spherical distance calculation given by + `great_circle_distance`. + """ + e = self._in_scalar(e) + + return _cy.edge_length(e, unit=unit) + +@staticmethod +def great_circle_distance(latlng1, latlng2, unit='km'): + """ + Compute the spherical distance between two (lat, lng) points. + AKA: great circle distance or "haversine" distance. + + todo: overload to allow two cell inputs? - return _cy.cell_area(h, unit=unit) - - def edge_length(self, e, unit='km'): - """ - Compute the spherical length of a specific H3 edge. - - Parameters - ---------- - h : H3Cell - unit: str - Unit for length result ('km', 'm', or 'rads') - - - Returns - ------- - The length of the edge in the given units - - - Notes - ----- - This function uses the spherical distance calculation given by - `great_circle_distance`. - """ - e = self._in_scalar(e) - - return _cy.edge_length(e, unit=unit) - - @staticmethod - def great_circle_distance(latlng1, latlng2, unit='km'): - """ - Compute the spherical distance between two (lat, lng) points. - AKA: great circle distance or "haversine" distance. - - todo: overload to allow two cell inputs? - - Parameters - ---------- - latlng1 : tuple - (lat, lng) tuple in degrees - latlng2 : tuple - (lat, lng) tuple in degrees - unit: str - Unit for distance result ('km', 'm', or 'rads') - - - Returns - ------- - The spherical distance between the points in the given units - """ - lat1, lng1 = latlng1 - lat2, lng2 = latlng2 - return _cy.great_circle_distance( - lat1, lng1, - lat2, lng2, - unit = unit - ) + Parameters + ---------- + latlng1 : tuple + (lat, lng) tuple in degrees + latlng2 : tuple + (lat, lng) tuple in degrees + unit: str + Unit for distance result ('km', 'm', or 'rads') + + + Returns + ------- + The spherical distance between the points in the given units + """ + lat1, lng1 = latlng1 + lat2, lng2 = latlng2 + return _cy.great_circle_distance( + lat1, lng1, + lat2, lng2, + unit = unit + ) From 6fba01243b72f928eb8b5c656bfcc2cc1f016c62 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Sat, 30 Dec 2023 16:12:40 -0800 Subject: [PATCH 04/15] remove self from function signatures --- src/h3/api/basic_int/_api_template.py | 70 +++++++++++++-------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/h3/api/basic_int/_api_template.py b/src/h3/api/basic_int/_api_template.py index 1da75934..d7abfd55 100644 --- a/src/h3/api/basic_int/_api_template.py +++ b/src/h3/api/basic_int/_api_template.py @@ -102,7 +102,7 @@ def average_hexagon_edge_length(res, unit='km'): """ return _cy.average_hexagon_edge_length(res, unit) -def is_valid_cell(self, h): +def is_valid_cell(h): """ Validates an H3 cell (hexagon or pentagon). @@ -116,7 +116,7 @@ def is_valid_cell(self, h): except (ValueError, TypeError): return False -def is_valid_directed_edge(self, edge): +def is_valid_directed_edge(edge): """ Validates an H3 unidirectional edge. @@ -130,7 +130,7 @@ def is_valid_directed_edge(self, edge): except (ValueError, TypeError): return False -def latlng_to_cell(self, lat, lng, res): +def latlng_to_cell(lat, lng, res): """ Return the cell containing the (lat, lng) point for a given resolution. @@ -142,7 +142,7 @@ def latlng_to_cell(self, lat, lng, res): """ return self._out_scalar(_cy.latlng_to_cell(lat, lng, res)) -def cell_to_latlng(self, h): +def cell_to_latlng(h): """ Return the center point of an H3 cell as a lat/lng pair. @@ -159,7 +159,7 @@ def cell_to_latlng(self, h): """ return _cy.cell_to_latlng(self._in_scalar(h)) -def get_resolution(self, h): +def get_resolution(h): """ Return the resolution of an H3 cell. @@ -174,7 +174,7 @@ def get_resolution(self, h): # todo: could also work for edges return _cy.get_resolution(self._in_scalar(h)) -def cell_to_parent(self, h, res=None): +def cell_to_parent(h, res=None): """ Get the parent of a cell. @@ -195,7 +195,7 @@ def cell_to_parent(self, h, res=None): return p -def grid_distance(self, h1, h2): +def grid_distance(h1, h2): """ Compute the H3 distance between two cells. @@ -222,7 +222,7 @@ def grid_distance(self, h1, h2): return d -def cell_to_boundary(self, h): +def cell_to_boundary(h): """ Return tuple of lat/lng pairs describing the cell boundary. @@ -236,7 +236,7 @@ def cell_to_boundary(self, h): """ return _cy.cell_to_boundary(self._in_scalar(h)) -def grid_disk(self, h, k=1): +def grid_disk(h, k=1): """ Return unordered set of cells with H3 distance ``<= k`` from ``h``. That is, the "filled-in" disk. @@ -255,7 +255,7 @@ def grid_disk(self, h, k=1): return self._out_unordered(mv) -def grid_ring(self, h, k=1): +def grid_ring(h, k=1): """ Return unordered set of cells with H3 distance ``== k`` from ``h``. That is, the "hollow" ring. @@ -274,7 +274,7 @@ def grid_ring(self, h, k=1): return self._out_unordered(mv) -def cell_to_children(self, h, res=None): +def cell_to_children(h, res=None): """ Children of a cell. @@ -294,7 +294,7 @@ def cell_to_children(self, h, res=None): return self._out_unordered(mv) # todo: nogil for expensive C operation? -def compact_cells(self, cells): +def compact_cells(cells): """ Compact a collection of H3 cells by combining smaller cells into larger cells, if all child cells @@ -314,7 +314,7 @@ def compact_cells(self, cells): return self._out_unordered(hc) -def uncompact_cells(self, cells, res): +def uncompact_cells(cells, res): """ Reverse the `compact_cells` operation. @@ -341,7 +341,7 @@ def uncompact_cells(self, cells, res): return self._out_unordered(hu) -def h3shape_to_cells(self, h3shape, res): +def h3shape_to_cells(h3shape, res): """ Return the set of H3 cells at a given resolution whose center points are contained within an `H3Poly` or `H3MultiPoly`. @@ -385,7 +385,7 @@ def h3shape_to_cells(self, h3shape, res): return self._out_unordered(mv) -def cells_to_h3shape(self, cells, tight=True): +def cells_to_h3shape(cells, tight=True): """ Return a H3MultiPoly describing the area covered by a set of H3 cells. @@ -419,7 +419,7 @@ def cells_to_h3shape(self, cells, tight=True): return out -def geo_to_cells(self, geo, res): +def geo_to_cells(geo, res): """ Convert from __geo_interface__ to cells. Parameters @@ -432,7 +432,7 @@ def geo_to_cells(self, geo, res): h3shape = geo_to_h3shape(geo) return self.h3shape_to_cells(h3shape, res) -def cells_to_geo(self, cells, tight=True): +def cells_to_geo(cells, tight=True): """ Parameters ---------- @@ -446,7 +446,7 @@ def cells_to_geo(self, cells, tight=True): h3shape = self.cells_to_h3shape(cells, tight=tight) return h3shape_to_geo(h3shape) -def is_pentagon(self, h): +def is_pentagon(h): """ Identify if an H3 cell is a pentagon. @@ -466,7 +466,7 @@ def is_pentagon(self, h): """ return _cy.is_pentagon(self._in_scalar(h)) -def get_base_cell_number(self, h): +def get_base_cell_number(h): """ Return the base cell *number* (``0`` to ``121``) of the given cell. @@ -488,7 +488,7 @@ def get_base_cell_number(self, h): """ return _cy.get_base_cell_number(self._in_scalar(h)) -def are_neighbor_cells(self, h1, h2): +def are_neighbor_cells(h1, h2): """ Returns ``True`` if ``h1`` and ``h2`` are neighboring cells. @@ -506,7 +506,7 @@ def are_neighbor_cells(self, h1, h2): return _cy.are_neighbor_cells(h1, h2) -def cells_to_directed_edge(self, origin, destination): +def cells_to_directed_edge(origin, destination): """ Create an H3 Index denoting a unidirectional edge. @@ -534,7 +534,7 @@ def cells_to_directed_edge(self, origin, destination): return e -def get_directed_edge_origin(self, e): +def get_directed_edge_origin(e): """ Origin cell from an H3 directed edge. @@ -552,7 +552,7 @@ def get_directed_edge_origin(self, e): return o -def get_directed_edge_destination(self, e): +def get_directed_edge_destination(e): """ Destination cell from an H3 directed edge. @@ -570,7 +570,7 @@ def get_directed_edge_destination(self, e): return d -def directed_edge_to_cells(self, e): +def directed_edge_to_cells(e): """ Return (origin, destination) tuple from H3 directed edge @@ -591,7 +591,7 @@ def directed_edge_to_cells(self, e): return o, d -def origin_to_directed_edges(self, origin): +def origin_to_directed_edges(origin): """ Return all directed edges starting from ``origin`` cell. @@ -607,10 +607,10 @@ def origin_to_directed_edges(self, origin): return self._out_unordered(mv) -def directed_edge_to_boundary(self, edge): +def directed_edge_to_boundary(edge): return _cy.directed_edge_to_boundary(self._in_scalar(edge)) -def grid_path_cells(self, start, end): +def grid_path_cells(start, end): """ Returns the ordered collection of cells denoting a minimum-length non-unique path between cells. @@ -629,7 +629,7 @@ def grid_path_cells(self, start, end): return self._out_ordered(mv) -def is_res_class_III(self, h): +def is_res_class_III(h): """ Determine if cell has orientation "Class II" or "Class III". @@ -658,7 +658,7 @@ def is_res_class_III(self, h): """ return _cy.is_res_class_iii(self._in_scalar(h)) -def get_pentagons(self, res): +def get_pentagons(res): """ Return all pentagons at a given resolution. @@ -691,7 +691,7 @@ def get_res0_cells(self): return self._out_unordered(mv) -def cell_to_center_child(self, h, res=None): +def cell_to_center_child(h, res=None): """ Get the center child of a cell at some finer resolution. @@ -712,7 +712,7 @@ def cell_to_center_child(self, h, res=None): return p -def get_icosahedron_faces(self, h): +def get_icosahedron_faces(h): """ Return icosahedron faces intersecting a given H3 cell. @@ -733,7 +733,7 @@ def get_icosahedron_faces(self, h): return faces -def cell_to_local_ij(self, origin, h): +def cell_to_local_ij(origin, h): """ Return local (i,j) coordinates of cell ``h`` in relation to ``origin`` cell @@ -771,7 +771,7 @@ def cell_to_local_ij(self, origin, h): return i, j -def local_ij_to_cell(self, origin, i, j): +def local_ij_to_cell(origin, i, j): """ Return cell at local (i,j) position relative to the ``origin`` cell. @@ -807,7 +807,7 @@ def local_ij_to_cell(self, origin, i, j): return h -def cell_area(self, h, unit='km^2'): +def cell_area(h, unit='km^2'): """ Compute the spherical surface area of a specific H3 cell. @@ -834,7 +834,7 @@ def cell_area(self, h, unit='km^2'): return _cy.cell_area(h, unit=unit) -def edge_length(self, e, unit='km'): +def edge_length(e, unit='km'): """ Compute the spherical length of a specific H3 edge. From 3f0aa4aa36181699e29d486fc264864976310c3c Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Sat, 30 Dec 2023 16:14:42 -0800 Subject: [PATCH 05/15] remove self. --- src/h3/api/basic_int/_api_template.py | 110 +++++++++++++------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/src/h3/api/basic_int/_api_template.py b/src/h3/api/basic_int/_api_template.py index d7abfd55..eedb9f97 100644 --- a/src/h3/api/basic_int/_api_template.py +++ b/src/h3/api/basic_int/_api_template.py @@ -111,7 +111,7 @@ def is_valid_cell(h): bool """ try: - h = self._in_scalar(h) + h = _in_scalar(h) return _cy.is_valid_cell(h) except (ValueError, TypeError): return False @@ -125,7 +125,7 @@ def is_valid_directed_edge(edge): bool """ try: - e = self._in_scalar(edge) + e = _in_scalar(edge) return _cy.is_valid_directed_edge(e) except (ValueError, TypeError): return False @@ -140,7 +140,7 @@ def latlng_to_cell(lat, lng, res): H3Cell """ - return self._out_scalar(_cy.latlng_to_cell(lat, lng, res)) + return _out_scalar(_cy.latlng_to_cell(lat, lng, res)) def cell_to_latlng(h): """ @@ -157,7 +157,7 @@ def cell_to_latlng(h): lng : float Longitude """ - return _cy.cell_to_latlng(self._in_scalar(h)) + return _cy.cell_to_latlng(_in_scalar(h)) def get_resolution(h): """ @@ -172,7 +172,7 @@ def get_resolution(h): int """ # todo: could also work for edges - return _cy.get_resolution(self._in_scalar(h)) + return _cy.get_resolution(_in_scalar(h)) def cell_to_parent(h, res=None): """ @@ -189,9 +189,9 @@ def cell_to_parent(h, res=None): ------- H3Cell """ - h = self._in_scalar(h) + h = _in_scalar(h) p = _cy.cell_to_parent(h, res) - p = self._out_scalar(p) + p = _out_scalar(p) return p @@ -215,8 +215,8 @@ def grid_distance(h1, h2): ------- int """ - h1 = self._in_scalar(h1) - h2 = self._in_scalar(h2) + h1 = _in_scalar(h1) + h2 = _in_scalar(h2) d = _cy.grid_distance(h1, h2) @@ -234,7 +234,7 @@ def cell_to_boundary(h): ------- tuple of (lat, lng) tuples """ - return _cy.cell_to_boundary(self._in_scalar(h)) + return _cy.cell_to_boundary(_in_scalar(h)) def grid_disk(h, k=1): """ @@ -251,9 +251,9 @@ def grid_disk(h, k=1): ------- unordered collection of H3Cell """ - mv = _cy.grid_disk(self._in_scalar(h), k) + mv = _cy.grid_disk(_in_scalar(h), k) - return self._out_unordered(mv) + return _out_unordered(mv) def grid_ring(h, k=1): """ @@ -270,9 +270,9 @@ def grid_ring(h, k=1): ------- unordered collection of H3Cell """ - mv = _cy.grid_ring(self._in_scalar(h), k) + mv = _cy.grid_ring(_in_scalar(h), k) - return self._out_unordered(mv) + return _out_unordered(mv) def cell_to_children(h, res=None): """ @@ -289,9 +289,9 @@ def cell_to_children(h, res=None): ------- unordered collection of H3Cell """ - mv = _cy.cell_to_children(self._in_scalar(h), res) + mv = _cy.cell_to_children(_in_scalar(h), res) - return self._out_unordered(mv) + return _out_unordered(mv) # todo: nogil for expensive C operation? def compact_cells(cells): @@ -309,10 +309,10 @@ def compact_cells(cells): unordered collection of H3Cell """ # todo: does compact_cells work on mixed-resolution collections? - hu = self._in_collection(cells) + hu = _in_collection(cells) hc = _cy.compact_cells(hu) - return self._out_unordered(hc) + return _out_unordered(hc) def uncompact_cells(cells, res): """ @@ -336,10 +336,10 @@ def uncompact_cells(cells, res): contains cell smaller than output res. https://github.com/uber/h3/blob/master/src/h3lib/lib/h3Index.c#L425 """ - hc = self._in_collection(cells) + hc = _in_collection(cells) hu = _cy.uncompact_cells(hc, res) - return self._out_unordered(hu) + return _out_unordered(hu) def h3shape_to_cells(h3shape, res): """ @@ -383,7 +383,7 @@ def h3shape_to_cells(h3shape, res): else: raise ValueError('Unrecognized type: ' + str(type(h3shape))) - return self._out_unordered(mv) + return _out_unordered(mv) def cells_to_h3shape(cells, tight=True): """ @@ -408,7 +408,7 @@ def cells_to_h3shape(cells, tight=True): >>> h3.cells_to_h3shape(cells, tight=False) """ - cells = self._in_collection(cells) + cells = _in_collection(cells) mpoly = _cy.cells_to_multi_polygon(cells) polys = [H3Poly(*poly) for poly in mpoly] @@ -430,7 +430,7 @@ def geo_to_cells(geo, res): Resolution of desired output cells. """ h3shape = geo_to_h3shape(geo) - return self.h3shape_to_cells(h3shape, res) + return h3shape_to_cells(h3shape, res) def cells_to_geo(cells, tight=True): """ @@ -443,7 +443,7 @@ def cells_to_geo(cells, tight=True): dict in `__geo_interface__` format """ - h3shape = self.cells_to_h3shape(cells, tight=tight) + h3shape = cells_to_h3shape(cells, tight=tight) return h3shape_to_geo(h3shape) def is_pentagon(h): @@ -464,7 +464,7 @@ def is_pentagon(h): A pentagon should *also* pass ``is_valid_cell()``. Will return ``False`` for valid H3Edge. """ - return _cy.is_pentagon(self._in_scalar(h)) + return _cy.is_pentagon(_in_scalar(h)) def get_base_cell_number(h): """ @@ -486,7 +486,7 @@ def get_base_cell_number(h): ------- int """ - return _cy.get_base_cell_number(self._in_scalar(h)) + return _cy.get_base_cell_number(_in_scalar(h)) def are_neighbor_cells(h1, h2): """ @@ -501,8 +501,8 @@ def are_neighbor_cells(h1, h2): ------- bool """ - h1 = self._in_scalar(h1) - h2 = self._in_scalar(h2) + h1 = _in_scalar(h1) + h2 = _in_scalar(h2) return _cy.are_neighbor_cells(h1, h2) @@ -527,10 +527,10 @@ def cells_to_directed_edge(origin, destination): ------- H3Edge """ - o = self._in_scalar(origin) - d = self._in_scalar(destination) + o = _in_scalar(origin) + d = _in_scalar(destination) e = _cy.cells_to_directed_edge(o, d) - e = self._out_scalar(e) + e = _out_scalar(e) return e @@ -546,9 +546,9 @@ def get_directed_edge_origin(e): ------- H3Cell """ - e = self._in_scalar(e) + e = _in_scalar(e) o = _cy.get_directed_edge_origin(e) - o = self._out_scalar(o) + o = _out_scalar(o) return o @@ -564,9 +564,9 @@ def get_directed_edge_destination(e): ------- H3Cell """ - e = self._in_scalar(e) + e = _in_scalar(e) d = _cy.get_directed_edge_destination(e) - d = self._out_scalar(d) + d = _out_scalar(d) return d @@ -585,9 +585,9 @@ def directed_edge_to_cells(e): H3Cell Destination cell of edge """ - e = self._in_scalar(e) + e = _in_scalar(e) o, d = _cy.directed_edge_to_cells(e) - o, d = self._out_scalar(o), self._out_scalar(d) + o, d = _out_scalar(o), _out_scalar(d) return o, d @@ -603,12 +603,12 @@ def origin_to_directed_edges(origin): ------- unordered collection of H3Edge """ - mv = _cy.origin_to_directed_edges(self._in_scalar(origin)) + mv = _cy.origin_to_directed_edges(_in_scalar(origin)) - return self._out_unordered(mv) + return _out_unordered(mv) def directed_edge_to_boundary(edge): - return _cy.directed_edge_to_boundary(self._in_scalar(edge)) + return _cy.directed_edge_to_boundary(_in_scalar(edge)) def grid_path_cells(start, end): """ @@ -625,9 +625,9 @@ def grid_path_cells(start, end): ordered collection of H3Cell Starting with ``start``, and ending with ``end``. """ - mv = _cy.grid_path_cells(self._in_scalar(start), self._in_scalar(end)) + mv = _cy.grid_path_cells(_in_scalar(start), _in_scalar(end)) - return self._out_ordered(mv) + return _out_ordered(mv) def is_res_class_III(h): """ @@ -656,7 +656,7 @@ def is_res_class_III(h): ---------- 1. https://uber.github.io/h3/#/documentation/core-library/coordinate-systems """ - return _cy.is_res_class_iii(self._in_scalar(h)) + return _cy.is_res_class_iii(_in_scalar(h)) def get_pentagons(res): """ @@ -673,9 +673,9 @@ def get_pentagons(res): """ mv = _cy.get_pentagons(res) - return self._out_unordered(mv) + return _out_unordered(mv) -def get_res0_cells(self): +def get_res0_cells(): """ Return all cells at resolution 0. @@ -689,7 +689,7 @@ def get_res0_cells(self): """ mv = _cy.get_res0_cells() - return self._out_unordered(mv) + return _out_unordered(mv) def cell_to_center_child(h, res=None): """ @@ -706,9 +706,9 @@ def cell_to_center_child(h, res=None): ------- H3Cell """ - h = self._in_scalar(h) + h = _in_scalar(h) p = _cy.cell_to_center_child(h, res) - p = self._out_scalar(p) + p = _out_scalar(p) return p @@ -728,7 +728,7 @@ def get_icosahedron_faces(h): ------- Python ``set`` of ``int`` """ - h = self._in_scalar(h) + h = _in_scalar(h) faces = _cy.get_icosahedron_faces(h) return faces @@ -764,8 +764,8 @@ def cell_to_local_ij(origin, h): This is done so we don't need to keep recomputing the coordinates of ``origin`` if not needed. """ - origin = self._in_scalar(origin) - h = self._in_scalar(h) + origin = _in_scalar(origin) + h = _in_scalar(h) i, j = _cy.cell_to_local_ij(origin, h) @@ -800,10 +800,10 @@ def local_ij_to_cell(origin, i, j): This is done so we don't need to keep recomputing the coordinates of ``origin`` if not needed. """ - origin = self._in_scalar(origin) + origin = _in_scalar(origin) h = _cy.local_ij_to_cell(origin, i, j) - h = self._out_scalar(h) + h = _out_scalar(h) return h @@ -830,7 +830,7 @@ def cell_area(h, unit='km^2'): The function uses the spherical distance calculation given by `great_circle_distance`. """ - h = self._in_scalar(h) + h = _in_scalar(h) return _cy.cell_area(h, unit=unit) @@ -855,7 +855,7 @@ def edge_length(e, unit='km'): This function uses the spherical distance calculation given by `great_circle_distance`. """ - e = self._in_scalar(e) + e = _in_scalar(e) return _cy.edge_length(e, unit=unit) From a74db069ac08ecaa18defe9eab8b786212b578c2 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Sat, 30 Dec 2023 16:16:01 -0800 Subject: [PATCH 06/15] remove staticmethod --- src/h3/api/basic_int/_api_template.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/h3/api/basic_int/_api_template.py b/src/h3/api/basic_int/_api_template.py index eedb9f97..051210eb 100644 --- a/src/h3/api/basic_int/_api_template.py +++ b/src/h3/api/basic_int/_api_template.py @@ -6,7 +6,6 @@ h3shape_to_geo, ) -@staticmethod def versions(): """ Version numbers for the Python (wrapper) and C (wrapped) libraries. @@ -28,7 +27,6 @@ def versions(): return v -@staticmethod def str_to_int(h): """ Converts a hexadecimal string to an H3 64-bit integer index. @@ -45,7 +43,6 @@ def str_to_int(h): """ return _cy.str_to_int(h) -@staticmethod def int_to_str(x): """ Converts an H3 64-bit integer index to a hexadecimal string. @@ -62,7 +59,6 @@ def int_to_str(x): """ return _cy.int_to_str(x) -@staticmethod def get_num_cells(res): """ Return the total number of *cells* (hexagons and pentagons) @@ -74,7 +70,6 @@ def get_num_cells(res): """ return _cy.get_num_cells(res) -@staticmethod def average_hexagon_area(res, unit='km^2'): """ Return the average area of an H3 *hexagon* @@ -88,7 +83,6 @@ def average_hexagon_area(res, unit='km^2'): """ return _cy.average_hexagon_area(res, unit) -@staticmethod def average_hexagon_edge_length(res, unit='km'): """ Return the average *hexagon* edge length @@ -859,7 +853,6 @@ def edge_length(e, unit='km'): return _cy.edge_length(e, unit=unit) -@staticmethod def great_circle_distance(latlng1, latlng2, unit='km'): """ Compute the spherical distance between two (lat, lng) points. From d579fb2ed4e1452967c2823686fa87189c62f7d1 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Mon, 1 Jan 2024 10:30:21 -0800 Subject: [PATCH 07/15] set up symlinks --- .coveragerc | 5 +++ src/h3/api/basic_int/__init__.py | 47 +---------------------- src/h3/api/basic_int/_api_funcs.py | 17 +++++++++ src/h3/api/basic_int/_api_template.py | 16 ++++++-- src/h3/api/basic_str/__init__.py | 54 +-------------------------- src/h3/api/basic_str/_api_funcs.py | 23 ++++++++++++ src/h3/api/memview_int/__init__.py | 42 +-------------------- src/h3/api/memview_int/_api_funcs.py | 8 ++++ src/h3/api/numpy_int/__init__.py | 54 +-------------------------- src/h3/api/numpy_int/_api_funcs.py | 17 +++++++++ tests/test_api_bindings_match.py | 8 ++-- 11 files changed, 91 insertions(+), 200 deletions(-) create mode 100644 .coveragerc create mode 100644 src/h3/api/basic_int/_api_funcs.py create mode 100644 src/h3/api/basic_str/_api_funcs.py create mode 100644 src/h3/api/memview_int/_api_funcs.py create mode 100644 src/h3/api/numpy_int/_api_funcs.py diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..40f2f518 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,5 @@ +[run] +omit = + */h3/api/basic_int/_api_template.py + */h3/api/memview_int/_api_template.py + */h3/api/numpy_int/_api_template.py diff --git a/src/h3/api/basic_int/__init__.py b/src/h3/api/basic_int/__init__.py index 7eb1b36e..c57b7a30 100644 --- a/src/h3/api/basic_int/__init__.py +++ b/src/h3/api/basic_int/__init__.py @@ -1,48 +1,3 @@ # flake8: noqa -from ._public_api import * -from ..._h3shape import ( - H3MultiPoly, - H3Poly, - H3Shape, - geo_to_h3shape, - h3shape_to_geo, -) +from ._api_template import * - - -""" -This API handles H3 Indexes of type `int`, using -basic Python collections (`set`, `list`, `tuple`). -`h3` will interpret these Indexes as unsigned 64-bit integers. - -Input collections: - -- `Iterable[int]` - -Output collections: - -- `Set[int]` for unordered -- `List[int]` for ordered -""" - -from ... import _cy -from .._api_template import _API_FUNCTIONS - - -def _id(x): - return x - - -def _in_collection(cells): - it = list(cells) - - return _cy.iter_to_mv(it) - - -_binding = _API_FUNCTIONS( - _in_scalar=_id, - _out_scalar=_id, - _in_collection=_in_collection, - _out_unordered=set, # todo: should this be an (immutable) frozenset? - _out_ordered=list, # todo: should this be an (immutable) tuple? -) diff --git a/src/h3/api/basic_int/_api_funcs.py b/src/h3/api/basic_int/_api_funcs.py new file mode 100644 index 00000000..5eae0320 --- /dev/null +++ b/src/h3/api/basic_int/_api_funcs.py @@ -0,0 +1,17 @@ +from ... import _cy + +def _id(x): + return x + + +def _in_collection(cells): + it = list(cells) + + return _cy.iter_to_mv(it) + + +_in_scalar = _id +_out_scalar = _id +_in_collection = _in_collection +_out_unordered = set +_out_ordered = list diff --git a/src/h3/api/basic_int/_api_template.py b/src/h3/api/basic_int/_api_template.py index 051210eb..5ff817f4 100644 --- a/src/h3/api/basic_int/_api_template.py +++ b/src/h3/api/basic_int/_api_template.py @@ -1,11 +1,21 @@ -from .. import _cy -from .._h3shape import ( +from ... import _cy +from ..._h3shape import ( + H3Shape, H3Poly, H3MultiPoly, geo_to_h3shape, h3shape_to_geo, ) +from ._api_funcs import ( + _in_scalar, + _out_scalar, + _in_collection, + _out_unordered, + _out_ordered, +) + + def versions(): """ Version numbers for the Python (wrapper) and C (wrapped) libraries. @@ -18,7 +28,7 @@ def versions(): ------- dict like ``{'c': 'X.Y.Z', 'python': 'A.B.C'}`` """ - from .._version import __version__ + from ..._version import __version__ v = { 'c': _cy.c_version(), diff --git a/src/h3/api/basic_str/__init__.py b/src/h3/api/basic_str/__init__.py index a64b019a..3b65cd6a 100644 --- a/src/h3/api/basic_str/__init__.py +++ b/src/h3/api/basic_str/__init__.py @@ -1,54 +1,2 @@ # flake8: noqa -from ._public_api import * -from ..._h3shape import ( - H3MultiPoly, - H3Poly, - H3Shape, - geo_to_h3shape, - h3shape_to_geo, -) - - -""" -This API handles H3 Indexes of type `str`, using -basic Python collections (`set`, `list`, `tuple`). -`h3` will interpret these Indexes as hexadecimal -representations of unsigned 64-bit integers. - -Input collections: - -- `Iterable[str]` - -Output collections: - -- `Set[str]` for unordered -- `List[str]` for ordered -""" - -from ... import _cy -from .._api_template import _API_FUNCTIONS - - -def _in_collection(cells): - it = [_cy.str_to_int(h) for h in cells] - - return _cy.iter_to_mv(it) - - -def _out_unordered(mv): - # todo: should this be an (immutable) frozenset? - return set(_cy.int_to_str(h) for h in mv) - - -def _out_ordered(mv): - # todo: should this be an (immutable) tuple? - return list(_cy.int_to_str(h) for h in mv) - - -_binding = _API_FUNCTIONS( - _in_scalar = _cy.str_to_int, - _out_scalar = _cy.int_to_str, - _in_collection = _in_collection, - _out_unordered = _out_unordered, - _out_ordered = _out_ordered, -) +from ._api_template import * diff --git a/src/h3/api/basic_str/_api_funcs.py b/src/h3/api/basic_str/_api_funcs.py new file mode 100644 index 00000000..25d18146 --- /dev/null +++ b/src/h3/api/basic_str/_api_funcs.py @@ -0,0 +1,23 @@ +from ... import _cy + +def _in_collection(cells): + it = [_cy.str_to_int(h) for h in cells] + + return _cy.iter_to_mv(it) + + +def _out_unordered(mv): + # todo: should this be an (immutable) frozenset? + return set(_cy.int_to_str(h) for h in mv) + + +def _out_ordered(mv): + # todo: should this be an (immutable) tuple? + return list(_cy.int_to_str(h) for h in mv) + + +_in_scalar = _cy.str_to_int +_out_scalar = _cy.int_to_str +_in_collection = _in_collection +_out_unordered = _out_unordered +_out_ordered = _out_ordered diff --git a/src/h3/api/memview_int/__init__.py b/src/h3/api/memview_int/__init__.py index 90028fac..3b65cd6a 100644 --- a/src/h3/api/memview_int/__init__.py +++ b/src/h3/api/memview_int/__init__.py @@ -1,42 +1,2 @@ # flake8: noqa -from ._public_api import * -from ..._h3shape import ( - H3MultiPoly, - H3Poly, - H3Shape, - geo_to_h3shape, - h3shape_to_geo, -) - - -""" -This API handles H3 Indexes of type `int` (specifically, `uint64`), -using Python `memoryview` objects for collections. -`h3` will interpret these Indexes as unsigned 64-bit integers. - -Input collections: - -- `memoryview[uint64]`, i.e., anything that supports the buffer protocol - - `dtype` must be `uint64`. for example, `long` will raise an error - - `list` or `set` inputs will not be accepted - -Output collections: - -- `memoryview[uint64]` for unordered -- `memoryview[uint64]` for ordered -""" - -from .._api_template import _API_FUNCTIONS - - -def _id(x): - return x - - -_binding = _API_FUNCTIONS( - _in_scalar = _id, - _out_scalar = _id, - _in_collection = _id, - _out_unordered = _id, - _out_ordered = _id, -) +from ._api_template import * diff --git a/src/h3/api/memview_int/_api_funcs.py b/src/h3/api/memview_int/_api_funcs.py new file mode 100644 index 00000000..4361c415 --- /dev/null +++ b/src/h3/api/memview_int/_api_funcs.py @@ -0,0 +1,8 @@ +def _id(x): + return x + +_in_scalar = _id +_out_scalar = _id +_in_collection = _id +_out_unordered = _id +_out_ordered = _id diff --git a/src/h3/api/numpy_int/__init__.py b/src/h3/api/numpy_int/__init__.py index 225689dd..3b65cd6a 100644 --- a/src/h3/api/numpy_int/__init__.py +++ b/src/h3/api/numpy_int/__init__.py @@ -1,54 +1,2 @@ # flake8: noqa -from ._public_api import * -from ..._h3shape import ( - H3MultiPoly, - H3Poly, - H3Shape, - geo_to_h3shape, - h3shape_to_geo, -) - - -""" -This API handles H3 Indexes of type `int` (specifically, `uint64`), -using `numpy.array` objects for collections. -`h3` will interpret these Indexes as unsigned 64-bit integers. - -This API is **optional**, and will only work if the -user has `numpy` installed. - -Input collections: - -- `Iterable[int]` - - works for `lists`, but not `sets` - - will attempt to convert `int` to `uint64` - - no memory copy is made if input dtype is `uint64` - -Output collections: - -- `np.ndarray[np.uint64]` for unordered -- `np.ndarray[np.uint64]` for ordered -""" - -import numpy as np - -from .._api_template import _API_FUNCTIONS - - -def _id(x): - return x - - -def _in_collection(x): - # array is copied only if dtype does not match - # `list`s should work, but not `set`s of integers - return np.asarray(x, dtype='uint64') - - -_binding = _API_FUNCTIONS( - _in_scalar = _id, - _out_scalar = _id, - _in_collection = _in_collection, - _out_unordered = np.asarray, - _out_ordered = np.asarray, -) +from ._api_template import * diff --git a/src/h3/api/numpy_int/_api_funcs.py b/src/h3/api/numpy_int/_api_funcs.py new file mode 100644 index 00000000..81cdddd9 --- /dev/null +++ b/src/h3/api/numpy_int/_api_funcs.py @@ -0,0 +1,17 @@ +import numpy as np + +def _id(x): + return x + + +def _in_collection(x): + # array is copied only if dtype does not match + # `list`s should work, but not `set`s of integers + return np.asarray(x, dtype='uint64') + + +_in_scalar = _id +_out_scalar = _id +_in_collection = _in_collection +_out_unordered = np.asarray +_out_ordered = np.asarray diff --git a/tests/test_api_bindings_match.py b/tests/test_api_bindings_match.py index 9bcb60b7..73d7bbd0 100644 --- a/tests/test_api_bindings_match.py +++ b/tests/test_api_bindings_match.py @@ -6,10 +6,10 @@ def test_api_copy_match(): import h3.api.numpy_int apis = [ - h3.api.basic_int._public_api, - h3.api.basic_str._public_api, - h3.api.memview_int._public_api, - h3.api.numpy_int._public_api, + h3.api.basic_int._api_template, + h3.api.basic_str._api_template, + h3.api.memview_int._api_template, + h3.api.numpy_int._api_template, ] api_set = {inspect.getsource(api) for api in apis} From 8c96c6b79a4a6f79f3adc1cefbc60b14d5d5df6d Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Mon, 1 Jan 2024 10:38:47 -0800 Subject: [PATCH 08/15] some linting --- src/h3/api/basic_int/_api_template.py | 46 +++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/src/h3/api/basic_int/_api_template.py b/src/h3/api/basic_int/_api_template.py index 5ff817f4..f9a06232 100644 --- a/src/h3/api/basic_int/_api_template.py +++ b/src/h3/api/basic_int/_api_template.py @@ -37,6 +37,7 @@ def versions(): return v + def str_to_int(h): """ Converts a hexadecimal string to an H3 64-bit integer index. @@ -53,6 +54,7 @@ def str_to_int(h): """ return _cy.str_to_int(h) + def int_to_str(x): """ Converts an H3 64-bit integer index to a hexadecimal string. @@ -69,6 +71,7 @@ def int_to_str(x): """ return _cy.int_to_str(x) + def get_num_cells(res): """ Return the total number of *cells* (hexagons and pentagons) @@ -80,6 +83,7 @@ def get_num_cells(res): """ return _cy.get_num_cells(res) + def average_hexagon_area(res, unit='km^2'): """ Return the average area of an H3 *hexagon* @@ -93,6 +97,7 @@ def average_hexagon_area(res, unit='km^2'): """ return _cy.average_hexagon_area(res, unit) + def average_hexagon_edge_length(res, unit='km'): """ Return the average *hexagon* edge length @@ -106,6 +111,7 @@ def average_hexagon_edge_length(res, unit='km'): """ return _cy.average_hexagon_edge_length(res, unit) + def is_valid_cell(h): """ Validates an H3 cell (hexagon or pentagon). @@ -120,6 +126,7 @@ def is_valid_cell(h): except (ValueError, TypeError): return False + def is_valid_directed_edge(edge): """ Validates an H3 unidirectional edge. @@ -134,6 +141,7 @@ def is_valid_directed_edge(edge): except (ValueError, TypeError): return False + def latlng_to_cell(lat, lng, res): """ Return the cell containing the (lat, lng) point @@ -146,6 +154,7 @@ def latlng_to_cell(lat, lng, res): """ return _out_scalar(_cy.latlng_to_cell(lat, lng, res)) + def cell_to_latlng(h): """ Return the center point of an H3 cell as a lat/lng pair. @@ -163,6 +172,7 @@ def cell_to_latlng(h): """ return _cy.cell_to_latlng(_in_scalar(h)) + def get_resolution(h): """ Return the resolution of an H3 cell. @@ -178,6 +188,7 @@ def get_resolution(h): # todo: could also work for edges return _cy.get_resolution(_in_scalar(h)) + def cell_to_parent(h, res=None): """ Get the parent of a cell. @@ -199,6 +210,7 @@ def cell_to_parent(h, res=None): return p + def grid_distance(h1, h2): """ Compute the H3 distance between two cells. @@ -226,6 +238,7 @@ def grid_distance(h1, h2): return d + def cell_to_boundary(h): """ Return tuple of lat/lng pairs describing the cell boundary. @@ -240,10 +253,11 @@ def cell_to_boundary(h): """ return _cy.cell_to_boundary(_in_scalar(h)) + def grid_disk(h, k=1): """ Return unordered set of cells with H3 distance ``<= k`` from ``h``. - That is, the "filled-in" disk. + That is, the 'filled-in' disk. Parameters ---------- @@ -259,6 +273,7 @@ def grid_disk(h, k=1): return _out_unordered(mv) + def grid_ring(h, k=1): """ Return unordered set of cells with H3 distance ``== k`` from ``h``. @@ -278,6 +293,7 @@ def grid_ring(h, k=1): return _out_unordered(mv) + def cell_to_children(h, res=None): """ Children of a cell. @@ -297,6 +313,7 @@ def cell_to_children(h, res=None): return _out_unordered(mv) + # todo: nogil for expensive C operation? def compact_cells(cells): """ @@ -318,6 +335,7 @@ def compact_cells(cells): return _out_unordered(hc) + def uncompact_cells(cells, res): """ Reverse the `compact_cells` operation. @@ -345,6 +363,7 @@ def uncompact_cells(cells, res): return _out_unordered(hu) + def h3shape_to_cells(h3shape, res): """ Return the set of H3 cells at a given resolution whose center points @@ -389,6 +408,7 @@ def h3shape_to_cells(h3shape, res): return _out_unordered(mv) + def cells_to_h3shape(cells, tight=True): """ Return a H3MultiPoly describing the area covered by a set of H3 cells. @@ -423,8 +443,9 @@ def cells_to_h3shape(cells, tight=True): return out + def geo_to_cells(geo, res): - """ Convert from __geo_interface__ to cells. + """Convert from __geo_interface__ to cells. Parameters ---------- @@ -436,6 +457,7 @@ def geo_to_cells(geo, res): h3shape = geo_to_h3shape(geo) return h3shape_to_cells(h3shape, res) + def cells_to_geo(cells, tight=True): """ Parameters @@ -450,6 +472,7 @@ def cells_to_geo(cells, tight=True): h3shape = cells_to_h3shape(cells, tight=tight) return h3shape_to_geo(h3shape) + def is_pentagon(h): """ Identify if an H3 cell is a pentagon. @@ -470,6 +493,7 @@ def is_pentagon(h): """ return _cy.is_pentagon(_in_scalar(h)) + def get_base_cell_number(h): """ Return the base cell *number* (``0`` to ``121``) of the given cell. @@ -492,6 +516,7 @@ def get_base_cell_number(h): """ return _cy.get_base_cell_number(_in_scalar(h)) + def are_neighbor_cells(h1, h2): """ Returns ``True`` if ``h1`` and ``h2`` are neighboring cells. @@ -510,6 +535,7 @@ def are_neighbor_cells(h1, h2): return _cy.are_neighbor_cells(h1, h2) + def cells_to_directed_edge(origin, destination): """ Create an H3 Index denoting a unidirectional edge. @@ -538,6 +564,7 @@ def cells_to_directed_edge(origin, destination): return e + def get_directed_edge_origin(e): """ Origin cell from an H3 directed edge. @@ -556,6 +583,7 @@ def get_directed_edge_origin(e): return o + def get_directed_edge_destination(e): """ Destination cell from an H3 directed edge. @@ -574,6 +602,7 @@ def get_directed_edge_destination(e): return d + def directed_edge_to_cells(e): """ Return (origin, destination) tuple from H3 directed edge @@ -595,6 +624,7 @@ def directed_edge_to_cells(e): return o, d + def origin_to_directed_edges(origin): """ Return all directed edges starting from ``origin`` cell. @@ -611,9 +641,11 @@ def origin_to_directed_edges(origin): return _out_unordered(mv) + def directed_edge_to_boundary(edge): return _cy.directed_edge_to_boundary(_in_scalar(edge)) + def grid_path_cells(start, end): """ Returns the ordered collection of cells denoting a @@ -633,6 +665,7 @@ def grid_path_cells(start, end): return _out_ordered(mv) + def is_res_class_III(h): """ Determine if cell has orientation "Class II" or "Class III". @@ -662,6 +695,7 @@ def is_res_class_III(h): """ return _cy.is_res_class_iii(_in_scalar(h)) + def get_pentagons(res): """ Return all pentagons at a given resolution. @@ -679,6 +713,7 @@ def get_pentagons(res): return _out_unordered(mv) + def get_res0_cells(): """ Return all cells at resolution 0. @@ -695,6 +730,7 @@ def get_res0_cells(): return _out_unordered(mv) + def cell_to_center_child(h, res=None): """ Get the center child of a cell at some finer resolution. @@ -716,6 +752,7 @@ def cell_to_center_child(h, res=None): return p + def get_icosahedron_faces(h): """ Return icosahedron faces intersecting a given H3 cell. @@ -737,6 +774,7 @@ def get_icosahedron_faces(h): return faces + def cell_to_local_ij(origin, h): """ Return local (i,j) coordinates of cell ``h`` in relation to ``origin`` cell @@ -775,6 +813,7 @@ def cell_to_local_ij(origin, h): return i, j + def local_ij_to_cell(origin, i, j): """ Return cell at local (i,j) position relative to the ``origin`` cell. @@ -811,6 +850,7 @@ def local_ij_to_cell(origin, i, j): return h + def cell_area(h, unit='km^2'): """ Compute the spherical surface area of a specific H3 cell. @@ -838,6 +878,7 @@ def cell_area(h, unit='km^2'): return _cy.cell_area(h, unit=unit) + def edge_length(e, unit='km'): """ Compute the spherical length of a specific H3 edge. @@ -863,6 +904,7 @@ def edge_length(e, unit='km'): return _cy.edge_length(e, unit=unit) + def great_circle_distance(latlng1, latlng2, unit='km'): """ Compute the spherical distance between two (lat, lng) points. From f9f226eb0fbb531410264d3a83d2c69c30f38f81 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Mon, 1 Jan 2024 10:50:20 -0800 Subject: [PATCH 09/15] test for bad H3Shape --- src/h3/api/basic_int/_api_funcs.py | 1 + src/h3/api/basic_int/_api_template.py | 2 ++ src/h3/api/basic_str/_api_funcs.py | 1 + src/h3/api/memview_int/_api_funcs.py | 1 + src/h3/api/numpy_int/_api_funcs.py | 1 + tests/polyfill/test_polygon_class.py | 9 +++++++++ 6 files changed, 15 insertions(+) diff --git a/src/h3/api/basic_int/_api_funcs.py b/src/h3/api/basic_int/_api_funcs.py index 5eae0320..31325419 100644 --- a/src/h3/api/basic_int/_api_funcs.py +++ b/src/h3/api/basic_int/_api_funcs.py @@ -1,5 +1,6 @@ from ... import _cy + def _id(x): return x diff --git a/src/h3/api/basic_int/_api_template.py b/src/h3/api/basic_int/_api_template.py index f9a06232..0d0b6507 100644 --- a/src/h3/api/basic_int/_api_template.py +++ b/src/h3/api/basic_int/_api_template.py @@ -403,6 +403,8 @@ def h3shape_to_cells(h3shape, res): elif isinstance(h3shape, H3MultiPoly): mpoly = h3shape mv = _cy.polygons_to_cells(mpoly.polys, res) + elif isinstance(h3shape, H3Shape): + raise ValueError('Unrecognized H3Shape: ' + str(h3shape)) else: raise ValueError('Unrecognized type: ' + str(type(h3shape))) diff --git a/src/h3/api/basic_str/_api_funcs.py b/src/h3/api/basic_str/_api_funcs.py index 25d18146..ab1608f1 100644 --- a/src/h3/api/basic_str/_api_funcs.py +++ b/src/h3/api/basic_str/_api_funcs.py @@ -1,5 +1,6 @@ from ... import _cy + def _in_collection(cells): it = [_cy.str_to_int(h) for h in cells] diff --git a/src/h3/api/memview_int/_api_funcs.py b/src/h3/api/memview_int/_api_funcs.py index 4361c415..9145baf0 100644 --- a/src/h3/api/memview_int/_api_funcs.py +++ b/src/h3/api/memview_int/_api_funcs.py @@ -1,6 +1,7 @@ def _id(x): return x + _in_scalar = _id _out_scalar = _id _in_collection = _id diff --git a/src/h3/api/numpy_int/_api_funcs.py b/src/h3/api/numpy_int/_api_funcs.py index 81cdddd9..3f38d1eb 100644 --- a/src/h3/api/numpy_int/_api_funcs.py +++ b/src/h3/api/numpy_int/_api_funcs.py @@ -1,5 +1,6 @@ import numpy as np + def _id(x): return x diff --git a/tests/polyfill/test_polygon_class.py b/tests/polyfill/test_polygon_class.py index 721eb752..ec450f56 100644 --- a/tests/polyfill/test_polygon_class.py +++ b/tests/polyfill/test_polygon_class.py @@ -35,3 +35,12 @@ def test_h3poly_len(): with pytest.raises(NotImplementedError): len(poly) + + +def test_bad_subclass(): + class H3Shoop(h3.H3Shape): + def __geo_interface__(): + pass + + with pytest.raises(ValueError): + h3.h3shape_to_cells(H3Shoop(), res=9) From 2fc16caa224c428c7479df6099c66e36177c6ca6 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Mon, 1 Jan 2024 11:20:01 -0800 Subject: [PATCH 10/15] symlink note --- src/h3/api/basic_int/_api_template.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/h3/api/basic_int/_api_template.py b/src/h3/api/basic_int/_api_template.py index 0d0b6507..2e60eff7 100644 --- a/src/h3/api/basic_int/_api_template.py +++ b/src/h3/api/basic_int/_api_template.py @@ -1,3 +1,5 @@ +# This file is **symlinked** across the APIs to ensure they are exactly the same. + from ... import _cy from ..._h3shape import ( H3Shape, From d84496e53be377c635119f40a8aeff6cfea4c36d Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Mon, 1 Jan 2024 12:42:47 -0800 Subject: [PATCH 11/15] just put everything in __init__.py --- .coveragerc | 6 +- src/h3/api/basic_int/__init__.py | 940 +++++++++++++++++++++++- src/h3/api/basic_int/_api_template.py | 939 ----------------------- src/h3/api/basic_str/__init__.py | 3 +- src/h3/api/basic_str/_api_template.py | 1 - src/h3/api/memview_int/__init__.py | 3 +- src/h3/api/memview_int/_api_template.py | 1 - src/h3/api/numpy_int/__init__.py | 3 +- src/h3/api/numpy_int/_api_template.py | 1 - tests/test_api_bindings_match.py | 8 +- 10 files changed, 948 insertions(+), 957 deletions(-) delete mode 100644 src/h3/api/basic_int/_api_template.py mode change 100644 => 120000 src/h3/api/basic_str/__init__.py delete mode 120000 src/h3/api/basic_str/_api_template.py mode change 100644 => 120000 src/h3/api/memview_int/__init__.py delete mode 120000 src/h3/api/memview_int/_api_template.py mode change 100644 => 120000 src/h3/api/numpy_int/__init__.py delete mode 120000 src/h3/api/numpy_int/_api_template.py diff --git a/.coveragerc b/.coveragerc index 40f2f518..662935de 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,5 +1,5 @@ [run] omit = - */h3/api/basic_int/_api_template.py - */h3/api/memview_int/_api_template.py - */h3/api/numpy_int/_api_template.py + */h3/api/basic_int/__init__.py + */h3/api/memview_int/__init__.py + */h3/api/numpy_int/__init__.py diff --git a/src/h3/api/basic_int/__init__.py b/src/h3/api/basic_int/__init__.py index c57b7a30..2e60eff7 100644 --- a/src/h3/api/basic_int/__init__.py +++ b/src/h3/api/basic_int/__init__.py @@ -1,3 +1,939 @@ -# flake8: noqa -from ._api_template import * +# This file is **symlinked** across the APIs to ensure they are exactly the same. +from ... import _cy +from ..._h3shape import ( + H3Shape, + H3Poly, + H3MultiPoly, + geo_to_h3shape, + h3shape_to_geo, +) + +from ._api_funcs import ( + _in_scalar, + _out_scalar, + _in_collection, + _out_unordered, + _out_ordered, +) + + +def versions(): + """ + Version numbers for the Python (wrapper) and C (wrapped) libraries. + + Versions are output as strings of the form ``'X.Y.Z'``. + C and Python should match on ``X`` (major) and ``Y`` (minor), + but may differ on ``Z`` (patch). + + Returns + ------- + dict like ``{'c': 'X.Y.Z', 'python': 'A.B.C'}`` + """ + from ..._version import __version__ + + v = { + 'c': _cy.c_version(), + 'python': __version__, + } + + return v + + +def str_to_int(h): + """ + Converts a hexadecimal string to an H3 64-bit integer index. + + Parameters + ---------- + h : str + Hexadecimal string like ``'89754e64993ffff'`` + + Returns + ------- + int + Unsigned 64-bit integer + """ + return _cy.str_to_int(h) + + +def int_to_str(x): + """ + Converts an H3 64-bit integer index to a hexadecimal string. + + Parameters + ---------- + x : int + Unsigned 64-bit integer + + Returns + ------- + str + Hexadecimal string like ``'89754e64993ffff'`` + """ + return _cy.int_to_str(x) + + +def get_num_cells(res): + """ + Return the total number of *cells* (hexagons and pentagons) + for the given resolution. + + Returns + ------- + int + """ + return _cy.get_num_cells(res) + + +def average_hexagon_area(res, unit='km^2'): + """ + Return the average area of an H3 *hexagon* + for the given resolution. + + This average *excludes* pentagons. + + Returns + ------- + float + """ + return _cy.average_hexagon_area(res, unit) + + +def average_hexagon_edge_length(res, unit='km'): + """ + Return the average *hexagon* edge length + for the given resolution. + + This average *excludes* pentagons. + + Returns + ------- + float + """ + return _cy.average_hexagon_edge_length(res, unit) + + +def is_valid_cell(h): + """ + Validates an H3 cell (hexagon or pentagon). + + Returns + ------- + bool + """ + try: + h = _in_scalar(h) + return _cy.is_valid_cell(h) + except (ValueError, TypeError): + return False + + +def is_valid_directed_edge(edge): + """ + Validates an H3 unidirectional edge. + + Returns + ------- + bool + """ + try: + e = _in_scalar(edge) + return _cy.is_valid_directed_edge(e) + except (ValueError, TypeError): + return False + + +def latlng_to_cell(lat, lng, res): + """ + Return the cell containing the (lat, lng) point + for a given resolution. + + Returns + ------- + H3Cell + + """ + return _out_scalar(_cy.latlng_to_cell(lat, lng, res)) + + +def cell_to_latlng(h): + """ + Return the center point of an H3 cell as a lat/lng pair. + + Parameters + ---------- + h : H3Cell + + Returns + ------- + lat : float + Latitude + lng : float + Longitude + """ + return _cy.cell_to_latlng(_in_scalar(h)) + + +def get_resolution(h): + """ + Return the resolution of an H3 cell. + + Parameters + ---------- + h : H3Cell + + Returns + ------- + int + """ + # todo: could also work for edges + return _cy.get_resolution(_in_scalar(h)) + + +def cell_to_parent(h, res=None): + """ + Get the parent of a cell. + + Parameters + ---------- + h : H3Cell + res : int or None, optional + The resolution for the parent + If ``None``, then ``res = resolution(h) - 1`` + + Returns + ------- + H3Cell + """ + h = _in_scalar(h) + p = _cy.cell_to_parent(h, res) + p = _out_scalar(p) + + return p + + +def grid_distance(h1, h2): + """ + Compute the H3 distance between two cells. + + The H3 distance is defined as the length of the shortest + path between the cells in the graph formed by connecting + adjacent cells. + + This function will raise an exception if the + cells are too far apart to compute the distance. + + Parameters + ---------- + h1 : H3Cell + h2 : H3Cell + + Returns + ------- + int + """ + h1 = _in_scalar(h1) + h2 = _in_scalar(h2) + + d = _cy.grid_distance(h1, h2) + + return d + + +def cell_to_boundary(h): + """ + Return tuple of lat/lng pairs describing the cell boundary. + + Parameters + ---------- + h : H3Cell + + Returns + ------- + tuple of (lat, lng) tuples + """ + return _cy.cell_to_boundary(_in_scalar(h)) + + +def grid_disk(h, k=1): + """ + Return unordered set of cells with H3 distance ``<= k`` from ``h``. + That is, the 'filled-in' disk. + + Parameters + ---------- + h : H3Cell + k : int + Size of disk. + + Returns + ------- + unordered collection of H3Cell + """ + mv = _cy.grid_disk(_in_scalar(h), k) + + return _out_unordered(mv) + + +def grid_ring(h, k=1): + """ + Return unordered set of cells with H3 distance ``== k`` from ``h``. + That is, the "hollow" ring. + + Parameters + ---------- + h : H3Cell + k : int + Size of ring. + + Returns + ------- + unordered collection of H3Cell + """ + mv = _cy.grid_ring(_in_scalar(h), k) + + return _out_unordered(mv) + + +def cell_to_children(h, res=None): + """ + Children of a cell. + + Parameters + ---------- + h : H3Cell + res : int or None, optional + The resolution for the children. + If ``None``, then ``res = resolution(h) + 1`` + + Returns + ------- + unordered collection of H3Cell + """ + mv = _cy.cell_to_children(_in_scalar(h), res) + + return _out_unordered(mv) + + +# todo: nogil for expensive C operation? +def compact_cells(cells): + """ + Compact a collection of H3 cells by combining + smaller cells into larger cells, if all child cells + are present. Input cells must all share the same resolution. + + Parameters + ---------- + cells : iterable of H3 Cells + + Returns + ------- + unordered collection of H3Cell + """ + # todo: does compact_cells work on mixed-resolution collections? + hu = _in_collection(cells) + hc = _cy.compact_cells(hu) + + return _out_unordered(hc) + + +def uncompact_cells(cells, res): + """ + Reverse the `compact_cells` operation. + + Return a collection of H3 cells, all of resolution ``res``. + + Parameters + ---------- + cells : iterable of H3Cell + res : int + Resolution of desired output cells. + + Returns + ------- + unordered collection of H3Cell + + Raises + ------ + todo: add test to make sure an error is returned when input + contains cell smaller than output res. + https://github.com/uber/h3/blob/master/src/h3lib/lib/h3Index.c#L425 + """ + hc = _in_collection(cells) + hu = _cy.uncompact_cells(hc, res) + + return _out_unordered(hu) + + +def h3shape_to_cells(h3shape, res): + """ + Return the set of H3 cells at a given resolution whose center points + are contained within an `H3Poly` or `H3MultiPoly`. + + Parameters + ---------- + h3shape : H3shape + res : int + Resolution of the output cells + + Returns + ------- + unordered collection of H3Cell + + Examples + -------- + + >>> poly = H3Poly( + ... [(37.68, -122.54), (37.68, -122.34), (37.82, -122.34), + ... (37.82, -122.54)], + ... ) + >>> h3.h3shape_to_cells(poly, 6) + {'862830807ffffff', + '862830827ffffff', + '86283082fffffff', + '862830877ffffff', + '862830947ffffff', + '862830957ffffff', + '86283095fffffff'} + """ + + # todo: not sure if i want this dispatch logic here. maybe in the objects? + if isinstance(h3shape, H3Poly): + poly = h3shape + mv = _cy.polygon_to_cells(poly.outer, res, holes=poly.holes) + elif isinstance(h3shape, H3MultiPoly): + mpoly = h3shape + mv = _cy.polygons_to_cells(mpoly.polys, res) + elif isinstance(h3shape, H3Shape): + raise ValueError('Unrecognized H3Shape: ' + str(h3shape)) + else: + raise ValueError('Unrecognized type: ' + str(type(h3shape))) + + return _out_unordered(mv) + + +def cells_to_h3shape(cells, tight=True): + """ + Return a H3MultiPoly describing the area covered by a set of H3 cells. + + Parameters + ---------- + cells : iterable of H3 cells + tight : bool + If True, return H3Poly if possible. If False, always return H3MultiPoly + + Returns + ------- + H3Poly | H3MultiPoly + + Examples + -------- + + >>> cells = ['8428309ffffffff', '842830dffffffff'] + >>> h3.cells_to_h3shape(cells, tight=True) + + >>> h3.cells_to_h3shape(cells, tight=False) + + """ + cells = _in_collection(cells) + mpoly = _cy.cells_to_multi_polygon(cells) + + polys = [H3Poly(*poly) for poly in mpoly] + out = H3MultiPoly(*polys) + + if tight and len(out) == 1: + out = out[0] + + return out + + +def geo_to_cells(geo, res): + """Convert from __geo_interface__ to cells. + + Parameters + ---------- + geo : an object implementing `__geo_interface__` or a dictionary in that format. + Both H3Poly and H3MultiPoly implement the interface. + res : int + Resolution of desired output cells. + """ + h3shape = geo_to_h3shape(geo) + return h3shape_to_cells(h3shape, res) + + +def cells_to_geo(cells, tight=True): + """ + Parameters + ---------- + cells : iterable of H3 Cells + + Returns + ------- + dict + in `__geo_interface__` format + """ + h3shape = cells_to_h3shape(cells, tight=tight) + return h3shape_to_geo(h3shape) + + +def is_pentagon(h): + """ + Identify if an H3 cell is a pentagon. + + Parameters + ---------- + h : H3Index + + Returns + ------- + bool + ``True`` if input is a valid H3 cell which is a pentagon. + + Notes + ----- + A pentagon should *also* pass ``is_valid_cell()``. + Will return ``False`` for valid H3Edge. + """ + return _cy.is_pentagon(_in_scalar(h)) + + +def get_base_cell_number(h): + """ + Return the base cell *number* (``0`` to ``121``) of the given cell. + + The base cell *number* and the H3Index are two different representations + of the same cell: the parent cell of resolution ``0``. + + The base cell *number* is encoded within the corresponding + H3Index. + + todo: could work with edges + + Parameters + ---------- + h : H3Cell + + Returns + ------- + int + """ + return _cy.get_base_cell_number(_in_scalar(h)) + + +def are_neighbor_cells(h1, h2): + """ + Returns ``True`` if ``h1`` and ``h2`` are neighboring cells. + + Parameters + ---------- + h1 : H3Cell + h2 : H3Cell + + Returns + ------- + bool + """ + h1 = _in_scalar(h1) + h2 = _in_scalar(h2) + + return _cy.are_neighbor_cells(h1, h2) + + +def cells_to_directed_edge(origin, destination): + """ + Create an H3 Index denoting a unidirectional edge. + + The edge is constructed from neighboring cells ``origin`` and + ``destination``. + + Parameters + ---------- + origin : H3Cell + destination : H3Cell + + Raises + ------ + ValueError + When cells are not adjacent. + + Returns + ------- + H3Edge + """ + o = _in_scalar(origin) + d = _in_scalar(destination) + e = _cy.cells_to_directed_edge(o, d) + e = _out_scalar(e) + + return e + + +def get_directed_edge_origin(e): + """ + Origin cell from an H3 directed edge. + + Parameters + ---------- + e : H3Edge + + Returns + ------- + H3Cell + """ + e = _in_scalar(e) + o = _cy.get_directed_edge_origin(e) + o = _out_scalar(o) + + return o + + +def get_directed_edge_destination(e): + """ + Destination cell from an H3 directed edge. + + Parameters + ---------- + e : H3Edge + + Returns + ------- + H3Cell + """ + e = _in_scalar(e) + d = _cy.get_directed_edge_destination(e) + d = _out_scalar(d) + + return d + + +def directed_edge_to_cells(e): + """ + Return (origin, destination) tuple from H3 directed edge + + Parameters + ---------- + e : H3Edge + + Returns + ------- + H3Cell + Origin cell of edge + H3Cell + Destination cell of edge + """ + e = _in_scalar(e) + o, d = _cy.directed_edge_to_cells(e) + o, d = _out_scalar(o), _out_scalar(d) + + return o, d + + +def origin_to_directed_edges(origin): + """ + Return all directed edges starting from ``origin`` cell. + + Parameters + ---------- + origin : H3Cell + + Returns + ------- + unordered collection of H3Edge + """ + mv = _cy.origin_to_directed_edges(_in_scalar(origin)) + + return _out_unordered(mv) + + +def directed_edge_to_boundary(edge): + return _cy.directed_edge_to_boundary(_in_scalar(edge)) + + +def grid_path_cells(start, end): + """ + Returns the ordered collection of cells denoting a + minimum-length non-unique path between cells. + + Parameters + ---------- + start : H3Cell + end : H3Cell + + Returns + ------- + ordered collection of H3Cell + Starting with ``start``, and ending with ``end``. + """ + mv = _cy.grid_path_cells(_in_scalar(start), _in_scalar(end)) + + return _out_ordered(mv) + + +def is_res_class_III(h): + """ + Determine if cell has orientation "Class II" or "Class III". + + The orientation of pentagons/hexagons on the icosahedron can be one + of two types: "Class II" or "Class III". + + All cells within a resolution have the same type, and the type + alternates between resolutions. + + "Class II" cells have resolutions: 0,2,4,6,8,10,12,14 + "Class III" cells have resolutions: 1,3,5,7,9,11,13,15 + + Parameters + ---------- + h : H3Cell + + Returns + ------- + bool + ``True`` if ``h`` is "Class III". + ``False`` if ``h`` is "Class II". + + References + ---------- + 1. https://uber.github.io/h3/#/documentation/core-library/coordinate-systems + """ + return _cy.is_res_class_iii(_in_scalar(h)) + + +def get_pentagons(res): + """ + Return all pentagons at a given resolution. + + Parameters + ---------- + res : int + Resolution of the pentagons + + Returns + ------- + unordered collection of H3Cell + """ + mv = _cy.get_pentagons(res) + + return _out_unordered(mv) + + +def get_res0_cells(): + """ + Return all cells at resolution 0. + + Parameters + ---------- + None + + Returns + ------- + unordered collection of H3Cell + """ + mv = _cy.get_res0_cells() + + return _out_unordered(mv) + + +def cell_to_center_child(h, res=None): + """ + Get the center child of a cell at some finer resolution. + + Parameters + ---------- + h : H3Cell + res : int or None, optional + The resolution for the child cell + If ``None``, then ``res = resolution(h) + 1`` + + Returns + ------- + H3Cell + """ + h = _in_scalar(h) + p = _cy.cell_to_center_child(h, res) + p = _out_scalar(p) + + return p + + +def get_icosahedron_faces(h): + """ + Return icosahedron faces intersecting a given H3 cell. + + There are twenty possible faces, ranging from 0--19. + + Note: Every interface returns a Python ``set`` of ``int``. + + Parameters + ---------- + h : H3Cell + + Returns + ------- + Python ``set`` of ``int`` + """ + h = _in_scalar(h) + faces = _cy.get_icosahedron_faces(h) + + return faces + + +def cell_to_local_ij(origin, h): + """ + Return local (i,j) coordinates of cell ``h`` in relation to ``origin`` cell + + + Parameters + ---------- + origin : H3Cell + Origin/central cell for defining i,j coordinates. + h: H3Cell + Destination cell whose i,j coordinates we'd like, based off + of the origin cell. + + + Returns + ------- + Tuple (i, j) of integer local coordinates of cell ``h`` + + + Notes + ----- + + The ``origin`` cell does not define (0, 0) for the IJ coordinate space. + (0, 0) refers to the center of the base cell containing origin at the + resolution of `origin`. + Subtracting the IJ coordinates of ``origin`` from every cell would get + you the property of (0, 0) being the ``origin``. + + This is done so we don't need to keep recomputing the coordinates of + ``origin`` if not needed. + """ + origin = _in_scalar(origin) + h = _in_scalar(h) + + i, j = _cy.cell_to_local_ij(origin, h) + + return i, j + + +def local_ij_to_cell(origin, i, j): + """ + Return cell at local (i,j) position relative to the ``origin`` cell. + + Parameters + ---------- + origin : H3Cell + Origin/central cell for defining i,j coordinates. + i, j: int + Integer coordinates with respect to ``origin`` cell. + + + Returns + ------- + H3Cell at local (i,j) position relative to the ``origin`` cell + + + Notes + ----- + + The ``origin`` cell does not define (0, 0) for the IJ coordinate space. + (0, 0) refers to the center of the base cell containing origin at the + resolution of ``origin``. + Subtracting the IJ coordinates of ``origin`` from every cell would get + you the property of (0, 0) being the ``origin``. + + This is done so we don't need to keep recomputing the coordinates of + ``origin`` if not needed. + """ + origin = _in_scalar(origin) + + h = _cy.local_ij_to_cell(origin, i, j) + h = _out_scalar(h) + + return h + + +def cell_area(h, unit='km^2'): + """ + Compute the spherical surface area of a specific H3 cell. + + Parameters + ---------- + h : H3Cell + unit: str + Unit for area result (``'km^2'``, 'm^2', or 'rads^2') + + + Returns + ------- + The area of the H3 cell in the given units + + + Notes + ----- + This function breaks the cell into spherical triangles, and computes + their spherical area. + The function uses the spherical distance calculation given by + `great_circle_distance`. + """ + h = _in_scalar(h) + + return _cy.cell_area(h, unit=unit) + + +def edge_length(e, unit='km'): + """ + Compute the spherical length of a specific H3 edge. + + Parameters + ---------- + h : H3Cell + unit: str + Unit for length result ('km', 'm', or 'rads') + + + Returns + ------- + The length of the edge in the given units + + + Notes + ----- + This function uses the spherical distance calculation given by + `great_circle_distance`. + """ + e = _in_scalar(e) + + return _cy.edge_length(e, unit=unit) + + +def great_circle_distance(latlng1, latlng2, unit='km'): + """ + Compute the spherical distance between two (lat, lng) points. + AKA: great circle distance or "haversine" distance. + + todo: overload to allow two cell inputs? + + Parameters + ---------- + latlng1 : tuple + (lat, lng) tuple in degrees + latlng2 : tuple + (lat, lng) tuple in degrees + unit: str + Unit for distance result ('km', 'm', or 'rads') + + + Returns + ------- + The spherical distance between the points in the given units + """ + lat1, lng1 = latlng1 + lat2, lng2 = latlng2 + return _cy.great_circle_distance( + lat1, lng1, + lat2, lng2, + unit = unit + ) diff --git a/src/h3/api/basic_int/_api_template.py b/src/h3/api/basic_int/_api_template.py deleted file mode 100644 index 2e60eff7..00000000 --- a/src/h3/api/basic_int/_api_template.py +++ /dev/null @@ -1,939 +0,0 @@ -# This file is **symlinked** across the APIs to ensure they are exactly the same. - -from ... import _cy -from ..._h3shape import ( - H3Shape, - H3Poly, - H3MultiPoly, - geo_to_h3shape, - h3shape_to_geo, -) - -from ._api_funcs import ( - _in_scalar, - _out_scalar, - _in_collection, - _out_unordered, - _out_ordered, -) - - -def versions(): - """ - Version numbers for the Python (wrapper) and C (wrapped) libraries. - - Versions are output as strings of the form ``'X.Y.Z'``. - C and Python should match on ``X`` (major) and ``Y`` (minor), - but may differ on ``Z`` (patch). - - Returns - ------- - dict like ``{'c': 'X.Y.Z', 'python': 'A.B.C'}`` - """ - from ..._version import __version__ - - v = { - 'c': _cy.c_version(), - 'python': __version__, - } - - return v - - -def str_to_int(h): - """ - Converts a hexadecimal string to an H3 64-bit integer index. - - Parameters - ---------- - h : str - Hexadecimal string like ``'89754e64993ffff'`` - - Returns - ------- - int - Unsigned 64-bit integer - """ - return _cy.str_to_int(h) - - -def int_to_str(x): - """ - Converts an H3 64-bit integer index to a hexadecimal string. - - Parameters - ---------- - x : int - Unsigned 64-bit integer - - Returns - ------- - str - Hexadecimal string like ``'89754e64993ffff'`` - """ - return _cy.int_to_str(x) - - -def get_num_cells(res): - """ - Return the total number of *cells* (hexagons and pentagons) - for the given resolution. - - Returns - ------- - int - """ - return _cy.get_num_cells(res) - - -def average_hexagon_area(res, unit='km^2'): - """ - Return the average area of an H3 *hexagon* - for the given resolution. - - This average *excludes* pentagons. - - Returns - ------- - float - """ - return _cy.average_hexagon_area(res, unit) - - -def average_hexagon_edge_length(res, unit='km'): - """ - Return the average *hexagon* edge length - for the given resolution. - - This average *excludes* pentagons. - - Returns - ------- - float - """ - return _cy.average_hexagon_edge_length(res, unit) - - -def is_valid_cell(h): - """ - Validates an H3 cell (hexagon or pentagon). - - Returns - ------- - bool - """ - try: - h = _in_scalar(h) - return _cy.is_valid_cell(h) - except (ValueError, TypeError): - return False - - -def is_valid_directed_edge(edge): - """ - Validates an H3 unidirectional edge. - - Returns - ------- - bool - """ - try: - e = _in_scalar(edge) - return _cy.is_valid_directed_edge(e) - except (ValueError, TypeError): - return False - - -def latlng_to_cell(lat, lng, res): - """ - Return the cell containing the (lat, lng) point - for a given resolution. - - Returns - ------- - H3Cell - - """ - return _out_scalar(_cy.latlng_to_cell(lat, lng, res)) - - -def cell_to_latlng(h): - """ - Return the center point of an H3 cell as a lat/lng pair. - - Parameters - ---------- - h : H3Cell - - Returns - ------- - lat : float - Latitude - lng : float - Longitude - """ - return _cy.cell_to_latlng(_in_scalar(h)) - - -def get_resolution(h): - """ - Return the resolution of an H3 cell. - - Parameters - ---------- - h : H3Cell - - Returns - ------- - int - """ - # todo: could also work for edges - return _cy.get_resolution(_in_scalar(h)) - - -def cell_to_parent(h, res=None): - """ - Get the parent of a cell. - - Parameters - ---------- - h : H3Cell - res : int or None, optional - The resolution for the parent - If ``None``, then ``res = resolution(h) - 1`` - - Returns - ------- - H3Cell - """ - h = _in_scalar(h) - p = _cy.cell_to_parent(h, res) - p = _out_scalar(p) - - return p - - -def grid_distance(h1, h2): - """ - Compute the H3 distance between two cells. - - The H3 distance is defined as the length of the shortest - path between the cells in the graph formed by connecting - adjacent cells. - - This function will raise an exception if the - cells are too far apart to compute the distance. - - Parameters - ---------- - h1 : H3Cell - h2 : H3Cell - - Returns - ------- - int - """ - h1 = _in_scalar(h1) - h2 = _in_scalar(h2) - - d = _cy.grid_distance(h1, h2) - - return d - - -def cell_to_boundary(h): - """ - Return tuple of lat/lng pairs describing the cell boundary. - - Parameters - ---------- - h : H3Cell - - Returns - ------- - tuple of (lat, lng) tuples - """ - return _cy.cell_to_boundary(_in_scalar(h)) - - -def grid_disk(h, k=1): - """ - Return unordered set of cells with H3 distance ``<= k`` from ``h``. - That is, the 'filled-in' disk. - - Parameters - ---------- - h : H3Cell - k : int - Size of disk. - - Returns - ------- - unordered collection of H3Cell - """ - mv = _cy.grid_disk(_in_scalar(h), k) - - return _out_unordered(mv) - - -def grid_ring(h, k=1): - """ - Return unordered set of cells with H3 distance ``== k`` from ``h``. - That is, the "hollow" ring. - - Parameters - ---------- - h : H3Cell - k : int - Size of ring. - - Returns - ------- - unordered collection of H3Cell - """ - mv = _cy.grid_ring(_in_scalar(h), k) - - return _out_unordered(mv) - - -def cell_to_children(h, res=None): - """ - Children of a cell. - - Parameters - ---------- - h : H3Cell - res : int or None, optional - The resolution for the children. - If ``None``, then ``res = resolution(h) + 1`` - - Returns - ------- - unordered collection of H3Cell - """ - mv = _cy.cell_to_children(_in_scalar(h), res) - - return _out_unordered(mv) - - -# todo: nogil for expensive C operation? -def compact_cells(cells): - """ - Compact a collection of H3 cells by combining - smaller cells into larger cells, if all child cells - are present. Input cells must all share the same resolution. - - Parameters - ---------- - cells : iterable of H3 Cells - - Returns - ------- - unordered collection of H3Cell - """ - # todo: does compact_cells work on mixed-resolution collections? - hu = _in_collection(cells) - hc = _cy.compact_cells(hu) - - return _out_unordered(hc) - - -def uncompact_cells(cells, res): - """ - Reverse the `compact_cells` operation. - - Return a collection of H3 cells, all of resolution ``res``. - - Parameters - ---------- - cells : iterable of H3Cell - res : int - Resolution of desired output cells. - - Returns - ------- - unordered collection of H3Cell - - Raises - ------ - todo: add test to make sure an error is returned when input - contains cell smaller than output res. - https://github.com/uber/h3/blob/master/src/h3lib/lib/h3Index.c#L425 - """ - hc = _in_collection(cells) - hu = _cy.uncompact_cells(hc, res) - - return _out_unordered(hu) - - -def h3shape_to_cells(h3shape, res): - """ - Return the set of H3 cells at a given resolution whose center points - are contained within an `H3Poly` or `H3MultiPoly`. - - Parameters - ---------- - h3shape : H3shape - res : int - Resolution of the output cells - - Returns - ------- - unordered collection of H3Cell - - Examples - -------- - - >>> poly = H3Poly( - ... [(37.68, -122.54), (37.68, -122.34), (37.82, -122.34), - ... (37.82, -122.54)], - ... ) - >>> h3.h3shape_to_cells(poly, 6) - {'862830807ffffff', - '862830827ffffff', - '86283082fffffff', - '862830877ffffff', - '862830947ffffff', - '862830957ffffff', - '86283095fffffff'} - """ - - # todo: not sure if i want this dispatch logic here. maybe in the objects? - if isinstance(h3shape, H3Poly): - poly = h3shape - mv = _cy.polygon_to_cells(poly.outer, res, holes=poly.holes) - elif isinstance(h3shape, H3MultiPoly): - mpoly = h3shape - mv = _cy.polygons_to_cells(mpoly.polys, res) - elif isinstance(h3shape, H3Shape): - raise ValueError('Unrecognized H3Shape: ' + str(h3shape)) - else: - raise ValueError('Unrecognized type: ' + str(type(h3shape))) - - return _out_unordered(mv) - - -def cells_to_h3shape(cells, tight=True): - """ - Return a H3MultiPoly describing the area covered by a set of H3 cells. - - Parameters - ---------- - cells : iterable of H3 cells - tight : bool - If True, return H3Poly if possible. If False, always return H3MultiPoly - - Returns - ------- - H3Poly | H3MultiPoly - - Examples - -------- - - >>> cells = ['8428309ffffffff', '842830dffffffff'] - >>> h3.cells_to_h3shape(cells, tight=True) - - >>> h3.cells_to_h3shape(cells, tight=False) - - """ - cells = _in_collection(cells) - mpoly = _cy.cells_to_multi_polygon(cells) - - polys = [H3Poly(*poly) for poly in mpoly] - out = H3MultiPoly(*polys) - - if tight and len(out) == 1: - out = out[0] - - return out - - -def geo_to_cells(geo, res): - """Convert from __geo_interface__ to cells. - - Parameters - ---------- - geo : an object implementing `__geo_interface__` or a dictionary in that format. - Both H3Poly and H3MultiPoly implement the interface. - res : int - Resolution of desired output cells. - """ - h3shape = geo_to_h3shape(geo) - return h3shape_to_cells(h3shape, res) - - -def cells_to_geo(cells, tight=True): - """ - Parameters - ---------- - cells : iterable of H3 Cells - - Returns - ------- - dict - in `__geo_interface__` format - """ - h3shape = cells_to_h3shape(cells, tight=tight) - return h3shape_to_geo(h3shape) - - -def is_pentagon(h): - """ - Identify if an H3 cell is a pentagon. - - Parameters - ---------- - h : H3Index - - Returns - ------- - bool - ``True`` if input is a valid H3 cell which is a pentagon. - - Notes - ----- - A pentagon should *also* pass ``is_valid_cell()``. - Will return ``False`` for valid H3Edge. - """ - return _cy.is_pentagon(_in_scalar(h)) - - -def get_base_cell_number(h): - """ - Return the base cell *number* (``0`` to ``121``) of the given cell. - - The base cell *number* and the H3Index are two different representations - of the same cell: the parent cell of resolution ``0``. - - The base cell *number* is encoded within the corresponding - H3Index. - - todo: could work with edges - - Parameters - ---------- - h : H3Cell - - Returns - ------- - int - """ - return _cy.get_base_cell_number(_in_scalar(h)) - - -def are_neighbor_cells(h1, h2): - """ - Returns ``True`` if ``h1`` and ``h2`` are neighboring cells. - - Parameters - ---------- - h1 : H3Cell - h2 : H3Cell - - Returns - ------- - bool - """ - h1 = _in_scalar(h1) - h2 = _in_scalar(h2) - - return _cy.are_neighbor_cells(h1, h2) - - -def cells_to_directed_edge(origin, destination): - """ - Create an H3 Index denoting a unidirectional edge. - - The edge is constructed from neighboring cells ``origin`` and - ``destination``. - - Parameters - ---------- - origin : H3Cell - destination : H3Cell - - Raises - ------ - ValueError - When cells are not adjacent. - - Returns - ------- - H3Edge - """ - o = _in_scalar(origin) - d = _in_scalar(destination) - e = _cy.cells_to_directed_edge(o, d) - e = _out_scalar(e) - - return e - - -def get_directed_edge_origin(e): - """ - Origin cell from an H3 directed edge. - - Parameters - ---------- - e : H3Edge - - Returns - ------- - H3Cell - """ - e = _in_scalar(e) - o = _cy.get_directed_edge_origin(e) - o = _out_scalar(o) - - return o - - -def get_directed_edge_destination(e): - """ - Destination cell from an H3 directed edge. - - Parameters - ---------- - e : H3Edge - - Returns - ------- - H3Cell - """ - e = _in_scalar(e) - d = _cy.get_directed_edge_destination(e) - d = _out_scalar(d) - - return d - - -def directed_edge_to_cells(e): - """ - Return (origin, destination) tuple from H3 directed edge - - Parameters - ---------- - e : H3Edge - - Returns - ------- - H3Cell - Origin cell of edge - H3Cell - Destination cell of edge - """ - e = _in_scalar(e) - o, d = _cy.directed_edge_to_cells(e) - o, d = _out_scalar(o), _out_scalar(d) - - return o, d - - -def origin_to_directed_edges(origin): - """ - Return all directed edges starting from ``origin`` cell. - - Parameters - ---------- - origin : H3Cell - - Returns - ------- - unordered collection of H3Edge - """ - mv = _cy.origin_to_directed_edges(_in_scalar(origin)) - - return _out_unordered(mv) - - -def directed_edge_to_boundary(edge): - return _cy.directed_edge_to_boundary(_in_scalar(edge)) - - -def grid_path_cells(start, end): - """ - Returns the ordered collection of cells denoting a - minimum-length non-unique path between cells. - - Parameters - ---------- - start : H3Cell - end : H3Cell - - Returns - ------- - ordered collection of H3Cell - Starting with ``start``, and ending with ``end``. - """ - mv = _cy.grid_path_cells(_in_scalar(start), _in_scalar(end)) - - return _out_ordered(mv) - - -def is_res_class_III(h): - """ - Determine if cell has orientation "Class II" or "Class III". - - The orientation of pentagons/hexagons on the icosahedron can be one - of two types: "Class II" or "Class III". - - All cells within a resolution have the same type, and the type - alternates between resolutions. - - "Class II" cells have resolutions: 0,2,4,6,8,10,12,14 - "Class III" cells have resolutions: 1,3,5,7,9,11,13,15 - - Parameters - ---------- - h : H3Cell - - Returns - ------- - bool - ``True`` if ``h`` is "Class III". - ``False`` if ``h`` is "Class II". - - References - ---------- - 1. https://uber.github.io/h3/#/documentation/core-library/coordinate-systems - """ - return _cy.is_res_class_iii(_in_scalar(h)) - - -def get_pentagons(res): - """ - Return all pentagons at a given resolution. - - Parameters - ---------- - res : int - Resolution of the pentagons - - Returns - ------- - unordered collection of H3Cell - """ - mv = _cy.get_pentagons(res) - - return _out_unordered(mv) - - -def get_res0_cells(): - """ - Return all cells at resolution 0. - - Parameters - ---------- - None - - Returns - ------- - unordered collection of H3Cell - """ - mv = _cy.get_res0_cells() - - return _out_unordered(mv) - - -def cell_to_center_child(h, res=None): - """ - Get the center child of a cell at some finer resolution. - - Parameters - ---------- - h : H3Cell - res : int or None, optional - The resolution for the child cell - If ``None``, then ``res = resolution(h) + 1`` - - Returns - ------- - H3Cell - """ - h = _in_scalar(h) - p = _cy.cell_to_center_child(h, res) - p = _out_scalar(p) - - return p - - -def get_icosahedron_faces(h): - """ - Return icosahedron faces intersecting a given H3 cell. - - There are twenty possible faces, ranging from 0--19. - - Note: Every interface returns a Python ``set`` of ``int``. - - Parameters - ---------- - h : H3Cell - - Returns - ------- - Python ``set`` of ``int`` - """ - h = _in_scalar(h) - faces = _cy.get_icosahedron_faces(h) - - return faces - - -def cell_to_local_ij(origin, h): - """ - Return local (i,j) coordinates of cell ``h`` in relation to ``origin`` cell - - - Parameters - ---------- - origin : H3Cell - Origin/central cell for defining i,j coordinates. - h: H3Cell - Destination cell whose i,j coordinates we'd like, based off - of the origin cell. - - - Returns - ------- - Tuple (i, j) of integer local coordinates of cell ``h`` - - - Notes - ----- - - The ``origin`` cell does not define (0, 0) for the IJ coordinate space. - (0, 0) refers to the center of the base cell containing origin at the - resolution of `origin`. - Subtracting the IJ coordinates of ``origin`` from every cell would get - you the property of (0, 0) being the ``origin``. - - This is done so we don't need to keep recomputing the coordinates of - ``origin`` if not needed. - """ - origin = _in_scalar(origin) - h = _in_scalar(h) - - i, j = _cy.cell_to_local_ij(origin, h) - - return i, j - - -def local_ij_to_cell(origin, i, j): - """ - Return cell at local (i,j) position relative to the ``origin`` cell. - - Parameters - ---------- - origin : H3Cell - Origin/central cell for defining i,j coordinates. - i, j: int - Integer coordinates with respect to ``origin`` cell. - - - Returns - ------- - H3Cell at local (i,j) position relative to the ``origin`` cell - - - Notes - ----- - - The ``origin`` cell does not define (0, 0) for the IJ coordinate space. - (0, 0) refers to the center of the base cell containing origin at the - resolution of ``origin``. - Subtracting the IJ coordinates of ``origin`` from every cell would get - you the property of (0, 0) being the ``origin``. - - This is done so we don't need to keep recomputing the coordinates of - ``origin`` if not needed. - """ - origin = _in_scalar(origin) - - h = _cy.local_ij_to_cell(origin, i, j) - h = _out_scalar(h) - - return h - - -def cell_area(h, unit='km^2'): - """ - Compute the spherical surface area of a specific H3 cell. - - Parameters - ---------- - h : H3Cell - unit: str - Unit for area result (``'km^2'``, 'm^2', or 'rads^2') - - - Returns - ------- - The area of the H3 cell in the given units - - - Notes - ----- - This function breaks the cell into spherical triangles, and computes - their spherical area. - The function uses the spherical distance calculation given by - `great_circle_distance`. - """ - h = _in_scalar(h) - - return _cy.cell_area(h, unit=unit) - - -def edge_length(e, unit='km'): - """ - Compute the spherical length of a specific H3 edge. - - Parameters - ---------- - h : H3Cell - unit: str - Unit for length result ('km', 'm', or 'rads') - - - Returns - ------- - The length of the edge in the given units - - - Notes - ----- - This function uses the spherical distance calculation given by - `great_circle_distance`. - """ - e = _in_scalar(e) - - return _cy.edge_length(e, unit=unit) - - -def great_circle_distance(latlng1, latlng2, unit='km'): - """ - Compute the spherical distance between two (lat, lng) points. - AKA: great circle distance or "haversine" distance. - - todo: overload to allow two cell inputs? - - Parameters - ---------- - latlng1 : tuple - (lat, lng) tuple in degrees - latlng2 : tuple - (lat, lng) tuple in degrees - unit: str - Unit for distance result ('km', 'm', or 'rads') - - - Returns - ------- - The spherical distance between the points in the given units - """ - lat1, lng1 = latlng1 - lat2, lng2 = latlng2 - return _cy.great_circle_distance( - lat1, lng1, - lat2, lng2, - unit = unit - ) diff --git a/src/h3/api/basic_str/__init__.py b/src/h3/api/basic_str/__init__.py deleted file mode 100644 index 3b65cd6a..00000000 --- a/src/h3/api/basic_str/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# flake8: noqa -from ._api_template import * diff --git a/src/h3/api/basic_str/__init__.py b/src/h3/api/basic_str/__init__.py new file mode 120000 index 00000000..92056a20 --- /dev/null +++ b/src/h3/api/basic_str/__init__.py @@ -0,0 +1 @@ +../basic_int/__init__.py \ No newline at end of file diff --git a/src/h3/api/basic_str/_api_template.py b/src/h3/api/basic_str/_api_template.py deleted file mode 120000 index 5fefb9d0..00000000 --- a/src/h3/api/basic_str/_api_template.py +++ /dev/null @@ -1 +0,0 @@ -../basic_int/_api_template.py \ No newline at end of file diff --git a/src/h3/api/memview_int/__init__.py b/src/h3/api/memview_int/__init__.py deleted file mode 100644 index 3b65cd6a..00000000 --- a/src/h3/api/memview_int/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# flake8: noqa -from ._api_template import * diff --git a/src/h3/api/memview_int/__init__.py b/src/h3/api/memview_int/__init__.py new file mode 120000 index 00000000..92056a20 --- /dev/null +++ b/src/h3/api/memview_int/__init__.py @@ -0,0 +1 @@ +../basic_int/__init__.py \ No newline at end of file diff --git a/src/h3/api/memview_int/_api_template.py b/src/h3/api/memview_int/_api_template.py deleted file mode 120000 index 5fefb9d0..00000000 --- a/src/h3/api/memview_int/_api_template.py +++ /dev/null @@ -1 +0,0 @@ -../basic_int/_api_template.py \ No newline at end of file diff --git a/src/h3/api/numpy_int/__init__.py b/src/h3/api/numpy_int/__init__.py deleted file mode 100644 index 3b65cd6a..00000000 --- a/src/h3/api/numpy_int/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# flake8: noqa -from ._api_template import * diff --git a/src/h3/api/numpy_int/__init__.py b/src/h3/api/numpy_int/__init__.py new file mode 120000 index 00000000..92056a20 --- /dev/null +++ b/src/h3/api/numpy_int/__init__.py @@ -0,0 +1 @@ +../basic_int/__init__.py \ No newline at end of file diff --git a/src/h3/api/numpy_int/_api_template.py b/src/h3/api/numpy_int/_api_template.py deleted file mode 120000 index 5fefb9d0..00000000 --- a/src/h3/api/numpy_int/_api_template.py +++ /dev/null @@ -1 +0,0 @@ -../basic_int/_api_template.py \ No newline at end of file diff --git a/tests/test_api_bindings_match.py b/tests/test_api_bindings_match.py index 73d7bbd0..1d771e18 100644 --- a/tests/test_api_bindings_match.py +++ b/tests/test_api_bindings_match.py @@ -6,10 +6,10 @@ def test_api_copy_match(): import h3.api.numpy_int apis = [ - h3.api.basic_int._api_template, - h3.api.basic_str._api_template, - h3.api.memview_int._api_template, - h3.api.numpy_int._api_template, + h3.api.basic_int, + h3.api.basic_str, + h3.api.memview_int, + h3.api.numpy_int, ] api_set = {inspect.getsource(api) for api in apis} From 9ecdd6e2f8bfc6409f3df0413bd1b842fd89e224 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Mon, 1 Jan 2024 13:30:44 -0800 Subject: [PATCH 12/15] reorganize conversion functions --- src/h3/api/basic_int/_api_funcs.py | 9 +++++---- src/h3/api/basic_str/_api_funcs.py | 10 +++------- src/h3/api/numpy_int/_api_funcs.py | 9 +++++---- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/h3/api/basic_int/_api_funcs.py b/src/h3/api/basic_int/_api_funcs.py index 31325419..f43ed22a 100644 --- a/src/h3/api/basic_int/_api_funcs.py +++ b/src/h3/api/basic_int/_api_funcs.py @@ -1,7 +1,11 @@ from ... import _cy -def _id(x): +def _in_scalar(x): + return x + + +def _out_scalar(x): return x @@ -11,8 +15,5 @@ def _in_collection(cells): return _cy.iter_to_mv(it) -_in_scalar = _id -_out_scalar = _id -_in_collection = _in_collection _out_unordered = set _out_ordered = list diff --git a/src/h3/api/basic_str/_api_funcs.py b/src/h3/api/basic_str/_api_funcs.py index ab1608f1..bad3a955 100644 --- a/src/h3/api/basic_str/_api_funcs.py +++ b/src/h3/api/basic_str/_api_funcs.py @@ -1,5 +1,8 @@ from ... import _cy +_in_scalar = _cy.str_to_int +_out_scalar = _cy.int_to_str + def _in_collection(cells): it = [_cy.str_to_int(h) for h in cells] @@ -15,10 +18,3 @@ def _out_unordered(mv): def _out_ordered(mv): # todo: should this be an (immutable) tuple? return list(_cy.int_to_str(h) for h in mv) - - -_in_scalar = _cy.str_to_int -_out_scalar = _cy.int_to_str -_in_collection = _in_collection -_out_unordered = _out_unordered -_out_ordered = _out_ordered diff --git a/src/h3/api/numpy_int/_api_funcs.py b/src/h3/api/numpy_int/_api_funcs.py index 3f38d1eb..728c952d 100644 --- a/src/h3/api/numpy_int/_api_funcs.py +++ b/src/h3/api/numpy_int/_api_funcs.py @@ -1,7 +1,11 @@ import numpy as np -def _id(x): +def _in_scalar(x): + return x + + +def _out_scalar(x): return x @@ -11,8 +15,5 @@ def _in_collection(x): return np.asarray(x, dtype='uint64') -_in_scalar = _id -_out_scalar = _id -_in_collection = _in_collection _out_unordered = np.asarray _out_ordered = np.asarray From 9cbfc93341ef6c36bb9afa428b8ba36e9d7d68d1 Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Mon, 1 Jan 2024 13:33:12 -0800 Subject: [PATCH 13/15] rename _api_funcs.py to _convert.py --- src/h3/api/basic_int/__init__.py | 2 +- src/h3/api/basic_int/{_api_funcs.py => _convert.py} | 0 src/h3/api/basic_str/{_api_funcs.py => _convert.py} | 0 src/h3/api/memview_int/{_api_funcs.py => _convert.py} | 0 src/h3/api/numpy_int/{_api_funcs.py => _convert.py} | 0 5 files changed, 1 insertion(+), 1 deletion(-) rename src/h3/api/basic_int/{_api_funcs.py => _convert.py} (100%) rename src/h3/api/basic_str/{_api_funcs.py => _convert.py} (100%) rename src/h3/api/memview_int/{_api_funcs.py => _convert.py} (100%) rename src/h3/api/numpy_int/{_api_funcs.py => _convert.py} (100%) diff --git a/src/h3/api/basic_int/__init__.py b/src/h3/api/basic_int/__init__.py index 2e60eff7..a9858cb8 100644 --- a/src/h3/api/basic_int/__init__.py +++ b/src/h3/api/basic_int/__init__.py @@ -9,7 +9,7 @@ h3shape_to_geo, ) -from ._api_funcs import ( +from ._convert import ( _in_scalar, _out_scalar, _in_collection, diff --git a/src/h3/api/basic_int/_api_funcs.py b/src/h3/api/basic_int/_convert.py similarity index 100% rename from src/h3/api/basic_int/_api_funcs.py rename to src/h3/api/basic_int/_convert.py diff --git a/src/h3/api/basic_str/_api_funcs.py b/src/h3/api/basic_str/_convert.py similarity index 100% rename from src/h3/api/basic_str/_api_funcs.py rename to src/h3/api/basic_str/_convert.py diff --git a/src/h3/api/memview_int/_api_funcs.py b/src/h3/api/memview_int/_convert.py similarity index 100% rename from src/h3/api/memview_int/_api_funcs.py rename to src/h3/api/memview_int/_convert.py diff --git a/src/h3/api/numpy_int/_api_funcs.py b/src/h3/api/numpy_int/_convert.py similarity index 100% rename from src/h3/api/numpy_int/_api_funcs.py rename to src/h3/api/numpy_int/_convert.py From 9429d56d0ab70d93d5172b077aed3fdecd060c2f Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Mon, 1 Jan 2024 13:54:55 -0800 Subject: [PATCH 14/15] use lazy import of numpy so we don't have to special case h3.api.numpy_int --- src/h3/api/__init__.py | 1 + src/h3/api/numpy_int/_convert.py | 12 +++++++----- tests/test_api_bindings_match.py | 1 - 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/h3/api/__init__.py b/src/h3/api/__init__.py index 9bc858a2..435b13a1 100644 --- a/src/h3/api/__init__.py +++ b/src/h3/api/__init__.py @@ -3,3 +3,4 @@ from . import basic_int from . import basic_str from . import memview_int +from . import numpy_int diff --git a/src/h3/api/numpy_int/_convert.py b/src/h3/api/numpy_int/_convert.py index 728c952d..d5063e4f 100644 --- a/src/h3/api/numpy_int/_convert.py +++ b/src/h3/api/numpy_int/_convert.py @@ -1,6 +1,3 @@ -import numpy as np - - def _in_scalar(x): return x @@ -10,10 +7,15 @@ def _out_scalar(x): def _in_collection(x): + import numpy as np # array is copied only if dtype does not match # `list`s should work, but not `set`s of integers return np.asarray(x, dtype='uint64') -_out_unordered = np.asarray -_out_ordered = np.asarray +def _out_unordered(x): + import numpy as np + return np.asarray(x) + + +_out_ordered = _out_unordered diff --git a/tests/test_api_bindings_match.py b/tests/test_api_bindings_match.py index 1d771e18..ccc2a9c1 100644 --- a/tests/test_api_bindings_match.py +++ b/tests/test_api_bindings_match.py @@ -3,7 +3,6 @@ def test_api_copy_match(): import h3 - import h3.api.numpy_int apis = [ h3.api.basic_int, From f841381b4e6de35a1b5dc46c4c9a57910f198c2d Mon Sep 17 00:00:00 2001 From: AJ Friend Date: Mon, 1 Jan 2024 14:01:16 -0800 Subject: [PATCH 15/15] DRY --- src/h3/api/basic_int/_convert.py | 3 +-- src/h3/api/numpy_int/_convert.py | 11 +++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/h3/api/basic_int/_convert.py b/src/h3/api/basic_int/_convert.py index f43ed22a..48cfcb94 100644 --- a/src/h3/api/basic_int/_convert.py +++ b/src/h3/api/basic_int/_convert.py @@ -5,8 +5,7 @@ def _in_scalar(x): return x -def _out_scalar(x): - return x +_out_scalar = _in_scalar def _in_collection(cells): diff --git a/src/h3/api/numpy_int/_convert.py b/src/h3/api/numpy_int/_convert.py index d5063e4f..2db5b14d 100644 --- a/src/h3/api/numpy_int/_convert.py +++ b/src/h3/api/numpy_int/_convert.py @@ -2,8 +2,7 @@ def _in_scalar(x): return x -def _out_scalar(x): - return x +_out_scalar = _in_scalar def _in_collection(x): @@ -13,9 +12,5 @@ def _in_collection(x): return np.asarray(x, dtype='uint64') -def _out_unordered(x): - import numpy as np - return np.asarray(x) - - -_out_ordered = _out_unordered +_out_unordered = _in_collection +_out_ordered = _in_collection