ssb-sgis 1.0.1__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.1.dist-info → ssb_sgis-1.0.2.dist-info}/LICENSE +1 -1
  54. {ssb_sgis-1.0.1.dist-info → ssb_sgis-1.0.2.dist-info}/METADATA +87 -16
  55. ssb_sgis-1.0.2.dist-info/RECORD +61 -0
  56. {ssb_sgis-1.0.1.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.1.dist-info/RECORD +0 -63
@@ -3,61 +3,15 @@
3
3
  import geopandas as gpd
4
4
  import numpy as np
5
5
  import pandas as pd
6
- from geopandas import GeoDataFrame, GeoSeries
6
+ from geopandas import GeoDataFrame
7
+ from geopandas import GeoSeries
7
8
  from pandas import DataFrame
8
9
  from shapely import shortest_line
9
10
 
10
- from ..geopandas_tools.conversion import coordinate_array, to_geoseries
11
- from ..geopandas_tools.geometry_types import get_geom_type
11
+ from ..geopandas_tools.conversion import coordinate_array
12
12
  from ..geopandas_tools.neighbors import k_nearest_neighbors
13
- from .nodes import make_edge_wkt_cols, make_node_ids
14
-
15
-
16
- def close_network_holes_to(
17
- lines: GeoDataFrame | GeoSeries,
18
- extend_to: GeoDataFrame | GeoSeries,
19
- max_distance: int | float,
20
- max_angle: int | float,
21
- ) -> GeoDataFrame | GeoSeries:
22
- if isinstance(lines, GeoSeries):
23
- lines = lines.to_frame("geometry")
24
- was_geoseries = True
25
- else:
26
- was_geoseries = False
27
-
28
- lines, _ = make_node_ids(lines)
29
-
30
- if isinstance(extend_to, GeoSeries):
31
- extend_to = extend_to.to_frame("geometry")
32
-
33
- if not (extend_to.geom_type == "Point").all():
34
- raise ValueError("'extend_to' must be singlepart point geometries")
35
-
36
- extend_to["wkt"] = extend_to.geometry.to_wkt()
37
- extend_to = extend_to.drop_duplicates("wkt")
38
- extend_to["node_id"] = range(len(extend_to))
39
-
40
- new_lines: GeoSeries = _close_holes_all_lines(
41
- lines, extend_to, max_distance=max_distance, max_angle=max_angle, idx_start=0
42
- )
43
-
44
- if was_geoseries:
45
- return pd.concat([lines.geometry, new_lines])
46
-
47
- new_lines = gpd.GeoDataFrame(
48
- {"geometry": new_lines}, geometry="geometry", crs=lines.crs
49
- )
50
-
51
- return pd.concat([lines, new_lines], ignore_index=True).drop(
52
- columns=[
53
- "source_wkt",
54
- "target_wkt",
55
- "source",
56
- "target",
57
- "n_source",
58
- "n_target",
59
- ]
60
- )
13
+ from .nodes import make_edge_wkt_cols
14
+ from .nodes import make_node_ids
61
15
 
62
16
 
63
17
  def close_network_holes(
@@ -65,7 +19,7 @@ def close_network_holes(
65
19
  max_distance: int | float,
66
20
  max_angle: int,
67
21
  hole_col: str | None = "hole",
68
- ):
22
+ ) -> GeoDataFrame:
69
23
  """Fills network gaps with straigt lines.
70
24
 
71
25
  Fills holes in the network by connecting deadends with the nodes that are
@@ -89,7 +43,7 @@ def close_network_holes(
89
43
  The holes will have missing values in the weight column used in
90
44
  NetworkAnalysis. These values must be filled before analysis.
91
45
 
92
- Examples
46
+ Examples:
93
47
  --------
94
48
  Read road data with small gaps.
95
49
 
@@ -105,10 +59,10 @@ def close_network_holes(
105
59
 
106
60
  >>> filled = sg.close_network_holes(roads, max_distance=1.1, max_angle=180)
107
61
  >>> filled.hole.value_counts()
108
- Name: connected, dtype: int64
62
+ hole
109
63
  0 93395
110
64
  1 7102
111
- Name: hole, dtype: int64
65
+ Name: count, dtype: int64
112
66
 
113
67
  Compare the number of isolated lines before and after.
114
68
 
@@ -128,15 +82,15 @@ def close_network_holes(
128
82
 
129
83
  >>> filled = sg.close_network_holes(roads, max_distance=1.1, max_angle=30)
130
84
  >>> filled.hole.value_counts()
85
+ hole
131
86
  0 93395
132
87
  1 7092
133
- Name: hole, dtype: int64
88
+ Name: count, dtype: int64
134
89
 
135
90
  It's not always wise to fill gaps. In the case of this data, these small gaps are
136
91
  intentional. They are road blocks where most cars aren't allowed to pass. Fill the
137
92
  holes only if it makes the travel times/routes more realistic.
138
93
  """
139
-
140
94
  lines, nodes = make_node_ids(gdf)
141
95
 
142
96
  # remove duplicates of lines going both directions
@@ -174,7 +128,7 @@ def close_network_holes(
174
128
  return pd.concat([lines, new_lines], ignore_index=True)
175
129
 
176
130
 
177
- def get_angle(array_a, array_b):
131
+ def get_angle(array_a: np.ndarray, array_b: np.ndarray) -> np.ndarray:
178
132
  dx = array_b[:, 0] - array_a[:, 0]
179
133
  dy = array_b[:, 1] - array_a[:, 1]
180
134
 
@@ -187,7 +141,7 @@ def close_network_holes_to_deadends(
187
141
  gdf: GeoDataFrame,
188
142
  max_distance: int | float,
189
143
  hole_col: str | None = "hole",
190
- ):
144
+ ) -> GeoDataFrame:
191
145
  """Fills gaps between two deadends if the distance is less than 'max_distance'.
192
146
 
193
147
  Fills holes between deadends in the network with straight lines if the distance is
@@ -202,7 +156,7 @@ def close_network_holes_to_deadends(
202
156
  Returns:
203
157
  The input GeoDataFrame with new lines added.
204
158
 
205
- Examples
159
+ Examples:
206
160
  --------
207
161
  Read road data with small gaps.
208
162
 
@@ -246,7 +200,7 @@ def close_network_holes_to_deadends(
246
200
  new_lines = make_edge_wkt_cols(new_lines)
247
201
 
248
202
  wkt_id_dict = {
249
- wkt: id for wkt, id in zip(nodes["wkt"], nodes["node_id"], strict=True)
203
+ wkt: id_ for wkt, id_ in zip(nodes["wkt"], nodes["node_id"], strict=True)
250
204
  }
251
205
  new_lines["source"] = new_lines["source_wkt"].map(wkt_id_dict)
252
206
  new_lines["target"] = new_lines["target_wkt"].map(wkt_id_dict)
@@ -259,7 +213,11 @@ def close_network_holes_to_deadends(
259
213
 
260
214
 
261
215
  def _close_holes_all_lines(
262
- lines, nodes, max_distance, max_angle, idx_start: int
216
+ lines: GeoDataFrame,
217
+ nodes: GeoDataFrame,
218
+ max_distance: int | None,
219
+ max_angle: int | None,
220
+ idx_start: int,
263
221
  ) -> GeoSeries:
264
222
  k = min(len(nodes), 50)
265
223
 
@@ -1,31 +1,23 @@
1
1
  """Cutting and splitting line geometries."""
2
+
2
3
  import warnings
3
4
 
4
5
  import numpy as np
5
6
  import pandas as pd
6
- from geopandas import GeoDataFrame, GeoSeries
7
- from pandas import DataFrame, Series
8
- from shapely import (
9
- buffer,
10
- extract_unique_points,
11
- force_2d,
12
- get_coordinates,
13
- get_parts,
14
- linestrings,
15
- touches,
16
- unary_union,
17
- )
18
- from shapely.geometry import LineString, Point
7
+ from geopandas import GeoDataFrame
8
+ from pandas import DataFrame
9
+ from pandas import Series
10
+ from shapely import extract_unique_points
11
+ from shapely import force_2d
12
+ from shapely.geometry import LineString
13
+ from shapely.geometry import Point
19
14
 
20
15
  from ..geopandas_tools.buffer_dissolve_explode import buff
21
16
  from ..geopandas_tools.conversion import to_gdf
22
17
  from ..geopandas_tools.geometry_types import get_geom_type
23
18
  from ..geopandas_tools.neighbors import get_k_nearest_neighbors
24
- from ..geopandas_tools.point_operations import (
25
- _shapely_snap,
26
- snap_all,
27
- snap_within_distance,
28
- )
19
+ from ..geopandas_tools.point_operations import snap_all
20
+ from ..geopandas_tools.point_operations import snap_within_distance
29
21
  from ..geopandas_tools.sfilter import sfilter_split
30
22
  from .nodes import make_edge_coords_cols
31
23
 
@@ -59,7 +51,7 @@ def split_lines_by_nearest_point(
59
51
  Raises:
60
52
  ValueError: If the crs of the input data differs.
61
53
 
62
- Examples
54
+ Examples:
63
55
  --------
64
56
  >>> from sgis import read_parquet_url, split_lines_by_nearest_point
65
57
  >>> roads = read_parquet_url("https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/roads_oslo_2022.parquet")
@@ -148,8 +140,8 @@ def split_lines_by_nearest_point(
148
140
  splitted_source = to_gdf(splitted["source_coords"], crs=gdf.crs)
149
141
  splitted_target = to_gdf(splitted["target_coords"], crs=gdf.crs)
150
142
 
151
- def get_nearest(splitted, snapped) -> pd.DataFrame:
152
- """find the nearest snapped point for each source and target of the lines"""
143
+ def get_nearest(splitted: GeoDataFrame, snapped: GeoDataFrame) -> pd.DataFrame:
144
+ """Find the nearest snapped point for each source and target of the lines."""
153
145
  return get_k_nearest_neighbors(splitted, snapped, k=1).loc[
154
146
  lambda x: x["distance"] <= PRECISION * 2
155
147
  ]
@@ -166,7 +158,7 @@ def split_lines_by_nearest_point(
166
158
  # now, we can replace the source/target coordinate with the coordinates of
167
159
  # the snapped points.
168
160
 
169
- splitted = change_line_endpoint(
161
+ splitted = _change_line_endpoint(
170
162
  splitted,
171
163
  indices=dists_source.index,
172
164
  pointmapper=pointmapper_source,
@@ -174,7 +166,7 @@ def split_lines_by_nearest_point(
174
166
  ) # i=0)
175
167
 
176
168
  # same for the lines where the target was split, but change the last coordinate
177
- splitted = change_line_endpoint(
169
+ splitted = _change_line_endpoint(
178
170
  splitted,
179
171
  indices=dists_target.index,
180
172
  pointmapper=pointmapper_target,
@@ -189,22 +181,38 @@ def split_lines_by_nearest_point(
189
181
  )
190
182
 
191
183
 
192
- def change_line_endpoint(
184
+ def _change_line_endpoint(
193
185
  gdf: GeoDataFrame,
194
186
  indices: pd.Index,
195
187
  pointmapper: pd.Series,
196
188
  change_what: str | int,
197
189
  ) -> GeoDataFrame:
198
- """
199
- Loop for each line where the source is the endpoint that was split
200
- change the first point of the line to the point it was split by
190
+ """Modify the endpoints of selected lines in a GeoDataFrame based on an index mapping.
191
+
192
+ This function updates the geometry of specified line features within a GeoDataFrame,
193
+ changing either the first or last point of each line to new coordinates provided by a mapping.
194
+ It is typically used in scenarios where line endpoints need to be adjusted to new locations,
195
+ such as in network adjustments or data corrections.
196
+
197
+ Args:
198
+ gdf: A GeoDataFrame containing line geometries.
199
+ indices: An Index object identifying the rows in the GeoDataFrame whose endpoints will be changed.
200
+ pointmapper: A Series mapping from the index of lines to new point geometries.
201
+ change_what: Specifies which endpoint of the line to change. Accepts 'first' or 0 for the
202
+ starting point, and 'last' or -1 for the ending point.
203
+
204
+ Returns:
205
+ A GeoDataFrame with the specified line endpoints updated according to the pointmapper.
206
+
207
+ Raises:
208
+ ValueError: If `change_what` is not one of the accepted values ('first', 'last', 0, -1).
201
209
  """
202
210
  assert gdf.index.is_unique
203
211
 
204
212
  if change_what == "first" or change_what == 0:
205
- to_be_changed = lambda x: ~x.index.duplicated(keep="first")
213
+ keep = "first"
206
214
  elif change_what == "last" or change_what == -1:
207
- to_be_changed = lambda x: ~x.index.duplicated(keep="last")
215
+ keep = "last"
208
216
  else:
209
217
  raise ValueError(
210
218
  f"change_what should be 'first' or 'last' or 0 or -1. Got {change_what}"
@@ -216,8 +224,10 @@ def change_line_endpoint(
216
224
  relevant_lines.geometry = extract_unique_points(relevant_lines.geometry)
217
225
  relevant_lines = relevant_lines.explode(index_parts=False)
218
226
 
219
- relevant_lines.loc[to_be_changed, "geometry"] = (
220
- relevant_lines.loc[to_be_changed].index.map(pointmapper).values
227
+ relevant_lines.loc[lambda x: ~x.index.duplicated(keep=keep), "geometry"] = (
228
+ relevant_lines.loc[lambda x: ~x.index.duplicated(keep=keep)]
229
+ .index.map(pointmapper)
230
+ .values
221
231
  )
222
232
 
223
233
  relevant_lines_mapped = relevant_lines.groupby(level=0)["geometry"].agg(LineString)
@@ -227,7 +237,9 @@ def change_line_endpoint(
227
237
  return gdf
228
238
 
229
239
 
230
- def cut_lines(gdf: GeoDataFrame, max_length: int, ignore_index=False) -> GeoDataFrame:
240
+ def cut_lines(
241
+ gdf: GeoDataFrame, max_length: int, ignore_index: bool = False
242
+ ) -> GeoDataFrame:
231
243
  """Cuts lines of a GeoDataFrame into pieces of a given length.
232
244
 
233
245
  Args:
@@ -242,7 +254,7 @@ def cut_lines(gdf: GeoDataFrame, max_length: int, ignore_index=False) -> GeoData
242
254
  Note:
243
255
  This method is time consuming for large networks and low 'max_length'.
244
256
 
245
- Examples
257
+ Examples:
246
258
  --------
247
259
  >>> from sgis import read_parquet_url, cut_lines
248
260
  >>> roads = read_parquet_url("https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/roads_oslo_2022.parquet")
@@ -314,7 +326,7 @@ def cut_lines_once(
314
326
  ignore_index: If True, the resulting axis will be labeled 0, 1, …, n - 1.
315
327
  Defaults to False.
316
328
 
317
- Examples
329
+ Examples:
318
330
  --------
319
331
  >>> from sgis import cut_lines_once, to_gdf
320
332
  >>> import pandas as pd
@@ -344,24 +356,24 @@ def cut_lines_once(
344
356
  >>> cut_lines_once(gdf, "dist")
345
357
  geometry dist
346
358
  0 LINESTRING (0.00000 0.00000, 0.70711 0.70711) 1
347
- 1 LINESTRING (0.70711 0.70711, 1.00000 1.00000, ... 1
348
- 2 LINESTRING (0.00000 0.00000, 1.00000 1.00000, ... 2
349
- 3 LINESTRING (1.41421 1.41421, 2.00000 2.00000) 2
350
- 4 LINESTRING (0.00000 0.00000, 1.00000 1.00000, ... 3
359
+ 0 LINESTRING (0.70711 0.70711, 1.00000 1.00000, ... 1
360
+ 0 LINESTRING (0.00000 0.00000, 1.00000 1.00000, ... 2
361
+ 0 LINESTRING (1.41421 1.41421, 2.00000 2.00000) 2
362
+ 0 LINESTRING (0.00000 0.00000, 1.00000 1.00000, ... 3
351
363
 
352
364
  Cut distance as list (same result as above).
353
365
 
354
366
  >>> cut_lines_once(gdf, [1, 2, 3])
355
367
  geometry dist
356
368
  0 LINESTRING (0.00000 0.00000, 0.70711 0.70711) 1
357
- 1 LINESTRING (0.70711 0.70711, 1.00000 1.00000, ... 1
358
- 2 LINESTRING (0.00000 0.00000, 1.00000 1.00000, ... 2
359
- 3 LINESTRING (1.41421 1.41421, 2.00000 2.00000) 2
360
- 4 LINESTRING (0.00000 0.00000, 1.00000 1.00000, ... 3
369
+ 0 LINESTRING (0.70711 0.70711, 1.00000 1.00000, ... 1
370
+ 0 LINESTRING (0.00000 0.00000, 1.00000 1.00000, ... 2
371
+ 0 LINESTRING (1.41421 1.41421, 2.00000 2.00000) 2
372
+ 0 LINESTRING (0.00000 0.00000, 1.00000 1.00000, ... 3
361
373
  """
362
374
 
363
375
  def _cut(line: LineString, distance: int | float) -> list[LineString]:
364
- """From the shapely docs"""
376
+ """From the shapely docs."""
365
377
  if distance <= 0.0 or distance >= line.length:
366
378
  return line
367
379
  coords = list(line.coords)
@@ -1,12 +1,14 @@
1
1
  """Prepare a GeoDataFrame of line geometries for directed network analysis."""
2
2
 
3
3
  import warnings
4
+ from collections.abc import Sequence
4
5
 
5
6
  import pandas as pd
6
7
  from geopandas import GeoDataFrame
7
8
  from shapely.constructive import reverse
8
9
 
9
- from ..helpers import return_two_vals, unit_is_meters
10
+ from ..helpers import return_two_vals
11
+ from ..helpers import unit_is_meters
10
12
 
11
13
 
12
14
  def make_directed_network_norway(gdf: GeoDataFrame, dropnegative: bool) -> GeoDataFrame:
@@ -26,7 +28,7 @@ def make_directed_network_norway(gdf: GeoDataFrame, dropnegative: bool) -> GeoDa
26
28
  network graph. Recode these rows to a non-negative values if you want
27
29
  to keep them.
28
30
 
29
- Examples
31
+ Examples:
30
32
  --------
31
33
  2022 data for the municipalities of Oslo and Eidskog can be read directly like this:
32
34
 
@@ -47,11 +49,12 @@ def make_directed_network_norway(gdf: GeoDataFrame, dropnegative: bool) -> GeoDa
47
49
  1944398 B 0.068239 0.068239 MULTILINESTRING Z ((258292.600 6648313.440 18....
48
50
  1944409 B 0.023629 0.023629 MULTILINESTRING Z ((258291.452 6648289.258 19....
49
51
  1944415 B 0.175876 0.175876 MULTILINESTRING Z ((260762.830 6650240.620 43....
50
- [93395 rows x 46 columns]
52
+ <BLANKLINE>
53
+ [93395 rows x 4 columns]
51
54
 
52
55
  And converted to a directed network like this:
53
56
 
54
- >>> roads_directed = sg.make_directed_network_norway(roads)
57
+ >>> roads_directed = sg.make_directed_network_norway(roads, dropnegative=True)
55
58
  >>> roads_directed[["minutes", "geometry"]]
56
59
  minutes geometry
57
60
  0 0.216611 MULTILINESTRING Z ((258028.440 6674249.890 413...
@@ -65,7 +68,8 @@ def make_directed_network_norway(gdf: GeoDataFrame, dropnegative: bool) -> GeoDa
65
68
  175622 0.036810 MULTILINESTRING Z ((268681.757 6651886.457 110...
66
69
  175623 0.003019 MULTILINESTRING Z ((268682.748 6651886.162 110...
67
70
  175624 0.036975 MULTILINESTRING Z ((268694.594 6651881.688 111...
68
- [175541 rows x 45 columns]
71
+ <BLANKLINE>
72
+ [175541 rows x 2 columns]
69
73
  """
70
74
  if gdf["drivetime_fw"].isna().any():
71
75
  raise ValueError("Missing values in the columns 'drivetime_fw'")
@@ -202,7 +206,9 @@ def make_directed_network(
202
206
  return gdf
203
207
 
204
208
 
205
- def _validate_minute_args(minute_cols, speed_col_kmh, flat_speed_kmh):
209
+ def _validate_minute_args(
210
+ minute_cols: Sequence[str], speed_col_kmh: str, flat_speed_kmh: str
211
+ ) -> None:
206
212
  if not minute_cols and not speed_col_kmh and not flat_speed_kmh:
207
213
  warnings.warn(
208
214
  "Minute column will not be calculated when both 'minute_cols', "
@@ -217,7 +223,9 @@ def _validate_minute_args(minute_cols, speed_col_kmh, flat_speed_kmh):
217
223
  )
218
224
 
219
225
 
220
- def _validate_direction_args(gdf, direction_col, direction_vals_bft):
226
+ def _validate_direction_args(
227
+ gdf: GeoDataFrame, direction_col: str, direction_vals_bft: Sequence[str]
228
+ ) -> None:
221
229
  if len(direction_vals_bft) != 3:
222
230
  raise ValueError(
223
231
  "'direction_vals_bft' should be tuple/list with values of directions "
@@ -22,7 +22,7 @@ def get_connected_components(gdf: GeoDataFrame) -> GeoDataFrame:
22
22
  Returns:
23
23
  The GeoDataFrame with a new column "connected".
24
24
 
25
- Examples
25
+ Examples:
26
26
  --------
27
27
  >>> from sgis import read_parquet_url, get_connected_components
28
28
  >>> roads = read_parquet_url("https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/roads_oslo_2022.parquet")
@@ -75,19 +75,20 @@ def get_component_size(gdf: GeoDataFrame) -> GeoDataFrame:
75
75
  Returns:
76
76
  A GeoDataFrame with a new column "component_size".
77
77
 
78
- Examples
78
+ Examples:
79
79
  --------
80
80
  >>> from sgis import read_parquet_url, get_component_size
81
81
  >>> roads = read_parquet_url("https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/roads_oslo_2022.parquet")
82
82
 
83
83
  >>> roads = get_component_size(roads)
84
84
  >>> roads.component_size.value_counts().head()
85
+ component_size
85
86
  79180 85638
86
87
  2 1601
87
88
  4 688
88
89
  6 406
89
90
  3 346
90
- Name: component_size, dtype: int64
91
+ Name: count, dtype: int64
91
92
  """
92
93
  gdf, _ = make_node_ids(gdf)
93
94
 
@@ -5,7 +5,8 @@ network, finding and removing isolated network islands and creating unique node
5
5
  """
6
6
 
7
7
  import warnings
8
- from copy import copy, deepcopy
8
+ from copy import copy
9
+ from copy import deepcopy
9
10
 
10
11
  import numpy as np
11
12
  from geopandas import GeoDataFrame
@@ -21,9 +22,12 @@ from .nodes import make_node_ids
21
22
  class Network:
22
23
  """Class used in NetworkAnalysis."""
23
24
 
24
- def __init__(self, gdf: GeoDataFrame):
25
+ def __init__(self, gdf: GeoDataFrame) -> None:
25
26
  """The lines are fixed, welded together rowwise and exploded. Creates node-ids.
26
27
 
28
+ Args:
29
+ gdf: GeoDataFrame of line geometries to make up the network.
30
+
27
31
  Raises:
28
32
  TypeError: If 'gdf' is not of type GeoDataFrame.
29
33
  ZeroLinesError: If 'gdf' has zero rows.
@@ -152,6 +156,7 @@ class Network:
152
156
  return True
153
157
 
154
158
  def get_edges(self) -> list[tuple[str, str]]:
159
+ """Get a list of edges in the network."""
155
160
  return [
156
161
  (str(source), str(target))
157
162
  for source, target in zip(
@@ -166,12 +171,12 @@ class Network:
166
171
  """Edge identifiers represented with source and target ids and the weight."""
167
172
  return [f"{s}_{t}_{w}" for (s, t), w in zip(edges, weights, strict=True)]
168
173
 
169
- def _update_nodes_if(self):
174
+ def _update_nodes_if(self) -> None:
170
175
  if not self._nodes_are_up_to_date():
171
176
  self._make_node_ids()
172
177
 
173
178
  @property
174
- def nodes(self):
179
+ def nodes(self) -> GeoDataFrame:
175
180
  """GeoDataFrame with the network nodes (line endpoints).
176
181
 
177
182
  Upon instantiation of the class, a GeoDataFrame of points is created from the
@@ -182,7 +187,7 @@ class Network:
182
187
  """
183
188
  return self._nodes
184
189
 
185
- def _warn_if_undirected(self):
190
+ def _warn_if_undirected(self) -> None:
186
191
  """Road data often have to be duplicated and flipped to make it directed."""
187
192
  if self.percent_bidirectional > 5:
188
193
  return
@@ -202,15 +207,15 @@ class Network:
202
207
  warnings.warn(mess, stacklevel=2)
203
208
 
204
209
  @property
205
- def percent_bidirectional(self):
210
+ def percent_bidirectional(self) -> float:
206
211
  """The percentage of lines that appear in both directions."""
207
212
  return self._percent_bidirectional
208
213
 
209
- def copy(self):
214
+ def copy(self) -> "Network":
210
215
  """Returns a shallow copy of the class instance."""
211
216
  return copy(self)
212
217
 
213
- def deepcopy(self):
218
+ def deepcopy(self) -> "Network":
214
219
  """Returns a deep copy of the class instance."""
215
220
  return deepcopy(self)
216
221
 
@@ -220,9 +225,6 @@ class Network:
220
225
  km = int(sum(self.gdf.length) / 1000)
221
226
  return f"{cl}({km} km, percent_bidirectional={self._percent_bidirectional})"
222
227
 
223
- def __iter__(self):
224
- """So the attributes can be iterated through."""
225
- return iter(self.__dict__.items())
226
-
227
- def __len__(self):
228
+ def __len__(self) -> int:
229
+ """Number og rows in the GeoDataFrame."""
228
230
  return len(self.gdf)