ssb-sgis 1.2.17__py3-none-any.whl → 1.3.1__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/general.py +5 -1
- sgis/io/dapla_functions.py +5 -5
- sgis/maps/norge_i_bilder.json +9518 -656
- sgis/maps/wms.py +121 -74
- {ssb_sgis-1.2.17.dist-info → ssb_sgis-1.3.1.dist-info}/METADATA +1 -1
- {ssb_sgis-1.2.17.dist-info → ssb_sgis-1.3.1.dist-info}/RECORD +8 -8
- {ssb_sgis-1.2.17.dist-info → ssb_sgis-1.3.1.dist-info}/LICENSE +0 -0
- {ssb_sgis-1.2.17.dist-info → ssb_sgis-1.3.1.dist-info}/WHEEL +0 -0
sgis/maps/wms.py
CHANGED
|
@@ -3,7 +3,6 @@ import datetime
|
|
|
3
3
|
import json
|
|
4
4
|
import re
|
|
5
5
|
from collections.abc import Iterable
|
|
6
|
-
from dataclasses import dataclass
|
|
7
6
|
from io import BytesIO
|
|
8
7
|
from pathlib import Path
|
|
9
8
|
from typing import Any
|
|
@@ -28,20 +27,14 @@ from ..geopandas_tools.conversion import to_shapely
|
|
|
28
27
|
from ..geopandas_tools.sfilter import sfilter
|
|
29
28
|
from ..raster.image_collection import Band
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
JSON_YEARS = [str(year) for year in range(2006, datetime.datetime.now().year + 1)]
|
|
34
|
-
|
|
35
|
-
DEFAULT_YEARS: tuple[str] = tuple(
|
|
36
|
-
str(year)
|
|
37
|
-
for year in range(
|
|
30
|
+
DEFAULT_YEARS: tuple[int] = tuple(
|
|
31
|
+
range(
|
|
38
32
|
int(datetime.datetime.now().year) - 10,
|
|
39
33
|
int(datetime.datetime.now().year) + 1,
|
|
40
34
|
)
|
|
41
35
|
)
|
|
42
36
|
|
|
43
37
|
|
|
44
|
-
@dataclass
|
|
45
38
|
class WmsLoader(abc.ABC):
|
|
46
39
|
"""Abstract base class for wms loaders.
|
|
47
40
|
|
|
@@ -49,32 +42,84 @@ class WmsLoader(abc.ABC):
|
|
|
49
42
|
which should return a list of folium.WmsTileLayer.
|
|
50
43
|
"""
|
|
51
44
|
|
|
45
|
+
_min_year: int = 1900
|
|
46
|
+
|
|
47
|
+
@abc.abstractmethod
|
|
48
|
+
def filter_tiles(
|
|
49
|
+
self, mask: GeoDataFrame | GeoSeries | Geometry | tuple[float]
|
|
50
|
+
) -> list[str]:
|
|
51
|
+
"""Filter relevant dates with pandas and geopandas because fast."""
|
|
52
|
+
|
|
52
53
|
@abc.abstractmethod
|
|
53
54
|
def get_tiles(self, bbox: Any, max_zoom: int = 40) -> list[folium.WmsTileLayer]:
|
|
54
55
|
"""Get all tiles intersecting with a bbox."""
|
|
55
56
|
|
|
56
57
|
@abc.abstractmethod
|
|
57
58
|
def load_tiles(self) -> None:
|
|
58
|
-
"""Load all tiles into self.
|
|
59
|
+
"""Load all tiles into self._tiles.
|
|
59
60
|
|
|
60
61
|
Not needed in sgis.explore.
|
|
61
62
|
"""
|
|
62
63
|
pass
|
|
63
64
|
|
|
65
|
+
def __repr__(self) -> str:
|
|
66
|
+
"""Print representation."""
|
|
67
|
+
return str(self)
|
|
68
|
+
|
|
69
|
+
def __str__(self) -> str:
|
|
70
|
+
"""String representation."""
|
|
71
|
+
|
|
72
|
+
def maybe_to_string(value: Any):
|
|
73
|
+
if isinstance(value, str):
|
|
74
|
+
return f"'{value}'"
|
|
75
|
+
return value
|
|
76
|
+
|
|
77
|
+
txt = ", ".join(
|
|
78
|
+
f"{k}={maybe_to_string(v)}"
|
|
79
|
+
for k, v in self.__dict__.items()
|
|
80
|
+
if not k.startswith("_")
|
|
81
|
+
)
|
|
82
|
+
return f"{self.__class__.__name__}({txt})"
|
|
83
|
+
|
|
64
84
|
|
|
65
|
-
@dataclass
|
|
66
85
|
class NorgeIBilderWms(WmsLoader):
|
|
67
|
-
"""Loads Norge i bilder tiles as folium.WmsTiles.
|
|
86
|
+
"""Loads Norge i bilder tiles as folium.WmsTiles.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
years: list of years to search for images.
|
|
90
|
+
contains: substrings to include in image search.
|
|
91
|
+
not_contains: substrings to exclude in image search.
|
|
92
|
+
show: Whether to show all layers upon initialisation of the map.
|
|
93
|
+
_use_json: Whether to use pre-made json file of image names and bounds/masks
|
|
94
|
+
if all years are within range. Defaults to True (much faster).
|
|
95
|
+
"""
|
|
68
96
|
|
|
69
|
-
years: Iterable[int | str] = DEFAULT_YEARS
|
|
70
|
-
contains: str | Iterable[str] | None = None
|
|
71
|
-
not_contains: str | Iterable[str] | None = None
|
|
72
|
-
show: bool | Iterable[int] | int = False
|
|
73
|
-
_use_json: bool = True
|
|
74
97
|
url: str = "https://wms.geonorge.no/skwms1/wms.nib-prosjekter"
|
|
98
|
+
_min_year: int = 1935
|
|
99
|
+
_json_path = Path(__file__).parent / "norge_i_bilder.json"
|
|
100
|
+
|
|
101
|
+
def __init__(
|
|
102
|
+
self,
|
|
103
|
+
years: Iterable[int | str] = DEFAULT_YEARS,
|
|
104
|
+
contains: str | Iterable[str] | None = None,
|
|
105
|
+
not_contains: str | Iterable[str] | None = None,
|
|
106
|
+
show: bool | Iterable[int] | int = False,
|
|
107
|
+
_use_json: bool = True,
|
|
108
|
+
) -> None:
|
|
109
|
+
"""Initialiser."""
|
|
110
|
+
self.years = [int(year) for year in years]
|
|
111
|
+
self.contains = contains
|
|
112
|
+
self.not_contains = not_contains
|
|
113
|
+
self.show = show
|
|
114
|
+
self._use_json = _use_json
|
|
115
|
+
|
|
116
|
+
if self._use_json:
|
|
117
|
+
self._load_from_json()
|
|
118
|
+
else:
|
|
119
|
+
self._tiles = None
|
|
75
120
|
|
|
76
121
|
def load_tiles(self, verbose: bool = False) -> None:
|
|
77
|
-
"""Load all Norge i bilder tiles into self.
|
|
122
|
+
"""Load all Norge i bilder tiles into self._tiles."""
|
|
78
123
|
name_pattern = r"<Name>(.*?)</Name>"
|
|
79
124
|
bbox_pattern = (
|
|
80
125
|
r"<EX_GeographicBoundingBox>.*?"
|
|
@@ -84,8 +129,12 @@ class NorgeIBilderWms(WmsLoader):
|
|
|
84
129
|
r"<northBoundLatitude>(.*?)</northBoundLatitude>.*?</EX_GeographicBoundingBox>"
|
|
85
130
|
)
|
|
86
131
|
|
|
132
|
+
url: str = (
|
|
133
|
+
"https://wms.geonorge.no/skwms1/wms.nib-prosjekter?SERVICE=WMS&REQUEST=GetCapabilities"
|
|
134
|
+
)
|
|
135
|
+
|
|
87
136
|
all_tiles: list[dict] = []
|
|
88
|
-
with urlopen(
|
|
137
|
+
with urlopen(url) as file:
|
|
89
138
|
xml_data: str = file.read().decode("utf-8")
|
|
90
139
|
|
|
91
140
|
for text in xml_data.split('<Layer queryable="1">')[1:]:
|
|
@@ -105,14 +154,20 @@ class NorgeIBilderWms(WmsLoader):
|
|
|
105
154
|
|
|
106
155
|
if (
|
|
107
156
|
not name
|
|
108
|
-
or not any(year in name for year in self.years)
|
|
157
|
+
or not any(str(year) in name for year in self.years)
|
|
109
158
|
or (
|
|
110
159
|
self.contains
|
|
111
|
-
and not any(
|
|
160
|
+
and not any(
|
|
161
|
+
re.search(x, name.lower())
|
|
162
|
+
for x in _string_as_list(self.contains)
|
|
163
|
+
)
|
|
112
164
|
)
|
|
113
165
|
or (
|
|
114
166
|
self.not_contains
|
|
115
|
-
and any(
|
|
167
|
+
and any(
|
|
168
|
+
re.search(x, name.lower())
|
|
169
|
+
for x in _string_as_list(self.not_contains)
|
|
170
|
+
)
|
|
116
171
|
)
|
|
117
172
|
):
|
|
118
173
|
continue
|
|
@@ -131,10 +186,10 @@ class NorgeIBilderWms(WmsLoader):
|
|
|
131
186
|
|
|
132
187
|
all_tiles.append(this_tile)
|
|
133
188
|
|
|
134
|
-
self.
|
|
189
|
+
self._tiles = sorted(all_tiles, key=lambda x: (x["year"]))
|
|
135
190
|
|
|
136
191
|
masks = self._get_norge_i_bilder_polygon_masks(verbose=verbose)
|
|
137
|
-
for tile in self.
|
|
192
|
+
for tile in self._tiles:
|
|
138
193
|
mask = masks.get(tile["name"], None)
|
|
139
194
|
tile["geometry"] = mask
|
|
140
195
|
|
|
@@ -143,7 +198,7 @@ class NorgeIBilderWms(WmsLoader):
|
|
|
143
198
|
from owslib.wms import WebMapService
|
|
144
199
|
from PIL import Image
|
|
145
200
|
|
|
146
|
-
relevant_names: dict[str, str] = {x["name"]: x["bbox"] for x in self.
|
|
201
|
+
relevant_names: dict[str, str] = {x["name"]: x["bbox"] for x in self._tiles}
|
|
147
202
|
assert len(relevant_names), relevant_names
|
|
148
203
|
|
|
149
204
|
url = "https://wms.geonorge.no/skwms1/wms.nib-mosaikk?SERVICE=WMS&REQUEST=GetCapabilities"
|
|
@@ -214,7 +269,7 @@ class NorgeIBilderWms(WmsLoader):
|
|
|
214
269
|
|
|
215
270
|
def get_tiles(self, mask: Any, max_zoom: int = 40) -> list[folium.WmsTileLayer]:
|
|
216
271
|
"""Get all Norge i bilder tiles intersecting with a mask (bbox or polygon)."""
|
|
217
|
-
if self.
|
|
272
|
+
if self._tiles is None:
|
|
218
273
|
self.load_tiles()
|
|
219
274
|
|
|
220
275
|
if not isinstance(mask, (GeoSeries | GeoDataFrame | Geometry)):
|
|
@@ -225,7 +280,7 @@ class NorgeIBilderWms(WmsLoader):
|
|
|
225
280
|
else:
|
|
226
281
|
show = False
|
|
227
282
|
|
|
228
|
-
relevant_tiles = self.
|
|
283
|
+
relevant_tiles = self.filter_tiles(mask)
|
|
229
284
|
tile_layers = {
|
|
230
285
|
name: folium.WmsTileLayer(
|
|
231
286
|
url="https://wms.geonorge.no/skwms1/wms.nib-prosjekter",
|
|
@@ -238,7 +293,7 @@ class NorgeIBilderWms(WmsLoader):
|
|
|
238
293
|
show=show,
|
|
239
294
|
max_zoom=max_zoom,
|
|
240
295
|
)
|
|
241
|
-
for name in relevant_tiles
|
|
296
|
+
for name in relevant_tiles
|
|
242
297
|
}
|
|
243
298
|
|
|
244
299
|
if not len(tile_layers):
|
|
@@ -254,59 +309,51 @@ class NorgeIBilderWms(WmsLoader):
|
|
|
254
309
|
|
|
255
310
|
return tile_layers
|
|
256
311
|
|
|
257
|
-
def
|
|
312
|
+
def filter_tiles(
|
|
313
|
+
self, mask: GeoDataFrame | GeoSeries | Geometry | tuple[float]
|
|
314
|
+
) -> list[str]:
|
|
258
315
|
"""Filter relevant dates with pandas and geopandas because fast."""
|
|
259
|
-
if not self.
|
|
260
|
-
return
|
|
261
|
-
df = pd.DataFrame(self.
|
|
262
|
-
filt = (df["name"].notna()) & (
|
|
316
|
+
if not self._tiles:
|
|
317
|
+
return []
|
|
318
|
+
df = pd.DataFrame(self._tiles)
|
|
319
|
+
filt = (df["name"].notna()) & (
|
|
320
|
+
df["year"].str.contains("|".join([str(year) for year in self.years]))
|
|
321
|
+
)
|
|
263
322
|
if self.contains:
|
|
264
|
-
for x in self.contains:
|
|
323
|
+
for x in _string_as_list(self.contains):
|
|
265
324
|
filt &= df["name"].str.contains(x)
|
|
266
325
|
if self.not_contains:
|
|
267
|
-
for x in self.not_contains:
|
|
326
|
+
for x in _string_as_list(self.not_contains):
|
|
268
327
|
filt &= ~df["name"].str.contains(x)
|
|
269
328
|
df = df[filt]
|
|
270
329
|
geoms = np.where(df["geometry"].notna(), df["geometry"], df["bbox"])
|
|
271
330
|
geoms = GeoSeries(geoms)
|
|
272
331
|
assert geoms.index.is_unique
|
|
273
|
-
return df.iloc[sfilter(geoms, mask).index]
|
|
274
|
-
|
|
275
|
-
def
|
|
276
|
-
"""
|
|
277
|
-
|
|
278
|
-
self.
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
self.tiles = [
|
|
296
|
-
{
|
|
297
|
-
key: (
|
|
298
|
-
value
|
|
299
|
-
if key not in ["bbox", "geometry"]
|
|
300
|
-
else shapely.wkt.loads(value)
|
|
301
|
-
)
|
|
302
|
-
for key, value in tile.items()
|
|
303
|
-
}
|
|
304
|
-
for tile in self.tiles
|
|
305
|
-
if any(str(year) in tile["name"] for year in self.years)
|
|
306
|
-
]
|
|
307
|
-
else:
|
|
308
|
-
self.tiles = None
|
|
332
|
+
return list(df.iloc[sfilter(geoms, mask).index]["name"])
|
|
333
|
+
|
|
334
|
+
def _load_from_json(self) -> None:
|
|
335
|
+
"""Load tiles from json file."""
|
|
336
|
+
try:
|
|
337
|
+
with open(self._json_path, encoding="utf-8") as file:
|
|
338
|
+
self._tiles = json.load(file)
|
|
339
|
+
except FileNotFoundError:
|
|
340
|
+
self._tiles = None
|
|
341
|
+
return
|
|
342
|
+
self._tiles = [
|
|
343
|
+
{
|
|
344
|
+
key: (
|
|
345
|
+
value
|
|
346
|
+
if key not in ["bbox", "geometry"]
|
|
347
|
+
else shapely.wkt.loads(value)
|
|
348
|
+
)
|
|
349
|
+
for key, value in tile.items()
|
|
350
|
+
}
|
|
351
|
+
for tile in self._tiles
|
|
352
|
+
if any(str(year) in tile["name"] for year in self.years)
|
|
353
|
+
]
|
|
309
354
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
355
|
+
|
|
356
|
+
def _string_as_list(x: str | list[str]) -> list[str] | None:
|
|
357
|
+
if not x:
|
|
358
|
+
return None
|
|
359
|
+
return [x] if isinstance(x, str) else list(x)
|
|
@@ -9,7 +9,7 @@ sgis/geopandas_tools/centerlines.py,sha256=Q65Sx01SeAlulBEd9oaZkB2maBBNdLcJwAbTI
|
|
|
9
9
|
sgis/geopandas_tools/cleaning.py,sha256=fST0xFztmyn-QUOAfvjZmu7aO_zPiolWK7gd7TR6ffI,24393
|
|
10
10
|
sgis/geopandas_tools/conversion.py,sha256=iX954YEpobmn_R1Ecx_zDU1RlWq_67pfbzMXtwTH04I,24162
|
|
11
11
|
sgis/geopandas_tools/duplicates.py,sha256=TDDM4u1n7SIkyJrOfl1Lno92AmUPqtXBHsj1IUKC0hI,14992
|
|
12
|
-
sgis/geopandas_tools/general.py,sha256=
|
|
12
|
+
sgis/geopandas_tools/general.py,sha256=DTQM8p-krgR3gA3miP931eCrxDCpF1ya7dRiMy3K_bs,44099
|
|
13
13
|
sgis/geopandas_tools/geocoding.py,sha256=sZjUW52ULhQWDLmU51C9_itBePkDuWkp8swvYaiYmJk,679
|
|
14
14
|
sgis/geopandas_tools/geometry_types.py,sha256=ijQDbQaZPqPGjBl707H4yooNXpk21RXyatI7itnvqLk,7603
|
|
15
15
|
sgis/geopandas_tools/neighbors.py,sha256=VZGOwwC3-C6KpwLQ3j0K5cOVInmckxIXoGMqPGkemk4,17606
|
|
@@ -23,7 +23,7 @@ sgis/geopandas_tools/utils.py,sha256=X0pRvB1tWgV_0BCrRS1HU9LtLGnZCpvVPxyqM9JGb0Y
|
|
|
23
23
|
sgis/helpers.py,sha256=4N6vFWQ3TYVzRHNcWY_fNa_GkFuaZB3vtCkkFND-qs0,9628
|
|
24
24
|
sgis/io/__init__.py,sha256=uyBr20YDqB2bQttrd5q1JuGOvX32A-MSvS7Wmw5f5qg,177
|
|
25
25
|
sgis/io/_is_dapla.py,sha256=wmfkSe98IrLhUg3dtXZusV6OVC8VlY1kbc5EQDf3P-Q,358
|
|
26
|
-
sgis/io/dapla_functions.py,sha256=
|
|
26
|
+
sgis/io/dapla_functions.py,sha256=YkS2QqNyZ_OcZXXUKnHEItvnO9vZ22k7RK30p-kGl0E,31861
|
|
27
27
|
sgis/io/opener.py,sha256=HWO3G1NB6bpXKM94JadCD513vjat1o1TFjWGWzyVasg,898
|
|
28
28
|
sgis/io/read_parquet.py,sha256=FvZYv1rLkUlrSaUY6QW6E1yntmntTeQuZ9ZRgCDO4IM,3776
|
|
29
29
|
sgis/maps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -33,10 +33,10 @@ sgis/maps/httpserver.py,sha256=I7tTn3hFaTCc-E-T_o9v0nXwMGaS2Xqd4MlWbq8k-J4,3014
|
|
|
33
33
|
sgis/maps/legend.py,sha256=gTEWCVIZH1cw6ULVVrJqRR4__uOZvrT3xxQ5lhZMVR4,26899
|
|
34
34
|
sgis/maps/map.py,sha256=iGa0o7NlUMErJCYJpVzkpDvuWJc8iDN_-tH2X2WcBlI,30802
|
|
35
35
|
sgis/maps/maps.py,sha256=fLK5WUlQ2YTm7t-8260lYxCFvpZN6j0Y-bVYCyv8NAY,23249
|
|
36
|
-
sgis/maps/norge_i_bilder.json,sha256=
|
|
36
|
+
sgis/maps/norge_i_bilder.json,sha256=wUYY8VTanI-C4bpNk7bmnQb8S-S3CBUIRLgdOoydnoQ,20197311
|
|
37
37
|
sgis/maps/thematicmap.py,sha256=ZtV4Hfylr1ST_cPzi11_lFIsTdY3D1o1EZQbPXZLwyM,25187
|
|
38
38
|
sgis/maps/tilesources.py,sha256=F4mFHxPwkiPJdVKzNkScTX6xbJAMIUtlTq4mQ83oguw,1746
|
|
39
|
-
sgis/maps/wms.py,sha256=
|
|
39
|
+
sgis/maps/wms.py,sha256=sCVpKxH1Rsd14GECW7BFh8yaWngpVWYvw9Yhuez1yW8,12482
|
|
40
40
|
sgis/networkanalysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
41
|
sgis/networkanalysis/_get_route.py,sha256=3m5xQdQqGtt51vcI5fcmYQAOQCeBtL6sorDoPxBNf84,7818
|
|
42
42
|
sgis/networkanalysis/_od_cost_matrix.py,sha256=zkyPX7ObT996ahaFJ2oI0D0SqQWbWyfy_qLtXwValPg,3434
|
|
@@ -61,7 +61,7 @@ sgis/raster/indices.py,sha256=efJmgfPg_VuSzXFosXV661IendF8CwPFWtMhyP4TMUg,222
|
|
|
61
61
|
sgis/raster/regex.py,sha256=4idTJ9vFtsGtbxcjJrx2VrpJJuDMP3bLdqF93Vc_cmY,3752
|
|
62
62
|
sgis/raster/sentinel_config.py,sha256=nySDqn2R8M6W8jguoBeSAK_zzbAsqmaI59i32446FwY,1268
|
|
63
63
|
sgis/raster/zonal.py,sha256=D4Gyptw-yOLTCO41peIuYbY-DANsJCG19xXDlf1QAz4,2299
|
|
64
|
-
ssb_sgis-1.
|
|
65
|
-
ssb_sgis-1.
|
|
66
|
-
ssb_sgis-1.
|
|
67
|
-
ssb_sgis-1.
|
|
64
|
+
ssb_sgis-1.3.1.dist-info/LICENSE,sha256=np3IfD5m0ZUofn_kVzDZqliozuiO6wrktw3LRPjyEiI,1073
|
|
65
|
+
ssb_sgis-1.3.1.dist-info/METADATA,sha256=mDvP7_0YnM5lKhKcu3IsX3OYwc-vMIL8mT09tBR_u6o,11624
|
|
66
|
+
ssb_sgis-1.3.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
67
|
+
ssb_sgis-1.3.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|