ssb-sgis 1.0.2__py3-none-any.whl → 1.0.3__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.
Files changed (42) hide show
  1. sgis/__init__.py +10 -6
  2. sgis/exceptions.py +2 -2
  3. sgis/geopandas_tools/bounds.py +17 -15
  4. sgis/geopandas_tools/buffer_dissolve_explode.py +24 -5
  5. sgis/geopandas_tools/conversion.py +15 -6
  6. sgis/geopandas_tools/duplicates.py +2 -2
  7. sgis/geopandas_tools/general.py +9 -5
  8. sgis/geopandas_tools/geometry_types.py +3 -3
  9. sgis/geopandas_tools/neighbors.py +3 -3
  10. sgis/geopandas_tools/point_operations.py +2 -2
  11. sgis/geopandas_tools/polygon_operations.py +5 -5
  12. sgis/geopandas_tools/sfilter.py +3 -3
  13. sgis/helpers.py +3 -3
  14. sgis/io/read_parquet.py +1 -1
  15. sgis/maps/examine.py +16 -2
  16. sgis/maps/explore.py +370 -57
  17. sgis/maps/legend.py +164 -72
  18. sgis/maps/map.py +184 -90
  19. sgis/maps/maps.py +92 -90
  20. sgis/maps/thematicmap.py +236 -83
  21. sgis/networkanalysis/closing_network_holes.py +2 -2
  22. sgis/networkanalysis/cutting_lines.py +3 -3
  23. sgis/networkanalysis/directednetwork.py +1 -1
  24. sgis/networkanalysis/finding_isolated_networks.py +2 -2
  25. sgis/networkanalysis/networkanalysis.py +7 -7
  26. sgis/networkanalysis/networkanalysisrules.py +1 -1
  27. sgis/networkanalysis/traveling_salesman.py +1 -1
  28. sgis/parallel/parallel.py +39 -19
  29. sgis/raster/__init__.py +0 -6
  30. sgis/raster/cube.py +51 -5
  31. sgis/raster/image_collection.py +2560 -0
  32. sgis/raster/indices.py +14 -5
  33. sgis/raster/raster.py +131 -236
  34. sgis/raster/sentinel_config.py +104 -0
  35. sgis/raster/zonal.py +0 -1
  36. {ssb_sgis-1.0.2.dist-info → ssb_sgis-1.0.3.dist-info}/METADATA +1 -1
  37. ssb_sgis-1.0.3.dist-info/RECORD +61 -0
  38. sgis/raster/methods_as_functions.py +0 -0
  39. sgis/raster/torchgeo.py +0 -171
  40. ssb_sgis-1.0.2.dist-info/RECORD +0 -61
  41. {ssb_sgis-1.0.2.dist-info → ssb_sgis-1.0.3.dist-info}/LICENSE +0 -0
  42. {ssb_sgis-1.0.2.dist-info → ssb_sgis-1.0.3.dist-info}/WHEEL +0 -0
sgis/parallel/parallel.py CHANGED
@@ -2,6 +2,7 @@ import functools
2
2
  import inspect
3
3
  import itertools
4
4
  import multiprocessing
5
+ import pickle
5
6
  import warnings
6
7
  from collections.abc import Callable
7
8
  from collections.abc import Collection
@@ -132,7 +133,7 @@ class Parallel:
132
133
  'iterable'.
133
134
 
134
135
  Examples:
135
- --------
136
+ ---------
136
137
  Multiply each list element by 2.
137
138
 
138
139
  >>> iterable = [1, 2, 3]
@@ -183,7 +184,7 @@ class Parallel:
183
184
  func_with_kwargs = functools.partial(func, **kwargs)
184
185
 
185
186
  if self.processes == 1:
186
- return list(map(func_with_kwargs, iterable))
187
+ return [func_with_kwargs(item) for item in iterable]
187
188
 
188
189
  iterable = list(iterable)
189
190
 
@@ -192,23 +193,42 @@ class Parallel:
192
193
 
193
194
  if not processes:
194
195
  return []
196
+ elif processes == 1:
197
+ return [func_with_kwargs(item) for item in iterable]
195
198
 
196
- if self.backend == "multiprocessing":
197
- with multiprocessing.get_context(self.context).Pool(
198
- processes, maxtasksperchild=self.maxtasksperchild, **self.kwargs
199
- ) as pool:
200
- try:
201
- return pool.map(
202
- func_with_kwargs, iterable, chunksize=self.chunksize
203
- )
204
- except Exception as e:
205
- pool.terminate()
206
- raise e
199
+ try:
200
+ if self.backend == "multiprocessing":
201
+ with multiprocessing.get_context(self.context).Pool(
202
+ processes, maxtasksperchild=self.maxtasksperchild, **self.kwargs
203
+ ) as pool:
204
+ try:
205
+ return pool.map(
206
+ func_with_kwargs, iterable, chunksize=self.chunksize
207
+ )
208
+ except Exception as e:
209
+ pool.terminate()
210
+ raise e
207
211
 
208
- with joblib.Parallel(
209
- n_jobs=processes, backend=self.backend, **self.kwargs
210
- ) as parallel:
211
- return parallel(joblib.delayed(func)(item, **kwargs) for item in iterable)
212
+ with joblib.Parallel(
213
+ n_jobs=processes, backend=self.backend, **self.kwargs
214
+ ) as parallel:
215
+ return parallel(
216
+ joblib.delayed(func)(item, **kwargs) for item in iterable
217
+ )
218
+ except pickle.PickleError as e:
219
+ unpicklable = []
220
+ for k, v in locals().items():
221
+ try:
222
+ pickle.dumps(v)
223
+ except pickle.PickleError:
224
+ unpicklable.append(k)
225
+ except TypeError:
226
+ pass
227
+ if unpicklable:
228
+ raise pickle.PickleError(
229
+ f"Cannot unpickle objects: {unpicklable}"
230
+ ) from e
231
+ raise e
212
232
 
213
233
  def starmap(
214
234
  self,
@@ -236,7 +256,7 @@ class Parallel:
236
256
  'iterable'.
237
257
 
238
258
  Examples:
239
- --------
259
+ ---------
240
260
  Multiply each list element by 2.
241
261
 
242
262
  >>> iterable = [(1, 2), (2, 3), (3, 4)]
@@ -947,7 +967,7 @@ def parallel_overlay(
947
967
 
948
968
 
949
969
  def _clean_intersection(
950
- df1: GeoDataFrame, df2: GeoDataFrame, to_print: str | None = None
970
+ df1: GeoDataFrame, df2: GeoDataFrame, to_print: str = ""
951
971
  ) -> GeoDataFrame:
952
972
  print(to_print, "- intersection chunk len:", len(df1))
953
973
  return clean_overlay(df1, df2, how="intersection")
sgis/raster/__init__.py CHANGED
@@ -1,6 +0,0 @@
1
- try:
2
- from .torchgeo import SENTINEL2_FILENAME_REGEX
3
- from .torchgeo import SENTINEL_2_BANDS
4
- from .torchgeo import SENTINEL_2_RBG_BANDS
5
- except ImportError:
6
- pass
sgis/raster/cube.py CHANGED
@@ -2,6 +2,7 @@ import functools
2
2
  import itertools
3
3
  import multiprocessing
4
4
  import re
5
+ import warnings
5
6
  from collections.abc import Callable
6
7
  from collections.abc import Iterable
7
8
  from collections.abc import Iterator
@@ -140,6 +141,10 @@ class DataCube:
140
141
  copy: If True, makes deep copies of Rasters provided.
141
142
  parallelizer: sgis.Parallel instance to handle concurrent operations.
142
143
  """
144
+ warnings.warn(
145
+ "This class is deprecated in favor of ImageCollection", stacklevel=1
146
+ )
147
+
143
148
  self._arrays = None
144
149
  self._res = res
145
150
  self.parallelizer = parallelizer
@@ -207,6 +212,7 @@ class DataCube:
207
212
  check_for_df: bool = True,
208
213
  contains: str | None = None,
209
214
  endswith: str = ".tif",
215
+ bands: str | list[str] | None = None,
210
216
  filename_regex: str | None = None,
211
217
  parallelizer: Parallel | None = None,
212
218
  file_system=None,
@@ -221,6 +227,7 @@ class DataCube:
221
227
  that holds metadata for the files in the directory.
222
228
  contains: Filter files containing specific substrings.
223
229
  endswith: Filter files that end with specific substrings.
230
+ bands: One or more band ids to keep.
224
231
  filename_regex: Regular expression to match file names
225
232
  and attributes (date, band, tile, resolution).
226
233
  parallelizer: sgis.Parallel instance for concurrent file processing.
@@ -233,6 +240,7 @@ class DataCube:
233
240
  kwargs["res"] = res
234
241
  kwargs["filename_regex"] = filename_regex
235
242
  kwargs["contains"] = contains
243
+ kwargs["bands"] = bands
236
244
  kwargs["endswith"] = endswith
237
245
 
238
246
  if is_dapla():
@@ -283,6 +291,7 @@ class DataCube:
283
291
  parallelizer: Parallel | None = None,
284
292
  file_system=None,
285
293
  contains: str | None = None,
294
+ bands: str | list[str] | None = None,
286
295
  endswith: str = ".tif",
287
296
  filename_regex: str | None = None,
288
297
  **kwargs,
@@ -296,6 +305,7 @@ class DataCube:
296
305
  file_system: File system to use for file operations, used in Dapla environment.
297
306
  contains: Filter files containing specific substrings.
298
307
  endswith: Filter files that end with specific substrings.
308
+ bands: One or more band ids to keep.
299
309
  filename_regex: Regular expression to match file names.
300
310
  **kwargs: Additional keyword arguments to pass to the raster loading function.
301
311
 
@@ -311,6 +321,10 @@ class DataCube:
311
321
  if filename_regex:
312
322
  compiled = re.compile(filename_regex, re.VERBOSE)
313
323
  paths = [path for path in paths if re.search(compiled, Path(path).name)]
324
+ if bands:
325
+ if isinstance(bands, str):
326
+ bands = [bands]
327
+ paths = [path for path in paths if any(band in str(path) for band in bands)]
314
328
 
315
329
  if not paths:
316
330
  return cls(crs=crs, parallelizer=parallelizer, res=res)
@@ -544,6 +558,19 @@ class DataCube:
544
558
  self.data = data
545
559
  return self
546
560
 
561
+ def sample(self, n: int, copy: bool = True, **kwargs) -> Self:
562
+ """Take n samples of the cube."""
563
+ if self.crs is None:
564
+ self._crs = get_common_crs(self.data)
565
+
566
+ cube = self.copy() if copy else self
567
+
568
+ cube.data = list(pd.Series(cube.data).sample(n))
569
+
570
+ cube.data = cube.run_raster_method("load", **kwargs)
571
+
572
+ return cube
573
+
547
574
  def load(self, copy: bool = True, **kwargs) -> Self:
548
575
  """Load all images as arrays into a DataCube copy."""
549
576
  if self.crs is None:
@@ -620,7 +647,7 @@ class DataCube:
620
647
  ).items()
621
648
  if key in ALLOWED_KEYS and key not in ["array", "indexes"]
622
649
  }
623
- if raster.array is None:
650
+ if raster.values is None:
624
651
  return [
625
652
  raster.__class__.from_dict({"indexes": i} | all_meta)
626
653
  for i in raster.indexes_as_tuple()
@@ -830,7 +857,7 @@ class DataCube:
830
857
  @property
831
858
  def arrays(self) -> list[np.ndarray]:
832
859
  """The arrays of the images as a list."""
833
- return [raster.array for raster in self]
860
+ return [raster.values for raster in self]
834
861
 
835
862
  @arrays.setter
836
863
  def arrays(self, new_arrays: list[np.ndarray]):
@@ -995,12 +1022,22 @@ class DataCube:
995
1022
 
996
1023
  def _check_for_array(self, text: str = "") -> None:
997
1024
  mess = "Arrays are not loaded. " + text
998
- if all(raster.array is None for raster in self):
1025
+ if all(raster.values is None for raster in self):
999
1026
  raise ValueError(mess)
1000
1027
 
1001
1028
  def __getitem__(
1002
1029
  self,
1003
- item: slice | int | Series | list | tuple | Callable | Geometry | BoundingBox,
1030
+ item: (
1031
+ str
1032
+ | slice
1033
+ | int
1034
+ | Series
1035
+ | list
1036
+ | tuple
1037
+ | Callable
1038
+ | Geometry
1039
+ | BoundingBox
1040
+ ),
1004
1041
  ) -> Self | Raster | TORCHGEO_RETURN_TYPE:
1005
1042
  """Select one or more of the Rasters based on indexing or spatial or boolean predicates.
1006
1043
 
@@ -1026,6 +1063,14 @@ class DataCube:
1026
1063
 
1027
1064
  """
1028
1065
  copy = self.copy()
1066
+ if isinstance(item, str) and copy.path is not None:
1067
+ copy.data = [raster for raster in copy if item in raster.path]
1068
+ if len(copy) == 1:
1069
+ return copy[0]
1070
+ elif not len(copy):
1071
+ return Raster()
1072
+ return copy
1073
+
1029
1074
  if isinstance(item, slice):
1030
1075
  copy.data = copy.data[item]
1031
1076
  return copy
@@ -1127,7 +1172,7 @@ def _merge(
1127
1172
  bounds: Any | None = None,
1128
1173
  **kwargs,
1129
1174
  ) -> DataCube:
1130
- if not all(r.array is None for r in cube):
1175
+ if not all(r.values is None for r in cube):
1131
1176
  raise ValueError("Arrays can't be loaded when calling merge.")
1132
1177
 
1133
1178
  bounds = to_bbox(bounds) if bounds is not None else bounds
@@ -1185,6 +1230,7 @@ def _merge_by_bounds(
1185
1230
 
1186
1231
 
1187
1232
  def _merge(cube: DataCube, **kwargs) -> DataCube:
1233
+ by = kwargs.pop("by")
1188
1234
  if cube.crs is None:
1189
1235
  cube._crs = get_common_crs(cube.data)
1190
1236