ssb-sgis 1.1.12__py3-none-any.whl → 1.1.15__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.
- sgis/geopandas_tools/polygon_operations.py +8 -13
- sgis/io/dapla_functions.py +26 -15
- sgis/maps/explore.py +1 -1
- sgis/maps/map.py +2 -0
- sgis/raster/image_collection.py +82 -18
- {ssb_sgis-1.1.12.dist-info → ssb_sgis-1.1.15.dist-info}/METADATA +1 -1
- {ssb_sgis-1.1.12.dist-info → ssb_sgis-1.1.15.dist-info}/RECORD +9 -9
- {ssb_sgis-1.1.12.dist-info → ssb_sgis-1.1.15.dist-info}/LICENSE +0 -0
- {ssb_sgis-1.1.12.dist-info → ssb_sgis-1.1.15.dist-info}/WHEEL +0 -0
|
@@ -205,9 +205,9 @@ def get_polygon_clusters(
|
|
|
205
205
|
|
|
206
206
|
def get_cluster_mapper(
|
|
207
207
|
gdf: GeoDataFrame | GeoSeries, predicate: str = "intersects"
|
|
208
|
-
) ->
|
|
209
|
-
|
|
210
|
-
|
|
208
|
+
) -> list[int]:
|
|
209
|
+
"""Returns a list of cluster indices corresponding to the order of the input GeoDataFrame or GeoSeries."""
|
|
210
|
+
gdf = gdf.reset_index(drop=True)
|
|
211
211
|
neighbors = get_neighbor_indices(gdf, gdf, predicate=predicate)
|
|
212
212
|
|
|
213
213
|
edges = [(source, target) for source, target in neighbors.items()]
|
|
@@ -215,11 +215,12 @@ def get_cluster_mapper(
|
|
|
215
215
|
graph = nx.Graph()
|
|
216
216
|
graph.add_edges_from(edges)
|
|
217
217
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
for
|
|
221
|
-
for
|
|
218
|
+
mapper = {
|
|
219
|
+
i: cluster_index
|
|
220
|
+
for cluster_index, component in enumerate(nx.connected_components(graph))
|
|
221
|
+
for i in component
|
|
222
222
|
}
|
|
223
|
+
return list(dict(sorted(mapper.items())).values())
|
|
223
224
|
|
|
224
225
|
|
|
225
226
|
def eliminate_by_longest(
|
|
@@ -425,12 +426,6 @@ def eliminate_by_longest(
|
|
|
425
426
|
explore_locals(center=_DEBUG_CONFIG["center"])
|
|
426
427
|
|
|
427
428
|
if not _recurse and len(isolated):
|
|
428
|
-
if 0:
|
|
429
|
-
isolated.geometry = isolated.buffer(
|
|
430
|
-
-PRECISION,
|
|
431
|
-
resolution=1,
|
|
432
|
-
join_style=2,
|
|
433
|
-
)
|
|
434
429
|
out, isolated = _recursively_eliminate_new_neighbors(
|
|
435
430
|
out,
|
|
436
431
|
isolated,
|
sgis/io/dapla_functions.py
CHANGED
|
@@ -109,7 +109,7 @@ def read_geopandas(
|
|
|
109
109
|
if single_eq_filter:
|
|
110
110
|
try:
|
|
111
111
|
expression = "".join(next(iter(filters))).replace("==", "=")
|
|
112
|
-
glob_func =
|
|
112
|
+
glob_func = _get_glob_func(file_system)
|
|
113
113
|
paths = glob_func(str(Path(gcs_path) / expression))
|
|
114
114
|
if paths:
|
|
115
115
|
return _read_geopandas_from_iterable(
|
|
@@ -543,7 +543,7 @@ def _write_partitioned_geoparquet(
|
|
|
543
543
|
if df[col].isna().all() and not kwargs.get("schema"):
|
|
544
544
|
raise ValueError("Must specify 'schema' when all rows are NA.")
|
|
545
545
|
|
|
546
|
-
glob_func =
|
|
546
|
+
glob_func = _get_glob_func(file_system)
|
|
547
547
|
|
|
548
548
|
if file_system.exists(path) and file_system.isfile(path):
|
|
549
549
|
_remove_file(path, file_system)
|
|
@@ -596,7 +596,7 @@ def _write_partitioned_geoparquet(
|
|
|
596
596
|
executor.map(threaded_write, dfs, paths)
|
|
597
597
|
|
|
598
598
|
|
|
599
|
-
def
|
|
599
|
+
def _get_glob_func(file_system) -> functools.partial:
|
|
600
600
|
try:
|
|
601
601
|
return functools.partial(file_system.glob)
|
|
602
602
|
except AttributeError:
|
|
@@ -724,9 +724,9 @@ def _read_partitioned_parquet(
|
|
|
724
724
|
**kwargs,
|
|
725
725
|
):
|
|
726
726
|
file_system = _get_file_system(file_system, kwargs)
|
|
727
|
+
glob_func = _get_glob_func(file_system)
|
|
727
728
|
|
|
728
729
|
if child_paths is None:
|
|
729
|
-
glob_func = _get_glob(file_system)
|
|
730
730
|
child_paths = list(glob_func(str(Path(path) / "**/*.parquet")))
|
|
731
731
|
|
|
732
732
|
filters = _filters_to_expression(filters)
|
|
@@ -735,18 +735,29 @@ def _read_partitioned_parquet(
|
|
|
735
735
|
bbox, _ = _get_bounds_parquet_from_open_file(file, file_system)
|
|
736
736
|
return shapely.box(*bbox).intersects(to_shapely(mask))
|
|
737
737
|
|
|
738
|
-
def read(
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
738
|
+
def read(child_path: str) -> pyarrow.Table | None:
|
|
739
|
+
try:
|
|
740
|
+
with file_system.open(child_path, "rb") as file:
|
|
741
|
+
if mask is not None and not intersects(file, mask):
|
|
742
|
+
return
|
|
742
743
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
744
|
+
# 'get' instead of 'pop' because dict is mutable
|
|
745
|
+
schema = kwargs.get("schema", pq.read_schema(file))
|
|
746
|
+
new_kwargs = {
|
|
747
|
+
key: value for key, value in kwargs.items() if key != "schema"
|
|
748
|
+
}
|
|
748
749
|
|
|
749
|
-
|
|
750
|
+
return read_func(file, schema=schema, filters=filters, **new_kwargs)
|
|
751
|
+
except ArrowInvalid as e:
|
|
752
|
+
if not len(
|
|
753
|
+
{
|
|
754
|
+
x
|
|
755
|
+
for x in glob_func(str(Path(child_path) / "**"))
|
|
756
|
+
if not paths_are_equal(child_path, x)
|
|
757
|
+
}
|
|
758
|
+
):
|
|
759
|
+
raise e
|
|
760
|
+
# allow not being able to read hard-to-delete empty directories
|
|
750
761
|
|
|
751
762
|
with ThreadPoolExecutor() as executor:
|
|
752
763
|
results = [
|
|
@@ -790,7 +801,7 @@ def paths_are_equal(path1: Path | str, path2: Path | str) -> bool:
|
|
|
790
801
|
|
|
791
802
|
|
|
792
803
|
def get_child_paths(path, file_system) -> list[str]:
|
|
793
|
-
glob_func =
|
|
804
|
+
glob_func = _get_glob_func(file_system)
|
|
794
805
|
return [
|
|
795
806
|
x
|
|
796
807
|
for x in glob_func(str(Path(path) / "**/*.parquet"))
|
sgis/maps/explore.py
CHANGED
sgis/maps/map.py
CHANGED
|
@@ -729,6 +729,8 @@ class Map:
|
|
|
729
729
|
"""Place the column values into groups."""
|
|
730
730
|
bins = bins.copy()
|
|
731
731
|
|
|
732
|
+
assert gdf.index.is_unique
|
|
733
|
+
|
|
732
734
|
# if equal lenght, convert to integer and check for equality
|
|
733
735
|
if len(bins) == len(self._unique_values):
|
|
734
736
|
if gdf[self._column].isna().all():
|
sgis/raster/image_collection.py
CHANGED
|
@@ -2,6 +2,7 @@ import datetime
|
|
|
2
2
|
import functools
|
|
3
3
|
import glob
|
|
4
4
|
import itertools
|
|
5
|
+
import json
|
|
5
6
|
import os
|
|
6
7
|
import random
|
|
7
8
|
import re
|
|
@@ -468,8 +469,8 @@ class _ImageBase:
|
|
|
468
469
|
regexes = (regexes,)
|
|
469
470
|
return tuple(re.compile(regexes, flags=re.VERBOSE) for regexes in regexes)
|
|
470
471
|
|
|
471
|
-
@staticmethod
|
|
472
472
|
def _metadata_to_nested_dict(
|
|
473
|
+
self,
|
|
473
474
|
metadata: str | Path | os.PathLike | dict | pd.DataFrame | None,
|
|
474
475
|
) -> dict[str, dict[str, Any]]:
|
|
475
476
|
"""Construct metadata dict from dictlike, DataFrame or file path.
|
|
@@ -896,9 +897,6 @@ class Band(_ImageBandBase):
|
|
|
896
897
|
if self._bounds is None:
|
|
897
898
|
raise ValueError("Must specify bounds when data is an array.")
|
|
898
899
|
if not (res is None or (callable(res) and res() is None)):
|
|
899
|
-
# if not (res is None or (callable(res) and res() is None)) and _res_as_tuple(
|
|
900
|
-
# res
|
|
901
|
-
# ) != _get_res_from_bounds(self._bounds, data.shape):
|
|
902
900
|
raise ValueError(
|
|
903
901
|
f"Cannot specify 'res' when data is an array. {res} and {_get_res_from_bounds(self._bounds, data.shape)}"
|
|
904
902
|
)
|
|
@@ -932,14 +930,16 @@ class Band(_ImageBandBase):
|
|
|
932
930
|
}
|
|
933
931
|
|
|
934
932
|
if self.metadata:
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
933
|
+
parent = _fix_path(str(Path(self.path).parent))
|
|
934
|
+
for key, value in self.metadata.get(parent, {}).items():
|
|
935
|
+
if key == "bands" and self.band_id in value:
|
|
936
|
+
band_metadata = value[self.band_id]
|
|
937
|
+
for band_key, band_value in band_metadata.items():
|
|
938
|
+
if band_key in dir(self):
|
|
939
|
+
setattr(self, f"_{band_key}", band_value)
|
|
940
|
+
else:
|
|
941
|
+
setattr(self, band_key, band_value)
|
|
942
|
+
continue
|
|
943
943
|
if key in dir(self):
|
|
944
944
|
setattr(self, f"_{key}", value)
|
|
945
945
|
else:
|
|
@@ -1592,6 +1592,22 @@ class Image(_ImageBandBase):
|
|
|
1592
1592
|
else:
|
|
1593
1593
|
self._all_file_paths = None
|
|
1594
1594
|
|
|
1595
|
+
if not self.metadata and "metadata.json" in {
|
|
1596
|
+
Path(x).name for x in self._all_file_paths
|
|
1597
|
+
}:
|
|
1598
|
+
with _open_func(
|
|
1599
|
+
next(
|
|
1600
|
+
iter(
|
|
1601
|
+
{
|
|
1602
|
+
x
|
|
1603
|
+
for x in self._all_file_paths
|
|
1604
|
+
if str(x).endswith("metadata.json")
|
|
1605
|
+
}
|
|
1606
|
+
)
|
|
1607
|
+
)
|
|
1608
|
+
) as file:
|
|
1609
|
+
self.metadata = json.load(file)
|
|
1610
|
+
|
|
1595
1611
|
if df is None:
|
|
1596
1612
|
if not self._all_file_paths:
|
|
1597
1613
|
self._all_file_paths = {self.path}
|
|
@@ -1616,12 +1632,10 @@ class Image(_ImageBandBase):
|
|
|
1616
1632
|
key: value for key, value in self.metadata.items() if self.path in key
|
|
1617
1633
|
}
|
|
1618
1634
|
|
|
1619
|
-
if self.metadata:
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
metadata = {}
|
|
1624
|
-
for key, value in metadata.items():
|
|
1635
|
+
if self.metadata.get(self.path, {}):
|
|
1636
|
+
for key, value in self.metadata[self.path].items():
|
|
1637
|
+
if key in {"bands"}:
|
|
1638
|
+
continue
|
|
1625
1639
|
if key in dir(self):
|
|
1626
1640
|
setattr(self, f"_{key}", value)
|
|
1627
1641
|
else:
|
|
@@ -1705,6 +1719,56 @@ class Image(_ImageBandBase):
|
|
|
1705
1719
|
|
|
1706
1720
|
return self
|
|
1707
1721
|
|
|
1722
|
+
def get_image_metadata_dict(self) -> dict:
|
|
1723
|
+
"""Creates a nested dict of metadata.
|
|
1724
|
+
|
|
1725
|
+
The dict structure will be:
|
|
1726
|
+
|
|
1727
|
+
{
|
|
1728
|
+
image_path: {
|
|
1729
|
+
image_attribute: value,
|
|
1730
|
+
...,
|
|
1731
|
+
"bands": {
|
|
1732
|
+
band_id: {
|
|
1733
|
+
band_attribute: band_value,
|
|
1734
|
+
},
|
|
1735
|
+
...,
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
"""
|
|
1740
|
+
path = self.path
|
|
1741
|
+
metadata = {
|
|
1742
|
+
path: {
|
|
1743
|
+
"bounds": self.bounds,
|
|
1744
|
+
"crs": str(pyproj.CRS(self.crs).to_string()),
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
for key in self.metadata_attributes:
|
|
1748
|
+
metadata[path][key] = getattr(self, key)
|
|
1749
|
+
|
|
1750
|
+
metadata[path]["bands"] = {}
|
|
1751
|
+
for band in self:
|
|
1752
|
+
metadata[path]["bands"][band.band_id] = {}
|
|
1753
|
+
for key in band.metadata_attributes:
|
|
1754
|
+
if key in self.metadata_attributes:
|
|
1755
|
+
continue
|
|
1756
|
+
metadata[path]["bands"][band.band_id][key] = getattr(band, key)
|
|
1757
|
+
return metadata
|
|
1758
|
+
|
|
1759
|
+
def write_image_metadata(self) -> None:
|
|
1760
|
+
"""Write file 'metadata.json' under image path.
|
|
1761
|
+
|
|
1762
|
+
The file will be used to give the image attributes
|
|
1763
|
+
and avoid the much slower metadata fetching with rasterio.
|
|
1764
|
+
|
|
1765
|
+
See method 'get_image_metadata_dict' for info on the structure of
|
|
1766
|
+
the json file.
|
|
1767
|
+
"""
|
|
1768
|
+
metadata = self.get_image_metadata_dict()
|
|
1769
|
+
with _open_func(str(Path(self.path) / "metadata.json"), "w") as file:
|
|
1770
|
+
json.dump(metadata, file)
|
|
1771
|
+
|
|
1708
1772
|
def _construct_image_from_bands(
|
|
1709
1773
|
self, data: Sequence[Band], res: int | None
|
|
1710
1774
|
) -> None:
|
|
@@ -15,21 +15,21 @@ sgis/geopandas_tools/geometry_types.py,sha256=ijQDbQaZPqPGjBl707H4yooNXpk21RXyat
|
|
|
15
15
|
sgis/geopandas_tools/neighbors.py,sha256=vduQlHeoZjHyD5pxDbjfonQ3-LAHGfPETxV7-L6Sg4M,16634
|
|
16
16
|
sgis/geopandas_tools/overlay.py,sha256=pMQK86t0ixKErHQsP0HC8RXHUXNxDCNdH6FK1MEJamM,25779
|
|
17
17
|
sgis/geopandas_tools/point_operations.py,sha256=JM4hvfIVxZaZdGNlGzcCurrKzkgC_b9hzbFYN42f9WY,6972
|
|
18
|
-
sgis/geopandas_tools/polygon_operations.py,sha256=
|
|
18
|
+
sgis/geopandas_tools/polygon_operations.py,sha256=1XiwQHYPbd3Xj3B68YamzyT_2kdiJrrwIku4JjVa2ks,50066
|
|
19
19
|
sgis/geopandas_tools/polygons_as_rings.py,sha256=BX_GZS6F9I4NbEpiOlNBd7zywJjdfdJVi_MkeONBuiM,14941
|
|
20
20
|
sgis/geopandas_tools/sfilter.py,sha256=SLcMYprQwnY5DNo0R7TGXk4m6u26H8o4PRn-RPhmeZY,9345
|
|
21
21
|
sgis/helpers.py,sha256=_h7ke9hJrRNhHW-ZX3gA95fOrX2s1ADKBMxc94p2F4Q,9627
|
|
22
22
|
sgis/io/__init__.py,sha256=uyBr20YDqB2bQttrd5q1JuGOvX32A-MSvS7Wmw5f5qg,177
|
|
23
23
|
sgis/io/_is_dapla.py,sha256=wmfkSe98IrLhUg3dtXZusV6OVC8VlY1kbc5EQDf3P-Q,358
|
|
24
|
-
sgis/io/dapla_functions.py,sha256=
|
|
24
|
+
sgis/io/dapla_functions.py,sha256=tRP8TrFnlxJ5gx3BZSWsvBAAzUveUysGPUxSHRewIck,30729
|
|
25
25
|
sgis/io/opener.py,sha256=HWO3G1NB6bpXKM94JadCD513vjat1o1TFjWGWzyVasg,898
|
|
26
26
|
sgis/io/read_parquet.py,sha256=FvZYv1rLkUlrSaUY6QW6E1yntmntTeQuZ9ZRgCDO4IM,3776
|
|
27
27
|
sgis/maps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
28
|
sgis/maps/examine.py,sha256=Pb0dH8JazU5E2svfQrzHO1Bi-sjy5SeyY6zoeMO34jE,9369
|
|
29
|
-
sgis/maps/explore.py,sha256=
|
|
29
|
+
sgis/maps/explore.py,sha256=0QDPaj5YU2Jps5iujuHu-vk_R9mFh_aOhbN1eU-fxcI,47898
|
|
30
30
|
sgis/maps/httpserver.py,sha256=eCDoB9x74kSLiGEj2X3O91t3oscY_ia17UNuaaJ6tCc,2472
|
|
31
31
|
sgis/maps/legend.py,sha256=lVRVCkhPmJRjGK23obFJZAO3qp6du1LYnobkkN7DPkc,26279
|
|
32
|
-
sgis/maps/map.py,sha256=
|
|
32
|
+
sgis/maps/map.py,sha256=lwQUJvK3I9dPNSxPeRty9ICodz_GXQBN1OHhghI7IsE,30439
|
|
33
33
|
sgis/maps/maps.py,sha256=gxu0rgcVygjudRtM1dVRmsUMilMUIg3vG-UgvASM91E,23072
|
|
34
34
|
sgis/maps/norge_i_bilder.json,sha256=W_mFfte3DxugWbEudZ5fadZ2JeFYb0hyab2Quf4oJME,481311
|
|
35
35
|
sgis/maps/thematicmap.py,sha256=w6q4_gIr8BubQgsPJkc6WXk-tmplDLGcKyjphhFp7ng,21873
|
|
@@ -54,12 +54,12 @@ sgis/parallel/parallel.py,sha256=CzHetSAr9wvSrEDFTqDq2xAsNuG1ig22-vcEOIoUVv4,396
|
|
|
54
54
|
sgis/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
55
|
sgis/raster/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
56
|
sgis/raster/base.py,sha256=tiZEuMcVK6hOm_aIjWhQ1WGshcjsxT1fFkuBSLFiMC0,7785
|
|
57
|
-
sgis/raster/image_collection.py,sha256=
|
|
57
|
+
sgis/raster/image_collection.py,sha256=QuqCJ1HYXiVu7c6EEO5bFDFj_4MG6qxjQu7-4BNE3nI,125269
|
|
58
58
|
sgis/raster/indices.py,sha256=-J1HYmnT240iozvgagvyis6K0_GHZHRuUrPOgyoeIrY,223
|
|
59
59
|
sgis/raster/regex.py,sha256=kYhVpRYzoXutx1dSYmqMoselWXww7MMEsTPmLZwHjbM,3759
|
|
60
60
|
sgis/raster/sentinel_config.py,sha256=nySDqn2R8M6W8jguoBeSAK_zzbAsqmaI59i32446FwY,1268
|
|
61
61
|
sgis/raster/zonal.py,sha256=D4Gyptw-yOLTCO41peIuYbY-DANsJCG19xXDlf1QAz4,2299
|
|
62
|
-
ssb_sgis-1.1.
|
|
63
|
-
ssb_sgis-1.1.
|
|
64
|
-
ssb_sgis-1.1.
|
|
65
|
-
ssb_sgis-1.1.
|
|
62
|
+
ssb_sgis-1.1.15.dist-info/LICENSE,sha256=np3IfD5m0ZUofn_kVzDZqliozuiO6wrktw3LRPjyEiI,1073
|
|
63
|
+
ssb_sgis-1.1.15.dist-info/METADATA,sha256=8cxb6fu1hpdI0vYDRV8GsJ5nLw6HH0Q6yuZZTCwL23M,11741
|
|
64
|
+
ssb_sgis-1.1.15.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
65
|
+
ssb_sgis-1.1.15.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|