ssb-sgis 1.0.12__tar.gz → 1.0.13__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.
Files changed (60) hide show
  1. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/PKG-INFO +1 -1
  2. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/pyproject.toml +1 -1
  3. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/maps/explore.py +28 -14
  4. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/raster/image_collection.py +37 -47
  5. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/LICENSE +0 -0
  6. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/README.md +0 -0
  7. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/__init__.py +0 -0
  8. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/debug_config.py +0 -0
  9. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/exceptions.py +0 -0
  10. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/geopandas_tools/__init__.py +0 -0
  11. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/geopandas_tools/bounds.py +0 -0
  12. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/geopandas_tools/buffer_dissolve_explode.py +0 -0
  13. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/geopandas_tools/centerlines.py +0 -0
  14. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/geopandas_tools/cleaning.py +0 -0
  15. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/geopandas_tools/conversion.py +0 -0
  16. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/geopandas_tools/duplicates.py +0 -0
  17. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/geopandas_tools/general.py +0 -0
  18. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/geopandas_tools/geocoding.py +0 -0
  19. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/geopandas_tools/geometry_types.py +0 -0
  20. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/geopandas_tools/neighbors.py +0 -0
  21. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/geopandas_tools/overlay.py +0 -0
  22. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/geopandas_tools/point_operations.py +0 -0
  23. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/geopandas_tools/polygon_operations.py +0 -0
  24. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/geopandas_tools/polygons_as_rings.py +0 -0
  25. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/geopandas_tools/sfilter.py +0 -0
  26. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/helpers.py +0 -0
  27. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/io/_is_dapla.py +0 -0
  28. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/io/dapla_functions.py +0 -0
  29. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/io/opener.py +0 -0
  30. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/io/read_parquet.py +0 -0
  31. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/maps/__init__.py +0 -0
  32. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/maps/examine.py +0 -0
  33. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/maps/httpserver.py +0 -0
  34. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/maps/legend.py +0 -0
  35. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/maps/map.py +0 -0
  36. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/maps/maps.py +0 -0
  37. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/maps/thematicmap.py +0 -0
  38. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/maps/tilesources.py +0 -0
  39. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/networkanalysis/__init__.py +0 -0
  40. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/networkanalysis/_get_route.py +0 -0
  41. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/networkanalysis/_od_cost_matrix.py +0 -0
  42. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/networkanalysis/_points.py +0 -0
  43. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/networkanalysis/_service_area.py +0 -0
  44. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/networkanalysis/closing_network_holes.py +0 -0
  45. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/networkanalysis/cutting_lines.py +0 -0
  46. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/networkanalysis/directednetwork.py +0 -0
  47. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/networkanalysis/finding_isolated_networks.py +0 -0
  48. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/networkanalysis/network.py +0 -0
  49. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/networkanalysis/networkanalysis.py +0 -0
  50. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/networkanalysis/networkanalysisrules.py +0 -0
  51. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/networkanalysis/nodes.py +0 -0
  52. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/networkanalysis/traveling_salesman.py +0 -0
  53. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/parallel/parallel.py +0 -0
  54. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/py.typed +0 -0
  55. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/raster/__init__.py +0 -0
  56. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/raster/base.py +0 -0
  57. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/raster/indices.py +0 -0
  58. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/raster/regex.py +0 -0
  59. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/raster/sentinel_config.py +0 -0
  60. {ssb_sgis-1.0.12 → ssb_sgis-1.0.13}/src/sgis/raster/zonal.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ssb-sgis
3
- Version: 1.0.12
3
+ Version: 1.0.13
4
4
  Summary: GIS functions used at Statistics Norway.
5
5
  Home-page: https://github.com/statisticsnorway/ssb-sgis
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "ssb-sgis"
3
- version = "1.0.12"
3
+ version = "1.0.13"
4
4
  description = "GIS functions used at Statistics Norway."
5
5
  authors = ["Morten Letnes <morten.letnes@ssb.no>"]
6
6
  license = "MIT"
@@ -183,7 +183,9 @@ def to_tile(tile: str | xyzservices.TileProvider, max_zoom: int) -> folium.TileL
183
183
  return folium.TileLayer(provider, name=name, attr=attr, max_zoom=max_zoom)
184
184
 
185
185
 
186
- def _single_band_to_arr(band, mask, name, raster_data_dict):
186
+ def _single_band_to_arr_is_too_much_nodata(
187
+ band, mask, name, raster_data_dict, max_nodata_percentage
188
+ ) -> bool:
187
189
  if band.has_array and mask is None:
188
190
  arr = band.values
189
191
  elif band.has_array:
@@ -191,8 +193,8 @@ def _single_band_to_arr(band, mask, name, raster_data_dict):
191
193
  else:
192
194
  arr = band.load(indexes=1, bounds=mask).values
193
195
 
194
- if _is_too_much_nodata([arr], band.nodata):
195
- return False
196
+ if _is_too_much_nodata([arr], band.nodata, max_nodata_percentage):
197
+ return True
196
198
 
197
199
  bounds: tuple = (
198
200
  _any_to_bbox_crs4326(mask, band.crs)
@@ -216,25 +218,28 @@ def _single_band_to_arr(band, mask, name, raster_data_dict):
216
218
  except Exception:
217
219
  raster_data_dict["date"] = None
218
220
 
219
- return True
221
+ return False
220
222
 
221
223
 
222
224
  def _is_too_much_nodata(
223
225
  arrays: list[np.ndarray],
224
- nodata: int | None = None,
225
- max_nodata_percentage: int = 100,
226
+ nodata: int | None,
227
+ max_nodata_percentage: int,
226
228
  ) -> bool:
227
229
  return (
228
230
  any(arr.shape[0] == 0 for arr in arrays)
229
231
  or any(
230
232
  (
231
233
  isinstance(arr, np.ma.core.MaskedArray)
232
- and np.mean((arr.mask) | (arr.data == nodata) | (np.isnan(arr.data)))
234
+ and np.mean(((arr.mask) | (arr.data == nodata) | (np.isnan(arr.data))))
233
235
  > (max_nodata_percentage / 100)
234
236
  )
235
237
  for arr in arrays
236
238
  )
237
- or any(np.mean(arr == nodata) > (max_nodata_percentage / 100) for arr in arrays)
239
+ or any(
240
+ np.mean((arr == nodata) | (np.isnan(arr))) > (max_nodata_percentage / 100)
241
+ for arr in arrays
242
+ )
238
243
  )
239
244
 
240
245
 
@@ -271,6 +276,7 @@ class Explore(Map):
271
276
  decimals: int = 6,
272
277
  max_images: int = 10,
273
278
  max_nodata_percentage: int = 100,
279
+ display: bool = True,
274
280
  **kwargs,
275
281
  ) -> None:
276
282
  """Initialiser.
@@ -299,6 +305,7 @@ class Explore(Map):
299
305
  decimals: Number of decimals in the coordinates.
300
306
  max_nodata_percentage: Maximum percentage nodata values (e.g. clouds) ro allow in
301
307
  image arrays.
308
+ display: Whether to display the map interactively.
302
309
  **kwargs: Additional keyword arguments. Can also be geometry-like objects
303
310
  where the key is the label.
304
311
  """
@@ -314,6 +321,7 @@ class Explore(Map):
314
321
  self.decimals = decimals
315
322
  self.max_images = max_images
316
323
  self.max_nodata_percentage = max_nodata_percentage
324
+ self.display = display
317
325
  self.legend = None
318
326
 
319
327
  self.browser = browser
@@ -457,8 +465,7 @@ class Explore(Map):
457
465
 
458
466
  if center is None:
459
467
  self.to_show = self._gdfs
460
- self._explore(**kwargs)
461
- return
468
+ return self._explore(**kwargs)
462
469
 
463
470
  size = size if size else 1000
464
471
 
@@ -622,6 +629,8 @@ class Explore(Map):
622
629
  f.write(self.map._repr_html_())
623
630
  elif self.browser:
624
631
  run_html_server(self.map._repr_html_())
632
+ elif not self.display:
633
+ return
625
634
  else:
626
635
  display(self.map)
627
636
 
@@ -1345,7 +1354,10 @@ def _add_one_image(
1345
1354
  if len(image) < 3:
1346
1355
  for band in image:
1347
1356
  name = _determine_label(band, band.name or name)
1348
- _single_band_to_arr(band, mask, name, raster_data_dict)
1357
+ if _single_band_to_arr_is_too_much_nodata(
1358
+ band, mask, name, raster_data_dict, max_nodata_percentage
1359
+ ):
1360
+ return
1349
1361
  return raster_data_dict
1350
1362
 
1351
1363
  def load(band_id: str) -> Band:
@@ -1428,8 +1440,10 @@ def _image_collection_to_background_map(
1428
1440
  return out
1429
1441
 
1430
1442
  raster_data_dict = {}
1431
- out.append(raster_data_dict)
1432
- _single_band_to_arr(band, mask, name, raster_data_dict)
1443
+ if not _single_band_to_arr_is_too_much_nodata(
1444
+ band, mask, name, raster_data_dict, max_nodata_percentage
1445
+ ):
1446
+ out.append(raster_data_dict)
1433
1447
  return out
1434
1448
 
1435
1449
  else:
@@ -1462,7 +1476,7 @@ def _image_collection_to_background_map(
1462
1476
  continue
1463
1477
  i = 1
1464
1478
  while x["label"] in {y["label"] for y in out}:
1465
- x["label"] = x["label"].rstrip(f"_{i}", "") + f"_{i + 1}"
1479
+ x["label"] = x["label"].rstrip(f"_{i}") + f"_{i + 1}"
1466
1480
  i += 1
1467
1481
 
1468
1482
  n_added_images += 1
@@ -499,6 +499,14 @@ class _ImageBase:
499
499
  "metadata": self.metadata,
500
500
  }
501
501
 
502
+ @property
503
+ def _common_init_kwargs_after_load(self) -> dict:
504
+ return {
505
+ k: v
506
+ for k, v in self._common_init_kwargs.items()
507
+ if k not in {"res", "metadata"}
508
+ }
509
+
502
510
  @property
503
511
  def path(self) -> str:
504
512
  try:
@@ -1136,8 +1144,8 @@ class Band(_ImageBandBase):
1136
1144
  if self.nodata is None or np.isnan(self.nodata):
1137
1145
  self.nodata = src.nodata
1138
1146
  else:
1139
- dtype_min_value = _get_dtype_min(src.dtypes[0])
1140
- dtype_max_value = _get_dtype_max(src.dtypes[0])
1147
+ dtype_min_value = _get_dtype_min_value(src.dtypes[0])
1148
+ dtype_max_value = _get_dtype_max_value(src.dtypes[0])
1141
1149
  if self.nodata > dtype_max_value or self.nodata < dtype_min_value:
1142
1150
  src._dtypes = tuple(
1143
1151
  rasterio.dtypes.get_minimum_dtype(self.nodata)
@@ -1240,17 +1248,17 @@ class Band(_ImageBandBase):
1240
1248
  if self.crs is None:
1241
1249
  raise ValueError("Cannot write None crs to image.")
1242
1250
 
1243
- if self.nodata:
1244
- # TODO take out .data if masked?
1245
- values_with_nodata = np.concatenate(
1246
- [self.values.flatten(), np.array([self.nodata])]
1247
- )
1248
- else:
1249
- values_with_nodata = self.values
1251
+ try:
1252
+ data = self.values.data
1253
+ except AttributeError:
1254
+ data = self.values
1255
+ data = np.array([np.min(data), np.max(data), self.nodata or 0])
1256
+ min_dtype = rasterio.dtypes.get_minimum_dtype(data)
1257
+
1250
1258
  profile = {
1251
1259
  "driver": driver,
1252
1260
  "compress": compress,
1253
- "dtype": rasterio.dtypes.get_minimum_dtype(values_with_nodata),
1261
+ "dtype": min_dtype,
1254
1262
  "crs": self.crs,
1255
1263
  "transform": self.transform,
1256
1264
  "nodata": self.nodata,
@@ -1263,7 +1271,7 @@ class Band(_ImageBandBase):
1263
1271
  with rasterio.open(f, "w", **profile) as dst:
1264
1272
 
1265
1273
  if dst.nodata is None:
1266
- dst.nodata = _get_dtype_min(dst.dtypes[0])
1274
+ dst.nodata = _get_dtype_min_value(dst.dtypes[0])
1267
1275
 
1268
1276
  if (
1269
1277
  isinstance(self.values, np.ma.core.MaskedArray)
@@ -1515,12 +1523,6 @@ class NDVIBand(Band):
1515
1523
  cmap: str = "Greens"
1516
1524
 
1517
1525
 
1518
- def median_as_int_and_minimum_dtype(arr: np.ndarray) -> np.ndarray:
1519
- arr = np.median(arr, axis=0).astype(int)
1520
- min_dtype = rasterio.dtypes.get_minimum_dtype(arr)
1521
- return arr.astype(min_dtype)
1522
-
1523
-
1524
1526
  class Image(_ImageBandBase):
1525
1527
  """Image consisting of one or more Bands."""
1526
1528
 
@@ -1742,7 +1744,7 @@ class Image(_ImageBandBase):
1742
1744
  arr,
1743
1745
  bounds=red.bounds,
1744
1746
  crs=red.crs,
1745
- **{k: v for k, v in red._common_init_kwargs.items() if k != "res"},
1747
+ **red._common_init_kwargs_after_load,
1746
1748
  )
1747
1749
 
1748
1750
  def get_brightness(
@@ -1773,7 +1775,7 @@ class Image(_ImageBandBase):
1773
1775
  brightness,
1774
1776
  bounds=red.bounds,
1775
1777
  crs=self.crs,
1776
- **{k: v for k, v in self._common_init_kwargs.items() if k != "res"},
1778
+ **self._common_init_kwargs_after_load,
1777
1779
  )
1778
1780
 
1779
1781
  def to_xarray(self) -> DataArray:
@@ -2102,7 +2104,7 @@ class ImageCollection(_ImageBase):
2102
2104
 
2103
2105
  for attr in by:
2104
2106
  if attr == "bounds":
2105
- # need integers to check equality when grouping
2107
+ # need integers to properly check equality when grouping
2106
2108
  df[attr] = [
2107
2109
  tuple(int(x) for x in band.bounds) for img in self for band in img
2108
2110
  ]
@@ -2322,9 +2324,8 @@ class ImageCollection(_ImageBase):
2322
2324
  arr,
2323
2325
  bounds=bounds,
2324
2326
  crs=crs,
2325
- **{k: v for k, v in self._common_init_kwargs.items() if k != "res"},
2327
+ **self._common_init_kwargs_after_load,
2326
2328
  )
2327
-
2328
2329
  band._merged = True
2329
2330
  return band
2330
2331
 
@@ -2390,19 +2391,20 @@ class ImageCollection(_ImageBase):
2390
2391
 
2391
2392
  arrs.append(arr)
2392
2393
  bands.append(
2393
- self.band_class(
2394
+ # self.band_class(
2395
+ Band(
2394
2396
  arr,
2395
2397
  bounds=out_bounds,
2396
2398
  crs=crs,
2397
2399
  band_id=band_id,
2398
- **{k: v for k, v in self._common_init_kwargs.items() if k != "res"},
2400
+ **self._common_init_kwargs_after_load,
2399
2401
  )
2400
2402
  )
2401
2403
 
2402
2404
  # return self.image_class( # TODO
2403
2405
  image = Image(
2404
2406
  bands,
2405
- band_class=self.band_class,
2407
+ # band_class=self.band_class,
2406
2408
  **self._common_init_kwargs,
2407
2409
  )
2408
2410
 
@@ -2431,25 +2433,17 @@ class ImageCollection(_ImageBase):
2431
2433
  continue
2432
2434
 
2433
2435
  _bounds = to_bbox(_bounds)
2434
- arr = np.array(
2435
- [
2436
- (
2437
- # band.load(
2438
- # bounds=(_bounds if _bounds is not None else None),
2439
- # **kwargs,
2440
- # )
2441
- # if not band.has_array
2442
- # else
2443
- band
2444
- ).values
2445
- for img in collection
2446
- for band in img
2447
- ]
2448
- )
2436
+ collection.load(bounds=(_bounds if _bounds is not None else None), **kwargs)
2437
+ arr = np.array([band.values for img in collection for band in img])
2449
2438
  arr = numpy_func(arr, axis=0)
2450
2439
  if as_int:
2451
2440
  arr = arr.astype(int)
2452
- min_dtype = rasterio.dtypes.get_minimum_dtype(arr)
2441
+ try:
2442
+ data = arr.data
2443
+ except AttributeError:
2444
+ data = arr
2445
+ data = np.array([np.min(data), np.max(data), self.nodata or 0])
2446
+ min_dtype = rasterio.dtypes.get_minimum_dtype(data)
2453
2447
  arr = arr.astype(min_dtype)
2454
2448
 
2455
2449
  if len(arr.shape) == 2:
@@ -3429,24 +3423,20 @@ def _date_is_within(
3429
3423
  return False
3430
3424
 
3431
3425
 
3432
- def _get_dtype_min(dtype: str | type) -> int | float:
3426
+ def _get_dtype_min_value(dtype: str | type) -> int | float:
3433
3427
  try:
3434
3428
  return np.iinfo(dtype).min
3435
3429
  except ValueError:
3436
3430
  return np.finfo(dtype).min
3437
3431
 
3438
3432
 
3439
- def _get_dtype_max(dtype: str | type) -> int | float:
3433
+ def _get_dtype_max_value(dtype: str | type) -> int | float:
3440
3434
  try:
3441
3435
  return np.iinfo(dtype).max
3442
3436
  except ValueError:
3443
3437
  return np.finfo(dtype).max
3444
3438
 
3445
3439
 
3446
- def _intesects(x, other) -> bool:
3447
- return box(*x.bounds).intersects(other)
3448
-
3449
-
3450
3440
  def _copy_and_add_df_parallel(
3451
3441
  group_values: tuple[Any, ...],
3452
3442
  group_df: pd.DataFrame,
File without changes
File without changes
File without changes
File without changes