vector2dggs 0.10.0__py3-none-any.whl → 0.11.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
vector2dggs/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__: str = "0.10.0"
1
+ __version__: str = "0.11.0"
vector2dggs/common.py CHANGED
@@ -18,7 +18,7 @@ from pathlib import Path, PurePath
18
18
  from urllib.parse import urlparse
19
19
  from tqdm import tqdm
20
20
  from tqdm.dask import TqdmCallback
21
- from multiprocessing.dummy import Pool
21
+ from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor, as_completed
22
22
  from shapely.geometry import GeometryCollection
23
23
 
24
24
  import vector2dggs.constants as const
@@ -313,6 +313,47 @@ def polyfill(
313
313
  def polyfill_star(args) -> None:
314
314
  return polyfill(*args)
315
315
 
316
+ def bisection_preparation(df: pd.DataFrame, dggs: str, parent_res: int, cut_crs: pyproj.CRS = None, cut_threshold: Union[None, float] = None) -> tuple[pd.DataFrame, pyproj.CRS, Union[None, float]]:
317
+ cut_threshold = float(cut_threshold) if cut_threshold != None else None
318
+
319
+ if cut_threshold and cut_crs:
320
+ df = df.to_crs(cut_crs)
321
+ else:
322
+ cut_crs = df.crs
323
+
324
+ if cut_crs is None:
325
+ LOGGER.warning("Input has no defined CRS, and cut_crs is not specified")
326
+ elif cut_threshold != 0:
327
+ LOGGER.debug("Cutting with CRS: %s", df.crs)
328
+
329
+ if not cut_crs.is_projected and cut_threshold != 0:
330
+ LOGGER.warning(
331
+ f"CRS {cut_crs} is not a projected coordinate system. (units: {cut_crs.axis_info[0].unit_name}) Bisection will result in sections of varying area"
332
+ )
333
+ elif cut_threshold != 0:
334
+ LOGGER.debug(
335
+ f"Using CRS units for input polygon bisection: {cut_crs.axis_info[0].unit_name}"
336
+ )
337
+
338
+ if cut_threshold == None:
339
+ unit_name = cut_crs.axis_info[0].unit_name
340
+ cut_threshold_m2 = const.DEFAULT_AREA_THRESHOLD_M2(dggs, (int(parent_res)))
341
+ if unit_name == "metre":
342
+ cut_threshold = cut_threshold_m2
343
+ elif unit_name == "feet":
344
+ cut_threshold = cut_threshold_m2 * 3.28084
345
+ else:
346
+ cut_threshold = 100000000 if cut_crs.is_projected else 0.5
347
+ LOGGER.warning(
348
+ f'Unspecified cut_threshold for {"projected" if cut_crs.is_projected else "geographic"} CRS: {cut_crs}, with squared units: {unit_name}'
349
+ )
350
+ LOGGER.debug(f"Using default cut_threshold of {cut_threshold} ({unit_name}^2)")
351
+
352
+ return df, cut_crs, cut_threshold
353
+
354
+ def bisect_geometry(geometry, cut_threshold):
355
+ return GeometryCollection(katana.katana(geometry, cut_threshold))
356
+
316
357
 
317
358
  def index(
318
359
  dggs: str,
@@ -326,7 +367,7 @@ def index(
326
367
  keep_attributes: bool,
327
368
  chunksize: int,
328
369
  spatial_sorting: str,
329
- cut_threshold: int,
370
+ cut_threshold: Union[None, float],
330
371
  processes: int,
331
372
  compression: str = "snappy",
332
373
  id_field: str = None,
@@ -356,9 +397,7 @@ def index(
356
397
  # Read file
357
398
  df = gpd.read_file(input_file, layer=layer)
358
399
 
359
- if cut_crs:
360
- df = df.to_crs(cut_crs)
361
- LOGGER.debug("Cutting with CRS: %s", df.crs)
400
+ df, cut_crs, cut_threshold = bisection_preparation(df, dggs, parent_res, cut_crs, cut_threshold)
362
401
 
363
402
  if id_field:
364
403
  df = df.set_index(id_field)
@@ -370,13 +409,21 @@ def index(
370
409
  # Remove all attributes except the geometry
371
410
  df = df.loc[:, ["geometry"]]
372
411
 
373
- LOGGER.debug("Cutting large geometries")
374
- with tqdm(total=df.shape[0], desc="Splitting") as pbar:
375
- for index, row in df.iterrows():
376
- df.loc[index, "geometry"] = GeometryCollection(
377
- katana.katana(row.geometry, cut_threshold)
378
- )
379
- pbar.update(1)
412
+ LOGGER.debug("Bisecting large geometries")
413
+
414
+ if cut_threshold is not None and cut_threshold > 0:
415
+ with ThreadPoolExecutor(max_workers=max(1, processes)) as executor:
416
+ futures = []
417
+ for index, row in df.iterrows():
418
+ future = executor.submit(bisect_geometry, row.geometry, cut_threshold)
419
+ futures.append((index, future))
420
+
421
+ with tqdm(total=len(futures), desc="Bisection") as pbar:
422
+ for index, future in futures:
423
+ df.at[index, "geometry"] = future.result()
424
+ pbar.update(1)
425
+ else:
426
+ LOGGER.debug("No bisection applied to input.")
380
427
 
381
428
  LOGGER.debug("Exploding geometry collections and multipolygons")
382
429
  df = (
@@ -427,28 +474,33 @@ def index(
427
474
  resolution,
428
475
  )
429
476
  with tempfile.TemporaryDirectory(suffix=".parquet") as tmpdir2:
430
- with Pool(processes=processes) as pool:
431
- args = [
432
- (
433
- dggs,
434
- dggsfunc,
435
- secondary_index_func,
436
- filepath,
437
- spatial_sort_col,
438
- resolution,
439
- parent_res,
440
- tmpdir2,
441
- compression,
442
- )
443
- for filepath in filepaths
444
- ]
445
- list(
446
- tqdm(
447
- pool.imap(polyfill_star, args),
448
- total=len(args),
449
- desc="DGGS indexing",
450
- )
477
+
478
+ args = [
479
+ (
480
+ dggs,
481
+ dggsfunc,
482
+ secondary_index_func,
483
+ filepath,
484
+ spatial_sort_col,
485
+ resolution,
486
+ parent_res,
487
+ tmpdir2,
488
+ compression,
451
489
  )
490
+ for filepath in filepaths
491
+ ]
492
+
493
+ with ProcessPoolExecutor(max_workers=processes) as executor:
494
+ futures = {executor.submit(polyfill_star, arg): arg for arg in args}
495
+
496
+ for future in tqdm(
497
+ as_completed(futures), total=len(futures), desc="DGGS indexing"
498
+ ):
499
+ try:
500
+ future.result()
501
+ except Exception as e:
502
+ LOGGER.error(f"Task failed with {e}")
503
+ raise (e)
452
504
 
453
505
  parent_partitioning(
454
506
  dggs,
vector2dggs/constants.py CHANGED
@@ -8,20 +8,6 @@ MIN_RHP, MAX_RHP = 0, 15
8
8
  MIN_S2, MAX_S2 = 0, 30
9
9
  MIN_GEOHASH, MAX_GEOHASH = 1, 12
10
10
 
11
- DEFAULTS = {
12
- "id": None,
13
- "k": False,
14
- "ch": 50,
15
- "s": "none",
16
- "crs": None,
17
- "c": 5000,
18
- "t": (multiprocessing.cpu_count() - 1),
19
- "cp": "snappy",
20
- "lyr": None,
21
- "g": "geom",
22
- "tempdir": tempfile.tempdir,
23
- }
24
-
25
11
  SPATIAL_SORTING_METHODS = ["hilbert", "morton", "geohash", "none"]
26
12
 
27
13
  DEFAULT_DGGS_PARENT_RES = {
@@ -70,6 +56,86 @@ S2_CELLS_MAX_AREA_M2_BY_LEVEL = {
70
56
  30: 0.93 * 1e-4,
71
57
  }
72
58
 
59
+ # https://h3geo.org/docs/core-library/restable/
60
+ H3_CELLS_MAX_AREA_KM2_BY_LEVEL = {
61
+ 0: 4977807.027442012,
62
+ 1: 729486.875275344,
63
+ 2: 104599.807218925,
64
+ 3: 14950.773301379,
65
+ 4: 2135.986983965,
66
+ 5: 305.144308779,
67
+ 6: 43.592111685,
68
+ 7: 6.227445905,
69
+ 8: 0.889635157,
70
+ 9: 0.127090737,
71
+ 10: 0.018155820,
72
+ 11: 0.002593689,
73
+ 12: 0.000370527,
74
+ 13: 0.000052932,
75
+ 14: 0.000007562,
76
+ 15: 0.000001080,
77
+ }
78
+
79
+ RHP_CELLS_AREA_KM2_BY_LEVEL = {
80
+ 0: 100151150.62856922,
81
+ 1: 11127905.62539658,
82
+ 2: 1236433.9583773974,
83
+ 3: 137381.55093082195,
84
+ 4: 15264.616770091328,
85
+ 5: 1696.0685300101482,
86
+ 6: 188.4520588900164,
87
+ 7: 20.939117654446267,
88
+ 8: 2.326568628271808,
89
+ 9: 0.25850762536353417,
90
+ 10: 0.02872306948483713,
91
+ 11: 0.003191452164981904,
92
+ 12: 0.0003546057961091004,
93
+ 13: 3.940064401212227 * 1e-5,
94
+ 14: 4.377849334680252 * 1e-6,
95
+ 15: 4.864277038533613 * 1e-7,
96
+ }
97
+
98
+ # https://www.movable-type.co.uk/scripts/geohash.html
99
+ GEOHASH_MAX_CELL_AREA_KM2_BY_LEVEL = {
100
+ 1: 5000 * 5000,
101
+ 2: 1250 * 625,
102
+ 3: 156 * 156,
103
+ 4: 39.1 * 19.5,
104
+ 5: 4.89 * 4.89,
105
+ 6: 1.22 * 0.61,
106
+ 7: 153 * 153 / 1e6,
107
+ 8: 38.2 * 19.1 / 1e6,
108
+ 9: 4.77 * 4.77 / 1e6,
109
+ 10: 1.19 * 0.596 / 1e6,
110
+ 11: 149 * 149 / 1e9,
111
+ 12: 37.2 * 18.6 / 1e9,
112
+ }
113
+
114
+ DGGS_CELL_AREA_M2_BY_RES = {
115
+ "s2": lambda res: S2_CELLS_MAX_AREA_M2_BY_LEVEL[res],
116
+ "h3": lambda res: H3_CELLS_MAX_AREA_KM2_BY_LEVEL[res] * 1e6,
117
+ "rhp": lambda res: RHP_CELLS_AREA_KM2_BY_LEVEL[res] * 1e6,
118
+ "geohash": lambda res: GEOHASH_MAX_CELL_AREA_KM2_BY_LEVEL[res] * 1e6,
119
+ }
120
+
121
+ DEFAULT_AREA_THRESHOLD_M2 = lambda dggs, parent_res: DGGS_CELL_AREA_M2_BY_RES[dggs](
122
+ parent_res
123
+ )
124
+
125
+ DEFAULTS = {
126
+ "id": None,
127
+ "k": False,
128
+ "ch": 50,
129
+ "s": "none",
130
+ "crs": None,
131
+ "c": None,
132
+ "t": (multiprocessing.cpu_count() - 1),
133
+ "cp": "snappy",
134
+ "lyr": None,
135
+ "g": "geom",
136
+ "tempdir": tempfile.tempdir,
137
+ }
138
+
73
139
 
74
140
  warnings.filterwarnings(
75
141
  "ignore"
vector2dggs/geohash.py CHANGED
@@ -217,10 +217,10 @@ def gh_compaction(
217
217
  @click.option(
218
218
  "-c",
219
219
  "--cut_threshold",
220
- required=True,
220
+ required=False,
221
221
  default=const.DEFAULTS["c"],
222
- type=int,
223
- help="Cutting up large geometries into smaller geometries based on a target length. Units are assumed to match the input CRS units unless the `--cut_crs` is also given, in which case units match the units of the supplied CRS.",
222
+ type=float,
223
+ help="Cutting up large geometries into smaller geometries based on a target area. Units are assumed to match the input CRS units unless the `--cut_crs` is also given, in which case units match the units of the supplied CRS. If left unspecified, the threshold will be the maximum area of a cell at the parent resolution, in square metres or feet according to the CRS. A threshold of 0 will skip bissection entirely (effectively ignoring --cut_crs).",
224
224
  nargs=1,
225
225
  )
226
226
  @click.option(
@@ -333,5 +333,3 @@ def geohash(
333
333
  )
334
334
  except:
335
335
  raise
336
- else:
337
- sys.exit(0)
vector2dggs/h3.py CHANGED
@@ -136,10 +136,10 @@ def h3compaction(
136
136
  @click.option(
137
137
  "-c",
138
138
  "--cut_threshold",
139
- required=True,
139
+ required=False,
140
140
  default=const.DEFAULTS["c"],
141
- type=int,
142
- help="Cutting up large geometries into smaller geometries based on a target length. Units are assumed to match the input CRS units unless the `--cut_crs` is also given, in which case units match the units of the supplied CRS.",
141
+ type=float,
142
+ help="Cutting up large geometries into smaller geometries based on a target area. Units are assumed to match the input CRS units unless the `--cut_crs` is also given, in which case units match the units of the supplied CRS. If left unspecified, the threshold will be the maximum area of a cell at the parent resolution, in square metres or feet according to the CRS. A threshold of 0 will skip bissection entirely (effectively ignoring --cut_crs).",
143
143
  nargs=1,
144
144
  )
145
145
  @click.option(
@@ -253,5 +253,3 @@ def h3(
253
253
  )
254
254
  except:
255
255
  raise
256
- else:
257
- sys.exit(0)
vector2dggs/katana.py CHANGED
@@ -30,14 +30,17 @@ def katana(
30
30
  geometry: Union[BaseGeometry, None],
31
31
  threshold: float,
32
32
  count: int = 0,
33
+ max_recursion_depth: int = 250,
33
34
  check_2D: bool = True,
34
35
  ) -> List[BaseGeometry]:
35
36
  """
36
37
  Recursively split a geometry into two parts across its shortest dimension.
37
38
  Invalid input `geometry` will silently be made valid (if possible).
38
39
  Any LinearRings will be converted to Polygons.
40
+ `threshold`: maximum acceptable area of the bounding box for any output geometry.
41
+ `count`: used to track recursion depth
39
42
  """
40
- if geometry is None:
43
+ if (geometry is None) or (geometry.is_empty):
41
44
  return []
42
45
  if isinstance(geometry, LinearRing):
43
46
  geometry = Polygon(geometry)
@@ -52,9 +55,7 @@ def katana(
52
55
  bounds = geometry.bounds
53
56
  width = bounds[2] - bounds[0]
54
57
  height = bounds[3] - bounds[1]
55
- if max(width, height) <= threshold or count == 250:
56
- # either the polygon is smaller than the threshold, or the maximum
57
- # number of recursions has been reached
58
+ if ((width * height) <= threshold) or (count >= max_recursion_depth):
58
59
  return [geometry]
59
60
  if height >= width:
60
61
  # split left to right
@@ -64,6 +65,8 @@ def katana(
64
65
  # split top to bottom
65
66
  a = box(bounds[0], bounds[1], bounds[0] + width / 2, bounds[3])
66
67
  b = box(bounds[0] + width / 2, bounds[1], bounds[2], bounds[3])
68
+ # Add additional vertices to help prevent indexing errors from use of EPSG:4386 later under the presence of long edges
69
+ a, b = map(lambda g: g.segmentize(min(width, height) / 4), [a, b])
67
70
  result = []
68
71
  for d in (
69
72
  a,
vector2dggs/rHP.py CHANGED
@@ -12,13 +12,9 @@ import geopandas as gpd
12
12
  from typing import Union
13
13
  from pathlib import Path
14
14
  from rhealpixdggs.conversion import compress_order_cells
15
+ from rhealpixdggs.rhp_wrappers import rhp_to_center_child
15
16
  from rhppandas.util.const import COLUMNS
16
17
 
17
- # from rhealpixdggs.rhp_wrappers import rhp_to_center_child, rhp_is_valid
18
- from rhealpixdggs.rhp_wrappers import rhp_is_valid
19
- from rhealpixdggs.dggs import RHEALPixDGGS
20
- from rhealpixdggs.dggs import WGS84_003
21
-
22
18
  import vector2dggs.constants as const
23
19
  import vector2dggs.common as common
24
20
 
@@ -57,54 +53,6 @@ def rhppolyfill(df: gpd.GeoDataFrame, resolution: int) -> pd.DataFrame:
57
53
  )
58
54
 
59
55
 
60
- # TODO replace when merged https://github.com/manaakiwhenua/rhealpixdggs-py/pull/37
61
- def rhp_to_center_child(
62
- rhpindex: str, res: int = None, dggs: RHEALPixDGGS = WGS84_003
63
- ) -> str:
64
- """
65
- Returns central child of rhpindex at resolution res (immediate central
66
- child if res == None).
67
-
68
- Returns None if the cell index is invalid.
69
-
70
- Returns None if the DGGS has an even number of cells on a side.
71
-
72
- EXAMPLES::
73
-
74
- >>> rhp_to_center_child('S001450634')
75
- 'S0014506344'
76
- >>> rhp_to_center_child('S001450634', res=13)
77
- 'S001450634444'
78
- >>> rhp_to_center_child('INVALID')
79
- """
80
- # Stop early if the cell index is invalid
81
- if not rhp_is_valid(rhpindex, dggs):
82
- return None
83
-
84
- # DGGSs with even numbers of cells on a side never have a cell at the centre
85
- if (dggs.N_side % 2) == 0:
86
- return None
87
-
88
- # Handle mismatch between cell resolution and requested child resolution
89
- parent_res = len(rhpindex) - 1
90
- if res is not None and res < parent_res:
91
- return rhpindex
92
-
93
- # Standard case (including parent_res == res)
94
- else:
95
- # res == None returns the central child from one level down (by convention)
96
- added_levels = 1 if res is None else res - parent_res
97
-
98
- # Derive index of centre child and append that to rhpindex
99
- # NOTE: only works for odd values of N_side
100
- c_index = int((dggs.N_side**2 - 1) / 2)
101
-
102
- # Append the required number of child digits to cell index
103
- child_index = rhpindex + "".join(str(c_index) for _ in range(0, added_levels))
104
-
105
- return child_index
106
-
107
-
108
56
  def compact_cells(cells: set[str]) -> set[str]:
109
57
  """
110
58
  Compact a set of rHEALPix DGGS cells.
@@ -205,17 +153,17 @@ def rhpcompaction(
205
153
  @click.option(
206
154
  "-c",
207
155
  "--cut_threshold",
208
- required=True,
156
+ required=False,
209
157
  default=const.DEFAULTS["c"],
210
- type=int,
211
- help="Cutting up large geometries into smaller geometries based on a target length. Units are assumed to match the input CRS units unless the `--cut_crs` is also given, in which case units match the units of the supplied CRS.",
158
+ type=float,
159
+ help="Cutting up large geometries into smaller geometries based on a target area. Units are assumed to match the input CRS units unless the `--cut_crs` is also given, in which case units match the units of the supplied CRS. If left unspecified, the threshold will be the maximum area of a cell at the parent resolution, in square metres or feet according to the CRS. A threshold of 0 will skip bissection entirely (effectively ignoring --cut_crs).",
212
160
  nargs=1,
213
161
  )
214
162
  @click.option(
215
163
  "-t",
216
164
  "--threads",
217
165
  required=False,
218
- default=const.DEFAULTS["t"],
166
+ default=1,
219
167
  type=int,
220
168
  help="Amount of threads used for operation",
221
169
  nargs=1,
@@ -322,5 +270,3 @@ def rhp(
322
270
  )
323
271
  except:
324
272
  raise
325
- else:
326
- sys.exit(0)
vector2dggs/s2.py CHANGED
@@ -9,6 +9,7 @@ from s2geometry import pywraps2 as S2
9
9
 
10
10
  import pandas as pd
11
11
  import geopandas as gpd
12
+ from shapely import force_2d
12
13
  from shapely.geometry import box, Polygon, LineString, Point
13
14
  from shapely.ops import transform
14
15
  from pyproj import CRS, Transformer
@@ -71,6 +72,7 @@ def s2_polyfill_polygons(df: gpd.GeoDataFrame, level: int) -> gpd.GeoDataFrame:
71
72
  def generate_s2_covering(
72
73
  geom: Polygon, level: int, centroid_inside: bool = True
73
74
  ) -> set[S2.S2CellId]:
75
+ geom = force_2d(geom)
74
76
  # Prepare loops: first the exterior loop, then the interior loops
75
77
  loops = []
76
78
  # Exterior ring
@@ -296,10 +298,10 @@ def s2_compaction(
296
298
  @click.option(
297
299
  "-c",
298
300
  "--cut_threshold",
299
- required=True,
301
+ required=False,
300
302
  default=const.DEFAULTS["c"],
301
- type=int,
302
- help="Cutting up large geometries into smaller geometries based on a target length. Units are assumed to match the input CRS units unless the `--cut_crs` is also given, in which case units match the units of the supplied CRS.",
303
+ type=float,
304
+ help="Cutting up large geometries into smaller geometries based on a target area. Units are assumed to match the input CRS units unless the `--cut_crs` is also given, in which case units match the units of the supplied CRS. If left unspecified, the threshold will be the maximum area of a cell at the parent resolution, in square metres or feet according to the CRS. A threshold of 0 will skip bissection entirely (effectively ignoring --cut_crs).",
303
305
  nargs=1,
304
306
  )
305
307
  @click.option(
@@ -412,5 +414,3 @@ def s2(
412
414
  )
413
415
  except:
414
416
  raise
415
- else:
416
- sys.exit(0)
@@ -1,7 +1,8 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.1
2
2
  Name: vector2dggs
3
- Version: 0.10.0
3
+ Version: 0.11.0
4
4
  Summary: CLI DGGS indexer for vector geospatial data
5
+ Home-page: https://github.com/manaakiwhenua/vector2dggs
5
6
  License: LGPL-3.0-or-later
6
7
  Keywords: dggs,vector,h3,rHEALPix,cli
7
8
  Author: James Ardo
@@ -12,8 +13,6 @@ Requires-Python: >=3.11,<4.0
12
13
  Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
13
14
  Classifier: Programming Language :: Python :: 3
14
15
  Classifier: Programming Language :: Python :: 3.11
15
- Classifier: Programming Language :: Python :: 3.12
16
- Classifier: Programming Language :: Python :: 3.13
17
16
  Classifier: Topic :: Scientific/Engineering
18
17
  Classifier: Topic :: Scientific/Engineering :: GIS
19
18
  Classifier: Topic :: Scientific/Engineering :: Information Analysis
@@ -30,6 +29,7 @@ Requires-Dist: psycopg2 (>=2.9.9,<3.0.0)
30
29
  Requires-Dist: pyarrow (>=20.0,<21.0)
31
30
  Requires-Dist: pyproj (>=3.7,<4.0)
32
31
  Requires-Dist: python-geohash (>=0.8.5,<0.9.0)
32
+ Requires-Dist: rhealpixdggs (>=0.5.12,<0.6.0)
33
33
  Requires-Dist: rhppandas (>=0.2.0,<0.3.0)
34
34
  Requires-Dist: rusty-polygon-geohasher (>=0.2.3,<0.3.0)
35
35
  Requires-Dist: s2geometry (>=0.9.0,<0.10.0)
@@ -121,12 +121,17 @@ Options:
121
121
  used for cutting large geometries (see
122
122
  `--cut_threshold`). Defaults to the same CRS
123
123
  as the input. Should be a valid EPSG code.
124
- -c, --cut_threshold INTEGER Cutting up large geometries into smaller
125
- geometries based on a target length. Units
126
- are assumed to match the input CRS units
127
- unless the `--cut_crs` is also given, in
128
- which case units match the units of the
129
- supplied CRS. [default: 5000; required]
124
+ -c, --cut_threshold FLOAT Cutting up large geometries into smaller
125
+ geometries based on a target area. Units are
126
+ assumed to match the input CRS units unless
127
+ the `--cut_crs` is also given, in which case
128
+ units match the units of the supplied CRS.
129
+ If left unspecified, the threshold will be
130
+ the maximum area of a cell at the parent
131
+ resolution, in square metres or feet
132
+ according to the CRS. A threshold of 0 will
133
+ skip bissection entirely (effectively
134
+ ignoring --cut_crs).
130
135
  -t, --threads INTEGER Amount of threads used for operation
131
136
  [default: NUM_CPUS - 1]
132
137
  -cp, --compression TEXT Compression method to use for the output
@@ -239,6 +244,17 @@ Alternatively, it is also possible to install using pip with `pip install -e .`,
239
244
 
240
245
  Please run `black .` before committing.
241
246
 
247
+ #### Tests
248
+
249
+ Tests are included. To run them, set up a poetry environment, then follow these instructons:
250
+
251
+ ```bash
252
+ cd tests
253
+ python ./test_vector2dggs.py
254
+ ```
255
+
256
+ Test data are included at `tests/data/`.
257
+
242
258
  ## Example commands
243
259
 
244
260
  With a local GPKG:
@@ -261,14 +277,14 @@ vector2dggs h3 -v DEBUG -id ogc_fid -r 9 -p 5 -t 4 --overwrite -lyr topo50_lake
261
277
  title={{vector2dggs}},
262
278
  author={Ardo, James and Law, Richard},
263
279
  url={https://github.com/manaakiwhenua/vector2dggs},
264
- version={0.10.0},
280
+ version={0.11.0},
265
281
  date={2023-04-20}
266
282
  }
267
283
  ```
268
284
 
269
285
  APA/Harvard
270
286
 
271
- > Ardo, J., & Law, R. (2023). vector2dggs (0.10.0) [Computer software]. https://github.com/manaakiwhenua/vector2dggs
287
+ > Ardo, J., & Law, R. (2023). vector2dggs (0.11.0) [Computer software]. https://github.com/manaakiwhenua/vector2dggs
272
288
 
273
289
  [![manaakiwhenua-standards](https://github.com/manaakiwhenua/vector2dggs/workflows/manaakiwhenua-standards/badge.svg)](https://github.com/manaakiwhenua/manaakiwhenua-standards)
274
290
 
@@ -0,0 +1,15 @@
1
+ vector2dggs/__init__.py,sha256=NhOfSRUl_Py_CNCpR3FsS3g_Mf4pHgA87tFPBLl3BQM,28
2
+ vector2dggs/cli.py,sha256=d_4skD62k6pXUWgDdVHbDwpe4A4yo62ZFx8Cp_6GpBA,767
3
+ vector2dggs/common.py,sha256=hHhO_BZVL8GLFpzwm9Jw5cSaIy1bNk_hs0snEcNNVjQ,16893
4
+ vector2dggs/constants.py,sha256=UIQoS8IsfeFR1eoIkmByq9UgoJkkqI3pbUqSNCEfFmo,3476
5
+ vector2dggs/geohash.py,sha256=Px4EcRJ1cFFVMHJB7zgZLZXKYXhLQObxvE_QqMYLEKw,10593
6
+ vector2dggs/h3.py,sha256=UnCH0m0Z9zGc_kSg5igFEz565f7Nr3aVe5Vex01WKRw,7603
7
+ vector2dggs/katana.py,sha256=xD8BJ5hRQZyioStIMRJ_Gt1D9x9RotqP-YrB3TKHCSM,3850
8
+ vector2dggs/rHP.py,sha256=KILnXvAzSinied3mLw4IURLlcK78U7xcSnKQ5afPzn4,8286
9
+ vector2dggs/s2.py,sha256=6i6ecnPykgGRG-ZiyUP7JjgQzPUUklMZwd-JYs4X8K8,13063
10
+ vector2dggs-0.11.0.dist-info/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
11
+ vector2dggs-0.11.0.dist-info/COPYING.LESSER,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
12
+ vector2dggs-0.11.0.dist-info/METADATA,sha256=hZhgdrp7LiRGfIPbWyC3nl1ri1RtOLN9BE85CgqyCQg,13170
13
+ vector2dggs-0.11.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
14
+ vector2dggs-0.11.0.dist-info/entry_points.txt,sha256=5h8LB9L2oOE5u_N7FRGtu4JDwa553iPs4u0XhcLeLZU,52
15
+ vector2dggs-0.11.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.3
2
+ Generator: poetry-core 1.6.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,15 +0,0 @@
1
- vector2dggs/__init__.py,sha256=qK7__omM0NPcz4bMM5qUWKeZsOEhxeVWv_P38IPNVnw,28
2
- vector2dggs/cli.py,sha256=d_4skD62k6pXUWgDdVHbDwpe4A4yo62ZFx8Cp_6GpBA,767
3
- vector2dggs/common.py,sha256=rQL1_rFr1VTyILffOZgdwPzZS1JThn4TBPswfCkMjbM,14471
4
- vector2dggs/constants.py,sha256=KdmBQCP_GCygzvDLtS8AMQM9i6QqOkf-9YQkh_AzrKc,1779
5
- vector2dggs/geohash.py,sha256=PVLkaaSVLgzDZNfuL0y3Xioh4pyvom845HuyLIAsLUY,10398
6
- vector2dggs/h3.py,sha256=Juvc8g4QWfDIco9RQHaX8p9S9rkW5QvusxpyO-G7eSs,7408
7
- vector2dggs/katana.py,sha256=v4BRzVCsroC6RzIYdxLfrr9eFOdmXb5S9jXBMs5tgSo,3571
8
- vector2dggs/rHP.py,sha256=E03dQngbT3LtksZkaM6QSJv983ZGpLeXRedjqsEQZZI,9869
9
- vector2dggs/s2.py,sha256=SOXMHQQq86bM88MDgBBemGiXIbuEIbrhLSgPwLKceLY,12809
10
- vector2dggs-0.10.0.dist-info/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
11
- vector2dggs-0.10.0.dist-info/COPYING.LESSER,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
12
- vector2dggs-0.10.0.dist-info/METADATA,sha256=LuNEa06-KpDdXDdbDynhkxUXp6nuaOQ7q9ycIrJODKs,12606
13
- vector2dggs-0.10.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
14
- vector2dggs-0.10.0.dist-info/entry_points.txt,sha256=5h8LB9L2oOE5u_N7FRGtu4JDwa553iPs4u0XhcLeLZU,52
15
- vector2dggs-0.10.0.dist-info/RECORD,,