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
sgis/__init__.py CHANGED
@@ -2,147 +2,133 @@ config = {
2
2
  "n_jobs": 1,
3
3
  }
4
4
 
5
- import sgis.raster.bands as bands
6
5
  import sgis.raster.indices as indices
7
- from sgis.raster.raster import Raster, get_shape_from_bounds, get_transform_from_bounds
6
+ from sgis.raster.raster import Raster
7
+ from sgis.raster.raster import get_shape_from_bounds
8
+ from sgis.raster.raster import get_transform_from_bounds
8
9
 
9
- from .geopandas_tools.bounds import (
10
- Gridlooper,
11
- bounds_to_points,
12
- bounds_to_polygon,
13
- get_total_bounds,
14
- gridloop,
15
- make_grid,
16
- make_grid_from_bbox,
17
- make_ssb_grid,
18
- points_in_bounds,
19
- )
20
- from .geopandas_tools.buffer_dissolve_explode import (
21
- buff,
22
- buffdiss,
23
- buffdissexp,
24
- buffdissexp_by_cluster,
25
- diss,
26
- diss_by_cluster,
27
- dissexp,
28
- dissexp_by_cluster,
29
- )
10
+ from .geopandas_tools.bounds import Gridlooper
11
+ from .geopandas_tools.bounds import bounds_to_points
12
+ from .geopandas_tools.bounds import bounds_to_polygon
13
+ from .geopandas_tools.bounds import get_total_bounds
14
+ from .geopandas_tools.bounds import gridloop
15
+ from .geopandas_tools.bounds import make_grid
16
+ from .geopandas_tools.bounds import make_grid_from_bbox
17
+ from .geopandas_tools.bounds import make_ssb_grid
18
+ from .geopandas_tools.bounds import points_in_bounds
19
+ from .geopandas_tools.buffer_dissolve_explode import buff
20
+ from .geopandas_tools.buffer_dissolve_explode import buffdiss
21
+ from .geopandas_tools.buffer_dissolve_explode import buffdissexp
22
+ from .geopandas_tools.buffer_dissolve_explode import buffdissexp_by_cluster
23
+ from .geopandas_tools.buffer_dissolve_explode import diss
24
+ from .geopandas_tools.buffer_dissolve_explode import diss_by_cluster
25
+ from .geopandas_tools.buffer_dissolve_explode import dissexp
26
+ from .geopandas_tools.buffer_dissolve_explode import dissexp_by_cluster
30
27
  from .geopandas_tools.centerlines import get_rough_centerlines
31
- from .geopandas_tools.cleaning import (
32
- coverage_clean,
33
- remove_interior_slivers,
34
- remove_spikes,
35
- split_and_eliminate_by_longest,
36
- split_by_neighbors,
37
- )
38
- from .geopandas_tools.conversion import (
39
- coordinate_array,
40
- from_4326,
41
- to_4326,
42
- to_bbox,
43
- to_gdf,
44
- to_geoseries,
45
- to_shapely,
46
- )
47
- from .geopandas_tools.duplicates import ( # drop_duplicate_geometries,
48
- get_intersections,
49
- update_geometries,
50
- )
51
- from .geopandas_tools.general import (
52
- clean_clip,
53
- clean_geoms,
54
- drop_inactive_geometry_columns,
55
- get_common_crs,
56
- get_grouped_centroids,
57
- random_points,
58
- random_points_in_polygons,
59
- rename_geometry_if,
60
- sort_large_first,
61
- sort_long_first,
62
- sort_short_first,
63
- sort_small_first,
64
- to_lines,
65
- )
66
- from .geopandas_tools.geocoding import address_to_coords, address_to_gdf
67
- from .geopandas_tools.geometry_types import (
68
- get_geom_type,
69
- is_single_geom_type,
70
- make_all_singlepart,
71
- to_single_geom_type,
72
- )
73
- from .geopandas_tools.neighbors import (
74
- get_all_distances,
75
- get_k_nearest_neighbors,
76
- get_neighbor_dfs,
77
- get_neighbor_indices,
78
- k_nearest_neighbors,
79
- sjoin_within_distance,
80
- )
28
+ from .geopandas_tools.cleaning import coverage_clean
29
+ from .geopandas_tools.cleaning import remove_spikes
30
+ from .geopandas_tools.cleaning import split_and_eliminate_by_longest
31
+ from .geopandas_tools.cleaning import split_by_neighbors
32
+ from .geopandas_tools.conversion import coordinate_array
33
+ from .geopandas_tools.conversion import from_4326
34
+ from .geopandas_tools.conversion import to_4326
35
+ from .geopandas_tools.conversion import to_bbox
36
+ from .geopandas_tools.conversion import to_gdf
37
+ from .geopandas_tools.conversion import to_geoseries
38
+ from .geopandas_tools.conversion import to_shapely
39
+ from .geopandas_tools.duplicates import get_intersections # drop_duplicate_geometries,
40
+ from .geopandas_tools.duplicates import update_geometries # drop_duplicate_geometries,
41
+ from .geopandas_tools.general import _rename_geometry_if
42
+ from .geopandas_tools.general import clean_clip
43
+ from .geopandas_tools.general import clean_geoms
44
+ from .geopandas_tools.general import drop_inactive_geometry_columns
45
+ from .geopandas_tools.general import get_common_crs
46
+ from .geopandas_tools.general import get_grouped_centroids
47
+ from .geopandas_tools.general import random_points
48
+ from .geopandas_tools.general import random_points_in_polygons
49
+ from .geopandas_tools.general import sort_large_first
50
+ from .geopandas_tools.general import sort_long_first
51
+ from .geopandas_tools.general import sort_short_first
52
+ from .geopandas_tools.general import sort_small_first
53
+ from .geopandas_tools.general import to_lines
54
+ from .geopandas_tools.geocoding import address_to_coords
55
+ from .geopandas_tools.geocoding import address_to_gdf
56
+ from .geopandas_tools.geometry_types import get_geom_type
57
+ from .geopandas_tools.geometry_types import is_single_geom_type
58
+ from .geopandas_tools.geometry_types import make_all_singlepart
59
+ from .geopandas_tools.geometry_types import to_single_geom_type
60
+ from .geopandas_tools.neighbors import get_all_distances
61
+ from .geopandas_tools.neighbors import get_k_nearest_neighbors
62
+ from .geopandas_tools.neighbors import get_neighbor_dfs
63
+ from .geopandas_tools.neighbors import get_neighbor_indices
64
+ from .geopandas_tools.neighbors import k_nearest_neighbors
65
+ from .geopandas_tools.neighbors import sjoin_within_distance
81
66
  from .geopandas_tools.overlay import clean_overlay
82
- from .geopandas_tools.point_operations import snap_all, snap_within_distance
83
- from .geopandas_tools.polygon_operations import (
84
- PolygonsAsRings,
85
- close_all_holes,
86
- close_small_holes,
87
- close_thin_holes,
88
- eliminate_by_largest,
89
- eliminate_by_longest,
90
- eliminate_by_smallest,
91
- get_cluster_mapper,
92
- get_gaps,
93
- get_holes,
94
- get_polygon_clusters,
95
- )
67
+ from .geopandas_tools.point_operations import snap_all
68
+ from .geopandas_tools.point_operations import snap_within_distance
69
+ from .geopandas_tools.polygon_operations import close_all_holes
70
+ from .geopandas_tools.polygon_operations import close_small_holes
71
+ from .geopandas_tools.polygon_operations import close_thin_holes
72
+ from .geopandas_tools.polygon_operations import eliminate_by_largest
73
+ from .geopandas_tools.polygon_operations import eliminate_by_longest
74
+ from .geopandas_tools.polygon_operations import eliminate_by_smallest
75
+ from .geopandas_tools.polygon_operations import get_cluster_mapper
76
+ from .geopandas_tools.polygon_operations import get_gaps
77
+ from .geopandas_tools.polygon_operations import get_holes
78
+ from .geopandas_tools.polygon_operations import get_polygon_clusters
96
79
  from .geopandas_tools.polygons_as_rings import PolygonsAsRings
97
- from .geopandas_tools.sfilter import sfilter, sfilter_inverse, sfilter_split
98
- from .helpers import get_object_name, sort_nans_last
80
+ from .geopandas_tools.sfilter import sfilter
81
+ from .geopandas_tools.sfilter import sfilter_inverse
82
+ from .geopandas_tools.sfilter import sfilter_split
83
+ from .helpers import get_object_name
84
+ from .helpers import sort_nans_last
99
85
  from .io.opener import opener
100
86
  from .io.read_parquet import read_parquet_url
101
87
  from .maps.examine import Examine
102
88
  from .maps.explore import Explore
103
89
  from .maps.httpserver import run_html_server
104
90
  from .maps.legend import Legend
105
- from .maps.maps import clipmap, explore, explore_locals, qtm, samplemap
91
+ from .maps.maps import clipmap
92
+ from .maps.maps import explore
93
+ from .maps.maps import explore_locals
94
+ from .maps.maps import qtm
95
+ from .maps.maps import samplemap
106
96
  from .maps.thematicmap import ThematicMap
107
97
  from .maps.tilesources import kartverket as kartverket_tiles
108
98
  from .maps.tilesources import xyz as xyztiles
109
- from .networkanalysis.closing_network_holes import (
110
- close_network_holes,
111
- close_network_holes_to_deadends,
112
- )
113
- from .networkanalysis.cutting_lines import (
114
- cut_lines,
115
- cut_lines_once,
116
- split_lines_by_nearest_point,
117
- )
118
- from .networkanalysis.directednetwork import (
119
- make_directed_network,
120
- make_directed_network_norway,
121
- )
122
- from .networkanalysis.finding_isolated_networks import (
123
- get_component_size,
124
- get_connected_components,
125
- )
99
+ from .networkanalysis.closing_network_holes import close_network_holes
100
+ from .networkanalysis.closing_network_holes import close_network_holes_to_deadends
101
+ from .networkanalysis.cutting_lines import cut_lines
102
+ from .networkanalysis.cutting_lines import cut_lines_once
103
+ from .networkanalysis.cutting_lines import split_lines_by_nearest_point
104
+ from .networkanalysis.directednetwork import make_directed_network
105
+ from .networkanalysis.directednetwork import make_directed_network_norway
106
+ from .networkanalysis.finding_isolated_networks import get_component_size
107
+ from .networkanalysis.finding_isolated_networks import get_connected_components
126
108
  from .networkanalysis.network import Network
127
109
  from .networkanalysis.networkanalysis import NetworkAnalysis
128
110
  from .networkanalysis.networkanalysisrules import NetworkAnalysisRules
129
- from .networkanalysis.nodes import (
130
- make_edge_coords_cols,
131
- make_edge_wkt_cols,
132
- make_node_ids,
133
- )
111
+ from .networkanalysis.nodes import make_edge_coords_cols
112
+ from .networkanalysis.nodes import make_edge_wkt_cols
113
+ from .networkanalysis.nodes import make_node_ids
134
114
  from .networkanalysis.traveling_salesman import traveling_salesman_problem
135
- from .parallel.parallel import Parallel, parallel_overlay
115
+ from .parallel.parallel import Parallel
116
+ from .parallel.parallel import parallel_overlay
136
117
  from .raster.cube import DataCube
137
-
138
-
139
- try:
140
- import sgis.raster.torchgeo as torchgeo
141
- except ImportError:
142
- pass
143
-
118
+ from .raster.cube import concat_cubes
119
+ from .raster.image_collection import Band
120
+ from .raster.image_collection import Image
121
+ from .raster.image_collection import ImageCollection
122
+ from .raster.image_collection import Sentinel2Band
123
+ from .raster.image_collection import Sentinel2CloudlessCollection
124
+ from .raster.image_collection import Sentinel2CloudlessImage
125
+ from .raster.image_collection import Sentinel2Collection
126
+ from .raster.image_collection import Sentinel2Image
127
+ from .raster.image_collection import concat_image_collections
144
128
 
145
129
  try:
146
- from .io.dapla_functions import check_files, read_geopandas, write_geopandas
130
+ from .io.dapla_functions import check_files
131
+ from .io.dapla_functions import read_geopandas
132
+ from .io.dapla_functions import write_geopandas
147
133
  except ImportError:
148
134
  pass
sgis/exceptions.py CHANGED
@@ -1,21 +1,23 @@
1
1
  """Some small exception classes."""
2
2
 
3
3
 
4
- class NoPointsWithinSearchToleranceError(Exception):
4
+ class NoPointsWithinSearchToleranceError(ValueError):
5
5
  """Exception for when the points are too far away from the network."""
6
6
 
7
7
  def __init__(
8
8
  self, what: str | None = None, search_tolerance: str | None = None
9
9
  ) -> None:
10
+ """Initialise error class."""
10
11
  self.what = what
11
12
  self.search_tolerance = search_tolerance
12
13
 
13
- def __str__(self):
14
+ def __str__(self) -> str:
15
+ """String representation."""
14
16
  return (
15
17
  f"No {self.what} within specified 'search_tolerance' "
16
18
  f"of {self.search_tolerance}"
17
19
  )
18
20
 
19
21
 
20
- class ZeroLinesError(Exception):
22
+ class ZeroLinesError(ValueError):
21
23
  """DataFrame has 0 rows."""
@@ -0,0 +1 @@
1
+ """Tools for vector gis."""
@@ -1,20 +1,24 @@
1
1
  import functools
2
- import numbers
3
- from collections.abc import Callable, Collection, Mapping
2
+ from collections.abc import Callable
4
3
  from dataclasses import dataclass
5
4
  from typing import Any
6
5
 
7
6
  import geopandas as gpd
8
7
  import numpy as np
9
8
  import pandas as pd
10
- from geopandas import GeoDataFrame, GeoSeries
11
- from pandas.api.types import is_dict_like
12
- from shapely import Geometry, box, extract_unique_points
9
+ from geopandas import GeoDataFrame
10
+ from geopandas import GeoSeries
11
+ from pyproj import CRS
12
+ from shapely import Geometry
13
+ from shapely import box
14
+ from shapely import extract_unique_points
13
15
  from shapely.geometry import Polygon
14
16
 
15
17
  from ..parallel.parallel import Parallel
16
- from .conversion import to_bbox, to_gdf
17
- from .general import clean_clip, is_bbox_like
18
+ from .conversion import to_bbox
19
+ from .conversion import to_gdf
20
+ from .general import clean_clip
21
+ from .general import is_bbox_like
18
22
 
19
23
 
20
24
  @dataclass
@@ -32,9 +36,8 @@ class Gridlooper:
32
36
  keep_geom_type: Whether to keep only the input geometry types after clipping.
33
37
  Defaults to True.
34
38
 
35
- Examples
36
- --------
37
-
39
+ Examples:
40
+ ---------
38
41
  Get some points and some polygons.
39
42
 
40
43
  >>> import sgis as sg
@@ -46,7 +49,7 @@ class Gridlooper:
46
49
  0 0 POLYGON ((263222.700 6651184.900, 263222.651 6...
47
50
  1 1 POLYGON ((272556.100 6653369.500, 272556.051 6...
48
51
  2 2 POLYGON ((270182.300 6653032.700, 270182.251 6...
49
- 3 3 POLYGON ((259904.800 6650339.700, 259904.751 6...
52
+ 3 3 POLYGON ((259904.800 6650339.700, 259904.751 6...
50
53
  4 4 POLYGON ((272976.200 6652889.100, 272976.151 6...
51
54
  .. ... ...
52
55
  995 995 POLYGON ((266901.700 6647844.500, 266901.651 6...
@@ -95,16 +98,24 @@ class Gridlooper:
95
98
  keep_geom_type: bool = True
96
99
  verbose: bool = False
97
100
 
98
- def __post_init__(self):
101
+ def __post_init__(self) -> None:
102
+ """Fix types."""
99
103
  if not isinstance(self.mask, GeoDataFrame):
100
104
  self.mask = to_gdf(self.mask)
101
105
 
102
- def run(self, func: Callable, *args, **kwargs):
103
- def intersects_mask(df):
106
+ def run(self, func: Callable, *args, **kwargs) -> Any | list[Any]:
107
+ """Run a function for each grid cell in a loop.
108
+
109
+ Returns a list of the return values from the function,
110
+ or a (Geo)DataFrame if self.concat is True.
111
+
112
+ """
113
+
114
+ def _intersects_mask(df: GeoDataFrame) -> pd.Series:
104
115
  return df.index.isin(df.sjoin(self.mask).index)
105
116
 
106
117
  grid: GeoSeries = (
107
- make_grid(self.mask, gridsize=self.gridsize).loc[intersects_mask].geometry
118
+ make_grid(self.mask, gridsize=self.gridsize).loc[_intersects_mask].geometry
108
119
  )
109
120
 
110
121
  n = len(grid)
@@ -137,7 +148,9 @@ class Gridlooper:
137
148
  return out if not self.concat else pd.concat(out, ignore_index=True)
138
149
 
139
150
  results = []
140
- for i, (unbuffered, buffered) in enumerate(zip(grid, buffered_grid)):
151
+ for i, (unbuffered, buffered) in enumerate(
152
+ zip(grid, buffered_grid, strict=False)
153
+ ):
141
154
  cell_kwargs = {
142
155
  key: _clip_if_isinstance(
143
156
  value, buffered, self.keep_geom_type, self.clip
@@ -200,13 +213,16 @@ def gridloop(
200
213
  kwargs: Keyword arguments to pass to the function. Arguments
201
214
  of type GeoDataFrame or GeoSeries will be clipped by the grid cells in
202
215
  a loop.
216
+ parallelizer: Optional instance of sgis.Parallel, to run the function in parallel.
203
217
 
204
218
  Returns:
205
219
  List of results with the same length as number of grid cells.
206
220
 
207
- Examples
208
- --------
221
+ Raises:
222
+ TypeError: If args or kwargs has a wrong type
209
223
 
224
+ Examples:
225
+ ---------
210
226
  Get some points and some polygons.
211
227
 
212
228
  >>> import sgis as sg
@@ -274,8 +290,10 @@ def gridloop(
274
290
  if not isinstance(mask, GeoDataFrame):
275
291
  mask = to_gdf(mask)
276
292
 
277
- intersects_mask = lambda df: df.index.isin(df.sjoin(mask).index)
278
- grid: GeoSeries = make_grid(mask, gridsize=gridsize).loc[intersects_mask].geometry
293
+ def _intersects_mask(df: GeoDataFrame) -> pd.Series:
294
+ return df.index.isin(df.sjoin(mask).index)
295
+
296
+ grid: GeoSeries = make_grid(mask, gridsize=gridsize).loc[_intersects_mask].geometry
279
297
 
280
298
  n = len(grid)
281
299
 
@@ -301,7 +319,7 @@ def gridloop(
301
319
  return out
302
320
 
303
321
  results = []
304
- for i, (unbuffered, buffered) in enumerate(zip(grid, buffered_grid)):
322
+ for i, (unbuffered, buffered) in enumerate(zip(grid, buffered_grid, strict=False)):
305
323
  cell_kwargs = {
306
324
  key: _clip_if_isinstance(value, buffered, keep_geom_type, clip)
307
325
  for key, value in kwargs.items()
@@ -333,7 +351,7 @@ def _clip_and_run_func(
333
351
  kwargs: dict,
334
352
  keep_geom_type: bool,
335
353
  clip: bool,
336
- ):
354
+ ) -> Any:
337
355
  cell_args = tuple(
338
356
  _clip_if_isinstance(value, grid_cell, keep_geom_type, clip) for value in args
339
357
  )
@@ -345,11 +363,13 @@ def _clip_and_run_func(
345
363
  return func(*cell_args, **cell_kwargs)
346
364
 
347
365
 
348
- def _clip_if_isinstance(value, cell, keep_geom_type, clip: bool):
349
- if not isinstance(value, (gpd.GeoDataFrame, gpd.GeoSeries, Geometry)):
366
+ def _clip_if_isinstance(
367
+ value: Any, cell: Geometry, keep_geom_type: bool, clip: bool
368
+ ) -> Any:
369
+ if not isinstance(value, (gpd.GeoDataFrame | gpd.GeoSeries | Geometry)):
350
370
  return value
351
371
 
352
- if isinstance(value, (gpd.GeoDataFrame, gpd.GeoSeries)):
372
+ if isinstance(value, (gpd.GeoDataFrame | gpd.GeoSeries)):
353
373
  if clip:
354
374
  return clean_clip(value, cell, keep_geom_type=keep_geom_type)
355
375
  return value.loc[value.intersects(cell)]
@@ -357,10 +377,12 @@ def _clip_if_isinstance(value, cell, keep_geom_type, clip: bool):
357
377
  return value.intersection(cell).make_valid()
358
378
 
359
379
 
360
- def _clip_back_to_unbuffered_grid(results, mask, keep_geom_type):
361
- if isinstance(results, (gpd.GeoDataFrame, gpd.GeoSeries, Geometry)):
380
+ def _clip_back_to_unbuffered_grid(
381
+ results: Any, mask: GeoDataFrame, keep_geom_type: bool
382
+ ) -> Any:
383
+ if isinstance(results, (gpd.GeoDataFrame | gpd.GeoSeries | Geometry)):
362
384
  return _clip_if_isinstance(results, mask, keep_geom_type, clip=True)
363
- elif isinstance(results, (pd.DataFrame, pd.Series, np.ndarray)):
385
+ elif isinstance(results, (pd.DataFrame | pd.Series | np.ndarray)):
364
386
  return results
365
387
  try:
366
388
  for key, value in results.items():
@@ -383,7 +405,7 @@ def make_grid_from_bbox(
383
405
  maxy: int | float,
384
406
  *_,
385
407
  gridsize: int | float,
386
- crs,
408
+ crs: CRS | int | str,
387
409
  ) -> GeoDataFrame:
388
410
  """Creates a polygon grid from a bounding box.
389
411
 
@@ -420,7 +442,7 @@ def make_grid(
420
442
  obj: GeoDataFrame | GeoSeries | Geometry | tuple,
421
443
  gridsize: int | float,
422
444
  *,
423
- crs=None,
445
+ crs: CRS | None = None,
424
446
  clip_to_bounds: bool = False,
425
447
  ) -> GeoDataFrame:
426
448
  """Create a polygon grid around geometries.
@@ -439,13 +461,12 @@ def make_grid(
439
461
  Returns:
440
462
  GeoDataFrame with grid polygons.
441
463
 
464
+ Raises:
465
+ ValueError: crs can only be None if obj is GeoDataFrame/GeoSeries.
466
+
442
467
  """
443
- if isinstance(obj, (GeoDataFrame, GeoSeries)):
468
+ if isinstance(obj, (GeoDataFrame | GeoSeries)):
444
469
  crs = obj.crs or crs
445
- elif not crs:
446
- raise ValueError(
447
- "'crs' cannot be None when 'obj' is not GeoDataFrame/GeoSeries."
448
- )
449
470
  if hasattr(obj, "__len__") and not len(obj):
450
471
  return GeoDataFrame({"geometry": []}, crs=crs)
451
472
 
@@ -475,14 +496,17 @@ def make_ssb_grid(
475
496
  Args:
476
497
  gdf: A GeoDataFrame.
477
498
  gridsize: Size of the grid in meters.
499
+ add: Number of grid cells to add on each side,
500
+ to make sure all data is covered by the grid.
478
501
 
479
502
  Returns:
480
503
  GeoDataFrame with grid geometries and a column 'SSBID'.
481
504
 
482
505
  Raises:
483
506
  ValueError: If the GeoDataFrame does not have 25833 as crs.
507
+ TypeError: if gdf has wrong type.
484
508
  """
485
- if not isinstance(gdf, (GeoDataFrame, GeoSeries)):
509
+ if not isinstance(gdf, (GeoDataFrame | GeoSeries)):
486
510
  raise TypeError("gdf must be GeoDataFrame og GeoSeries.")
487
511
 
488
512
  if not gdf.crs.equals(25833):
@@ -551,6 +575,7 @@ def add_grid_id(
551
575
  Args:
552
576
  gdf: A GeoDataFrame.
553
577
  gridsize: Size of the grid in meters.
578
+ out_column: Name of column for the grid id.
554
579
 
555
580
  Returns:
556
581
  The input GeoDataFrame with a new grid id column.
@@ -587,9 +612,9 @@ def bounds_to_polygon(
587
612
  Returns:
588
613
  GeoDataFrame of box polygons with length and index of 'gdf'.
589
614
 
590
- Examples
591
- --------
592
-
615
+ Examples:
616
+ ---------
617
+ >>> import sgis as sg
593
618
  >>> gdf = sg.to_gdf([MultiPoint([(0, 0), (1, 1)]), Point(0, 0)])
594
619
  >>> gdf
595
620
  geometry
@@ -622,8 +647,9 @@ def bounds_to_points(
622
647
  Returns:
623
648
  GeoDataFrame of multipoints with same length and index as 'gdf'.
624
649
 
625
- Examples
626
- --------
650
+ Examples:
651
+ ---------
652
+ >>> import sgis as sg
627
653
  >>> gdf = sg.to_gdf([MultiPoint([(0, 0), (1, 1)]), Point(0, 0)])
628
654
  >>> gdf
629
655
  geometry
@@ -643,19 +669,32 @@ def bounds_to_points(
643
669
 
644
670
 
645
671
  def get_total_bounds(
646
- *geometries: GeoDataFrame | GeoSeries | Geometry,
672
+ *geometries: GeoDataFrame | GeoSeries | Geometry, strict: bool = False
647
673
  ) -> tuple[float, float, float, float]:
648
674
  """Get a combined total bounds of multiple geometry objects."""
649
675
  xs, ys = [], []
650
676
  for obj in geometries:
651
- minx, miny, maxx, maxy = to_bbox(obj)
652
- xs += [minx, maxx]
653
- ys += [miny, maxy]
677
+ try:
678
+ minx, miny, maxx, maxy = to_bbox(obj)
679
+ xs += [minx, maxx]
680
+ ys += [miny, maxy]
681
+ except Exception as e:
682
+ try:
683
+ for x in obj:
684
+ minx, miny, maxx, maxy = to_bbox(x)
685
+ xs += [minx, maxx]
686
+ ys += [miny, maxy]
687
+ except Exception as e2:
688
+ if strict:
689
+ raise e2 from e
690
+ else:
691
+ continue
654
692
  return min(xs), min(ys), max(xs), max(ys)
655
693
 
656
694
 
657
- def points_in_bounds(gdf: GeoDataFrame | GeoSeries, n2: int):
658
- if not isinstance(gdf, (GeoDataFrame, GeoSeries)) and is_bbox_like(gdf):
695
+ def points_in_bounds(gdf: GeoDataFrame | GeoSeries, n2: int) -> GeoDataFrame:
696
+ """Get a GeoDataFrame of points within the bounds of the GeoDataFrame."""
697
+ if not isinstance(gdf, (GeoDataFrame | GeoSeries)) and is_bbox_like(gdf):
659
698
  minx, miny, maxx, maxy = gdf
660
699
  else:
661
700
  minx, miny, maxx, maxy = gdf.total_bounds