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
@@ -4,22 +4,27 @@ The class has five analysis methods: od_cost_matrix, get_route, get_k_routes,
4
4
  get_route_frequencies and service_area.
5
5
  """
6
6
 
7
-
8
- from copy import copy, deepcopy
7
+ from copy import copy
8
+ from copy import deepcopy
9
9
  from datetime import datetime
10
10
  from time import perf_counter
11
+ from typing import Any
11
12
 
12
13
  import igraph
13
14
  import numpy as np
14
15
  import pandas as pd
15
16
  from geopandas import GeoDataFrame
16
17
  from igraph import Graph
17
- from pandas import DataFrame, MultiIndex
18
+ from pandas import DataFrame
19
+ from pandas import MultiIndex
18
20
 
19
21
  from ..geopandas_tools.general import _push_geom_col
20
- from ._get_route import _get_k_routes, _get_route, _get_route_frequencies
22
+ from ._get_route import _get_k_routes
23
+ from ._get_route import _get_route
24
+ from ._get_route import _get_route_frequencies
21
25
  from ._od_cost_matrix import _od_cost_matrix
22
- from ._points import Destinations, Origins
26
+ from ._points import Destinations
27
+ from ._points import Origins
23
28
  from ._service_area import _service_area
24
29
  from .cutting_lines import split_lines_by_nearest_point
25
30
  from .network import Network
@@ -46,24 +51,12 @@ class NetworkAnalysis:
46
51
  line segments that were visited with an added column for how many times the
47
52
  segments were used.
48
53
 
49
- Args:
50
- network: A GeoDataFrame of line geometries.
51
- rules: The rules for the analysis, either as an instance of
52
- NetworkAnalysisRules or a dictionary with the parameters
53
- as keys.
54
- log: If True (default), a DataFrame with information about each
55
- analysis run will be stored in the 'log' attribute.
56
- detailed_log: If True, the log DataFrame will include columns for
57
- all arguments passed to the analysis method, plus standard deviation and
58
- percentiles (25th, 50th, 75th) of the weight column in the results.
59
- Defaults to False.
60
-
61
54
  Attributes:
62
55
  network: A Network instance that holds the lines and nodes (points).
63
56
  rules: NetworkAnalysisRules instance.
64
57
  log: A DataFrame with information about each analysis run.
65
58
 
66
- Examples
59
+ Examples:
67
60
  --------
68
61
  Read example data.
69
62
 
@@ -79,13 +72,15 @@ class NetworkAnalysis:
79
72
  ... direction_col="oneway",
80
73
  ... direction_vals_bft=("B", "FT", "TF"),
81
74
  ... minute_cols=("drivetime_fw", "drivetime_bw"),
75
+ ... dropnegative=True,
76
+ ... dropna=True,
82
77
  ... )
83
78
 
84
- >>> rules = sg.NetworkAnalysisRules(weight="minutes")
79
+ >>> rules = sg.NetworkAnalysisRules(weight="minutes", directed=True)
85
80
  >>> nwa = sg.NetworkAnalysis(network=directed_roads, rules=rules, detailed_log=False)
86
81
  >>> nwa
87
82
  NetworkAnalysis(
88
- network=DirectedNetwork(6364 km, percent_bidirectional=87),
83
+ network=Network(6364 km, percent_bidirectional=87),
89
84
  rules=NetworkAnalysisRules(weight=minutes, directed=True, search_tolerance=250, search_factor=0, split_lines=False, ...),
90
85
  log=True, detailed_log=True,
91
86
  )
@@ -100,7 +95,22 @@ class NetworkAnalysis:
100
95
  rules: NetworkAnalysisRules | dict,
101
96
  log: bool = True,
102
97
  detailed_log: bool = False,
103
- ):
98
+ ) -> None:
99
+ """Initialise NetworkAnalysis instance.
100
+
101
+ Args:
102
+ network: A GeoDataFrame of line geometries.
103
+ rules: The rules for the analysis, either as an instance of
104
+ NetworkAnalysisRules or a dictionary with the parameters
105
+ as keys.
106
+ log: If True (default), a DataFrame with information about each
107
+ analysis run will be stored in the 'log' attribute.
108
+ detailed_log: If True, the log DataFrame will include columns for
109
+ all arguments passed to the analysis method, plus standard deviation and
110
+ percentiles (25th, 50th, 75th) of the weight column in the results.
111
+ Defaults to False.
112
+
113
+ """
104
114
  if not isinstance(rules, NetworkAnalysisRules):
105
115
  rules = NetworkAnalysisRules(**rules)
106
116
 
@@ -125,8 +135,8 @@ class NetworkAnalysis:
125
135
  self._graph_updated_count = 0
126
136
  self._k_nearest_points = 50
127
137
 
128
- def _check_if_holes_are_nan(self):
129
- HOLES_ARE_NAN = (
138
+ def _check_if_holes_are_nan(self) -> None:
139
+ holes_are_nan: str = (
130
140
  "Network holes have been filled by straigt lines, but the rows have "
131
141
  f"NaN values in the {self.rules.weight!r} column. Either remove NaNs "
132
142
  "or fill these values with a numeric value (e.g. 0)."
@@ -140,7 +150,7 @@ class NetworkAnalysis:
140
150
  .all()
141
151
  )
142
152
  ):
143
- raise ValueError(HOLES_ARE_NAN)
153
+ raise ValueError(holes_are_nan)
144
154
 
145
155
  def od_cost_matrix(
146
156
  self,
@@ -180,13 +190,13 @@ class NetworkAnalysis:
180
190
  GeoDataFrames. If lines is True, also returns a geometry column with
181
191
  straight lines between origin and destination.
182
192
 
183
- Examples
193
+ Examples:
184
194
  --------
185
195
  Create the NetworkAnalysis instance.
186
196
 
187
197
  >>> import sgis as sg
188
198
  >>> roads = sg.read_parquet_url("https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/roads_oslo_2022.parquet")
189
- >>> directed_roads = sg.remove_isolated(roads).pipe(sg.make_directed_network_norway)
199
+ >>> directed_roads = sg.get_connected_components(roads).loc[lambda x: x["connected"] == 1].pipe(sg.make_directed_network_norway, dropnegative=True)
190
200
  >>> rules = sg.NetworkAnalysisRules(weight="minutes", directed=True)
191
201
  >>> nwa = sg.NetworkAnalysis(network=directed_roads, rules=rules, detailed_log=False)
192
202
 
@@ -423,8 +433,8 @@ class NetworkAnalysis:
423
433
  destinations: GeoDataFrame,
424
434
  weight_df: DataFrame | None = None,
425
435
  default_weight: int | float | None = None,
426
- strict: bool = False,
427
436
  rowwise: bool = False,
437
+ strict: bool = False,
428
438
  frequency_col: str = "frequency",
429
439
  ) -> GeoDataFrame:
430
440
  """Finds the number of times each line segment was visited in all trips.
@@ -446,6 +456,11 @@ class NetworkAnalysis:
446
456
  index, destination index and weight. In that order) or only a weight
447
457
  column and a MultiIndex where level 0 is origin index and level 1 is
448
458
  destination index.
459
+ default_weight: If set, OD pairs not represented in 'weight_df'
460
+ will be given a default weight value.
461
+ rowwise: if False (default), it will calculate the cost from each
462
+ origins to each destination. If true, it will calculate the cost from
463
+ origin 1 to destination 1, origin 2 to destination 2 and so on.
449
464
  strict: If True, all OD pairs must be in weigth_df if specified. Defaults
450
465
  to False.
451
466
  frequency_col: Name of column with the number of times each road was
@@ -463,14 +478,14 @@ class NetworkAnalysis:
463
478
  ValueError: If weight_df is not a DataFrame with one or three columns
464
479
  that contain weights and all indices of 'origins' and 'destinations'.
465
480
 
466
- Examples
481
+ Examples:
467
482
  --------
468
483
  Create the NetworkAnalysis instance.
469
484
 
470
485
  >>> import sgis as sg
471
486
  >>> import pandas as pd
472
487
  >>> roads = sg.read_parquet_url("https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/roads_oslo_2022.parquet")
473
- >>> directed_roads = sg.remove_isolated(roads).pipe(sg.make_directed_network_norway)
488
+ >>> directed_roads = sg.get_connected_components(roads).loc[lambda x: x["connected"] == 1].pipe(sg.make_directed_network_norway, dropnegative=True)
474
489
  >>> rules = sg.NetworkAnalysisRules(weight="minutes", directed=True)
475
490
  >>> nwa = sg.NetworkAnalysis(network=directed_roads, rules=rules, detailed_log=False)
476
491
 
@@ -599,10 +614,13 @@ class NetworkAnalysis:
599
614
  # map to temporary ids
600
615
  ori_idx_mapper = {v: k for k, v in self.origins.idx_dict.items()}
601
616
  des_idx_mapper = {v: k for k, v in self.destinations.idx_dict.items()}
602
- multiindex_mapper = lambda x: (
603
- ori_idx_mapper.get(x[0]),
604
- des_idx_mapper.get(x[1]),
605
- )
617
+
618
+ def multiindex_mapper(x: tuple[int, int]) -> tuple[int, int]:
619
+ return (
620
+ ori_idx_mapper.get(x[0]),
621
+ des_idx_mapper.get(x[1]),
622
+ )
623
+
606
624
  weight_df.index = weight_df.index.map(multiindex_mapper)
607
625
  else:
608
626
  od_pairs = self._create_od_pairs(
@@ -673,13 +691,13 @@ class NetworkAnalysis:
673
691
  Also returns a weight column and the columns 'origin' and 'destination',
674
692
  containing the indices of the origins and destinations GeoDataFrames.
675
693
 
676
- Examples
694
+ Examples:
677
695
  --------
678
696
  Create the NetworkAnalysis instance.
679
697
 
680
698
  >>> import sgis as sg
681
699
  >>> roads = sg.read_parquet_url("https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/roads_oslo_2022.parquet")
682
- >>> directed_roads = sg.remove_isolated(roads).pipe(sg.make_directed_network_norway)
700
+ >>> directed_roads = sg.get_connected_components(roads).loc[lambda x: x["connected"] == 1].pipe(sg.make_directed_network_norway, dropnegative=True)
683
701
  >>> rules = sg.NetworkAnalysisRules(weight="minutes", directed=True)
684
702
  >>> nwa = sg.NetworkAnalysis(network=directed_roads, rules=rules, detailed_log=False)
685
703
 
@@ -801,13 +819,13 @@ class NetworkAnalysis:
801
819
  Raises:
802
820
  ValueError: if drop_middle_percent is not between 0 and 100.
803
821
 
804
- Examples
822
+ Examples:
805
823
  --------
806
824
  Create the NetworkAnalysis instance.
807
825
 
808
826
  >>> import sgis as sg
809
827
  >>> roads = sg.read_parquet_url('https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/roads_oslo_2022.parquet')
810
- >>> directed_roads = sg.remove_isolated(roads).pipe(sg.make_directed_network_norway)
828
+ >>> directed_roads = sg.get_connected_components(roads).loc[lambda x: x["connected"] == 1].pipe(sg.make_directed_network_norway, dropnegative=True)
811
829
  >>> rules = sg.NetworkAnalysisRules(weight="minutes", directed=True)
812
830
  >>> nwa = sg.NetworkAnalysis(network=directed_roads, rules=rules, detailed_log=False)
813
831
 
@@ -943,17 +961,17 @@ class NetworkAnalysis:
943
961
  a dissolved line geometry. If dissolve is False, it will return each line
944
962
  that is part of the service area.
945
963
 
946
- See also:
964
+ See Also:
947
965
  precice_service_area: Equivelent method where lines are also cut to get
948
966
  precice results.
949
967
 
950
- Examples
968
+ Examples:
951
969
  --------
952
970
  Create the NetworkAnalysis instance.
953
971
 
954
972
  >>> import sgis as sg
955
973
  >>> roads = sg.read_parquet_url("https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/roads_oslo_2022.parquet")
956
- >>> directed_roads = sg.remove_isolated(roads).pipe(sg.make_directed_network_norway)
974
+ >>> directed_roads = sg.get_connected_components(roads).loc[lambda x: x["connected"] == 1].pipe(sg.make_directed_network_norway, dropnegative=True)
957
975
  >>> rules = sg.NetworkAnalysisRules(weight="minutes", directed=True)
958
976
  >>> nwa = sg.NetworkAnalysis(network=directed_roads, rules=rules, detailed_log=False)
959
977
 
@@ -972,7 +990,6 @@ class NetworkAnalysis:
972
990
 
973
991
  Service areas of 5, 10 and 15 minutes from three origin points.
974
992
 
975
- >>> nwa = NetworkAnalysis(network=nw, rules=rules)
976
993
  >>> service_areas = nwa.service_area(
977
994
  ... points.iloc[:2],
978
995
  ... breaks=[5, 10, 15],
@@ -1072,16 +1089,16 @@ class NetworkAnalysis:
1072
1089
  geometry. If dissolve is False, it will return all the columns of the
1073
1090
  network.gdf as well.
1074
1091
 
1075
- See also:
1092
+ See Also:
1076
1093
  service_area: Faster method where lines are not cut to get precice results.
1077
1094
 
1078
- Examples
1095
+ Examples:
1079
1096
  --------
1080
1097
  Create the NetworkAnalysis instance.
1081
1098
 
1082
1099
  >>> import sgis as sg
1083
1100
  >>> roads = sg.read_parquet_url("https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/roads_oslo_2022.parquet")
1084
- >>> directed_roads = sg.remove_isolated(roads).pipe(sg.make_directed_network_norway)
1101
+ >>> directed_roads = sg.get_connected_components(roads).loc[lambda x: x["connected"] == 1].pipe(sg.make_directed_network_norway, dropnegative=True)
1085
1102
  >>> rules = sg.NetworkAnalysisRules(weight="minutes", directed=True)
1086
1103
  >>> nwa = sg.NetworkAnalysis(network=directed_roads, rules=rules, detailed_log=False)
1087
1104
 
@@ -1099,7 +1116,6 @@ class NetworkAnalysis:
1099
1116
 
1100
1117
  Service areas of 5, 10 and 15 minutes from three origin points.
1101
1118
 
1102
- >>> nwa = NetworkAnalysis(network=nw, rules=rules)
1103
1119
  >>> sa = nwa.precice_service_area(
1104
1120
  ... points.iloc[:2],
1105
1121
  ... breaks=[5, 10, 15],
@@ -1189,7 +1205,7 @@ class NetworkAnalysis:
1189
1205
  "the trip frequency for this origin-destination pair."
1190
1206
  )
1191
1207
 
1192
- if not isinstance(weight_df, (DataFrame, pd.Series)):
1208
+ if not isinstance(weight_df, (DataFrame | pd.Series)):
1193
1209
  raise ValueError(error_message)
1194
1210
 
1195
1211
  if isinstance(weight_df, pd.Series):
@@ -1221,7 +1237,7 @@ class NetworkAnalysis:
1221
1237
  def _make_sure_index_match(
1222
1238
  weight_df: DataFrame,
1223
1239
  od_pairs: MultiIndex,
1224
- ):
1240
+ ) -> None:
1225
1241
  """Make sure this index matches the index of origins and destinations."""
1226
1242
  if not od_pairs.isin(weight_df.index).all():
1227
1243
  if not od_pairs.isin(weight_df.index).any():
@@ -1321,7 +1337,7 @@ class NetworkAnalysis:
1321
1337
  for key, value in kwargs.items():
1322
1338
  if isinstance(value, np.ndarray):
1323
1339
  value = list(value)
1324
- if isinstance(value, (list, tuple)):
1340
+ if isinstance(value, (list | tuple)):
1325
1341
  value = [str(x) for x in value]
1326
1342
  value = ", ".join(value)
1327
1343
  df[key] = value
@@ -1329,7 +1345,10 @@ class NetworkAnalysis:
1329
1345
  self.log = pd.concat([self.log, df], ignore_index=True)
1330
1346
 
1331
1347
  def _prepare_network_analysis(
1332
- self, origins, destinations=None, rowwise: bool = False
1348
+ self,
1349
+ origins: GeoDataFrame,
1350
+ destinations: GeoDataFrame | None = None,
1351
+ rowwise: bool = False,
1333
1352
  ) -> None:
1334
1353
  """Prepares the weight column, node ids, origins, destinations and graph.
1335
1354
 
@@ -1337,7 +1356,6 @@ class NetworkAnalysis:
1337
1356
  has changed. this method is run inside od_cost_matrix, get_route and
1338
1357
  service_area.
1339
1358
  """
1340
-
1341
1359
  if rowwise and len(origins) != len(destinations):
1342
1360
  raise ValueError(
1343
1361
  "'origins' and 'destinations' must have the same length when "
@@ -1585,14 +1603,14 @@ class NetworkAnalysis:
1585
1603
  ]
1586
1604
 
1587
1605
  @staticmethod
1588
- def _sort_breaks(breaks) -> list[float | int]:
1606
+ def _sort_breaks(breaks: str | list | tuple | int | float) -> list[float | int]:
1589
1607
  if isinstance(breaks, str):
1590
1608
  breaks = float(breaks)
1591
1609
 
1592
1610
  if hasattr(breaks, "__iter__"):
1593
1611
  return list(sorted(list(breaks)))
1594
1612
 
1595
- if isinstance(breaks, (int, float)):
1613
+ if isinstance(breaks, (int | float)):
1596
1614
  return [breaks]
1597
1615
 
1598
1616
  raise ValueError(
@@ -1628,11 +1646,11 @@ class NetworkAnalysis:
1628
1646
  "\n)"
1629
1647
  )
1630
1648
 
1631
- def __getitem__(self, item):
1649
+ def __getitem__(self, item: str) -> Any:
1632
1650
  """To be able to write self['origins'] as well as self.origins."""
1633
1651
  return getattr(self, item)
1634
1652
 
1635
- def copy(self, deep=True):
1653
+ def copy(self, deep: bool = True) -> "NetworkAnalysis":
1636
1654
  """Returns a (deep) copy of the class instance.
1637
1655
 
1638
1656
  Args:
@@ -3,7 +3,9 @@
3
3
  The class is to be used as the 'rules' parameter in the NetworkAnalysis
4
4
  class.
5
5
  """
6
- from copy import copy, deepcopy
6
+
7
+ from copy import copy
8
+ from copy import deepcopy
7
9
  from dataclasses import dataclass
8
10
 
9
11
  from geopandas import GeoDataFrame
@@ -20,7 +22,7 @@ class NetworkAnalysisRules:
20
22
  Args:
21
23
  weight: Either a column in the GeoDataFrame of the Network or
22
24
  'meters'/'metres'. A 'minutes' column can be created with the
23
- 'make_directed_network' method of the DirectedNetwork class.
25
+ 'make_directed_network' or 'make_directed_network_norway' functions.
24
26
  directed: Whether the lines will be considered traversable in both directions.
25
27
  search_tolerance: distance to search for nodes in the network. Origins and
26
28
  destinations further away from the network than the search_tolerance will
@@ -47,7 +49,7 @@ class NetworkAnalysisRules:
47
49
  them. Defaults to None, meaning 0 weight is added for these edges. If set
48
50
  to 1, the weight will be equal to the straigt line distance.
49
51
 
50
- Examples
52
+ Examples:
51
53
  --------
52
54
  Read testdata.
53
55
 
@@ -59,12 +61,12 @@ class NetworkAnalysisRules:
59
61
  values.
60
62
 
61
63
  >>> rules = sg.NetworkAnalysisRules(weight="minutes", directed=True)
62
- >>> directed_roads = sg.remove_isolated(roads).pipe(sg.make_directed_network_norway)
63
- >>> nwa = sg.NetworkAnalysis(network=directed_roads, rules=rules, detailed_log=False)
64
+ >>> directed_roads = sg.get_connected_components(roads).loc[lambda x: x["connected"] == 1].pipe(sg.make_directed_network_norway, dropnegative=True)
65
+ >>> nwa = sg.NetworkAnalysis(network=directed_roads, rules=rules, detailed_log=True)
64
66
  >>> nwa
65
67
  NetworkAnalysis(
66
- network=DirectedNetwork(6364 km, percent_bidirectional=87),
67
- rules=NetworkAnalysisRules(weight=minutes, search_tolerance=250, search_factor=0, split_lines=False, ...),
68
+ network=Network(6364 km, percent_bidirectional=87),
69
+ rules=NetworkAnalysisRules(weight=minutes, directed=True, search_tolerance=250, search_factor=0, split_lines=True, ...),
68
70
  log=True, detailed_log=True,
69
71
  )
70
72
 
@@ -171,7 +173,7 @@ class NetworkAnalysisRules:
171
173
  nodedist_multiplier: int | float | None = None
172
174
  nodedist_kmh: int | float | None = None
173
175
 
174
- def _update_rules(self):
176
+ def _update_rules(self) -> None:
175
177
  """Stores the rules as separate attributes.
176
178
 
177
179
  Used for checking whether the rules have changed and the graph have to be
@@ -185,7 +187,7 @@ class NetworkAnalysisRules:
185
187
  self._nodedist_multiplier = self.nodedist_multiplier
186
188
  self._nodedist_kmh = self.nodedist_kmh
187
189
 
188
- def _rules_have_changed(self):
190
+ def _rules_have_changed(self) -> bool:
189
191
  """Checks if any of the rules have changed since the graph was last created.
190
192
 
191
193
  If no rules have changed, time can be saved by not remaking the graph
@@ -235,8 +237,8 @@ class NetworkAnalysisRules:
235
237
  self._check_for_negative_values(gdf, self.weight)
236
238
  return gdf
237
239
 
238
- # at this point, the weight is wrong. Now to determine the error
239
- # message
240
+ # at this point, the weight is wrong.
241
+ # Now to determine the error message
240
242
 
241
243
  if "meter" in self.weight or "metre" in self.weight:
242
244
  raise ValueError(
@@ -257,7 +259,7 @@ class NetworkAnalysisRules:
257
259
  raise KeyError(incorrect_weight_column)
258
260
 
259
261
  @staticmethod
260
- def _check_for_nans(df, col):
262
+ def _check_for_nans(df: GeoDataFrame, col: str) -> None:
261
263
  """Remove NaNs and give warning if there are any."""
262
264
  if all(df[col].isna()):
263
265
  raise ValueError(f"All values in the {col!r} column are NaN.")
@@ -270,7 +272,7 @@ class NetworkAnalysisRules:
270
272
  )
271
273
 
272
274
  @staticmethod
273
- def _check_for_negative_values(df, col):
275
+ def _check_for_negative_values(df: GeoDataFrame, col: str) -> None:
274
276
  """Remove negative values and give warning if there are any."""
275
277
  negative = sum(df[col] < 0)
276
278
  if negative:
@@ -280,7 +282,7 @@ class NetworkAnalysisRules:
280
282
  )
281
283
 
282
284
  @staticmethod
283
- def _try_to_float(df, col):
285
+ def _try_to_float(df: GeoDataFrame, col: str) -> GeoDataFrame:
284
286
  """Try to convert weight column to float, raise ValueError if it fails."""
285
287
  try:
286
288
  df[col] = df[col].astype(float)
@@ -291,8 +293,10 @@ class NetworkAnalysisRules:
291
293
  ) from e
292
294
  return df
293
295
 
294
- def copy(self):
296
+ def copy(self) -> "NetworkAnalysisRules":
297
+ """Return a shallow copy the instance."""
295
298
  return copy(self)
296
299
 
297
- def deepcopy(self):
300
+ def deepcopy(self) -> "NetworkAnalysisRules":
301
+ """Return a deep copy the instance."""
298
302
  return deepcopy(self)
@@ -6,7 +6,8 @@ to use explicitly.
6
6
 
7
7
  import geopandas as gpd
8
8
  import pandas as pd
9
- from geopandas import GeoDataFrame, GeoSeries
9
+ from geopandas import GeoDataFrame
10
+ from geopandas import GeoSeries
10
11
  from shapely.geometry import Point
11
12
 
12
13
  from ..geopandas_tools.general import _push_geom_col
@@ -34,7 +35,6 @@ def make_node_ids(
34
35
  Note:
35
36
  The lines must be singlepart linestrings.
36
37
  """
37
-
38
38
  gdf = gdf.explode(index_parts=False).explode(index_parts=False)
39
39
 
40
40
  if wkt:
@@ -192,7 +192,6 @@ def _prepare_make_edge_cols_simple(
192
192
  lines: GeoDataFrame,
193
193
  ) -> tuple[GeoDataFrame, GeoDataFrame]:
194
194
  """Faster version of _prepare_make_edge_cols."""
195
-
196
195
  endpoints = lines[lines._geometry_column_name].boundary.explode(ignore_index=True)
197
196
 
198
197
  if len(lines) and len(endpoints) / len(lines) != 2:
@@ -1,6 +1,7 @@
1
1
  import networkx as nx
2
2
  import pandas as pd
3
- from geopandas import GeoDataFrame, GeoSeries
3
+ from geopandas import GeoDataFrame
4
+ from geopandas import GeoSeries
4
5
  from networkx.utils import pairwise
5
6
  from shapely.geometry import Point
6
7
 
@@ -30,8 +31,10 @@ def traveling_salesman_problem(
30
31
  Returns:
31
32
  List of Points making up the traveling salesman's path.
32
33
 
33
- Examples
34
+ Examples:
34
35
  --------
36
+ >>> import sgis as sg
37
+ >>> from shapely.geometry import LineString
35
38
  >>> points = sg.to_gdf(
36
39
  ... [
37
40
  ... (0, 0),