ssb-sgis 1.0.0__py3-none-any.whl → 1.0.2__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 +97 -115
- sgis/exceptions.py +3 -1
- sgis/geopandas_tools/__init__.py +1 -0
- sgis/geopandas_tools/bounds.py +75 -38
- sgis/geopandas_tools/buffer_dissolve_explode.py +38 -34
- sgis/geopandas_tools/centerlines.py +53 -44
- sgis/geopandas_tools/cleaning.py +87 -104
- sgis/geopandas_tools/conversion.py +149 -101
- sgis/geopandas_tools/duplicates.py +31 -17
- sgis/geopandas_tools/general.py +76 -48
- sgis/geopandas_tools/geometry_types.py +21 -7
- sgis/geopandas_tools/neighbors.py +20 -8
- sgis/geopandas_tools/overlay.py +136 -53
- sgis/geopandas_tools/point_operations.py +9 -8
- sgis/geopandas_tools/polygon_operations.py +48 -56
- sgis/geopandas_tools/polygons_as_rings.py +121 -78
- sgis/geopandas_tools/sfilter.py +14 -14
- sgis/helpers.py +114 -56
- sgis/io/dapla_functions.py +32 -23
- sgis/io/opener.py +13 -6
- sgis/io/read_parquet.py +1 -1
- sgis/maps/examine.py +39 -26
- sgis/maps/explore.py +112 -66
- sgis/maps/httpserver.py +12 -12
- sgis/maps/legend.py +124 -65
- sgis/maps/map.py +66 -41
- sgis/maps/maps.py +31 -29
- sgis/maps/thematicmap.py +46 -33
- sgis/maps/tilesources.py +3 -8
- sgis/networkanalysis/_get_route.py +5 -4
- sgis/networkanalysis/_od_cost_matrix.py +44 -1
- sgis/networkanalysis/_points.py +10 -4
- sgis/networkanalysis/_service_area.py +5 -2
- sgis/networkanalysis/closing_network_holes.py +20 -62
- sgis/networkanalysis/cutting_lines.py +55 -43
- sgis/networkanalysis/directednetwork.py +15 -7
- sgis/networkanalysis/finding_isolated_networks.py +4 -3
- sgis/networkanalysis/network.py +15 -13
- sgis/networkanalysis/networkanalysis.py +72 -54
- sgis/networkanalysis/networkanalysisrules.py +20 -16
- sgis/networkanalysis/nodes.py +2 -3
- sgis/networkanalysis/traveling_salesman.py +5 -2
- sgis/parallel/parallel.py +337 -127
- sgis/raster/__init__.py +6 -0
- sgis/raster/base.py +9 -3
- sgis/raster/cube.py +280 -208
- sgis/raster/cubebase.py +15 -29
- sgis/raster/indices.py +3 -7
- sgis/raster/methods_as_functions.py +0 -124
- sgis/raster/raster.py +313 -127
- sgis/raster/torchgeo.py +58 -37
- sgis/raster/zonal.py +38 -13
- {ssb_sgis-1.0.0.dist-info → ssb_sgis-1.0.2.dist-info}/LICENSE +1 -1
- {ssb_sgis-1.0.0.dist-info → ssb_sgis-1.0.2.dist-info}/METADATA +89 -18
- ssb_sgis-1.0.2.dist-info/RECORD +61 -0
- {ssb_sgis-1.0.0.dist-info → ssb_sgis-1.0.2.dist-info}/WHEEL +1 -1
- sgis/raster/bands.py +0 -48
- sgis/raster/gradient.py +0 -78
- ssb_sgis-1.0.0.dist-info/RECORD +0 -63
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import numbers
|
|
2
2
|
import re
|
|
3
|
-
from collections.abc import
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from collections.abc import Collection
|
|
5
|
+
from collections.abc import Iterable
|
|
6
|
+
from collections.abc import Iterator
|
|
7
|
+
from collections.abc import Mapping
|
|
8
|
+
from collections.abc import Sized
|
|
4
9
|
from typing import Any
|
|
5
10
|
|
|
6
11
|
import geopandas as gpd
|
|
@@ -10,38 +15,44 @@ import pyproj
|
|
|
10
15
|
import rasterio
|
|
11
16
|
import shapely
|
|
12
17
|
from affine import Affine
|
|
13
|
-
from geopandas import GeoDataFrame
|
|
14
|
-
from
|
|
18
|
+
from geopandas import GeoDataFrame
|
|
19
|
+
from geopandas import GeoSeries
|
|
20
|
+
from numpy.typing import NDArray
|
|
21
|
+
from pandas.api.types import is_array_like
|
|
22
|
+
from pandas.api.types import is_dict_like
|
|
23
|
+
from pandas.api.types import is_list_like
|
|
15
24
|
from pyproj import CRS
|
|
16
25
|
from rasterio import features
|
|
17
|
-
from shapely import Geometry
|
|
26
|
+
from shapely import Geometry
|
|
27
|
+
from shapely import box
|
|
28
|
+
from shapely import wkb
|
|
29
|
+
from shapely import wkt
|
|
18
30
|
from shapely.errors import GEOSException
|
|
19
|
-
from shapely.geometry import Point
|
|
31
|
+
from shapely.geometry import Point
|
|
32
|
+
from shapely.geometry import shape
|
|
20
33
|
from shapely.ops import unary_union
|
|
21
34
|
|
|
22
|
-
|
|
23
35
|
try:
|
|
24
36
|
from torchgeo.datasets.geo import RasterDataset
|
|
25
37
|
except ImportError:
|
|
26
38
|
|
|
27
|
-
class RasterDataset:
|
|
28
|
-
"""Placeholder"""
|
|
39
|
+
class RasterDataset: # type: ignore
|
|
40
|
+
"""Placeholder."""
|
|
29
41
|
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
|
|
43
|
+
def crs_to_string(crs: Any) -> str:
|
|
44
|
+
"""Extract the string of a CRS-like object."""
|
|
33
45
|
if crs is None:
|
|
34
46
|
return "None"
|
|
35
47
|
crs = pyproj.CRS(crs)
|
|
36
48
|
crs_str = str(crs.to_json_dict()["name"])
|
|
37
49
|
pattern = r"\d{4,5}"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
except AttributeError:
|
|
41
|
-
return crs_str
|
|
50
|
+
match = re.search(pattern, crs_str)
|
|
51
|
+
return match.group() if match else crs_str
|
|
42
52
|
|
|
43
53
|
|
|
44
54
|
def to_geoseries(obj: Any, crs: Any | None = None) -> GeoSeries:
|
|
55
|
+
"""Convert an object to GeoSeries."""
|
|
45
56
|
if crs is None:
|
|
46
57
|
try:
|
|
47
58
|
crs = obj.crs
|
|
@@ -73,19 +84,30 @@ def to_geoseries(obj: Any, crs: Any | None = None) -> GeoSeries:
|
|
|
73
84
|
return GeoSeries(obj, index=index, crs=crs)
|
|
74
85
|
|
|
75
86
|
|
|
76
|
-
def to_shapely(obj) -> Geometry:
|
|
87
|
+
def to_shapely(obj: Any) -> Geometry:
|
|
88
|
+
"""Convert a geometry object or bounding box to a shapely Geometry."""
|
|
77
89
|
if isinstance(obj, Geometry):
|
|
78
90
|
return obj
|
|
79
91
|
if not hasattr(obj, "__iter__"):
|
|
80
92
|
raise TypeError(type(obj))
|
|
81
93
|
if hasattr(obj, "unary_union"):
|
|
82
94
|
return obj.unary_union
|
|
83
|
-
# if is_bbox_like(obj):
|
|
84
|
-
# return box(*obj)
|
|
85
95
|
try:
|
|
86
96
|
return Point(*obj)
|
|
87
97
|
except TypeError:
|
|
98
|
+
pass
|
|
99
|
+
try:
|
|
88
100
|
return box(*to_bbox(obj))
|
|
101
|
+
except TypeError:
|
|
102
|
+
pass
|
|
103
|
+
try:
|
|
104
|
+
return shapely.wkt.loads(obj)
|
|
105
|
+
except TypeError:
|
|
106
|
+
pass
|
|
107
|
+
try:
|
|
108
|
+
return shapely.wkb.loads(obj)
|
|
109
|
+
except TypeError:
|
|
110
|
+
pass
|
|
89
111
|
|
|
90
112
|
|
|
91
113
|
def to_bbox(
|
|
@@ -105,24 +127,24 @@ def to_bbox(
|
|
|
105
127
|
return tuple(obj.bounds)
|
|
106
128
|
|
|
107
129
|
try:
|
|
108
|
-
minx = int(np.min(obj["minx"]))
|
|
109
|
-
miny = int(np.min(obj["miny"]))
|
|
110
|
-
maxx = int(np.max(obj["maxx"]))
|
|
111
|
-
maxy = int(np.max(obj["maxy"]))
|
|
130
|
+
minx = int(np.min(obj["minx"])) # type: ignore [index]
|
|
131
|
+
miny = int(np.min(obj["miny"])) # type: ignore [index]
|
|
132
|
+
maxx = int(np.max(obj["maxx"])) # type: ignore [index]
|
|
133
|
+
maxy = int(np.max(obj["maxy"])) # type: ignore [index]
|
|
112
134
|
return minx, miny, maxx, maxy
|
|
113
135
|
except Exception:
|
|
114
136
|
try:
|
|
115
|
-
minx = int(np.min(obj.minx))
|
|
116
|
-
miny = int(np.min(obj.miny))
|
|
117
|
-
maxx = int(np.max(obj.maxx))
|
|
118
|
-
maxy = int(np.max(obj.maxy))
|
|
137
|
+
minx = int(np.min(obj.minx)) # type: ignore [union-attr]
|
|
138
|
+
miny = int(np.min(obj.miny)) # type: ignore [union-attr]
|
|
139
|
+
maxx = int(np.max(obj.maxx)) # type: ignore [union-attr]
|
|
140
|
+
maxy = int(np.max(obj.maxy)) # type: ignore [union-attr]
|
|
119
141
|
return minx, miny, maxx, maxy
|
|
120
142
|
except Exception:
|
|
121
143
|
pass
|
|
122
144
|
|
|
123
145
|
if hasattr(obj, "geometry"):
|
|
124
146
|
try:
|
|
125
|
-
return tuple(GeoSeries(obj["geometry"]).total_bounds)
|
|
147
|
+
return tuple(GeoSeries(obj["geometry"]).total_bounds) # type: ignore [index]
|
|
126
148
|
except Exception:
|
|
127
149
|
return tuple(GeoSeries(obj.geometry).total_bounds)
|
|
128
150
|
|
|
@@ -140,7 +162,7 @@ def to_bbox(
|
|
|
140
162
|
raise TypeError(f"Cannot convert type {obj.__class__.__name__}{of_length} to bbox")
|
|
141
163
|
|
|
142
164
|
|
|
143
|
-
def from_4326(lon: float, lat: float, crs=25833):
|
|
165
|
+
def from_4326(lon: float, lat: float, crs=25833) -> tuple[float, float]:
|
|
144
166
|
"""Get utm 33 N coordinates from lonlat (4326)."""
|
|
145
167
|
transformer = pyproj.Transformer.from_crs(
|
|
146
168
|
"EPSG:4326", f"EPSG:{crs}", always_xy=True
|
|
@@ -148,7 +170,7 @@ def from_4326(lon: float, lat: float, crs=25833):
|
|
|
148
170
|
return transformer.transform(lon, lat)
|
|
149
171
|
|
|
150
172
|
|
|
151
|
-
def to_4326(lon: float, lat: float, crs=25833):
|
|
173
|
+
def to_4326(lon: float, lat: float, crs=25833) -> tuple[float, float]:
|
|
152
174
|
"""Get degree coordinates 33 N coordinates from lonlat (4326)."""
|
|
153
175
|
transformer = pyproj.Transformer.from_crs(
|
|
154
176
|
f"EPSG:{crs}", "EPSG:4326", always_xy=True
|
|
@@ -158,20 +180,37 @@ def to_4326(lon: float, lat: float, crs=25833):
|
|
|
158
180
|
|
|
159
181
|
def coordinate_array(
|
|
160
182
|
gdf: GeoDataFrame | GeoSeries,
|
|
161
|
-
strict=False,
|
|
162
|
-
|
|
183
|
+
strict: bool = False,
|
|
184
|
+
include_z: bool = False,
|
|
185
|
+
) -> NDArray[np.float64]:
|
|
163
186
|
"""Creates a 2d ndarray of coordinates from point geometries.
|
|
164
187
|
|
|
165
188
|
Args:
|
|
166
189
|
gdf: GeoDataFrame or GeoSeries of point geometries.
|
|
190
|
+
strict: If False (default), geometries without coordinates
|
|
191
|
+
are given the value None.
|
|
192
|
+
include_z: Whether to include z-coordinates. Defaults to False.
|
|
167
193
|
|
|
168
194
|
Returns:
|
|
169
195
|
np.ndarray of np.ndarrays of coordinates.
|
|
170
196
|
|
|
171
|
-
Examples
|
|
197
|
+
Examples:
|
|
172
198
|
--------
|
|
173
|
-
>>>
|
|
174
|
-
>>> points =
|
|
199
|
+
>>> import sgis as sg
|
|
200
|
+
>>> points = sg.to_gdf(
|
|
201
|
+
... [
|
|
202
|
+
... (0, 1),
|
|
203
|
+
... (1, 0),
|
|
204
|
+
... (1, 1),
|
|
205
|
+
... (0, 0),
|
|
206
|
+
... (0.5, 0.5),
|
|
207
|
+
... (0.5, 0.25),
|
|
208
|
+
... (0.25, 0.25),
|
|
209
|
+
... (0.75, 0.75),
|
|
210
|
+
... (0.25, 0.75),
|
|
211
|
+
... (0.75, 0.25),
|
|
212
|
+
... ]
|
|
213
|
+
... )
|
|
175
214
|
>>> points
|
|
176
215
|
geometry
|
|
177
216
|
0 POINT (0.59376 0.92577)
|
|
@@ -179,26 +218,23 @@ def coordinate_array(
|
|
|
179
218
|
2 POINT (0.74841 0.10627)
|
|
180
219
|
3 POINT (0.00966 0.87868)
|
|
181
220
|
4 POINT (0.38046 0.87879)
|
|
182
|
-
>>> coordinate_array(points)
|
|
221
|
+
>>> sg.coordinate_array(points)
|
|
183
222
|
array([[0.59376221, 0.92577159],
|
|
184
223
|
[0.34074678, 0.91650446],
|
|
185
224
|
[0.74840912, 0.10626954],
|
|
186
225
|
[0.00965935, 0.87867915],
|
|
187
226
|
[0.38045827, 0.87878816]])
|
|
188
|
-
>>> coordinate_array(points.geometry)
|
|
227
|
+
>>> sg.coordinate_array(points.geometry)
|
|
189
228
|
array([[0.59376221, 0.92577159],
|
|
190
229
|
[0.34074678, 0.91650446],
|
|
191
230
|
[0.74840912, 0.10626954],
|
|
192
231
|
[0.00965935, 0.87867915],
|
|
193
232
|
[0.38045827, 0.87878816]])
|
|
194
233
|
"""
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
return
|
|
199
|
-
return np.array(
|
|
200
|
-
[(geom.x, geom.y) if hasattr(geom, "x") else (None, None) for geom in gdf]
|
|
201
|
-
)
|
|
234
|
+
try:
|
|
235
|
+
return shapely.get_coordinates(gdf.geometry.values, include_z=include_z)
|
|
236
|
+
except AttributeError:
|
|
237
|
+
return shapely.get_coordinates(gdf, include_z=include_z)
|
|
202
238
|
|
|
203
239
|
|
|
204
240
|
def to_gdf(
|
|
@@ -242,18 +278,18 @@ def to_gdf(
|
|
|
242
278
|
Returns:
|
|
243
279
|
A GeoDataFrame with one column, the geometry column.
|
|
244
280
|
|
|
245
|
-
Examples
|
|
281
|
+
Examples:
|
|
246
282
|
--------
|
|
247
|
-
>>>
|
|
283
|
+
>>> import sgis as sg
|
|
248
284
|
>>> coords = (10, 60)
|
|
249
|
-
>>> to_gdf(coords, crs=4326)
|
|
285
|
+
>>> sg.to_gdf(coords, crs=4326)
|
|
250
286
|
geometry
|
|
251
287
|
0 POINT (10.00000 60.00000)
|
|
252
288
|
|
|
253
289
|
From wkt.
|
|
254
290
|
|
|
255
291
|
>>> wkt = "POINT (10 60)"
|
|
256
|
-
>>> to_gdf(wkt, crs=4326)
|
|
292
|
+
>>> sg.to_gdf(wkt, crs=4326)
|
|
257
293
|
geometry
|
|
258
294
|
0 POINT (10.00000 60.00000)
|
|
259
295
|
|
|
@@ -264,7 +300,7 @@ def to_gdf(
|
|
|
264
300
|
x y
|
|
265
301
|
1 10 60
|
|
266
302
|
3 11 59
|
|
267
|
-
>>> gdf = to_gdf(df, geometry=["x", "y"], crs=4326)
|
|
303
|
+
>>> gdf = sg.to_gdf(df, geometry=["x", "y"], crs=4326)
|
|
268
304
|
>>> gdf
|
|
269
305
|
x y geometry
|
|
270
306
|
1 10 60 POINT (10.00000 60.00000)
|
|
@@ -278,7 +314,7 @@ def to_gdf(
|
|
|
278
314
|
col geometry
|
|
279
315
|
0 1 point (10 60)
|
|
280
316
|
1 2 (11, 59)
|
|
281
|
-
>>> gdf = to_gdf(df, crs=4326)
|
|
317
|
+
>>> gdf = sg.to_gdf(df, crs=4326)
|
|
282
318
|
>>> gdf
|
|
283
319
|
col geometry
|
|
284
320
|
0 1 POINT (10.00000 60.00000)
|
|
@@ -287,7 +323,7 @@ def to_gdf(
|
|
|
287
323
|
From Series.
|
|
288
324
|
|
|
289
325
|
>>> series = Series({1: (10, 60), 3: (11, 59)})
|
|
290
|
-
>>> to_gdf(series)
|
|
326
|
+
>>> sg.to_gdf(series)
|
|
291
327
|
geometry
|
|
292
328
|
1 POINT (10.00000 60.00000)
|
|
293
329
|
3 POINT (11.00000 59.00000)
|
|
@@ -296,20 +332,20 @@ def to_gdf(
|
|
|
296
332
|
is constructed beforehand.
|
|
297
333
|
|
|
298
334
|
>>> coordslist = [(10, 60), (11, 59)]
|
|
299
|
-
>>> to_gdf(coordslist, crs=4326)
|
|
335
|
+
>>> sg.to_gdf(coordslist, crs=4326)
|
|
300
336
|
geometry
|
|
301
337
|
0 POINT (10.00000 60.00000)
|
|
302
338
|
1 POINT (11.00000 59.00000)
|
|
303
339
|
|
|
304
340
|
>>> from shapely.geometry import LineString
|
|
305
|
-
>>> to_gdf(LineString(coordslist), crs=4326)
|
|
341
|
+
>>> sg.to_gdf(LineString(coordslist), crs=4326)
|
|
306
342
|
geometry
|
|
307
343
|
0 LINESTRING (10.00000 60.00000, 11.00000 59.00000)
|
|
308
344
|
|
|
309
345
|
From 2 or 3 dimensional array.
|
|
310
346
|
|
|
311
347
|
>>> arr = np.random.randint(100, size=(5, 3))
|
|
312
|
-
>>> to_gdf(arr)
|
|
348
|
+
>>> sg.to_gdf(arr)
|
|
313
349
|
geometry
|
|
314
350
|
0 POINT Z (82.000 88.000 82.000)
|
|
315
351
|
1 POINT Z (70.000 92.000 20.000)
|
|
@@ -333,17 +369,25 @@ def to_gdf(
|
|
|
333
369
|
|
|
334
370
|
if crs is None:
|
|
335
371
|
try:
|
|
336
|
-
crs = obj.crs
|
|
372
|
+
crs = obj.crs # type: ignore
|
|
337
373
|
except AttributeError:
|
|
338
374
|
try:
|
|
339
|
-
matches = re.search(r"SRID=(\d+);", obj)
|
|
375
|
+
matches = re.search(r"SRID=(\d+);", obj) # type: ignore
|
|
340
376
|
except TypeError:
|
|
341
377
|
try:
|
|
342
|
-
matches = re.search(r"SRID=(\d+);", obj[0])
|
|
378
|
+
matches = re.search(r"SRID=(\d+);", obj[0]) # type: ignore
|
|
343
379
|
except Exception:
|
|
344
380
|
pass
|
|
345
381
|
try:
|
|
346
|
-
crs = CRS(
|
|
382
|
+
crs = CRS(
|
|
383
|
+
int(
|
|
384
|
+
"".join(
|
|
385
|
+
x
|
|
386
|
+
for x in matches.group(0) # type:ignore
|
|
387
|
+
if x.isnumeric()
|
|
388
|
+
)
|
|
389
|
+
)
|
|
390
|
+
)
|
|
347
391
|
except Exception:
|
|
348
392
|
pass
|
|
349
393
|
|
|
@@ -362,13 +406,13 @@ def to_gdf(
|
|
|
362
406
|
crs=crs,
|
|
363
407
|
)
|
|
364
408
|
|
|
365
|
-
if is_array_like(geometry) and len(geometry) == len(obj):
|
|
409
|
+
if is_array_like(geometry) and len(geometry) == len(obj): # type: ignore
|
|
366
410
|
geometry = GeoSeries(
|
|
367
|
-
_make_one_shapely_geom(g) for g in geometry if g is not None
|
|
411
|
+
_make_one_shapely_geom(g) for g in geometry if g is not None # type: ignore
|
|
368
412
|
)
|
|
369
413
|
return GeoDataFrame(obj, geometry=geometry, crs=crs, **kwargs)
|
|
370
414
|
|
|
371
|
-
geom_col: str =
|
|
415
|
+
geom_col: str = _find_geometry_column(obj, geometry) # type: ignore[no-redef]
|
|
372
416
|
index = kwargs.pop("index", None)
|
|
373
417
|
|
|
374
418
|
# get done with iterators that get consumed by 'all'
|
|
@@ -393,7 +437,7 @@ def to_gdf(
|
|
|
393
437
|
if is_nested_geojson(obj):
|
|
394
438
|
# crs = crs or get_crs_from_dict(obj)
|
|
395
439
|
obj = pd.concat(
|
|
396
|
-
(GeoSeries(_from_json(g)) for g in obj if g is not None),
|
|
440
|
+
(GeoSeries(_from_json(g)) for g in obj if g is not None), # type: ignore
|
|
397
441
|
ignore_index=True,
|
|
398
442
|
)
|
|
399
443
|
if index is not None:
|
|
@@ -401,14 +445,14 @@ def to_gdf(
|
|
|
401
445
|
return GeoDataFrame({geom_col: obj}, geometry=geom_col, crs=crs, **kwargs)
|
|
402
446
|
# list etc.
|
|
403
447
|
else:
|
|
404
|
-
obj = GeoSeries(
|
|
448
|
+
obj = GeoSeries(_make_shapely_geoms(obj), index=index)
|
|
405
449
|
return GeoDataFrame(
|
|
406
450
|
{geom_col: obj}, geometry=geom_col, index=index, crs=crs, **kwargs
|
|
407
451
|
)
|
|
408
452
|
|
|
409
453
|
# now we have dict, Series or DataFrame
|
|
410
454
|
|
|
411
|
-
obj = obj.copy()
|
|
455
|
+
obj = obj.copy() # type: ignore [union-attr]
|
|
412
456
|
|
|
413
457
|
# preserve Series/DataFrame index
|
|
414
458
|
index = obj.index if hasattr(obj, "index") and index is None else index
|
|
@@ -417,7 +461,7 @@ def to_gdf(
|
|
|
417
461
|
if isinstance(obj, pd.DataFrame):
|
|
418
462
|
notna = obj[geom_col].notna()
|
|
419
463
|
obj.loc[notna, geom_col] = list(
|
|
420
|
-
|
|
464
|
+
_make_shapely_geoms(obj.loc[notna, geom_col])
|
|
421
465
|
)
|
|
422
466
|
obj[geom_col] = GeoSeries(obj[geom_col])
|
|
423
467
|
return GeoDataFrame(obj, geometry=geom_col, crs=crs, **kwargs)
|
|
@@ -426,27 +470,27 @@ def to_gdf(
|
|
|
426
470
|
dict(obj), geometry=geom_col, crs=crs, index=[0], **kwargs
|
|
427
471
|
)
|
|
428
472
|
if not hasattr(obj[geom_col], "__iter__") or len(obj[geom_col]) == 1:
|
|
429
|
-
obj[geom_col] =
|
|
473
|
+
obj[geom_col] = _make_shapely_geoms(obj[geom_col])
|
|
430
474
|
return GeoDataFrame(
|
|
431
475
|
dict(obj), geometry=geom_col, crs=crs, index=index, **kwargs
|
|
432
476
|
)
|
|
433
|
-
obj[geom_col] = GeoSeries(
|
|
477
|
+
obj[geom_col] = GeoSeries(_make_shapely_geoms(obj[geom_col]), index=index)
|
|
434
478
|
return GeoDataFrame(dict(obj), geometry=geom_col, crs=crs, **kwargs)
|
|
435
479
|
|
|
436
|
-
if geometry and all(g in obj for g in geometry):
|
|
480
|
+
if geometry and all(g in obj for g in geometry): # type: ignore [union-attr]
|
|
437
481
|
obj[geom_col] = _geoseries_from_xyz(obj, geometry, index=index)
|
|
438
482
|
return GeoDataFrame(obj, geometry=geom_col, crs=crs, **kwargs)
|
|
439
483
|
|
|
440
484
|
if len(obj.keys()) == 1:
|
|
441
|
-
key =
|
|
485
|
+
key = next(iter(obj.keys()))
|
|
442
486
|
if isinstance(obj, dict):
|
|
443
487
|
geoseries = GeoSeries(
|
|
444
|
-
|
|
488
|
+
_make_shapely_geoms(next(iter(obj.values()))), index=index
|
|
445
489
|
)
|
|
446
490
|
elif isinstance(obj, pd.Series):
|
|
447
|
-
geoseries = GeoSeries(
|
|
491
|
+
geoseries = GeoSeries(_make_shapely_geoms(obj), index=index)
|
|
448
492
|
else:
|
|
449
|
-
geoseries = GeoSeries(
|
|
493
|
+
geoseries = GeoSeries(_make_shapely_geoms(obj.iloc[:, 0]), index=index)
|
|
450
494
|
return GeoDataFrame({key: geoseries}, geometry=key, crs=crs, **kwargs)
|
|
451
495
|
|
|
452
496
|
if geometry and geom_col not in obj or isinstance(obj, pd.DataFrame):
|
|
@@ -471,7 +515,9 @@ def to_gdf(
|
|
|
471
515
|
return GeoDataFrame(geometry=geoseries, crs=crs, **kwargs)
|
|
472
516
|
|
|
473
517
|
|
|
474
|
-
def _array_to_geojson(
|
|
518
|
+
def _array_to_geojson(
|
|
519
|
+
array: np.ndarray, transform: Affine
|
|
520
|
+
) -> list[tuple[dict, Geometry]]:
|
|
475
521
|
try:
|
|
476
522
|
return [
|
|
477
523
|
(value, shape(geom))
|
|
@@ -498,7 +544,7 @@ def get_transform_from_bounds(
|
|
|
498
544
|
return rasterio.transform.from_bounds(minx, miny, maxx, maxy, width, height)
|
|
499
545
|
|
|
500
546
|
|
|
501
|
-
def
|
|
547
|
+
def _make_shapely_geoms(obj: Any) -> Geometry | Any:
|
|
502
548
|
if _is_one_geometry(obj):
|
|
503
549
|
return _make_one_shapely_geom(obj)
|
|
504
550
|
if isinstance(obj, dict) and "coordinates" in obj:
|
|
@@ -506,21 +552,12 @@ def make_shapely_geoms(obj):
|
|
|
506
552
|
return (_make_one_shapely_geom(g) for g in obj)
|
|
507
553
|
|
|
508
554
|
|
|
509
|
-
|
|
510
|
-
if
|
|
511
|
-
return False
|
|
512
|
-
|
|
513
|
-
classname = obj.__class__.__name__.lower()
|
|
514
|
-
if "bounding" not in classname and "box" not in classname:
|
|
515
|
-
return False
|
|
516
|
-
|
|
517
|
-
if len(obj) == 4 and all(isinstance(x, numbers.Number) for x in obj):
|
|
518
|
-
return True
|
|
555
|
+
def is_bbox_like(obj: Any) -> bool:
|
|
556
|
+
"""Returns True if the object is an iterable of 4 numbers.
|
|
519
557
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
def is_bbox_like(obj) -> bool:
|
|
558
|
+
Args:
|
|
559
|
+
obj: Any object.
|
|
560
|
+
"""
|
|
524
561
|
if (
|
|
525
562
|
hasattr(obj, "__len__")
|
|
526
563
|
and len(obj) == 4
|
|
@@ -531,13 +568,19 @@ def is_bbox_like(obj) -> bool:
|
|
|
531
568
|
return False
|
|
532
569
|
|
|
533
570
|
|
|
534
|
-
def is_nested_geojson(obj) -> bool:
|
|
571
|
+
def is_nested_geojson(obj: Any) -> bool:
|
|
572
|
+
"""Returns True if the object is an iterable of all dicts.
|
|
573
|
+
|
|
574
|
+
Args:
|
|
575
|
+
obj: Any object.
|
|
576
|
+
"""
|
|
535
577
|
if hasattr(obj, "__iter__") and all(isinstance(g, dict) for g in obj):
|
|
536
578
|
return True
|
|
537
579
|
return False
|
|
538
580
|
|
|
539
581
|
|
|
540
|
-
def get_crs_from_dict(obj):
|
|
582
|
+
def get_crs_from_dict(obj: Any) -> CRS | None | Any:
|
|
583
|
+
"""Try to extract the 'crs' attribute of the object or an object in the object."""
|
|
541
584
|
if (
|
|
542
585
|
not hasattr(obj, "__iter__")
|
|
543
586
|
or not is_dict_like(obj)
|
|
@@ -566,7 +609,7 @@ def get_crs_from_dict(obj):
|
|
|
566
609
|
return None
|
|
567
610
|
|
|
568
611
|
|
|
569
|
-
def _from_json(obj: dict):
|
|
612
|
+
def _from_json(obj: dict | list[dict]) -> Geometry:
|
|
570
613
|
if not isinstance(obj, dict) and isinstance(obj[0], dict):
|
|
571
614
|
return [_from_json(g) for g in obj]
|
|
572
615
|
if "geometry" in obj:
|
|
@@ -574,7 +617,7 @@ def _from_json(obj: dict):
|
|
|
574
617
|
if "features" in obj:
|
|
575
618
|
return _from_json(obj["features"])
|
|
576
619
|
coords = obj["coordinates"]
|
|
577
|
-
constructor = eval("shapely.geometry." + obj.get("type", Point))
|
|
620
|
+
constructor: Callable = eval("shapely.geometry." + obj.get("type", Point))
|
|
578
621
|
try:
|
|
579
622
|
return constructor(coords)
|
|
580
623
|
except TypeError:
|
|
@@ -583,16 +626,18 @@ def _from_json(obj: dict):
|
|
|
583
626
|
return constructor(coords)
|
|
584
627
|
|
|
585
628
|
|
|
586
|
-
def _series_like_to_geoseries(obj, index):
|
|
629
|
+
def _series_like_to_geoseries(obj: Iterable, index: Iterable) -> GeoSeries:
|
|
587
630
|
if index is None:
|
|
588
631
|
index = obj.keys()
|
|
589
632
|
if isinstance(obj, dict):
|
|
590
|
-
return GeoSeries(
|
|
633
|
+
return GeoSeries(_make_shapely_geoms(list(obj.values())), index=index)
|
|
591
634
|
else:
|
|
592
|
-
return GeoSeries(
|
|
635
|
+
return GeoSeries(_make_shapely_geoms(obj.values), index=index)
|
|
593
636
|
|
|
594
637
|
|
|
595
|
-
def _geoseries_to_gdf(
|
|
638
|
+
def _geoseries_to_gdf(
|
|
639
|
+
obj: GeoSeries, geometry: str | GeoSeries | Iterable, crs: CRS | Any, **kwargs
|
|
640
|
+
) -> GeoDataFrame:
|
|
596
641
|
if not crs:
|
|
597
642
|
crs = obj.crs
|
|
598
643
|
else:
|
|
@@ -601,7 +646,7 @@ def _geoseries_to_gdf(obj: GeoSeries, geometry, crs, **kwargs) -> GeoDataFrame:
|
|
|
601
646
|
return GeoDataFrame({geometry: obj}, geometry=geometry, crs=crs, **kwargs)
|
|
602
647
|
|
|
603
648
|
|
|
604
|
-
def
|
|
649
|
+
def _find_geometry_column(obj: Any, geometry: GeoSeries | Iterable | None) -> str:
|
|
605
650
|
if geometry is None:
|
|
606
651
|
return "geometry"
|
|
607
652
|
|
|
@@ -621,9 +666,12 @@ def find_geometry_column(obj, geometry) -> str:
|
|
|
621
666
|
)
|
|
622
667
|
|
|
623
668
|
|
|
624
|
-
def _geoseries_from_xyz(
|
|
669
|
+
def _geoseries_from_xyz(
|
|
670
|
+
obj: Any,
|
|
671
|
+
geometry: Iterable[float, float] | Iterable[float, float, float],
|
|
672
|
+
index: Iterable | None,
|
|
673
|
+
) -> GeoSeries:
|
|
625
674
|
"""Make geoseries from the geometry column or columns (x y (z))."""
|
|
626
|
-
|
|
627
675
|
if len(geometry) == 2:
|
|
628
676
|
x, y = geometry
|
|
629
677
|
z = None
|
|
@@ -640,17 +688,17 @@ def _geoseries_from_xyz(obj, geometry, index) -> GeoSeries:
|
|
|
640
688
|
return gpd.GeoSeries.from_xy(x=obj[x], y=obj[y], z=z, index=index)
|
|
641
689
|
|
|
642
690
|
|
|
643
|
-
def _is_one_geometry(obj) -> bool:
|
|
691
|
+
def _is_one_geometry(obj: Any) -> bool:
|
|
644
692
|
if (
|
|
645
|
-
isinstance(obj, (str, bytes, Geometry))
|
|
693
|
+
isinstance(obj, (str, bytes, Geometry)) # type: ignore [unreachable]
|
|
646
694
|
or all(isinstance(i, numbers.Number) for i in obj)
|
|
647
695
|
or not hasattr(obj, "__iter__")
|
|
648
696
|
):
|
|
649
697
|
return True
|
|
650
|
-
return False
|
|
698
|
+
return False # type: ignore [unreachable]
|
|
651
699
|
|
|
652
700
|
|
|
653
|
-
def _make_one_shapely_geom(obj):
|
|
701
|
+
def _make_one_shapely_geom(obj: Any) -> Geometry:
|
|
654
702
|
"""Create shapely geometry from wkt, wkb or coordinate tuple.
|
|
655
703
|
|
|
656
704
|
Works recursively if the object is a nested iterable.
|
|
@@ -2,20 +2,26 @@ from collections.abc import Iterable
|
|
|
2
2
|
|
|
3
3
|
import networkx as nx
|
|
4
4
|
import pandas as pd
|
|
5
|
-
from geopandas import GeoDataFrame
|
|
6
|
-
from
|
|
5
|
+
from geopandas import GeoDataFrame
|
|
6
|
+
from geopandas import GeoSeries
|
|
7
|
+
from shapely import STRtree
|
|
8
|
+
from shapely import difference
|
|
9
|
+
from shapely import make_valid
|
|
10
|
+
from shapely import simplify
|
|
11
|
+
from shapely import unary_union
|
|
7
12
|
from shapely.errors import GEOSException
|
|
8
13
|
|
|
9
|
-
from .general import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
from .geometry_types import
|
|
16
|
-
from .overlay import _run_overlay_dask
|
|
17
|
-
from .
|
|
18
|
-
|
|
14
|
+
from .general import _determine_geom_type_args
|
|
15
|
+
from .general import _parallel_unary_union_geoseries
|
|
16
|
+
from .general import _push_geom_col
|
|
17
|
+
from .general import clean_geoms
|
|
18
|
+
from .geometry_types import get_geom_type
|
|
19
|
+
from .geometry_types import make_all_singlepart
|
|
20
|
+
from .geometry_types import to_single_geom_type
|
|
21
|
+
from .overlay import _run_overlay_dask
|
|
22
|
+
from .overlay import clean_overlay
|
|
23
|
+
from .overlay import make_valid_and_keep_geom_type
|
|
24
|
+
from .sfilter import sfilter_inverse
|
|
19
25
|
|
|
20
26
|
PRECISION = 1e-3
|
|
21
27
|
|
|
@@ -44,8 +50,10 @@ def update_geometries(
|
|
|
44
50
|
"line" or "point".
|
|
45
51
|
grid_size: Precision grid size to round the geometries. Will use the highest
|
|
46
52
|
precision of the inputs by default.
|
|
53
|
+
n_jobs: Number of threads.
|
|
54
|
+
predicate: Spatial predicate for the spatial tree.
|
|
47
55
|
|
|
48
|
-
Example
|
|
56
|
+
Example:
|
|
49
57
|
------
|
|
50
58
|
Create two circles and get the overlap.
|
|
51
59
|
|
|
@@ -107,7 +115,7 @@ def update_geometries(
|
|
|
107
115
|
# select geometries from 'right', index from 'left', dissolve by 'left'
|
|
108
116
|
erasers = pd.Series(copied.geometry.loc[indices.values].values, index=indices.index)
|
|
109
117
|
if n_jobs > 1:
|
|
110
|
-
erasers =
|
|
118
|
+
erasers = _parallel_unary_union_geoseries(
|
|
111
119
|
erasers,
|
|
112
120
|
level=0,
|
|
113
121
|
n_jobs=n_jobs,
|
|
@@ -198,11 +206,13 @@ def get_intersections(
|
|
|
198
206
|
keep_geom_type: Whether to keep the original geometry type.
|
|
199
207
|
If mixed geometry types and keep_geom_type=True,
|
|
200
208
|
an exception is raised.
|
|
209
|
+
n_jobs: Number of threads.
|
|
210
|
+
predicate: Spatial predicate for the spatial tree.
|
|
201
211
|
|
|
202
212
|
Returns:
|
|
203
213
|
A GeoDataFrame of the overlapping polygons.
|
|
204
214
|
|
|
205
|
-
Examples
|
|
215
|
+
Examples:
|
|
206
216
|
--------
|
|
207
217
|
Create three partially overlapping polygons.
|
|
208
218
|
|
|
@@ -284,7 +294,11 @@ def get_intersections(
|
|
|
284
294
|
|
|
285
295
|
|
|
286
296
|
def _get_intersecting_geometries(
|
|
287
|
-
gdf: GeoDataFrame,
|
|
297
|
+
gdf: GeoDataFrame,
|
|
298
|
+
geom_type: str | None,
|
|
299
|
+
keep_geom_type: bool,
|
|
300
|
+
n_jobs: int,
|
|
301
|
+
predicate: str | None,
|
|
288
302
|
) -> GeoDataFrame:
|
|
289
303
|
right = gdf[[gdf._geometry_column_name]]
|
|
290
304
|
right["idx_right"] = right.index
|
|
@@ -376,7 +390,7 @@ def _get_duplicate_geometry_groups(
|
|
|
376
390
|
tree = STRtree(gdf.geometry.values)
|
|
377
391
|
left, right = tree.query(gdf.geometry.values, predicate="within")
|
|
378
392
|
|
|
379
|
-
edges = list(zip(left, right))
|
|
393
|
+
edges = list(zip(left, right, strict=False))
|
|
380
394
|
|
|
381
395
|
graph = nx.Graph()
|
|
382
396
|
graph.add_edges_from(edges)
|