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.
Files changed (59) hide show
  1. sgis/__init__.py +97 -115
  2. sgis/exceptions.py +3 -1
  3. sgis/geopandas_tools/__init__.py +1 -0
  4. sgis/geopandas_tools/bounds.py +75 -38
  5. sgis/geopandas_tools/buffer_dissolve_explode.py +38 -34
  6. sgis/geopandas_tools/centerlines.py +53 -44
  7. sgis/geopandas_tools/cleaning.py +87 -104
  8. sgis/geopandas_tools/conversion.py +149 -101
  9. sgis/geopandas_tools/duplicates.py +31 -17
  10. sgis/geopandas_tools/general.py +76 -48
  11. sgis/geopandas_tools/geometry_types.py +21 -7
  12. sgis/geopandas_tools/neighbors.py +20 -8
  13. sgis/geopandas_tools/overlay.py +136 -53
  14. sgis/geopandas_tools/point_operations.py +9 -8
  15. sgis/geopandas_tools/polygon_operations.py +48 -56
  16. sgis/geopandas_tools/polygons_as_rings.py +121 -78
  17. sgis/geopandas_tools/sfilter.py +14 -14
  18. sgis/helpers.py +114 -56
  19. sgis/io/dapla_functions.py +32 -23
  20. sgis/io/opener.py +13 -6
  21. sgis/io/read_parquet.py +1 -1
  22. sgis/maps/examine.py +39 -26
  23. sgis/maps/explore.py +112 -66
  24. sgis/maps/httpserver.py +12 -12
  25. sgis/maps/legend.py +124 -65
  26. sgis/maps/map.py +66 -41
  27. sgis/maps/maps.py +31 -29
  28. sgis/maps/thematicmap.py +46 -33
  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 +20 -62
  35. sgis/networkanalysis/cutting_lines.py +55 -43
  36. sgis/networkanalysis/directednetwork.py +15 -7
  37. sgis/networkanalysis/finding_isolated_networks.py +4 -3
  38. sgis/networkanalysis/network.py +15 -13
  39. sgis/networkanalysis/networkanalysis.py +72 -54
  40. sgis/networkanalysis/networkanalysisrules.py +20 -16
  41. sgis/networkanalysis/nodes.py +2 -3
  42. sgis/networkanalysis/traveling_salesman.py +5 -2
  43. sgis/parallel/parallel.py +337 -127
  44. sgis/raster/__init__.py +6 -0
  45. sgis/raster/base.py +9 -3
  46. sgis/raster/cube.py +280 -208
  47. sgis/raster/cubebase.py +15 -29
  48. sgis/raster/indices.py +3 -7
  49. sgis/raster/methods_as_functions.py +0 -124
  50. sgis/raster/raster.py +313 -127
  51. sgis/raster/torchgeo.py +58 -37
  52. sgis/raster/zonal.py +38 -13
  53. {ssb_sgis-1.0.0.dist-info → ssb_sgis-1.0.2.dist-info}/LICENSE +1 -1
  54. {ssb_sgis-1.0.0.dist-info → ssb_sgis-1.0.2.dist-info}/METADATA +89 -18
  55. ssb_sgis-1.0.2.dist-info/RECORD +61 -0
  56. {ssb_sgis-1.0.0.dist-info → ssb_sgis-1.0.2.dist-info}/WHEEL +1 -1
  57. sgis/raster/bands.py +0 -48
  58. sgis/raster/gradient.py +0 -78
  59. ssb_sgis-1.0.0.dist-info/RECORD +0 -63
sgis/__init__.py CHANGED
@@ -2,140 +2,120 @@ 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
118
 
138
-
139
119
  try:
140
120
  import sgis.raster.torchgeo as torchgeo
141
121
  except ImportError:
@@ -143,6 +123,8 @@ except ImportError:
143
123
 
144
124
 
145
125
  try:
146
- from .io.dapla_functions import check_files, read_geopandas, write_geopandas
126
+ from .io.dapla_functions import check_files
127
+ from .io.dapla_functions import read_geopandas
128
+ from .io.dapla_functions import write_geopandas
147
129
  except ImportError:
148
130
  pass
sgis/exceptions.py CHANGED
@@ -7,10 +7,12 @@ class NoPointsWithinSearchToleranceError(Exception):
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}"
@@ -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
39
+ Examples:
36
40
  --------
37
-
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,
424
446
  clip_to_bounds: bool = False,
425
447
  ) -> GeoDataFrame:
426
448
  """Create a polygon grid around geometries.
@@ -439,8 +461,11 @@ 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
470
  elif not crs:
446
471
  raise ValueError(
@@ -475,14 +500,17 @@ def make_ssb_grid(
475
500
  Args:
476
501
  gdf: A GeoDataFrame.
477
502
  gridsize: Size of the grid in meters.
503
+ add: Number of grid cells to add on each side,
504
+ to make sure all data is covered by the grid.
478
505
 
479
506
  Returns:
480
507
  GeoDataFrame with grid geometries and a column 'SSBID'.
481
508
 
482
509
  Raises:
483
510
  ValueError: If the GeoDataFrame does not have 25833 as crs.
511
+ TypeError: if gdf has wrong type.
484
512
  """
485
- if not isinstance(gdf, (GeoDataFrame, GeoSeries)):
513
+ if not isinstance(gdf, (GeoDataFrame | GeoSeries)):
486
514
  raise TypeError("gdf must be GeoDataFrame og GeoSeries.")
487
515
 
488
516
  if not gdf.crs.equals(25833):
@@ -551,6 +579,7 @@ def add_grid_id(
551
579
  Args:
552
580
  gdf: A GeoDataFrame.
553
581
  gridsize: Size of the grid in meters.
582
+ out_column: Name of column for the grid id.
554
583
 
555
584
  Returns:
556
585
  The input GeoDataFrame with a new grid id column.
@@ -587,9 +616,9 @@ def bounds_to_polygon(
587
616
  Returns:
588
617
  GeoDataFrame of box polygons with length and index of 'gdf'.
589
618
 
590
- Examples
619
+ Examples:
591
620
  --------
592
-
621
+ >>> import sgis as sg
593
622
  >>> gdf = sg.to_gdf([MultiPoint([(0, 0), (1, 1)]), Point(0, 0)])
594
623
  >>> gdf
595
624
  geometry
@@ -622,8 +651,9 @@ def bounds_to_points(
622
651
  Returns:
623
652
  GeoDataFrame of multipoints with same length and index as 'gdf'.
624
653
 
625
- Examples
654
+ Examples:
626
655
  --------
656
+ >>> import sgis as sg
627
657
  >>> gdf = sg.to_gdf([MultiPoint([(0, 0), (1, 1)]), Point(0, 0)])
628
658
  >>> gdf
629
659
  geometry
@@ -643,19 +673,26 @@ def bounds_to_points(
643
673
 
644
674
 
645
675
  def get_total_bounds(
646
- *geometries: GeoDataFrame | GeoSeries | Geometry,
676
+ *geometries: GeoDataFrame | GeoSeries | Geometry, strict: bool = False
647
677
  ) -> tuple[float, float, float, float]:
648
678
  """Get a combined total bounds of multiple geometry objects."""
649
679
  xs, ys = [], []
650
680
  for obj in geometries:
651
- minx, miny, maxx, maxy = to_bbox(obj)
681
+ try:
682
+ minx, miny, maxx, maxy = to_bbox(obj)
683
+ except Exception as e:
684
+ if strict:
685
+ raise e
686
+ else:
687
+ continue
652
688
  xs += [minx, maxx]
653
689
  ys += [miny, maxy]
654
690
  return min(xs), min(ys), max(xs), max(ys)
655
691
 
656
692
 
657
- def points_in_bounds(gdf: GeoDataFrame | GeoSeries, n2: int):
658
- if not isinstance(gdf, (GeoDataFrame, GeoSeries)) and is_bbox_like(gdf):
693
+ def points_in_bounds(gdf: GeoDataFrame | GeoSeries, n2: int) -> GeoDataFrame:
694
+ """Get a GeoDataFrame of points within the bounds of the GeoDataFrame."""
695
+ if not isinstance(gdf, (GeoDataFrame | GeoSeries)) and is_bbox_like(gdf):
659
696
  minx, miny, maxx, maxy = gdf
660
697
  else:
661
698
  minx, miny, maxx, maxy = gdf.total_bounds