ssb-sgis 1.0.1__py3-none-any.whl → 1.0.3__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.
Files changed (60) hide show
  1. sgis/__init__.py +107 -121
  2. sgis/exceptions.py +5 -3
  3. sgis/geopandas_tools/__init__.py +1 -0
  4. sgis/geopandas_tools/bounds.py +86 -47
  5. sgis/geopandas_tools/buffer_dissolve_explode.py +62 -39
  6. sgis/geopandas_tools/centerlines.py +53 -44
  7. sgis/geopandas_tools/cleaning.py +87 -104
  8. sgis/geopandas_tools/conversion.py +164 -107
  9. sgis/geopandas_tools/duplicates.py +33 -19
  10. sgis/geopandas_tools/general.py +84 -52
  11. sgis/geopandas_tools/geometry_types.py +24 -10
  12. sgis/geopandas_tools/neighbors.py +23 -11
  13. sgis/geopandas_tools/overlay.py +136 -53
  14. sgis/geopandas_tools/point_operations.py +11 -10
  15. sgis/geopandas_tools/polygon_operations.py +53 -61
  16. sgis/geopandas_tools/polygons_as_rings.py +121 -78
  17. sgis/geopandas_tools/sfilter.py +17 -17
  18. sgis/helpers.py +116 -58
  19. sgis/io/dapla_functions.py +32 -23
  20. sgis/io/opener.py +13 -6
  21. sgis/io/read_parquet.py +2 -2
  22. sgis/maps/examine.py +55 -28
  23. sgis/maps/explore.py +471 -112
  24. sgis/maps/httpserver.py +12 -12
  25. sgis/maps/legend.py +285 -134
  26. sgis/maps/map.py +248 -129
  27. sgis/maps/maps.py +123 -119
  28. sgis/maps/thematicmap.py +260 -94
  29. sgis/maps/tilesources.py +3 -8
  30. sgis/networkanalysis/_get_route.py +5 -4
  31. sgis/networkanalysis/_od_cost_matrix.py +44 -1
  32. sgis/networkanalysis/_points.py +10 -4
  33. sgis/networkanalysis/_service_area.py +5 -2
  34. sgis/networkanalysis/closing_network_holes.py +22 -64
  35. sgis/networkanalysis/cutting_lines.py +58 -46
  36. sgis/networkanalysis/directednetwork.py +16 -8
  37. sgis/networkanalysis/finding_isolated_networks.py +6 -5
  38. sgis/networkanalysis/network.py +15 -13
  39. sgis/networkanalysis/networkanalysis.py +79 -61
  40. sgis/networkanalysis/networkanalysisrules.py +21 -17
  41. sgis/networkanalysis/nodes.py +2 -3
  42. sgis/networkanalysis/traveling_salesman.py +6 -3
  43. sgis/parallel/parallel.py +372 -142
  44. sgis/raster/base.py +9 -3
  45. sgis/raster/cube.py +331 -213
  46. sgis/raster/cubebase.py +15 -29
  47. sgis/raster/image_collection.py +2560 -0
  48. sgis/raster/indices.py +17 -12
  49. sgis/raster/raster.py +356 -275
  50. sgis/raster/sentinel_config.py +104 -0
  51. sgis/raster/zonal.py +38 -14
  52. {ssb_sgis-1.0.1.dist-info → ssb_sgis-1.0.3.dist-info}/LICENSE +1 -1
  53. {ssb_sgis-1.0.1.dist-info → ssb_sgis-1.0.3.dist-info}/METADATA +87 -16
  54. ssb_sgis-1.0.3.dist-info/RECORD +61 -0
  55. {ssb_sgis-1.0.1.dist-info → ssb_sgis-1.0.3.dist-info}/WHEEL +1 -1
  56. sgis/raster/bands.py +0 -48
  57. sgis/raster/gradient.py +0 -78
  58. sgis/raster/methods_as_functions.py +0 -124
  59. sgis/raster/torchgeo.py +0 -150
  60. ssb_sgis-1.0.1.dist-info/RECORD +0 -63
@@ -1,59 +1,26 @@
1
- from typing import Any, Callable, Iterable
1
+ from collections.abc import Callable
2
+ from typing import Any
2
3
 
3
- import geopandas as gpd
4
- import igraph
5
- import networkx as nx
6
4
  import numpy as np
7
5
  import pandas as pd
8
- from geopandas import GeoDataFrame, GeoSeries
6
+ from geopandas import GeoDataFrame
7
+ from geopandas import GeoSeries
9
8
  from geopandas.array import GeometryArray
10
- from IPython.display import display
11
- from networkx.algorithms import approximation as approx
12
- from numpy import ndarray
13
9
  from numpy.typing import NDArray
14
- from pandas import Index
15
- from shapely import (
16
- Geometry,
17
- STRtree,
18
- area,
19
- box,
20
- buffer,
21
- centroid,
22
- difference,
23
- distance,
24
- extract_unique_points,
25
- get_coordinates,
26
- get_exterior_ring,
27
- get_interior_ring,
28
- get_num_interior_rings,
29
- get_parts,
30
- intersection,
31
- intersects,
32
- is_empty,
33
- is_ring,
34
- length,
35
- line_merge,
36
- linearrings,
37
- linestrings,
38
- make_valid,
39
- points,
40
- polygons,
41
- segmentize,
42
- simplify,
43
- unary_union,
44
- voronoi_polygons,
45
- )
46
- from shapely.errors import GEOSException
47
- from shapely.geometry import (
48
- LinearRing,
49
- LineString,
50
- MultiLineString,
51
- MultiPoint,
52
- Point,
53
- Polygon,
54
- )
55
-
56
- from .conversion import to_gdf, to_geoseries
10
+ from pyproj import CRS
11
+ from shapely import get_coordinates
12
+ from shapely import get_exterior_ring
13
+ from shapely import get_interior_ring
14
+ from shapely import get_num_interior_rings
15
+ from shapely import linearrings
16
+ from shapely import make_valid
17
+ from shapely import polygons
18
+ from shapely import unary_union
19
+ from shapely.geometry import LinearRing
20
+ from shapely.geometry import Polygon
21
+
22
+ from .conversion import to_gdf
23
+ from .conversion import to_geoseries
57
24
 
58
25
 
59
26
  class PolygonsAsRings:
@@ -62,10 +29,18 @@ class PolygonsAsRings:
62
29
  def __init__(
63
30
  self,
64
31
  polys: GeoDataFrame | GeoSeries | GeometryArray,
65
- crs=None,
32
+ crs: CRS | Any | None = None,
66
33
  allow_multipart: bool = False,
67
34
  gridsize: int | None = None,
68
- ):
35
+ ) -> None:
36
+ """Initialize the PolygonsAsRings object with polygons and optional CRS information.
37
+
38
+ Args:
39
+ polys: GeoDataFrame, GeoSeries, or GeometryArray containing polygon geometries.
40
+ crs: Coordinate Reference System to be used, defaults to None.
41
+ allow_multipart: Allow multipart polygons if True, defaults to False.
42
+ gridsize: Size of the grid for any grid operations, defaults to None.
43
+ """
69
44
  if not isinstance(polys, (pd.DataFrame, pd.Series, GeometryArray)):
70
45
  raise TypeError(type(polys))
71
46
 
@@ -125,7 +100,16 @@ class PolygonsAsRings:
125
100
 
126
101
  self.rings = pd.concat([exterior, interiors])
127
102
 
128
- def get_rings(self, agg: bool = False):
103
+ def get_rings(self, agg: bool = False) -> GeoDataFrame | GeoSeries | np.ndarray:
104
+ """Retrieve rings from the polygons, optionally aggregating them.
105
+
106
+ Args:
107
+ agg: If True, aggregate the rings into single geometries.
108
+
109
+ Returns:
110
+ The rings either aggregated or separated, in the type of
111
+ the input polygons.
112
+ """
129
113
  gdf = self.gdf.copy()
130
114
  rings = self.rings.copy()
131
115
  if not len(rings):
@@ -144,9 +128,21 @@ class PolygonsAsRings:
144
128
  return self.polyclass(gdf.geometry.values)
145
129
 
146
130
  def apply_numpy_func_to_interiors(
147
- self, func: Callable, args: tuple | None = None, kwargs: dict | None = None
148
- ):
149
- """Run an array function on only the interior rings of the polygons."""
131
+ self,
132
+ func: Callable,
133
+ args: tuple | None = None,
134
+ kwargs: dict | None = None,
135
+ ) -> "PolygonsAsRings":
136
+ """Apply a numpy function specifically to the interior rings of the polygons.
137
+
138
+ Args:
139
+ func: Numpy function to apply.
140
+ args: Tuple of positional arguments for the function.
141
+ kwargs: Dictionary of keyword arguments for the function.
142
+
143
+ Returns:
144
+ PolygonsAsRings: The instance itself after applying the function.
145
+ """
150
146
  kwargs = kwargs or {}
151
147
  args = args or ()
152
148
  arr: NDArray[LinearRing] = self.rings.loc[self.is_interior].values
@@ -159,9 +155,21 @@ class PolygonsAsRings:
159
155
  return self
160
156
 
161
157
  def apply_numpy_func(
162
- self, func: Callable, args: tuple | None = None, kwargs: dict | None = None
163
- ):
164
- """Run a function that takes an array of lines/rings and returns an array of lines/rings."""
158
+ self,
159
+ func: Callable,
160
+ args: tuple | None = None,
161
+ kwargs: dict | None = None,
162
+ ) -> "PolygonsAsRings":
163
+ """Apply a numpy function to all rings of the polygons.
164
+
165
+ Args:
166
+ func: Numpy function to apply.
167
+ args: Tuple of positional arguments for the function.
168
+ kwargs: Dictionary of keyword arguments for the function.
169
+
170
+ Returns:
171
+ PolygonsAsRings: The instance itself after applying the function.
172
+ """
165
173
  kwargs = kwargs or {}
166
174
  args = args or ()
167
175
 
@@ -172,18 +180,30 @@ class PolygonsAsRings:
172
180
  f"Different length of results. Got {len(results)} and {len(self.rings)} original rings"
173
181
  )
174
182
 
175
- self.rings.loc[:] = results
183
+ self.rings.loc[:] = results # type: ignore [call-overload]
176
184
 
177
185
  return self
178
186
 
179
187
  def apply_geoseries_func(
180
- self, func: Callable, args: tuple | None = None, kwargs: dict | None = None
181
- ):
182
- """Run a function that takes a GeoSeries and returns a GeoSeries."""
188
+ self,
189
+ func: Callable,
190
+ args: tuple | None = None,
191
+ kwargs: dict | None = None,
192
+ ) -> "PolygonsAsRings":
193
+ """Apply a function that operates on a GeoSeries to the rings.
194
+
195
+ Args:
196
+ func: Function to apply that expects a GeoSeries.
197
+ args: Tuple of positional arguments for the function.
198
+ kwargs: Dictionary of keyword arguments for the function.
199
+
200
+ Returns:
201
+ PolygonsAsRings: The instance itself after applying the function.
202
+ """
183
203
  kwargs = kwargs or {}
184
204
  args = args or ()
185
205
 
186
- self.rings.loc[:] = np.array(
206
+ self.rings.loc[:] = np.array( # type: ignore [call-overload]
187
207
  func(
188
208
  GeoSeries(
189
209
  self.rings.values,
@@ -198,9 +218,21 @@ class PolygonsAsRings:
198
218
  return self
199
219
 
200
220
  def apply_gdf_func(
201
- self, func: Callable, args: tuple | None = None, kwargs: dict | None = None
202
- ):
203
- """Run a function that takes a GeoDataFrame and returns a GeoDataFrame."""
221
+ self,
222
+ func: Callable,
223
+ args: tuple | None = None,
224
+ kwargs: dict | None = None,
225
+ ) -> "PolygonsAsRings":
226
+ """Apply a function that operates on a GeoDataFrame to the rings.
227
+
228
+ Args:
229
+ func: Function to apply that expects a GeoDataFrame.
230
+ args: Tuple of positional arguments for the function.
231
+ kwargs: Dictionary of keyword arguments for the function.
232
+
233
+ Returns:
234
+ PolygonsAsRings: The instance itself after applying the function.
235
+ """
204
236
  kwargs = kwargs or {}
205
237
  args = args or ()
206
238
 
@@ -214,7 +246,7 @@ class PolygonsAsRings:
214
246
 
215
247
  gdf.index = self.rings.index
216
248
 
217
- self.rings.loc[:] = func(
249
+ self.rings.loc[:] = func( # type: ignore [call-overload]
218
250
  gdf,
219
251
  *args,
220
252
  **kwargs,
@@ -223,15 +255,17 @@ class PolygonsAsRings:
223
255
  return self
224
256
 
225
257
  @property
226
- def is_interior(self):
258
+ def is_interior(self) -> bool:
259
+ """Returns a boolean Series of whether the row is an interior ring."""
227
260
  return self.rings.index.get_level_values(0) == 1
228
261
 
229
262
  @property
230
- def is_exterior(self):
263
+ def is_exterior(self) -> bool:
264
+ """Returns a boolean Series of whether the row is an exterior ring."""
231
265
  return self.rings.index.get_level_values(0) == 0
232
266
 
233
267
  @property
234
- def _interiors_index(self):
268
+ def _interiors_index(self) -> pd.MultiIndex:
235
269
  """A three-leveled MultiIndex.
236
270
 
237
271
  Used to separate interior and exterior and sort the interior in
@@ -254,7 +288,7 @@ class PolygonsAsRings:
254
288
  )
255
289
 
256
290
  @property
257
- def _exterior_index(self):
291
+ def _exterior_index(self) -> pd.MultiIndex:
258
292
  """A three-leveled MultiIndex.
259
293
 
260
294
  Used to separate interior and exterior in the 'to_numpy' method.
@@ -274,8 +308,8 @@ class PolygonsAsRings:
274
308
  self.gdf.geometry = self.to_numpy()
275
309
  return self.gdf
276
310
 
277
- def to_geoseries(self) -> GeoDataFrame:
278
- """Return the GeoDataFrame with polygons."""
311
+ def to_geoseries(self) -> GeoSeries:
312
+ """Return the GeoSeries with polygons."""
279
313
  self.gdf.geometry = self.to_numpy()
280
314
  return self.gdf.geometry
281
315
 
@@ -293,7 +327,7 @@ class PolygonsAsRings:
293
327
  try:
294
328
  return make_valid(polygons(exterior.values))
295
329
  except Exception:
296
- return _geoms_to_linearrings_fallback(exterior)
330
+ return _geoms_to_linearrings_fallback(exterior).values
297
331
 
298
332
  empty_interiors = pd.Series(
299
333
  [None for _ in range(len(self.gdf) * self.max_rings)],
@@ -312,10 +346,18 @@ class PolygonsAsRings:
312
346
  try:
313
347
  return make_valid(polygons(exterior.values, interiors.values))
314
348
  except Exception:
315
- return _geoms_to_linearrings_fallback(exterior, interiors)
349
+ return _geoms_to_linearrings_fallback(exterior, interiors).values
350
+
351
+
352
+ def get_linearring_series(geoms: GeoDataFrame | GeoSeries) -> pd.Series:
353
+ """Convert geometries into a series of LinearRings.
316
354
 
355
+ Args:
356
+ geoms: GeoDataFrame or GeoSeries from which to extract LinearRings.
317
357
 
318
- def get_linearring_series(geoms: Any) -> pd.Series:
358
+ Returns:
359
+ pd.Series: A series containing LinearRings.
360
+ """
319
361
  geoms = to_geoseries(geoms).explode(index_parts=False)
320
362
  coords, indices = get_coordinates(geoms, return_index=True)
321
363
  return pd.Series(linearrings(coords, indices=indices), index=geoms.index)
@@ -325,6 +367,7 @@ def _geoms_to_linearrings_fallback(
325
367
  exterior: pd.Series, interiors: pd.Series | None = None
326
368
  ) -> pd.Series:
327
369
  exterior.index = exterior.index.get_level_values(1)
370
+ assert exterior.index.is_monotonic_increasing
328
371
 
329
372
  exterior = get_linearring_series(exterior)
330
373
 
@@ -2,12 +2,12 @@ import warnings
2
2
 
3
3
  import numpy as np
4
4
  import pandas as pd
5
- from geopandas import GeoDataFrame, GeoSeries
5
+ from geopandas import GeoDataFrame
6
+ from geopandas import GeoSeries
6
7
  from shapely import Geometry
7
8
 
8
9
  from .conversion import to_gdf
9
10
 
10
-
11
11
  gdf_type_error_message = "'gdf' should be of type GeoDataFrame or GeoSeries."
12
12
 
13
13
 
@@ -34,9 +34,9 @@ def sfilter(
34
34
  A copy of 'gdf' with only the rows matching the
35
35
  spatial predicate with 'other'.
36
36
 
37
- Examples
38
- --------
39
-
37
+ Examples:
38
+ ---------
39
+ >>> import sgis as sg
40
40
  >>> df1 = sg.to_gdf([(0, 0), (0, 1)])
41
41
  >>> df1
42
42
  geometry
@@ -71,7 +71,7 @@ def sfilter(
71
71
  0 POINT (0.00000 0.00000)
72
72
 
73
73
  """
74
- if not isinstance(gdf, (GeoDataFrame, GeoSeries)):
74
+ if not isinstance(gdf, (GeoDataFrame | GeoSeries)):
75
75
  raise TypeError(gdf_type_error_message)
76
76
 
77
77
  other = _sfilter_checks(other, crs=gdf.crs)
@@ -100,9 +100,9 @@ def sfilter_split(
100
100
  A tuple of GeoDataFrames, one with the rows that match the spatial predicate
101
101
  and one with the rows that do not.
102
102
 
103
- Examples
104
- --------
105
-
103
+ Examples:
104
+ ---------
105
+ >>> import sgis as sg
106
106
  >>> df1 = sg.to_gdf([(0, 0), (0, 1)])
107
107
  >>> df1
108
108
  geometry
@@ -140,7 +140,7 @@ def sfilter_split(
140
140
  >>> not_intersecting = df1.loc[~filt]
141
141
 
142
142
  """
143
- if not isinstance(gdf, (GeoDataFrame, GeoSeries)):
143
+ if not isinstance(gdf, (GeoDataFrame | GeoSeries)):
144
144
  raise TypeError(gdf_type_error_message)
145
145
 
146
146
  other = _sfilter_checks(other, crs=gdf.crs)
@@ -149,7 +149,7 @@ def sfilter_split(
149
149
 
150
150
  return (
151
151
  gdf.iloc[indices],
152
- gdf.iloc[pd.Index(range(len(gdf))).difference(indices)],
152
+ gdf.iloc[pd.Index(range(len(gdf))).difference(pd.Index(indices))],
153
153
  )
154
154
 
155
155
 
@@ -171,9 +171,9 @@ def sfilter_inverse(
171
171
  A copy of 'gdf' with only the rows that do not match the
172
172
  spatial predicate with 'other'.
173
173
 
174
- Examples
175
- --------
176
-
174
+ Examples:
175
+ ---------
176
+ >>> import sgis as sg
177
177
  >>> df1 = sg.to_gdf([(0, 0), (0, 1)])
178
178
  >>> df1
179
179
  geometry
@@ -205,14 +205,14 @@ def sfilter_inverse(
205
205
  >>> not_intersecting = df1.loc[~df1.intersects(df2.unary_union)]
206
206
 
207
207
  """
208
- if not isinstance(gdf, (GeoDataFrame, GeoSeries)):
208
+ if not isinstance(gdf, (GeoDataFrame | GeoSeries)):
209
209
  raise TypeError(gdf_type_error_message)
210
210
 
211
211
  other = _sfilter_checks(other, crs=gdf.crs)
212
212
 
213
213
  indices = _get_sfilter_indices(gdf, other, predicate)
214
214
 
215
- return gdf.iloc[pd.Index(range(len(gdf))).difference(indices)]
215
+ return gdf.iloc[pd.Index(range(len(gdf))).difference(pd.Index(indices))]
216
216
 
217
217
 
218
218
  def _sfilter_checks(other, crs):
@@ -256,7 +256,7 @@ def _get_sfilter_indices(
256
256
  predicate : string
257
257
  Binary predicate to query.
258
258
 
259
- Returns
259
+ Returns:
260
260
  -------
261
261
  DataFrame
262
262
  DataFrame with matching indices in