dggrid4py 0.5.1.dev0__tar.gz → 0.5.3__tar.gz

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.
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dggrid4py
3
- Version: 0.5.1.dev0
3
+ Version: 0.5.3
4
4
  Summary: a Python library to run highlevel functions of DGGRIDv7 and v8
5
5
  License-Expression: AGPL-3.0-or-later
6
6
  License-File: LICENSE
7
7
  Keywords: python,GIS,DGGS,DGGRID,hexagons,grids,spatial statistics,IGEO7
8
8
  Author: Alexander Kmoch
9
9
  Author-email: alexander.kmoch@ut.ee
10
- Requires-Python: >=3.9, <4.0.0
10
+ Requires-Python: >=3.10, <4.0.0
11
11
  Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Environment :: Console
13
13
  Classifier: Intended Audience :: Education
@@ -20,11 +20,12 @@ Classifier: Operating System :: Unix
20
20
  Classifier: Operating System :: POSIX :: Linux
21
21
  Classifier: Topic :: Scientific/Engineering :: GIS
22
22
  Requires-Dist: fiona (>=1.8.0,<2.0.0)
23
- Requires-Dist: geopandas (>=0.9.0,<=1.1.0)
24
- Requires-Dist: numpy (>=1.22.0,<=2.3.0)
25
- Requires-Dist: pandas (>=1.4.0,<2.4.0)
26
- Requires-Dist: pygeodesy (==25.10.10)
27
- Requires-Dist: shapely (>=1.8.0,<=2.1.0)
23
+ Requires-Dist: geopandas (>=0.9.0,<2.0.0)
24
+ Requires-Dist: numpy (>=1.22.0,<3.0.0)
25
+ Requires-Dist: pandas (>=1.4.0,<3.0.0)
26
+ Requires-Dist: pygeodesy (>=25.10.10,<26.0.0)
27
+ Requires-Dist: pyogrio (>=0.11.1,<0.12.0)
28
+ Requires-Dist: shapely (>=1.8.0,<3.0.0)
28
29
  Description-Content-Type: text/markdown
29
30
 
30
31
  # dggrid4py - a Python library to run highlevel functions of DGGRID
@@ -13,4 +13,4 @@ import dggrid4py.igeo7 as igeo7
13
13
  import dggrid4py.tool as tool
14
14
  from .auxlat import geoseries_to_authalic, geoseries_to_geodetic, geodetic_to_authalic, authalic_to_geodetic
15
15
 
16
- __version__ = "0.5.1-dev"
16
+ __version__ = "0.5.3"
@@ -189,7 +189,7 @@ DggsOutputCellLabelTypeT = DggsOutputCellLabelTypeV7T | DggsOutputCellLabelTypeV
189
189
  DggsCellOutputControlT = Literal["OUTPUT_ALL", "OUTPUT_OCCUPIED"]
190
190
  cell_output_controls = get_args(DggsCellOutputControlT)
191
191
 
192
- DggsInputAddressTypeT = Literal[
192
+ DggsInputAddressTypeV7T = Literal[
193
193
  'GEO', # geodetic coordinates -123.36 43.22 20300 Roseburg
194
194
  'Q2DI', # quad number and (i, j) coordinates on that quad
195
195
  'SEQNUM', # DGGS index - linear address (1 to size-of-DGG), not supported for parameter input_address_type if dggs_aperture_type is SEQUENCE
@@ -204,7 +204,36 @@ DggsInputAddressTypeT = Literal[
204
204
  'ZORDER', # index system ZORDER especially useful for ISEA3H, ISEA4H and mixed aperture
205
205
  'ZORDER_STRING' # numerical digits representation of ZORDER (as characters, not an integer)
206
206
  ]
207
- input_address_types = get_args(DggsInputAddressTypeT)
207
+ input_address_types_v7 = get_args(DggsInputAddressTypeV7T)
208
+
209
+ DggsInputAddressTypeV8T = Literal[
210
+ 'GEO', # geodetic coordinates -123.36 43.22 20300 Roseburg
211
+ 'Q2DI', # quad number and (i, j) coordinates on that quad
212
+ 'SEQNUM', # DGGS index - linear address (1 to size-of-DGG), not supported for parameter input_address_type if dggs_aperture_type is SEQUENCE
213
+ 'INTERLEAVE', # digit-interleaved form of Q2DI, only supported for parameter output_address_type; only available for hexagonal aperture 3 and 4 grids
214
+ 'PLANE', # (x, y) coordinates on unfolded ISEA plane, only supported for parameter output_address_type;
215
+ 'Q2DD', # quad number and (x, y) coordinates on that quad
216
+ 'PROJTRI', # PROJTRI - triangle number and (x, y) coordinates within that triangle on the ISEA plane
217
+ 'VERTEX2DD', # vertex number, triangle number, and (x, y) coordinates on ISEA plane
218
+ 'AIGEN', # Arc/Info Generate file format
219
+ 'HIERNDX',
220
+ ]
221
+ input_address_types_v8 = get_args(DggsInputAddressTypeV8T)
222
+
223
+ DggsInputAddressTypeT = DggsInputAddressTypeV7T | DggsInputAddressTypeV8T
224
+
225
+ input_extra_fields_v7 = {}
226
+
227
+ DggsInputHierNdxSystemT = Literal['Z3', 'Z7', 'ZORDER']
228
+ input_hier_ndx_systems = get_args(DggsInputHierNdxSystemT)
229
+
230
+ DggsInputHierNdxFormT = Literal['INT64', 'DIGIT_STRING']
231
+ input_hier_ndx_forms = get_args(DggsInputHierNdxFormT)
232
+
233
+ input_extra_fields_v8 = {
234
+ 'input_hier_ndx_system': input_hier_ndx_systems,
235
+ 'input_hier_ndx_form': input_hier_ndx_forms,
236
+ }
208
237
 
209
238
  ### CUSTOM args
210
239
  DggsProjectionT = Literal["ISEA", "FULLER"]
@@ -270,8 +299,11 @@ DggridMetaConfigT = TypedDict(
270
299
  "clip_cell_res": int,
271
300
  "clip_cell_addresses": str,
272
301
  "clip_region_files": str,
302
+ "clip_cell_densification": int,
273
303
  "clipper_scale_factor": float,
274
304
  "input_address_type": DggsInputAddressTypeT,
305
+ "input_hier_ndx_form": DggsInputHierNdxFormT,
306
+ "input_hier_ndx_system": DggsInputHierNdxSystemT,
275
307
  "input_delimiter": str,
276
308
  "input_file_name": str,
277
309
  "input_files": str,
@@ -298,6 +330,7 @@ DggridMetaConfigT = TypedDict(
298
330
  "output_cell_label_type": DggsOutputCellLabelTypeT,
299
331
  "output_count": bool,
300
332
  "output_count_field_name": str,
333
+ "output_num_classes": bool,
301
334
  "output_delimiter": str,
302
335
  "output_file_name": str,
303
336
  "output_first_seqnum": int,
@@ -332,6 +365,13 @@ def __getattr__(name): # for backward compatibility helper/warning
332
365
  DeprecationWarning
333
366
  )
334
367
  return output_address_types_v7
368
+ if name == 'input_address_types':
369
+ warnings.warn(
370
+ "Definitions 'input_address_types' has been deprecated. "
371
+ "Use the 'DGGRID.input_address_types' of desired version!",
372
+ DeprecationWarning
373
+ )
374
+ return output_address_types_v7
335
375
  raise AttributeError(f"module {__name__} has no attribute {name}")
336
376
 
337
377
 
@@ -513,11 +553,18 @@ class Dggs:
513
553
  'dggs_vert0_lat': 'pole_lat_deg',
514
554
  'dggs_vert0_lon': 'pole_lon_deg',
515
555
  'dggs_num_aperture_4_res': 'mixed_aperture_level',
556
+ 'res': 'resolution',
516
557
  # Add more aliases as needed
517
558
  }, init=False, repr=False)
518
559
 
519
- def _resolve_key(self, key: str) -> str:
560
+ def _resolve_key(self, key: str, strict: bool = False) -> str | None:
520
561
  # If key is an alias, return canonical; else if it's a canonical, return as is
562
+ if strict:
563
+ if key in self._aliases:
564
+ return self._aliases[key]
565
+ elif key in self.__dataclass_fields__:
566
+ return key
567
+ return None
521
568
  return self._aliases.get(key, key)
522
569
 
523
570
  def set_par(self, par_key: str, par_value: DggridMetaConfigParameterT):
@@ -550,6 +597,19 @@ class Dggs:
550
597
  def metafile(self) -> DggridMetafileT:
551
598
  return copy.copy(dg_grid_meta(self))
552
599
 
600
+ def update(self, strict=False, **kwargs):
601
+ """
602
+ Back propagate keyword parameters if they can be mapped to an attribute handled by this class.
603
+
604
+ :param strict:
605
+ If True, only keys that match existing attributes will be set.
606
+ If False, unknown keys will be set with provided name.
607
+ """
608
+ for key, value in kwargs.items():
609
+ found = self._resolve_key(key, strict=strict)
610
+ if found:
611
+ self.set_par(found, value)
612
+
553
613
  def dg_closest_res_to_area (self, area, resround,metric,show_info=True):
554
614
  raise ValueError('not yet implemented')
555
615
 
@@ -603,6 +663,16 @@ class DGGRID(abc.ABC):
603
663
  def output_extra_fields(self) -> dict[str, Iterable[DggridMetaConfigParameterT]]:
604
664
  raise NotImplementedError
605
665
 
666
+ @property
667
+ @abc.abstractmethod
668
+ def input_address_types(self) -> Iterable[DggsInputAddressTypeT]:
669
+ raise NotImplementedError
670
+
671
+ @property
672
+ @abc.abstractmethod
673
+ def input_extra_fields(self) -> dict[str, Iterable[DggridMetaConfigParameterT]]:
674
+ raise NotImplementedError
675
+
606
676
  def check_output_extra_fields(
607
677
  self,
608
678
  output_conf: DggridMetaConfigT | dict[str, DggridMetaConfigParameterT],
@@ -616,6 +686,19 @@ class DGGRID(abc.ABC):
616
686
  output_conf_extra[conf] = out
617
687
  return output_conf_extra
618
688
 
689
+ def check_input_extra_fields(
690
+ self,
691
+ input_conf: DggridMetaConfigT | dict[str, DggridMetaConfigParameterT],
692
+ ) -> DggridMetaConfigT:
693
+ input_conf_extra: DggridMetaConfigT = {}
694
+ for conf, allowed in self.input_extra_fields.items():
695
+ if conf in input_conf:
696
+ _in = input_conf[conf]
697
+ if _in not in allowed:
698
+ raise ValueError(f"input_conf '{conf}' has invalid value '{_in}'. Allowed are {allowed}")
699
+ input_conf_extra[conf] = _in
700
+ return input_conf_extra
701
+
619
702
  def check_gdal_support(self):
620
703
  if self.has_gdal:
621
704
  print(f"GDAL types should be possible: has GDAL={self.has_gdal}")
@@ -728,6 +811,7 @@ class DGGRID(abc.ABC):
728
811
  metafile = []
729
812
  metafile.append("dggrid_operation " + dggrid_operation)
730
813
 
814
+ dggs.update(**subset_conf, **output_conf)
731
815
  dggs_config_meta = dggs.metafile
732
816
 
733
817
  for cmd in dggs_config_meta:
@@ -771,8 +855,11 @@ class DGGRID(abc.ABC):
771
855
  else:
772
856
  raise ValueError('something is not correct in subset_conf')
773
857
 
774
- if 'input_address_type' in subset_conf.keys() and subset_conf.get('input_address_type', 'NOPE') in input_address_types:
858
+ if 'input_address_type' in subset_conf.keys() and subset_conf.get('input_address_type', 'NOPE') in self.input_address_types:
775
859
  metafile.append("input_address_type " + subset_conf['input_address_type'])
860
+ for elem in self.input_extra_fields:
861
+ if elem in subset_conf:
862
+ metafile.append(f"{elem} {subset_conf[elem]}")
776
863
 
777
864
  # join grid gen params add to metafile
778
865
 
@@ -867,7 +954,7 @@ class DGGRID(abc.ABC):
867
954
  self,
868
955
  dggs: Dggs,
869
956
  subset_conf: DggridMetaConfigT,
870
- **output_conf_extra: DggridMetaConfigParameterT,
957
+ **conf_extra: DggridMetaConfigParameterT,
871
958
  ) -> DggridApiOutputT:
872
959
  """
873
960
  Address Conversion. Transform a file of locations from one address form (such as longitude/latitude) to another (such as DGG cell indexes).
@@ -885,7 +972,7 @@ class DGGRID(abc.ABC):
885
972
  if (
886
973
  'input_file_name' in subset_conf.keys() and
887
974
  'input_address_type' in subset_conf.keys() and
888
- subset_conf['input_address_type'] in input_address_types
975
+ subset_conf['input_address_type'] in self.input_address_types
889
976
  ):
890
977
  for elem in filter(lambda x: x.startswith('input_') , subset_conf.keys()):
891
978
  metafile.append(f"{elem} " + str(subset_conf[elem]))
@@ -893,24 +980,33 @@ class DGGRID(abc.ABC):
893
980
  raise ValueError('no input filename or type given')
894
981
 
895
982
  # join grid gen params add to metafile
896
- subset_conf.update(specify_resolution(**output_conf_extra))
897
- subset_conf.update(specify_orient_type_args(**output_conf_extra))
898
- subset_conf.update(specify_topo_aperture(**output_conf_extra))
983
+ subset_conf.update(specify_resolution(**conf_extra))
984
+ subset_conf.update(specify_orient_type_args(**conf_extra))
985
+ subset_conf.update(specify_topo_aperture(**conf_extra))
986
+
987
+ input_extras = self.check_input_extra_fields(conf_extra)
988
+ subset_conf.update(input_extras)
989
+ if subset_conf:
990
+ for elem, value in subset_conf.items():
991
+ metafile.append(f"{elem} " + str(value))
899
992
 
900
993
  # transform output_types
994
+ output_conf = {}
901
995
  if (
902
- 'output_file_name' in output_conf.keys() and
903
- 'output_address_type' in output_conf.keys() and
904
- output_conf['output_address_type'] in self.output_address_types
996
+ 'output_file_name' in conf_extra.keys() and
997
+ 'output_address_type' in conf_extra.keys() and
998
+ conf_extra['output_address_type'] in self.output_address_types
905
999
  ):
906
- for elem in filter(lambda x: x.startswith('output_') , output_conf.keys()):
907
- metafile.append(f"{elem} " + str(output_conf[elem]))
1000
+ for elem in filter(lambda x: x.startswith('output_') , conf_extra.keys()):
1001
+ output_conf[elem] = conf_extra[elem]
1002
+ metafile.append(f"{elem} " + str(conf_extra[elem]))
908
1003
  else:
909
1004
  raise ValueError('no output filename or type given')
910
1005
 
911
- output_extras = self.check_output_extra_fields(output_conf_extra)
1006
+ output_extras = self.check_output_extra_fields(conf_extra)
912
1007
  if output_extras:
913
1008
  for elem, value in output_extras.items():
1009
+ output_conf[elem] = value
914
1010
  metafile.append(f"{elem} " + str(value))
915
1011
 
916
1012
  result = self.run(metafile)
@@ -930,7 +1026,7 @@ class DGGRID(abc.ABC):
930
1026
  self,
931
1027
  dggs: Dggs,
932
1028
  subset_conf: DggridMetaConfigT,
933
- **output_conf_extra: DggridMetaConfigParameterT,
1029
+ **conf_extra: DggridMetaConfigParameterT,
934
1030
  ) -> DggridApiOutputT:
935
1031
  """
936
1032
  Point Value Binning. Bin a set of floating-point values associated with point locations into the cells of a DGG,
@@ -972,7 +1068,7 @@ class DGGRID(abc.ABC):
972
1068
  if (
973
1069
  'input_file_name' in subset_conf.keys() and
974
1070
  'input_address_type' in subset_conf.keys() and
975
- subset_conf['input_address_type'] in input_address_types
1071
+ subset_conf['input_address_type'] in self.input_address_types
976
1072
  ):
977
1073
  for elem in filter(lambda x: x.startswith('input_') , subset_conf.keys()):
978
1074
  metafile.append(f"{elem} " + subset_conf[elem])
@@ -980,24 +1076,33 @@ class DGGRID(abc.ABC):
980
1076
  raise ValueError('no input filename or type given')
981
1077
 
982
1078
  # join grid gen params add to metafile
983
- subset_conf.update(specify_resolution(**output_conf_extra))
984
- subset_conf.update(specify_orient_type_args(**output_conf_extra))
985
- subset_conf.update(specify_topo_aperture(**output_conf_extra))
1079
+ subset_conf.update(specify_resolution(**conf_extra))
1080
+ subset_conf.update(specify_orient_type_args(**conf_extra))
1081
+ subset_conf.update(specify_topo_aperture(**conf_extra))
1082
+
1083
+ input_extras = self.check_input_extra_fields(conf_extra)
1084
+ subset_conf.update(input_extras)
1085
+ if subset_conf:
1086
+ for elem, value in subset_conf.items():
1087
+ metafile.append(f"{elem} " + str(value))
986
1088
 
987
1089
  # transform output_types
1090
+ output_conf = {}
988
1091
  if (
989
- 'output_file_name' in output_conf.keys() and
990
- 'output_address_type' in output_conf.keys() and
991
- output_conf['output_address_type'] in self.output_address_types
1092
+ 'output_file_name' in conf_extra.keys() and
1093
+ 'output_address_type' in conf_extra.keys() and
1094
+ conf_extra['output_address_type'] in self.output_address_types
992
1095
  ):
993
- for elem in filter(lambda x: x.startswith('output_') , output_conf.keys()):
994
- metafile.append(f"{elem} " + output_conf[elem])
1096
+ for elem in filter(lambda x: x.startswith('output_') , conf_extra.keys()):
1097
+ output_conf[elem] = conf_extra[elem]
1098
+ metafile.append(f"{elem} " + conf_extra[elem])
995
1099
  else:
996
1100
  raise ValueError('no output filename or type given')
997
1101
 
998
- output_extras = self.check_output_extra_fields(output_conf_extra)
1102
+ output_extras = self.check_output_extra_fields(conf_extra)
999
1103
  if output_extras:
1000
1104
  for elem, value in output_extras.items():
1105
+ output_conf[elem] = conf_extra[elem]
1001
1106
  metafile.append(f"{elem} " + str(value))
1002
1107
 
1003
1108
  result = self.run(metafile)
@@ -1017,7 +1122,7 @@ class DGGRID(abc.ABC):
1017
1122
  self,
1018
1123
  dggs: Dggs,
1019
1124
  subset_conf: DggridMetaConfigT,
1020
- **output_conf_extra: DggridMetaConfigParameterT,
1125
+ **conf_extra: DggridMetaConfigParameterT,
1021
1126
  ) -> DggridApiOutputT:
1022
1127
  """
1023
1128
  Presence/Absence Binning. Given a set of input files, each containing point locations associated with a particular class, DGGRID outputs,
@@ -1036,7 +1141,7 @@ class DGGRID(abc.ABC):
1036
1141
  if (
1037
1142
  'input_file_name' in subset_conf.keys() and
1038
1143
  'input_address_type' in subset_conf.keys() and
1039
- subset_conf['input_address_type'] in input_address_types
1144
+ subset_conf['input_address_type'] in self.input_address_types
1040
1145
  ):
1041
1146
  for elem in filter(lambda x: x.startswith('input_') , subset_conf.keys()):
1042
1147
  metafile.append(f"{elem} " + subset_conf[elem])
@@ -1044,25 +1149,45 @@ class DGGRID(abc.ABC):
1044
1149
  raise ValueError('no input filename or type given')
1045
1150
 
1046
1151
  # join grid gen params add to metafile
1047
- subset_conf.update(specify_resolution(**output_conf_extra))
1048
- subset_conf.update(specify_orient_type_args(**output_conf_extra))
1049
- subset_conf.update(specify_topo_aperture(**output_conf_extra))
1152
+ subset_conf.update(specify_resolution(**conf_extra))
1153
+ subset_conf.update(specify_orient_type_args(**conf_extra))
1154
+ subset_conf.update(specify_topo_aperture(**conf_extra))
1155
+
1156
+ input_extras = self.check_input_extra_fields(conf_extra)
1157
+ subset_conf.update(input_extras)
1158
+ if subset_conf:
1159
+ for elem, value in subset_conf.items():
1160
+ metafile.append(f"{elem} " + str(value))
1161
+
1162
+ # pre-patch elements before integrating them to the metafile
1163
+ if "output_delimiter" in conf_extra:
1164
+ delim = conf_extra["output_delimiter"] or " "
1165
+ delim = f'"{delim}"' if '"' not in delim else delim
1166
+ conf_extra["output_delimiter"] = delim
1167
+ if "cell_output_control" in conf_extra and conf_extra["cell_output_control"] not in cell_output_controls:
1168
+ raise ValueError(f"cell_output_control must be one of: {cell_output_controls}")
1169
+ for field in ["output_count", "output_num_classes"]:
1170
+ if field in conf_extra:
1171
+ conf_extra[field] = str(conf_extra[field]).upper() # bool or string-like bool
1050
1172
 
1051
1173
  # transform output_types
1174
+ output_conf = {}
1052
1175
  if (
1053
- 'output_file_name' in output_conf.keys() and
1054
- 'output_address_type' in output_conf.keys() and
1055
- output_conf['output_address_type'] in self.output_address_types
1176
+ 'output_file_name' in conf_extra.keys() and
1177
+ 'output_address_type' in conf_extra.keys() and
1178
+ conf_extra['output_address_type'] in self.output_address_types
1056
1179
  ):
1057
- for elem in filter(lambda x: x.startswith('output_') , output_conf.keys()):
1058
- metafile.append(f"{elem} " + output_conf[elem])
1180
+ for elem in filter(lambda x: x.startswith('output_') or x.startswith('cell_output_'), conf_extra.keys()):
1181
+ output_conf[elem] = conf_extra[elem]
1182
+ metafile.append(f"{elem} " + conf_extra[elem])
1059
1183
  else:
1060
1184
  raise ValueError('no output filename or type given')
1061
1185
 
1062
- output_extras = self.check_output_extra_fields(output_conf_extra)
1186
+ output_extras = self.check_output_extra_fields(conf_extra)
1063
1187
  if output_extras:
1064
1188
  for elem, value in output_extras.items():
1065
- metafile.append(f"{elem} " + str(value))
1189
+ output_conf[elem] = conf_extra[elem]
1190
+ metafile.append(f"{elem} {value!s}")
1066
1191
 
1067
1192
  result = self.run(metafile)
1068
1193
 
@@ -1184,7 +1309,7 @@ class DGGRID(abc.ABC):
1184
1309
  clip_geom: "AnyGeometry" = None,
1185
1310
  split_dateline: bool = False,
1186
1311
  output_address_type: DggsOutputAddressTypeT | None = None,
1187
- **output_conf_extra: DggridMetaConfigParameterT,
1312
+ **conf_extra: DggridMetaConfigParameterT,
1188
1313
  ) -> gpd.GeoDataFrame:
1189
1314
  """
1190
1315
  generates a DGGS grid and returns all the cells as GeoDataFrame with geometry type Polygon
@@ -1194,6 +1319,7 @@ class DGGRID(abc.ABC):
1194
1319
  tmp_id = uuid.uuid4()
1195
1320
  tmp_dir = self.working_dir
1196
1321
  dggs = dgselect(dggs_type = dggs_type, res= resolution, mixed_aperture_level=mixed_aperture_level)
1322
+ dggs.update(**conf_extra, strict=True)
1197
1323
 
1198
1324
  subset_conf: DggridMetaConfigT = { 'update_frequency': 100000, 'clip_subset_type': 'WHOLE_EARTH' }
1199
1325
 
@@ -1213,11 +1339,14 @@ class DGGRID(abc.ABC):
1213
1339
  'clip_subset_type': cast(DggsClipSubsetTypeT, self.tmp_geo_out['legacy_driver'].upper())
1214
1340
  })
1215
1341
 
1216
- subset_conf.update(specify_resolution(**output_conf_extra))
1217
- subset_conf.update(specify_orient_type_args(**output_conf_extra))
1218
- subset_conf.update(specify_topo_aperture(**output_conf_extra))
1342
+ subset_conf.update(specify_resolution(**conf_extra))
1343
+ subset_conf.update(specify_orient_type_args(**conf_extra))
1344
+ subset_conf.update(specify_topo_aperture(**conf_extra))
1345
+
1346
+ input_extras = self.check_input_extra_fields(conf_extra)
1347
+ subset_conf.update(input_extras)
1219
1348
 
1220
- output_extras = self.check_output_extra_fields(output_conf_extra)
1349
+ output_extras = self.check_output_extra_fields(conf_extra)
1221
1350
  output_conf: DggridMetaConfigT = {
1222
1351
  'cell_output_type': 'GDAL',
1223
1352
  'point_output_type': 'NONE',
@@ -1278,7 +1407,7 @@ class DGGRID(abc.ABC):
1278
1407
  mixed_aperture_level: int = None,
1279
1408
  clip_geom: "AnyGeometry" = None,
1280
1409
  output_address_type: DggsOutputAddressTypeT | None = None,
1281
- **output_conf_extra: DggridMetaConfigParameterT,
1410
+ **conf_extra: DggridMetaConfigParameterT,
1282
1411
  ) -> gpd.GeoDataFrame:
1283
1412
  """
1284
1413
  generates a DGGS grid and returns all the cell's centroid as GeoDataFrame with geometry type Point
@@ -1288,6 +1417,7 @@ class DGGRID(abc.ABC):
1288
1417
  tmp_id = uuid.uuid4()
1289
1418
  tmp_dir = self.working_dir
1290
1419
  dggs = dgselect(dggs_type = dggs_type, res= resolution, mixed_aperture_level=mixed_aperture_level)
1420
+ dggs.update(**conf_extra, strict=True)
1291
1421
 
1292
1422
  subset_conf: DggridMetaConfigT = { 'update_frequency': 100000, 'clip_subset_type': 'WHOLE_EARTH' }
1293
1423
 
@@ -1307,11 +1437,14 @@ class DGGRID(abc.ABC):
1307
1437
  'clip_subset_type': cast(DggsClipSubsetTypeT, self.tmp_geo_out['legacy_driver'].upper())
1308
1438
  })
1309
1439
 
1310
- subset_conf.update(specify_resolution(**output_conf_extra))
1311
- subset_conf.update(specify_orient_type_args(**output_conf_extra))
1312
- subset_conf.update(specify_topo_aperture(**output_conf_extra))
1440
+ subset_conf.update(specify_resolution(**conf_extra))
1441
+ subset_conf.update(specify_orient_type_args(**conf_extra))
1442
+ subset_conf.update(specify_topo_aperture(**conf_extra))
1443
+
1444
+ input_extras = self.check_input_extra_fields(conf_extra)
1445
+ subset_conf.update(input_extras)
1313
1446
 
1314
- output_extras = self.check_output_extra_fields(output_conf_extra)
1447
+ output_extras = self.check_output_extra_fields(conf_extra)
1315
1448
  output_conf: DggridMetaConfigT = {
1316
1449
  'point_output_type': 'GDAL',
1317
1450
  'cell_output_type': 'NONE',
@@ -1373,7 +1506,7 @@ class DGGRID(abc.ABC):
1373
1506
  clip_cell_res: int = 1,
1374
1507
  input_address_type: DggsInputAddressTypeT = 'SEQNUM',
1375
1508
  output_address_type: DggsOutputAddressTypeT = 'SEQNUM',
1376
- **output_conf_extra: DggridMetaConfigParameterT,
1509
+ **conf_extra: DggridMetaConfigParameterT,
1377
1510
  ) -> gpd.GeoDataFrame:
1378
1511
  """
1379
1512
  generates a DGGS grid and returns all the cells as GeoDataFrame with geometry type Polygon
@@ -1383,6 +1516,7 @@ class DGGRID(abc.ABC):
1383
1516
  tmp_id = uuid.uuid4()
1384
1517
  tmp_dir = self.working_dir
1385
1518
  dggs = dgselect(dggs_type = dggs_type, res= resolution, mixed_aperture_level=mixed_aperture_level)
1519
+ dggs.update(**conf_extra, strict=True)
1386
1520
 
1387
1521
  subset_conf: DggridMetaConfigT = { 'update_frequency': 100000, 'clip_subset_type': clip_subset_type }
1388
1522
  seq_df = None
@@ -1400,8 +1534,8 @@ class DGGRID(abc.ABC):
1400
1534
  # TODO, for Z3, Z7, ZORDER can potentially also be COARSE_CELLS / aka parent cells?
1401
1535
  # clip_subset_type should INPUT_ADDRESS_TYPE for the equivalent of SEQNUM (tp use input_address_type Z3 ...), or COARSE_CELLS as an actual parent cell type clip (also for Z3 ..)
1402
1536
  if (
1403
- input_address_type in ['Z3', 'Z3_STRING', 'Z7', 'Z7_STRING', 'ZORDER', 'ZORDER_STRING']
1404
- and output_address_type in ['Z3', 'Z3_STRING', 'Z7', 'Z7_STRING', 'ZORDER', 'ZORDER_STRING']
1537
+ input_address_type in self.input_address_types
1538
+ and output_address_type in self.output_address_types
1405
1539
  ):
1406
1540
  subset_conf.update(
1407
1541
  {
@@ -1419,12 +1553,21 @@ class DGGRID(abc.ABC):
1419
1553
  'clip_cell_addresses' : " ".join([str(address) for address in cell_id_list])
1420
1554
  }
1421
1555
  )
1556
+ if "clip_cell_densification" in conf_extra:
1557
+ subset_conf.update(
1558
+ {
1559
+ 'clip_cell_densification' : conf_extra['clip_cell_densification']
1560
+ }
1561
+ )
1422
1562
 
1423
- subset_conf.update(specify_resolution(**output_conf_extra))
1424
- subset_conf.update(specify_orient_type_args(**output_conf_extra))
1425
- subset_conf.update(specify_topo_aperture(**output_conf_extra))
1563
+ subset_conf.update(specify_resolution(**conf_extra))
1564
+ subset_conf.update(specify_orient_type_args(**conf_extra))
1565
+ subset_conf.update(specify_topo_aperture(**conf_extra))
1426
1566
 
1427
- output_extras = self.check_output_extra_fields(output_conf_extra)
1567
+ input_extras = self.check_input_extra_fields(conf_extra)
1568
+ subset_conf.update(input_extras)
1569
+
1570
+ output_extras = self.check_output_extra_fields(conf_extra)
1428
1571
  output_conf: DggridMetaConfigT = {
1429
1572
  'cell_output_type': 'GDAL',
1430
1573
  'point_output_type': 'NONE',
@@ -1510,7 +1653,7 @@ class DGGRID(abc.ABC):
1510
1653
  clip_cell_res: int = 1,
1511
1654
  input_address_type: DggsInputAddressTypeT = 'SEQNUM',
1512
1655
  output_address_type: DggsOutputAddressTypeT = 'SEQNUM',
1513
- **output_conf_extra: DggridMetaConfigParameterT,
1656
+ **conf_extra: DggridMetaConfigParameterT,
1514
1657
  ):
1515
1658
  """
1516
1659
  generates a DGGS grid and returns all the cell's centroid as GeoDataFrame with geometry type Point
@@ -1520,6 +1663,7 @@ class DGGRID(abc.ABC):
1520
1663
  tmp_id = uuid.uuid4()
1521
1664
  tmp_dir = self.working_dir
1522
1665
  dggs = dgselect(dggs_type = dggs_type, res= resolution, mixed_aperture_level=mixed_aperture_level)
1666
+ dggs.update(**conf_extra, strict=True)
1523
1667
 
1524
1668
  subset_conf: DggridMetaConfigT = { 'update_frequency': 100000, 'clip_subset_type': clip_subset_type }
1525
1669
  seq_df = None
@@ -1538,8 +1682,8 @@ class DGGRID(abc.ABC):
1538
1682
  # TODO, for Z3, Z7, ZORDER can potentially also be COARSE_CELLS / aka parent cells?
1539
1683
  # clip_subset_type should INPUT_ADDRESS_TYPE for the equivalent of SEQNUM (tp use input_address_type Z3 ...), or COARSE_CELLS as an actual parent cell type clip (also for Z3 ..)
1540
1684
  if (
1541
- input_address_type in ['Z3', 'Z3_STRING', 'Z7', 'Z7_STRING', 'ZORDER', 'ZORDER_STRING']
1542
- and output_address_type in ['Z3', 'Z3_STRING', 'Z7', 'Z7_STRING', 'ZORDER', 'ZORDER_STRING']
1685
+ input_address_type in self.input_address_types
1686
+ and output_address_type in self.output_address_types
1543
1687
  ):
1544
1688
  subset_conf.update(
1545
1689
  {
@@ -1558,11 +1702,13 @@ class DGGRID(abc.ABC):
1558
1702
  }
1559
1703
  )
1560
1704
 
1561
- subset_conf.update(specify_resolution(**output_conf_extra))
1562
- subset_conf.update(specify_orient_type_args(**output_conf_extra))
1563
- subset_conf.update(specify_topo_aperture(**output_conf_extra))
1705
+ subset_conf.update(specify_resolution(**conf_extra))
1706
+ subset_conf.update(specify_orient_type_args(**conf_extra))
1707
+ subset_conf.update(specify_topo_aperture(**conf_extra))
1564
1708
 
1565
- output_extras = self.check_output_extra_fields(output_conf_extra)
1709
+ input_extras = self.check_input_extra_fields(conf_extra)
1710
+ subset_conf.update(input_extras)
1711
+ output_extras = self.check_output_extra_fields(conf_extra)
1566
1712
  output_conf: DggridMetaConfigT = {
1567
1713
  'point_output_type': 'GDAL',
1568
1714
  'cell_output_type': 'NONE',
@@ -1633,7 +1779,7 @@ class DGGRID(abc.ABC):
1633
1779
  mixed_aperture_level: int = None,
1634
1780
  clip_geom: "AnyGeometry" = None,
1635
1781
  output_address_type: DggsOutputAddressTypeT | None = None,
1636
- **output_conf_extra: DggridMetaConfigParameterT,
1782
+ **conf_extra: DggridMetaConfigParameterT,
1637
1783
  ) -> pd.DataFrame:
1638
1784
  """
1639
1785
  generates a DGGS grid and returns all the cellids as a pandas dataframe
@@ -1644,6 +1790,7 @@ class DGGRID(abc.ABC):
1644
1790
  tmp_id = uuid.uuid4()
1645
1791
  tmp_dir = self.working_dir
1646
1792
  dggs = dgselect(dggs_type = dggs_type, res= resolution, mixed_aperture_level=mixed_aperture_level)
1793
+ dggs.update(**conf_extra, strict=True)
1647
1794
 
1648
1795
  subset_conf = { 'update_frequency': 100000, 'clip_subset_type': 'WHOLE_EARTH' }
1649
1796
 
@@ -1663,11 +1810,14 @@ class DGGRID(abc.ABC):
1663
1810
  'clip_subset_type': cast(DggsClipSubsetTypeT, self.tmp_geo_out['legacy_driver'].upper())
1664
1811
  })
1665
1812
 
1666
- subset_conf.update(specify_resolution(**output_conf_extra))
1667
- subset_conf.update(specify_orient_type_args(**output_conf_extra))
1668
- subset_conf.update(specify_topo_aperture(**output_conf_extra))
1813
+ subset_conf.update(specify_resolution(**conf_extra))
1814
+ subset_conf.update(specify_orient_type_args(**conf_extra))
1815
+ subset_conf.update(specify_topo_aperture(**conf_extra))
1816
+
1817
+ input_extras = self.check_input_extra_fields(conf_extra)
1818
+ subset_conf.update(input_extras)
1669
1819
 
1670
- output_extras = self.check_output_extra_fields(output_conf_extra)
1820
+ output_extras = self.check_output_extra_fields(conf_extra)
1671
1821
  output_conf: DggridMetaConfigT = {
1672
1822
  'point_output_type': 'TEXT',
1673
1823
  'cell_output_type': 'NONE',
@@ -1715,7 +1865,7 @@ class DGGRID(abc.ABC):
1715
1865
  mixed_aperture_level: int = None,
1716
1866
  split_dateline: bool = False,
1717
1867
  output_address_type: DggsOutputAddressTypeT | None = None,
1718
- **output_conf_extra: DggridMetaConfigParameterT,
1868
+ **conf_extra: DggridMetaConfigParameterT,
1719
1869
  ) -> gpd.GeoDataFrame:
1720
1870
  """
1721
1871
  takes a GeoDataFrame with point geometry and optional additional value columns and returns:
@@ -1725,9 +1875,10 @@ class DGGRID(abc.ABC):
1725
1875
  tmp_id = uuid.uuid4()
1726
1876
  tmp_dir = self.working_dir
1727
1877
  dggs = dgselect(dggs_type = dggs_type, res= resolution, mixed_aperture_level=mixed_aperture_level)
1878
+ dggs.update(**conf_extra, strict=True)
1728
1879
 
1729
1880
  cols = set(geodf_points_wgs84.columns.tolist())
1730
- cols = cols - set('geometry')
1881
+ cols = cols - {'geometry'}
1731
1882
  geodf_points_wgs84['lon'] = geodf_points_wgs84['geometry'].x
1732
1883
  geodf_points_wgs84['lat'] = geodf_points_wgs84['geometry'].y
1733
1884
  cols_ordered = ['lon', 'lat']
@@ -1741,11 +1892,14 @@ class DGGRID(abc.ABC):
1741
1892
  'input_address_type': 'GEO',
1742
1893
  'input_delimiter': "\" \""
1743
1894
  }
1744
- subset_conf.update(specify_resolution(**output_conf_extra))
1745
- subset_conf.update(specify_orient_type_args(**output_conf_extra))
1746
- subset_conf.update(specify_topo_aperture(**output_conf_extra))
1895
+ subset_conf.update(specify_resolution(**conf_extra))
1896
+ subset_conf.update(specify_orient_type_args(**conf_extra))
1897
+ subset_conf.update(specify_topo_aperture(**conf_extra))
1898
+
1899
+ input_extras = self.check_input_extra_fields(conf_extra)
1900
+ subset_conf.update(input_extras)
1747
1901
 
1748
- output_extras = self.check_output_extra_fields(output_conf_extra)
1902
+ output_extras = self.check_output_extra_fields(conf_extra)
1749
1903
  output_conf = {
1750
1904
  'output_file_name': str( (Path(tmp_dir) / f"{output_address_type}_{tmp_id}.txt").resolve()),
1751
1905
  'output_address_type': 'SEQNUM',
@@ -1781,15 +1935,19 @@ class DGGRID(abc.ABC):
1781
1935
  return geodf_points_wgs84
1782
1936
  else:
1783
1937
  # grid_gen from seqnums
1938
+ dggs_conf = dggs.to_dict()
1784
1939
  gdf = self.grid_cell_polygons_from_cellids(
1785
1940
  cell_id_list=cell_id_list,
1786
- dggs_type=dggs_type,
1941
+ # dggs_type=dggs_type, passed via dggs_conf
1787
1942
  resolution=resolution,
1788
1943
  mixed_aperture_level=mixed_aperture_level,
1789
1944
  input_address_type=output_address_type,
1790
- output_address_type=self.output_address_types,
1945
+ output_address_type=output_address_type,
1946
+ **dggs_conf, # ensure any extra parameters are passed on
1791
1947
  )
1792
1948
  try:
1949
+ # avoid conflict between input 'name' column and generated 'name' Zone ID
1950
+ gdf.rename(columns={'name': 'zone'}, inplace=True)
1793
1951
  for col in cols_ordered:
1794
1952
  gdf[col] = geodf_points_wgs84[col].values
1795
1953
  except Exception:
@@ -1809,7 +1967,7 @@ class DGGRID(abc.ABC):
1809
1967
  mixed_aperture_level: int = None,
1810
1968
  input_address_type: DggsInputAddressTypeT = 'SEQNUM',
1811
1969
  output_address_type: DggsOutputAddressTypeT = 'SEQNUM',
1812
- **output_conf_extra: DggridMetaConfigParameterT,
1970
+ **conf_extra: DggridMetaConfigParameterT,
1813
1971
  ) -> pd.DataFrame:
1814
1972
  """
1815
1973
  generates the DGGS for the input cell_ids and returns all the transformed cell_ids
@@ -1818,11 +1976,12 @@ class DGGRID(abc.ABC):
1818
1976
  tmp_id = uuid.uuid4()
1819
1977
  tmp_dir = self.working_dir
1820
1978
  dggs = dgselect(dggs_type = dggs_type, res= resolution, mixed_aperture_level=mixed_aperture_level)
1979
+ dggs.update(**conf_extra, strict=True)
1821
1980
 
1822
1981
  if cell_id_list is None or len(cell_id_list) <= 0:
1823
1982
  raise ValueError("Expecting cell_id_list to transform.")
1824
1983
 
1825
- if not input_address_type in input_address_types:
1984
+ if not input_address_type in self.input_address_types:
1826
1985
  raise ValueError(f"unknown input_address_type: {input_address_type}")
1827
1986
 
1828
1987
  if not output_address_type in self.output_address_types:
@@ -1837,7 +1996,10 @@ class DGGRID(abc.ABC):
1837
1996
  'input_delimiter': "\" \""
1838
1997
  }
1839
1998
 
1840
- output_extras = self.check_output_extra_fields(output_conf_extra)
1999
+ input_extras = self.check_input_extra_fields(conf_extra)
2000
+ subset_conf.update(input_extras)
2001
+
2002
+ output_extras = self.check_output_extra_fields(conf_extra)
1841
2003
  output_conf: DggridMetaConfigT = {
1842
2004
  'output_file_name': str( (Path(tmp_dir) / f"temp_out_{output_address_type}_{tmp_id}.txt").resolve()),
1843
2005
  'output_address_type': output_address_type,
@@ -1867,12 +2029,24 @@ class DGGRIDv7(DGGRID):
1867
2029
  version = 7
1868
2030
  output_address_types: DggsOutputAddressTypeV7T = output_address_types_v7
1869
2031
  output_extra_fields = output_extra_fields_v7
2032
+ input_address_types: DggsInputAddressTypeV7T = input_address_types_v7
2033
+ input_extra_fields = input_extra_fields_v7
1870
2034
 
1871
2035
 
1872
2036
  class DGGRIDv8(DGGRID):
1873
2037
  version = 8
1874
2038
  output_address_types: DggsOutputAddressTypeV8T = output_address_types_v8
1875
2039
  output_extra_fields = output_extra_fields_v8
2040
+ input_address_types: DggsInputAddressTypeV8T = input_address_types_v8
2041
+ input_extra_fields = input_extra_fields_v8
2042
+
2043
+
2044
+ class AnyDGGRID(DGGRID):
2045
+ version = 0
2046
+ output_address_types: DggsOutputAddressTypeT = list(set(output_address_types_v7) | set(output_address_types_v8))
2047
+ output_extra_fields = list(set(output_extra_fields_v7) | set(output_extra_fields_v8))
2048
+ input_address_types: DggsInputAddressTypeT = list(set(input_address_types_v7) | set(input_address_types_v8))
2049
+ input_extra_fields = list(set(input_extra_fields_v7) | set(input_extra_fields_v8))
1876
2050
 
1877
2051
 
1878
2052
  #############################################################
@@ -2029,7 +2203,7 @@ def specify_resolution(
2029
2203
  if dggs_proj is None or dggs_res_specify_type is None:
2030
2204
  return {}
2031
2205
 
2032
- if not proj_spec in list(dggs_projections) or not dggs_res_specify_type in dggs_res_specify_types:
2206
+ if not dggs_proj in list(dggs_projections) or not dggs_res_specify_type in dggs_res_specify_types:
2033
2207
  raise ValueError("base projection (ISEA or FULLER) or resolution spec unknown")
2034
2208
 
2035
2209
  if dggs_res_specify_type == "SPECIFIED":
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dggrid4py"
3
- version = "0.5.1-dev"
3
+ version = "0.5.3"
4
4
  description = "a Python library to run highlevel functions of DGGRIDv7 and v8"
5
5
  authors = [
6
6
  { name = "Alexander Kmoch", email = "alexander.kmoch@ut.ee" },
@@ -26,16 +26,18 @@ classifiers = [
26
26
  "Operating System :: POSIX :: Linux",
27
27
  "Topic :: Scientific/Engineering :: GIS"
28
28
  ]
29
- requires-python = ">=3.9, <4.0.0"
29
+ requires-python = ">=3.10, <4.0.0"
30
30
 
31
31
  [tool.poetry.dependencies]
32
- python = ">=3.9, <4.0.0"
33
- numpy = ">=1.22.0, <=2.3.0"
34
- pandas = ">=1.4.0, <2.4.0"
32
+ python = ">=3.10, <4.0.0"
33
+ numpy = ">=1.22.0, <3.0.0"
34
+ pandas = ">=1.4.0, <3.0.0"
35
35
  fiona = ">=1.8.0, <2.0.0"
36
- shapely = ">=1.8.0, <=2.1.0"
37
- geopandas = ">=0.9.0, <=1.1.0"
38
- pygeodesy = "25.10.10"
36
+ shapely = ">=1.8.0, <3.0.0"
37
+ geopandas = ">=0.9.0, <2.0.0"
38
+ pygeodesy = "^25.10.10"
39
+ pyogrio = "^0.11.1"
40
+
39
41
 
40
42
 
41
43
  [tool.poetry.group.dev.dependencies]
@@ -48,7 +50,8 @@ pytest = "*"
48
50
  [tool.poetry.group.docs.dependencies]
49
51
  sphinx = ">=7.1.2, <8.3.0"
50
52
  sphinx-rtd-theme = ">=1.3.0rc1, <4.0.0"
51
- myst-parser = ">=3.0.0, <5.0.0"
53
+ # myst-parser requires Python 3.10+
54
+ myst-parser = "^4.0.1"
52
55
 
53
56
 
54
57
  [build-system]
File without changes
File without changes