ssb-sgis 1.0.12__py3-none-any.whl → 1.0.14__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/__init__.py +1 -0
- sgis/maps/explore.py +51 -15
- sgis/maps/maps.py +21 -0
- sgis/maps/norge_i_bilder.json +11517 -0
- sgis/maps/norge_i_bilder_wms.py +165 -0
- sgis/raster/image_collection.py +37 -47
- {ssb_sgis-1.0.12.dist-info → ssb_sgis-1.0.14.dist-info}/METADATA +1 -1
- {ssb_sgis-1.0.12.dist-info → ssb_sgis-1.0.14.dist-info}/RECORD +10 -8
- {ssb_sgis-1.0.12.dist-info → ssb_sgis-1.0.14.dist-info}/LICENSE +0 -0
- {ssb_sgis-1.0.12.dist-info → ssb_sgis-1.0.14.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import json
|
|
3
|
+
import re
|
|
4
|
+
from collections.abc import Iterable
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
from urllib.request import urlopen
|
|
9
|
+
|
|
10
|
+
import folium
|
|
11
|
+
import shapely
|
|
12
|
+
|
|
13
|
+
from ..geopandas_tools.conversion import to_shapely
|
|
14
|
+
|
|
15
|
+
JSON_PATH = Path(__file__).parent / "norge_i_bilder.json"
|
|
16
|
+
|
|
17
|
+
DEFAULT_YEARS: tuple[str] = tuple(
|
|
18
|
+
str(year)
|
|
19
|
+
for year in range(
|
|
20
|
+
int(datetime.datetime.now().year) - 8,
|
|
21
|
+
int(datetime.datetime.now().year) + 1,
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class NorgeIBilderWms:
|
|
28
|
+
"""Loads Norge i bilder tiles as folium.WmsTiles."""
|
|
29
|
+
|
|
30
|
+
years: Iterable[int | str] = DEFAULT_YEARS
|
|
31
|
+
contains: str | Iterable[str] | None = None
|
|
32
|
+
not_contains: str | Iterable[str] | None = None
|
|
33
|
+
|
|
34
|
+
def load_tiles(self) -> None:
|
|
35
|
+
"""Load all Norge i bilder tiles into self.tiles."""
|
|
36
|
+
url = "https://wms.geonorge.no/skwms1/wms.nib-prosjekter?SERVICE=WMS&REQUEST=GetCapabilities"
|
|
37
|
+
|
|
38
|
+
name_pattern = r"<Name>(.*?)</Name>"
|
|
39
|
+
bbox_pattern = (
|
|
40
|
+
r"<EX_GeographicBoundingBox>.*?"
|
|
41
|
+
r"<westBoundLongitude>(.*?)</westBoundLongitude>.*?"
|
|
42
|
+
r"<eastBoundLongitude>(.*?)</eastBoundLongitude>.*?"
|
|
43
|
+
r"<southBoundLatitude>(.*?)</southBoundLatitude>.*?"
|
|
44
|
+
r"<northBoundLatitude>(.*?)</northBoundLatitude>.*?</EX_GeographicBoundingBox>"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
all_tiles: list[dict] = []
|
|
48
|
+
with urlopen(url) as file:
|
|
49
|
+
xml_data: str = file.read().decode("utf-8")
|
|
50
|
+
|
|
51
|
+
for text in xml_data.split('<Layer queryable="1">')[1:]:
|
|
52
|
+
|
|
53
|
+
# Extract bounding box values
|
|
54
|
+
bbox_match = re.search(bbox_pattern, text, re.DOTALL)
|
|
55
|
+
if bbox_match:
|
|
56
|
+
minx, maxx, miny, maxy = (
|
|
57
|
+
float(bbox_match.group(i)) for i in [1, 2, 3, 4]
|
|
58
|
+
)
|
|
59
|
+
this_bbox = shapely.box(minx, miny, maxx, maxy)
|
|
60
|
+
else:
|
|
61
|
+
this_bbox = None
|
|
62
|
+
|
|
63
|
+
name_match = re.search(name_pattern, text, re.DOTALL)
|
|
64
|
+
name = name_match.group(1) if name_match else None
|
|
65
|
+
|
|
66
|
+
if (
|
|
67
|
+
not name
|
|
68
|
+
or not any(year in name for year in self.years)
|
|
69
|
+
or (
|
|
70
|
+
self.contains
|
|
71
|
+
and not any(re.search(x, name.lower()) for x in self.contains)
|
|
72
|
+
)
|
|
73
|
+
or (
|
|
74
|
+
self.not_contains
|
|
75
|
+
and any(re.search(x, name.lower()) for x in self.not_contains)
|
|
76
|
+
)
|
|
77
|
+
):
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
this_tile = {}
|
|
81
|
+
this_tile["name"] = name
|
|
82
|
+
this_tile["bbox"] = this_bbox
|
|
83
|
+
year = name.split(" ")[-1]
|
|
84
|
+
if year.isnumeric() and len(year) == 4:
|
|
85
|
+
this_tile["year"] = year
|
|
86
|
+
else:
|
|
87
|
+
this_tile["year"] = "9999"
|
|
88
|
+
all_tiles.append(this_tile)
|
|
89
|
+
|
|
90
|
+
self.tiles = sorted(all_tiles, key=lambda x: x["year"])
|
|
91
|
+
|
|
92
|
+
def get_tiles(self, bbox: Any, max_zoom: int = 40) -> list[folium.WmsTileLayer]:
|
|
93
|
+
"""Get all Norge i bilder tiles intersecting with a bbox."""
|
|
94
|
+
if self.tiles is None:
|
|
95
|
+
self.load_tiles()
|
|
96
|
+
|
|
97
|
+
all_tiles = {}
|
|
98
|
+
|
|
99
|
+
bbox = to_shapely(bbox)
|
|
100
|
+
|
|
101
|
+
for tile in self.tiles:
|
|
102
|
+
if not tile["bbox"] or not tile["bbox"].intersects(bbox):
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
name = tile["name"]
|
|
106
|
+
|
|
107
|
+
if (
|
|
108
|
+
not name
|
|
109
|
+
or not any(year in name for year in self.years)
|
|
110
|
+
or (
|
|
111
|
+
self.contains
|
|
112
|
+
and not any(re.search(x, name.lower()) for x in self.contains)
|
|
113
|
+
)
|
|
114
|
+
or (
|
|
115
|
+
self.not_contains
|
|
116
|
+
and any(re.search(x, name.lower()) for x in self.not_contains)
|
|
117
|
+
)
|
|
118
|
+
):
|
|
119
|
+
continue
|
|
120
|
+
|
|
121
|
+
all_tiles[name] = folium.WmsTileLayer(
|
|
122
|
+
url="https://wms.geonorge.no/skwms1/wms.nib-prosjekter",
|
|
123
|
+
name=name,
|
|
124
|
+
layers=name,
|
|
125
|
+
format="image/png", # Tile format
|
|
126
|
+
transparent=True, # Allow transparency
|
|
127
|
+
version="1.3.0", # WMS version
|
|
128
|
+
attr="© <a href='https://www.geonorge.no/'>Geonorge</a>",
|
|
129
|
+
show=False,
|
|
130
|
+
max_zoom=max_zoom,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
return all_tiles
|
|
134
|
+
|
|
135
|
+
def __post_init__(self) -> None:
|
|
136
|
+
"""Fix typings."""
|
|
137
|
+
if self.contains and isinstance(self.contains, str):
|
|
138
|
+
self.contains = [self.contains.lower()]
|
|
139
|
+
elif self.contains:
|
|
140
|
+
self.contains = [x.lower() for x in self.contains]
|
|
141
|
+
|
|
142
|
+
if self.not_contains and isinstance(self.not_contains, str):
|
|
143
|
+
self.not_contains = [self.not_contains.lower()]
|
|
144
|
+
elif self.not_contains:
|
|
145
|
+
self.not_contains = [x.lower() for x in self.not_contains]
|
|
146
|
+
|
|
147
|
+
self.years = [str(int(year)) for year in self.years]
|
|
148
|
+
|
|
149
|
+
if all(year in DEFAULT_YEARS for year in self.years):
|
|
150
|
+
try:
|
|
151
|
+
with open(JSON_PATH, encoding="utf-8") as file:
|
|
152
|
+
self.tiles = json.load(file)
|
|
153
|
+
except FileNotFoundError:
|
|
154
|
+
self.tiles = None
|
|
155
|
+
return
|
|
156
|
+
self.tiles = [
|
|
157
|
+
{
|
|
158
|
+
key: value if key != "bbox" else shapely.wkt.loads(value)
|
|
159
|
+
for key, value in tile.items()
|
|
160
|
+
}
|
|
161
|
+
for tile in self.tiles
|
|
162
|
+
if any(str(year) in tile["name"] for year in self.years)
|
|
163
|
+
]
|
|
164
|
+
else:
|
|
165
|
+
self.tiles = None
|
sgis/raster/image_collection.py
CHANGED
|
@@ -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 =
|
|
1140
|
-
dtype_max_value =
|
|
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
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
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":
|
|
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 =
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
sgis/__init__.py,sha256=
|
|
1
|
+
sgis/__init__.py,sha256=h_5A40stVg0dKwoQRDmv_owy-x-n66pDpM7XtcHMSwE,7404
|
|
2
2
|
sgis/debug_config.py,sha256=Tfr19kU46hSkkspsIJcrUWvlhaL4U3-f8xEPkujSCAQ,593
|
|
3
3
|
sgis/exceptions.py,sha256=WNaEBPNNx0rmz-YDzlFX4vIE7ocJQruUTqS2RNAu2zU,660
|
|
4
4
|
sgis/geopandas_tools/__init__.py,sha256=bo8lFMcltOz7TtWAi52_ekR2gd3mjfBfKeMDV5zuqFY,28
|
|
@@ -24,11 +24,13 @@ sgis/io/opener.py,sha256=HWO3G1NB6bpXKM94JadCD513vjat1o1TFjWGWzyVasg,898
|
|
|
24
24
|
sgis/io/read_parquet.py,sha256=FvZYv1rLkUlrSaUY6QW6E1yntmntTeQuZ9ZRgCDO4IM,3776
|
|
25
25
|
sgis/maps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
26
|
sgis/maps/examine.py,sha256=Pb0dH8JazU5E2svfQrzHO1Bi-sjy5SeyY6zoeMO34jE,9369
|
|
27
|
-
sgis/maps/explore.py,sha256=
|
|
27
|
+
sgis/maps/explore.py,sha256=LO5ESH4e5M_fXIZzvnd-lkGyYmWcARcJYhpcZX6fLsg,47301
|
|
28
28
|
sgis/maps/httpserver.py,sha256=7Od9JMCtntcIQKk_TchetojMHzFHT9sPw7GANahI97c,1982
|
|
29
29
|
sgis/maps/legend.py,sha256=lVRVCkhPmJRjGK23obFJZAO3qp6du1LYnobkkN7DPkc,26279
|
|
30
30
|
sgis/maps/map.py,sha256=smaf9i53EoRZWmZjn9UuqlhzUvVs1XKo2ItIpHxyuik,29592
|
|
31
|
-
sgis/maps/maps.py,sha256=
|
|
31
|
+
sgis/maps/maps.py,sha256=vOB09wiquW7-wGEqHJMotFOBX8tFfnD4gcvvpYf5Wfo,23599
|
|
32
|
+
sgis/maps/norge_i_bilder.json,sha256=W_mFfte3DxugWbEudZ5fadZ2JeFYb0hyab2Quf4oJME,481311
|
|
33
|
+
sgis/maps/norge_i_bilder_wms.py,sha256=Pb1puQFCZfEO_ng915_aOkB17wpZLbRMnUEBAiLPzjQ,5698
|
|
32
34
|
sgis/maps/thematicmap.py,sha256=yAE1xEfubJcDmBlOJf-Q3SVae1ZHIEMP-YB95Wy8cRw,21691
|
|
33
35
|
sgis/maps/tilesources.py,sha256=F4mFHxPwkiPJdVKzNkScTX6xbJAMIUtlTq4mQ83oguw,1746
|
|
34
36
|
sgis/networkanalysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -49,12 +51,12 @@ sgis/parallel/parallel.py,sha256=SlC_mOwvSSyWTKUcxLMGkuWHUkEC6dXTlN0Jn5cAtxA,396
|
|
|
49
51
|
sgis/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
52
|
sgis/raster/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
53
|
sgis/raster/base.py,sha256=tiZEuMcVK6hOm_aIjWhQ1WGshcjsxT1fFkuBSLFiMC0,7785
|
|
52
|
-
sgis/raster/image_collection.py,sha256=
|
|
54
|
+
sgis/raster/image_collection.py,sha256=STPh8m2WRAkuQfuJC1VNA2XLZ6FHT8I7qjcAntVWi2o,122573
|
|
53
55
|
sgis/raster/indices.py,sha256=-J1HYmnT240iozvgagvyis6K0_GHZHRuUrPOgyoeIrY,223
|
|
54
56
|
sgis/raster/regex.py,sha256=kYhVpRYzoXutx1dSYmqMoselWXww7MMEsTPmLZwHjbM,3759
|
|
55
57
|
sgis/raster/sentinel_config.py,sha256=nySDqn2R8M6W8jguoBeSAK_zzbAsqmaI59i32446FwY,1268
|
|
56
58
|
sgis/raster/zonal.py,sha256=D4Gyptw-yOLTCO41peIuYbY-DANsJCG19xXDlf1QAz4,2299
|
|
57
|
-
ssb_sgis-1.0.
|
|
58
|
-
ssb_sgis-1.0.
|
|
59
|
-
ssb_sgis-1.0.
|
|
60
|
-
ssb_sgis-1.0.
|
|
59
|
+
ssb_sgis-1.0.14.dist-info/LICENSE,sha256=np3IfD5m0ZUofn_kVzDZqliozuiO6wrktw3LRPjyEiI,1073
|
|
60
|
+
ssb_sgis-1.0.14.dist-info/METADATA,sha256=fey5-NS1CXLcspl94MR5TNZeGIXjwbQmefO3UCTYkJI,11741
|
|
61
|
+
ssb_sgis-1.0.14.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
62
|
+
ssb_sgis-1.0.14.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|