scgraph 2.1.2__tar.gz → 2.3.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: scgraph
3
- Version: 2.1.2
3
+ Version: 2.3.0
4
4
  Summary: Determine an approximate route between two points on earth.
5
5
  Author-email: Connor Makowski <conmak@mit.edu>
6
6
  Project-URL: Homepage, https://github.com/connor-makowski/scgraph
@@ -9,7 +9,7 @@ Project-URL: Documentation, https://connor-makowski.github.io/scgraph/core.html
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.6
12
+ Requires-Python: >=3.10
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
15
 
@@ -57,7 +57,9 @@ Low Level: https://connor-makowski.github.io/scgraph/scgraph/core.html
57
57
 
58
58
  ## Setup
59
59
 
60
- Make sure you have Python 3.6.x (or higher) installed on your system. You can download it [here](https://www.python.org/downloads/).
60
+ Make sure you have Python 3.10.x (or higher) installed on your system. You can download it [here](https://www.python.org/downloads/).
61
+
62
+ Support for python3.6-3.9 is available up to version 2.2.0
61
63
 
62
64
  ## Installation
63
65
 
@@ -67,7 +69,10 @@ pip install scgraph
67
69
 
68
70
  ## Use with Google Colab
69
71
 
70
- See the example [here](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/example.ipynb)
72
+ - [Getting Started](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/getting_started.ipynb)
73
+ - [Creating A Multi Path Geojson](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/multi_path_geojson.ipynb)
74
+ - [Modifying A Geograph](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/geograph_modifications.ipynb)
75
+
71
76
 
72
77
  # Getting Started
73
78
 
@@ -101,7 +106,7 @@ In the above example, the `output` variable is a dictionary with three keys: `le
101
106
  - `ft` (feet)
102
107
  - `coordinate_path`: A list of lists [`latitude`,`longitude`] that make up the shortest path
103
108
 
104
- For more examples including viewing the output on a map, see the [example notebook](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/example.ipynb).
109
+ For more examples including viewing the output on a map, see the [example notebook](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/getting_started.ipynb).
105
110
 
106
111
  ## Included GeoGraphs
107
112
 
@@ -165,15 +170,17 @@ output = marnet_geograph.get_shortest_path(
165
170
  get_line_path(output, filename='output.geojson')
166
171
  ```
167
172
 
173
+ Modify an existing geograph: See the notebook [here](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/geograph_modifications.ipynb)
174
+
168
175
 
169
176
  You can specify your own custom graphs for direct access to the solving algorithms. This requires the use of the low level `Graph` class
170
177
 
171
178
  ```py
172
179
  from scgraph import Graph
173
180
 
174
- # Define a graph
181
+ # Define an arbitrary graph
175
182
  # See the graph definitions here:
176
- # https://connor-makowski.github.io/scgraph/scgraph/core.html
183
+ # https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
177
184
  graph = [
178
185
  {1: 5, 2: 1},
179
186
  {0: 5, 2: 2, 3: 1},
@@ -198,25 +205,67 @@ from scgraph import GeoGraph
198
205
 
199
206
  # Define nodes
200
207
  # See the nodes definitions here:
201
- # https://connor-makowski.github.io/scgraph/scgraph/core.html
208
+ # https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
202
209
  nodes = [
203
- [0,0],
204
- [0,1],
205
- [1,0],
206
- [1,1],
207
- [1,2],
208
- [2,1]
210
+ # London
211
+ [51.5074, -0.1278],
212
+ # Paris
213
+ [48.8566, 2.3522],
214
+ # Berlin
215
+ [52.5200, 13.4050],
216
+ # Rome
217
+ [41.9028, 12.4964],
218
+ # Madrid
219
+ [40.4168, -3.7038],
220
+ # Lisbon
221
+ [38.7223, -9.1393]
209
222
  ]
210
223
  # Define a graph
211
224
  # See the graph definitions here:
212
- # https://connor-makowski.github.io/scgraph/scgraph/core.html
225
+ # https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
213
226
  graph = [
214
- {1: 5, 2: 1},
215
- {0: 5, 2: 2, 3: 1},
216
- {0: 1, 1: 2, 3: 4, 4: 8},
217
- {1: 1, 2: 4, 4: 3, 5: 6},
218
- {2: 8, 3: 3},
219
- {3: 6}
227
+ # From London
228
+ {
229
+ # To Paris
230
+ 1: 311,
231
+ },
232
+ # From Paris
233
+ {
234
+ # To London
235
+ 0: 311,
236
+ # To Berlin
237
+ 2: 878,
238
+ # To Rome
239
+ 3: 1439,
240
+ # To Madrid
241
+ 4: 1053
242
+ },
243
+ # From Berlin
244
+ {
245
+ # To Paris
246
+ 1: 878,
247
+ # To Rome
248
+ 3: 1181,
249
+ },
250
+ # From Rome
251
+ {
252
+ # To Paris
253
+ 1: 1439,
254
+ # To Berlin
255
+ 2: 1181,
256
+ },
257
+ # From Madrid
258
+ {
259
+ # To Paris
260
+ 1: 1053,
261
+ # To Lisbon
262
+ 5: 623
263
+ },
264
+ # From Lisbon
265
+ {
266
+ # To Madrid
267
+ 4: 623
268
+ }
220
269
  ]
221
270
 
222
271
  # Create a GeoGraph object
@@ -229,23 +278,31 @@ my_geograph.validate_graph()
229
278
  my_geograph.validate_nodes()
230
279
 
231
280
  # Get the shortest path between two points
281
+ # In this case, Birmingham England and Zaragoza Spain
282
+ # Since Birmingham and Zaragoza are not in the graph,
283
+ # the algorithm will add them into the graph.
284
+ # See: https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph.get_shortest_path
285
+ # Expected output would be to go from
286
+ # Birmingham -> London -> Paris -> Madrid -> Zaragoza
287
+
232
288
  output = my_geograph.get_shortest_path(
233
- origin_node = {'latitude': 0, 'longitude': 0},
234
- destination_node = {'latitude': 2, 'longitude': 1}
289
+ # Birmingham England
290
+ origin_node = {'latitude': 52.4862, 'longitude': -1.8904},
291
+ # Zaragoza Spain
292
+ destination_node = {'latitude': 41.6488, 'longitude': -0.8891}
235
293
  )
236
- #=>
294
+ print(output)
237
295
  # {
238
- # "coordinate_path": [
239
- # [0,0],
240
- # [0,0],
241
- # [1,0],
242
- # [0,1],
243
- # [1,1],
244
- # [2,1],
245
- # [2,1]
246
- # ],
247
- # "length": 10
296
+ # 'length': 1799.4323,
297
+ # 'coordinate_path': [
298
+ # [52.4862, -1.8904],
299
+ # [51.5074, -0.1278],
300
+ # [48.8566, 2.3522],
301
+ # [40.4168, -3.7038],
302
+ # [41.6488, -0.8891]
303
+ # ]
248
304
  # }
305
+
249
306
  ```
250
307
 
251
308
  ## Attributions and Thanks
@@ -42,7 +42,9 @@ Low Level: https://connor-makowski.github.io/scgraph/scgraph/core.html
42
42
 
43
43
  ## Setup
44
44
 
45
- Make sure you have Python 3.6.x (or higher) installed on your system. You can download it [here](https://www.python.org/downloads/).
45
+ Make sure you have Python 3.10.x (or higher) installed on your system. You can download it [here](https://www.python.org/downloads/).
46
+
47
+ Support for python3.6-3.9 is available up to version 2.2.0
46
48
 
47
49
  ## Installation
48
50
 
@@ -52,7 +54,10 @@ pip install scgraph
52
54
 
53
55
  ## Use with Google Colab
54
56
 
55
- See the example [here](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/example.ipynb)
57
+ - [Getting Started](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/getting_started.ipynb)
58
+ - [Creating A Multi Path Geojson](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/multi_path_geojson.ipynb)
59
+ - [Modifying A Geograph](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/geograph_modifications.ipynb)
60
+
56
61
 
57
62
  # Getting Started
58
63
 
@@ -86,7 +91,7 @@ In the above example, the `output` variable is a dictionary with three keys: `le
86
91
  - `ft` (feet)
87
92
  - `coordinate_path`: A list of lists [`latitude`,`longitude`] that make up the shortest path
88
93
 
89
- For more examples including viewing the output on a map, see the [example notebook](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/example.ipynb).
94
+ For more examples including viewing the output on a map, see the [example notebook](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/getting_started.ipynb).
90
95
 
91
96
  ## Included GeoGraphs
92
97
 
@@ -150,15 +155,17 @@ output = marnet_geograph.get_shortest_path(
150
155
  get_line_path(output, filename='output.geojson')
151
156
  ```
152
157
 
158
+ Modify an existing geograph: See the notebook [here](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/geograph_modifications.ipynb)
159
+
153
160
 
154
161
  You can specify your own custom graphs for direct access to the solving algorithms. This requires the use of the low level `Graph` class
155
162
 
156
163
  ```py
157
164
  from scgraph import Graph
158
165
 
159
- # Define a graph
166
+ # Define an arbitrary graph
160
167
  # See the graph definitions here:
161
- # https://connor-makowski.github.io/scgraph/scgraph/core.html
168
+ # https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
162
169
  graph = [
163
170
  {1: 5, 2: 1},
164
171
  {0: 5, 2: 2, 3: 1},
@@ -183,25 +190,67 @@ from scgraph import GeoGraph
183
190
 
184
191
  # Define nodes
185
192
  # See the nodes definitions here:
186
- # https://connor-makowski.github.io/scgraph/scgraph/core.html
193
+ # https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
187
194
  nodes = [
188
- [0,0],
189
- [0,1],
190
- [1,0],
191
- [1,1],
192
- [1,2],
193
- [2,1]
195
+ # London
196
+ [51.5074, -0.1278],
197
+ # Paris
198
+ [48.8566, 2.3522],
199
+ # Berlin
200
+ [52.5200, 13.4050],
201
+ # Rome
202
+ [41.9028, 12.4964],
203
+ # Madrid
204
+ [40.4168, -3.7038],
205
+ # Lisbon
206
+ [38.7223, -9.1393]
194
207
  ]
195
208
  # Define a graph
196
209
  # See the graph definitions here:
197
- # https://connor-makowski.github.io/scgraph/scgraph/core.html
210
+ # https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
198
211
  graph = [
199
- {1: 5, 2: 1},
200
- {0: 5, 2: 2, 3: 1},
201
- {0: 1, 1: 2, 3: 4, 4: 8},
202
- {1: 1, 2: 4, 4: 3, 5: 6},
203
- {2: 8, 3: 3},
204
- {3: 6}
212
+ # From London
213
+ {
214
+ # To Paris
215
+ 1: 311,
216
+ },
217
+ # From Paris
218
+ {
219
+ # To London
220
+ 0: 311,
221
+ # To Berlin
222
+ 2: 878,
223
+ # To Rome
224
+ 3: 1439,
225
+ # To Madrid
226
+ 4: 1053
227
+ },
228
+ # From Berlin
229
+ {
230
+ # To Paris
231
+ 1: 878,
232
+ # To Rome
233
+ 3: 1181,
234
+ },
235
+ # From Rome
236
+ {
237
+ # To Paris
238
+ 1: 1439,
239
+ # To Berlin
240
+ 2: 1181,
241
+ },
242
+ # From Madrid
243
+ {
244
+ # To Paris
245
+ 1: 1053,
246
+ # To Lisbon
247
+ 5: 623
248
+ },
249
+ # From Lisbon
250
+ {
251
+ # To Madrid
252
+ 4: 623
253
+ }
205
254
  ]
206
255
 
207
256
  # Create a GeoGraph object
@@ -214,23 +263,31 @@ my_geograph.validate_graph()
214
263
  my_geograph.validate_nodes()
215
264
 
216
265
  # Get the shortest path between two points
266
+ # In this case, Birmingham England and Zaragoza Spain
267
+ # Since Birmingham and Zaragoza are not in the graph,
268
+ # the algorithm will add them into the graph.
269
+ # See: https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph.get_shortest_path
270
+ # Expected output would be to go from
271
+ # Birmingham -> London -> Paris -> Madrid -> Zaragoza
272
+
217
273
  output = my_geograph.get_shortest_path(
218
- origin_node = {'latitude': 0, 'longitude': 0},
219
- destination_node = {'latitude': 2, 'longitude': 1}
274
+ # Birmingham England
275
+ origin_node = {'latitude': 52.4862, 'longitude': -1.8904},
276
+ # Zaragoza Spain
277
+ destination_node = {'latitude': 41.6488, 'longitude': -0.8891}
220
278
  )
221
- #=>
279
+ print(output)
222
280
  # {
223
- # "coordinate_path": [
224
- # [0,0],
225
- # [0,0],
226
- # [1,0],
227
- # [0,1],
228
- # [1,1],
229
- # [2,1],
230
- # [2,1]
231
- # ],
232
- # "length": 10
281
+ # 'length': 1799.4323,
282
+ # 'coordinate_path': [
283
+ # [52.4862, -1.8904],
284
+ # [51.5074, -0.1278],
285
+ # [48.8566, 2.3522],
286
+ # [40.4168, -3.7038],
287
+ # [41.6488, -0.8891]
288
+ # ]
233
289
  # }
290
+
234
291
  ```
235
292
 
236
293
  ## Attributions and Thanks
@@ -12,13 +12,13 @@ build-backend = "setuptools.build_meta"
12
12
 
13
13
  [project]
14
14
  name = "scgraph"
15
- version = "2.1.2"
15
+ version = "2.3.0"
16
16
  description = "Determine an approximate route between two points on earth."
17
17
  authors = [
18
18
  {name="Connor Makowski", email="conmak@mit.edu"}
19
19
  ]
20
20
  readme = "README.md"
21
- requires-python = ">=3.6"
21
+ requires-python = ">=3.10"
22
22
  classifiers = [
23
23
  "Programming Language :: Python :: 3",
24
24
  'License :: OSI Approved :: MIT License',
@@ -1,4 +1,4 @@
1
- from .utils import haversine, hard_round, distance_converter
1
+ from .utils import haversine, hard_round, distance_converter, get_line_path
2
2
  import json
3
3
 
4
4
 
@@ -16,11 +16,10 @@ class Graph:
16
16
 
17
17
  Required Arguments:
18
18
 
19
- - `graph`
19
+ - `graph`:
20
20
  - Type: list of dictionaries
21
- - What: A list of dictionaries where the indicies are origin node ids and the values are dictionaries of destination node indices and distances
22
- - Note: All nodes must be included as origins in the graph regardless of if they have any connected destinations
23
-
21
+ - See: https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
22
+
24
23
  Optional Arguments:
25
24
 
26
25
  - `check_symmetry`
@@ -75,10 +74,9 @@ class Graph:
75
74
 
76
75
  Required Arguments:
77
76
 
78
- - `graph`
77
+ - `graph`:
79
78
  - Type: list of dictionaries
80
- - What: A list of dictionaries where the indicies are origin node ids and the values are dictionaries of destination node ids and distances
81
- - Note: All nodes must be included as origins in the graph regardless of if they have any connected destinations
79
+ - See: https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
82
80
 
83
81
  Optional Arguments:
84
82
 
@@ -121,9 +119,9 @@ class Graph:
121
119
 
122
120
  Required Arguments:
123
121
 
124
- - `graph`
125
- - Type: list[dict]
126
- - What: A list of dictionaries where the indicies are origin node ids and the values are dictionaries of destination node ids and distances
122
+ - `graph`:
123
+ - Type: list of dictionaries
124
+ - See: https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
127
125
  - `origin_id`
128
126
  - Type: int
129
127
  - What: The id of the origin node from the graph dictionary to start the shortest path from
@@ -167,9 +165,9 @@ class Graph:
167
165
 
168
166
  Required Arguments:
169
167
 
170
- - `graph`
171
- - Type: list[dict]
172
- - What: A list of dictionaries where the indicies are origin node ids and the values are dictionaries of destination node ids and distances
168
+ - `graph`:
169
+ - Type: list of dictionaries
170
+ - See: https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
173
171
  - `origin_id`
174
172
  - Type: int
175
173
  - What: The id of the origin node from the graph dictionary to start the shortest path from
@@ -242,9 +240,9 @@ class Graph:
242
240
 
243
241
  Required Arguments:
244
242
 
245
- - `graph`
246
- - Type: list[dict]
247
- - What: A list of dictionaries where the indicies are origin node ids and the values are dictionaries of destination node ids and distances
243
+ - `graph`:
244
+ - Type: list of dictionaries
245
+ - See: https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
248
246
  - `origin_id`
249
247
  - Type: int
250
248
  - What: The id of the origin node from the graph dictionary to start the shortest path from
@@ -298,7 +296,7 @@ class Graph:
298
296
 
299
297
  class GeoGraph:
300
298
  def __init__(
301
- self, graph: list[dict], nodes: list[list[int, float]]
299
+ self, graph: list[dict], nodes: list[list[float|int]]
302
300
  ) -> None:
303
301
  """
304
302
  Function:
@@ -311,13 +309,74 @@ class GeoGraph:
311
309
  - Type: list of dictionaries
312
310
  - What: A list of dictionaries where the indicies are origin node ids and the values are dictionaries of destination node indices and distances
313
311
  - Note: All nodes must be included as origins in the graph regardless of if they have any connected destinations
312
+ - EG:
313
+ ```
314
+ [
315
+ # From London
316
+ {
317
+ # To Paris
318
+ 1: 311,
319
+ },
320
+ # From Paris
321
+ {
322
+ # To London
323
+ 0: 311,
324
+ # To Berlin
325
+ 2: 878,
326
+ # To Rome
327
+ 3: 1439,
328
+ # To Madrid
329
+ 4: 1053
330
+ },
331
+ # From Berlin
332
+ {
333
+ # To Paris
334
+ 1: 878,
335
+ # To Rome
336
+ 3: 1181,
337
+ },
338
+ # From Rome
339
+ {
340
+ # To Paris
341
+ 1: 1439,
342
+ # To Berlin
343
+ 2: 1181,
344
+ },
345
+ # From Madrid
346
+ {
347
+ # To Paris
348
+ 1: 1053,
349
+ # To Lisbon
350
+ 5: 623
351
+ },
352
+ # From Lisbon
353
+ {
354
+ # To Madrid
355
+ 4: 623
356
+ }
357
+ ]
358
+ ```
314
359
  - `nodes`
315
- - Type: list of lists
360
+ - Type: list of lists of ints or floats
316
361
  - What: A list of lists where the values are coordinates (latitude then longitude)
317
362
  - Note: The length of the nodes list must be the same as that of the graph list
318
-
319
- Optional Arguments:
320
-
363
+ - EG:
364
+ ```
365
+ [
366
+ # London
367
+ [51.5074, 0.1278],
368
+ # Paris
369
+ [48.8566, 2.3522],
370
+ # Berlin
371
+ [52.5200, 13.4050],
372
+ # Rome
373
+ [41.9028, 12.4964],
374
+ # Madrid
375
+ [40.4168, 3.7038],
376
+ # Lisbon
377
+ [38.7223, 9.1393]
378
+ ]
379
+ ```
321
380
  """
322
381
  self.graph = graph
323
382
  self.nodes = nodes
@@ -397,17 +456,17 @@ class GeoGraph:
397
456
 
398
457
  def get_shortest_path(
399
458
  self,
400
- origin_node: dict[int, float],
401
- destination_node: dict[int, float],
459
+ origin_node: dict[float|int],
460
+ destination_node: dict[float|int],
402
461
  output_units: str = "km",
403
462
  algorithm_fn=Graph.dijkstra_makowski,
404
- off_graph_circuity: [int, float] = 1,
463
+ off_graph_circuity: [float|int] = 1,
405
464
  node_addition_type: str = "quadrant",
406
- node_addition_circuity: [int, float] = 4,
465
+ node_addition_circuity: [float|int] = 4,
407
466
  geograph_units: str = "km",
408
467
  output_coordinate_path: str = "list_of_lists",
409
468
  output_path: bool = False,
410
- node_addition_lat_lon_bound: [int, float] = 5,
469
+ node_addition_lat_lon_bound: [float|int] = 5,
411
470
  node_addition_math: str = "euclidean",
412
471
  **kwargs,
413
472
  ) -> dict:
@@ -450,6 +509,7 @@ class GeoGraph:
450
509
  - 'Graph.dijkstra_makowski': A modified dijkstra algorithm that uses a sparse distance matrix to identify the shortest path
451
510
  - Any user defined algorithm that takes the arguments:
452
511
  - `graph`: A dictionary of dictionaries where the keys are origin node ids and the values are dictionaries of destination node ids and distances
512
+ - See: https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
453
513
  - `origin`: The id of the origin node from the graph dictionary to start the shortest path from
454
514
  - `destination`: The id of the destination node from the graph dictionary to end the shortest path at
455
515
  - `off_graph_circuity`
@@ -582,9 +642,9 @@ class GeoGraph:
582
642
  def adujust_circuity_length(
583
643
  self,
584
644
  output: dict,
585
- node_addition_circuity: [float, int],
586
- off_graph_circuity: [float, int],
587
- ) -> [float, int]:
645
+ node_addition_circuity: [float|int],
646
+ off_graph_circuity: [float|int],
647
+ ) -> [float|int]:
588
648
  """
589
649
  Function:
590
650
 
@@ -619,7 +679,7 @@ class GeoGraph:
619
679
  4,
620
680
  )
621
681
 
622
- def get_coordinate_path(self, path: list[int]) -> list[dict[int, float]]:
682
+ def get_coordinate_path(self, path: list[int]) -> list[dict[float|int]]:
623
683
  """
624
684
  Function:
625
685
 
@@ -663,11 +723,11 @@ class GeoGraph:
663
723
  def get_node_distances(
664
724
  self,
665
725
  node: list,
666
- circuity: [int, float],
726
+ circuity: [float|int],
667
727
  node_addition_type: str,
668
728
  node_addition_math: str,
669
- lat_lon_bound: [int, float],
670
- ) -> dict[int, float]:
729
+ lat_lon_bound: [float|int],
730
+ ) -> dict[float|int]:
671
731
  """
672
732
  Function:
673
733
 
@@ -767,11 +827,11 @@ class GeoGraph:
767
827
 
768
828
  def add_node(
769
829
  self,
770
- node: dict[int, float],
771
- circuity: [int, float],
830
+ node: dict[float|int],
831
+ circuity: [float|int],
772
832
  node_addition_type: str = "quadrant",
773
833
  node_addition_math: str = "euclidean",
774
- lat_lon_bound: [int, float] = 5,
834
+ lat_lon_bound: [float|int] = 5,
775
835
  ) -> int:
776
836
  """
777
837
  Function:
@@ -877,13 +937,13 @@ class GeoGraph:
877
937
 
878
938
  """
879
939
  features = []
880
- for origin_idx, destinations in self.graph.items():
940
+ for origin_idx, destinations in enumerate(self.graph):
881
941
  for destination_idx, distance in destinations.items():
882
942
  # Create an undirected graph for geojson purposes
883
943
  if origin_idx > destination_idx:
884
944
  continue
885
- origin = self.nodes.get(origin_idx)
886
- destination = self.nodes.get(destination_idx)
945
+ origin = self.nodes[origin_idx]
946
+ destination = self.nodes[destination_idx]
887
947
  features.append(
888
948
  {
889
949
  "type": "Feature",
@@ -895,10 +955,10 @@ class GeoGraph:
895
955
  "geometry": {
896
956
  "type": "LineString",
897
957
  "coordinates": [
898
- [origin["longitude"], origin["latitude"]],
958
+ [origin[1], origin[0]],
899
959
  [
900
- destination["longitude"],
901
- destination["latitude"],
960
+ destination[1],
961
+ destination[0],
902
962
  ],
903
963
  ],
904
964
  },
@@ -908,3 +968,395 @@ class GeoGraph:
908
968
  out_dict = {"type": "FeatureCollection", "features": features}
909
969
  with open(filename, "w") as f:
910
970
  json.dump(out_dict, f)
971
+
972
+ def save_as_geograph(self, name: str) -> None:
973
+ """
974
+ Function:
975
+
976
+ - Save the current geograph as an importable python file
977
+
978
+ Required Arguments:
979
+
980
+ - `name`
981
+ - Type: str
982
+ - What: The name of the geograph and file
983
+ - EG: 'custom'
984
+ - Stored as: 'custom.py'
985
+ - In your current directory
986
+ - Import as: 'from .custom import custom_geograph'
987
+ """
988
+ self.validate_nodes()
989
+ self.validate_graph(check_symmetry=True, check_connected=False)
990
+ out_string = f"""from scgraph.core import GeoGraph\ngraph={str(self.graph)}\nnodes={str(self.nodes)}\n{name}_geograph = GeoGraph(graph=graph, nodes=nodes)"""
991
+ with open(name + ".py", "w") as f:
992
+ f.write(out_string)
993
+
994
+ def mod_remove_arc(
995
+ self, origin_idx: int, destination_idx: int, undirected: bool = True
996
+ ) -> None:
997
+ """
998
+ Function:
999
+
1000
+ - Remove an arc from the graph
1001
+
1002
+ Required Arguments:
1003
+
1004
+ - `origin_idx`
1005
+ - Type: int
1006
+ - What: The index of the origin node
1007
+ - `destination_idx`
1008
+ - Type: int
1009
+ - What: The index of the destination node
1010
+
1011
+ Optional Arguments:
1012
+
1013
+ - `undirected`
1014
+ - Type: bool
1015
+ - What: Whether to remove the arc in both directions
1016
+ - Default: True
1017
+ """
1018
+ assert origin_idx < len(self.graph), "Origin node does not exist"
1019
+ assert destination_idx < len(
1020
+ self.graph
1021
+ ), "Destination node does not exist"
1022
+ assert destination_idx in self.graph[origin_idx], "Arc does not exist"
1023
+ del self.graph[origin_idx][destination_idx]
1024
+ if undirected:
1025
+ if origin_idx in self.graph[destination_idx]:
1026
+ del self.graph[destination_idx][origin_idx]
1027
+
1028
+ def mod_add_node(
1029
+ self, latitude: [float|int], longitude: [float|int]
1030
+ ) -> int:
1031
+ """
1032
+ Function:
1033
+
1034
+ - Add a node to the graph
1035
+
1036
+ Required Arguments:
1037
+
1038
+ - `latitude`
1039
+ - Type: int | float
1040
+ - What: The latitude of the node
1041
+ - `longitude`
1042
+ - Type: int | float
1043
+ - What: The longitude of the node
1044
+
1045
+ Returns:
1046
+
1047
+ - The index of the new node
1048
+ """
1049
+ self.nodes.append([latitude, longitude])
1050
+ self.graph.append({})
1051
+ return len(self.graph) - 1
1052
+
1053
+ def mod_add_arc(
1054
+ self,
1055
+ origin_idx: int,
1056
+ destination_idx: int,
1057
+ distance: [float|int] = 0,
1058
+ use_haversine_distance=True,
1059
+ undirected: bool = True,
1060
+ ) -> None:
1061
+ """
1062
+ Function:
1063
+
1064
+ - Add an arc to the graph
1065
+
1066
+ Required Arguments:
1067
+
1068
+ - `origin_idx`
1069
+ - Type: int
1070
+ - What: The index of the origin node
1071
+ - `destination_idx`
1072
+ - Type: int
1073
+ - What: The index of the destination node
1074
+
1075
+ Optional Arguments:
1076
+
1077
+ - `distance`
1078
+ - Type: int | float
1079
+ - What: The distance between the origin and destination nodes in terms of the graph distance (normally km)
1080
+ - Default: 0
1081
+ - `use_haversine_distance`
1082
+ - Type: bool
1083
+ - What: Whether to calculate the haversine distance (km) between the nodes when calculating the distance
1084
+ - Default: True
1085
+ - Note: If true, overrides the `distance` argument
1086
+ - `undirected`
1087
+ - Type: bool
1088
+ - What: Whether to add the arc in both directions
1089
+ - Default: True
1090
+ """
1091
+ assert origin_idx < len(self.graph), "Origin node does not exist"
1092
+ assert destination_idx < len(
1093
+ self.graph
1094
+ ), "Destination node does not exist"
1095
+ if use_haversine_distance:
1096
+ distance = haversine(
1097
+ self.nodes[origin_idx], self.nodes[destination_idx]
1098
+ )
1099
+ self.graph[origin_idx][destination_idx] = distance
1100
+ if undirected:
1101
+ self.graph[destination_idx][origin_idx] = distance
1102
+
1103
+
1104
+ def load_geojson_as_geograph(geojson_filename: str) -> GeoGraph:
1105
+ """
1106
+ Function:
1107
+
1108
+ - Create a CustomGeoGraph object loaded from a geojson file
1109
+
1110
+ Required Arguments:
1111
+
1112
+ - `geojson_filename`
1113
+ - Type: str
1114
+ - What: The filename of the geojson file to load
1115
+ - Note: All arcs read in will be undirected
1116
+ - Note: This geojson file must be formatted in a specific way
1117
+ - The geojson file must be a FeatureCollection
1118
+ - Each feature must be a LineString with two coordinate pairs
1119
+ - The first coordinate pair must be the origin node
1120
+ - The second coordinate pair must be the destination node
1121
+ - The properties of the feature must include the distance between the origin and destination nodes
1122
+ - The properties of the feature must include the origin and destination node idxs
1123
+ - Origin and destination node idxs must be integers between 0 and n-1 where n is the number of nodes in the graph
1124
+ - EG:
1125
+ ```
1126
+ {
1127
+ "type": "FeatureCollection",
1128
+ "features": [
1129
+ {
1130
+ "type": "Feature",
1131
+ "properties": {
1132
+ "origin_idx": 0,
1133
+ "destination_idx": 1,
1134
+ "distance": 10
1135
+ },
1136
+ "geometry": {
1137
+ "type": "LineString",
1138
+ "coordinates": [
1139
+ [121.47, 31.23],
1140
+ [121.48, 31.24]
1141
+ ]
1142
+ }
1143
+ }
1144
+ ]
1145
+ }
1146
+ ```
1147
+ """
1148
+ with open(geojson_filename, "r") as f:
1149
+ geojson_features = json.load(f).get("features", [])
1150
+
1151
+ nodes_dict = {}
1152
+ graph_dict = {}
1153
+ for feature in geojson_features:
1154
+ properties = feature.get("properties", {})
1155
+ origin_idx = properties.get("origin_idx")
1156
+ destination_idx = properties.get("destination_idx")
1157
+ distance = properties.get("distance")
1158
+ geometry = feature.get("geometry", {})
1159
+ coordinates = geometry.get("coordinates", [])
1160
+
1161
+ # Validations
1162
+ assert (
1163
+ feature.get("type") == "Feature"
1164
+ ), "All features must be of type 'Feature'"
1165
+ assert (
1166
+ geometry.get("type") == "LineString"
1167
+ ), "All geometries must be of type 'LineString'"
1168
+ assert (
1169
+ len(coordinates) == 2
1170
+ ), "All LineStrings must have exactly 2 coordinates"
1171
+ assert isinstance(
1172
+ origin_idx, int
1173
+ ), "All features must have an 'origin_idx' property that is an integer"
1174
+ assert isinstance(
1175
+ destination_idx, int
1176
+ ), "All features must have a 'destination_idx' property that is an integer"
1177
+ assert isinstance(
1178
+ distance, (int, float)
1179
+ ), "All features must have a 'distance' property that is a number"
1180
+ assert (
1181
+ origin_idx >= 0
1182
+ ), "All origin_idxs must be greater than or equal to 0"
1183
+ assert (
1184
+ destination_idx >= 0
1185
+ ), "All destination_idxs must be greater than or equal to 0"
1186
+ assert distance >= 0, "All distances must be greater than or equal to 0"
1187
+ origin = coordinates[0]
1188
+ destination = coordinates[1]
1189
+ assert isinstance(origin, list), "All coordinates must be lists"
1190
+ assert isinstance(destination, list), "All coordinates must be lists"
1191
+ assert len(origin) == 2, "All coordinates must have a length of 2"
1192
+ assert len(destination) == 2, "All coordinates must have a length of 2"
1193
+ assert all(
1194
+ [isinstance(i, (int, float)) for i in origin]
1195
+ ), "All coordinates must be numeric"
1196
+ assert all(
1197
+ [isinstance(i, (int, float)) for i in destination]
1198
+ ), "All coordinates must be numeric"
1199
+ # assert all([origin[0] >= -90, origin[0] <= 90, origin[1] >= -180, origin[1] <= 180]), "All coordinates must be valid latitudes and longitudes"
1200
+ # assert all([destination[0] >= -90, destination[0] <= 90, destination[1] >= -180, destination[1] <= 180]), "All coordinates must be valid latitudes and longitudes"
1201
+
1202
+ # Update the data
1203
+ nodes_dict[origin_idx] = origin
1204
+ nodes_dict[destination_idx] = destination
1205
+ graph_dict[origin_idx] = {
1206
+ **graph_dict.get(origin_idx, {}),
1207
+ destination_idx: distance,
1208
+ }
1209
+ graph_dict[destination_idx] = {
1210
+ **graph_dict.get(destination_idx, {}),
1211
+ origin_idx: distance,
1212
+ }
1213
+ assert len(nodes_dict) == len(
1214
+ graph_dict
1215
+ ), "All nodes must be included as origins in the graph dictionary"
1216
+ nodes = [
1217
+ [i[1][1], i[1][0]]
1218
+ for i in sorted(nodes_dict.items(), key=lambda x: x[0])
1219
+ ]
1220
+ ordered_graph_tuple = sorted(graph_dict.items(), key=lambda x: x[0])
1221
+ graph_map = {i[0]: idx for idx, i in enumerate(ordered_graph_tuple)}
1222
+ graph = [
1223
+ {graph_map[k]: v for k, v in i[1].items()} for i in ordered_graph_tuple
1224
+ ]
1225
+ return GeoGraph(graph=graph, nodes=nodes)
1226
+
1227
+
1228
+ def get_multi_path_geojson(
1229
+ routes: list[dict],
1230
+ filename: [str, None] = None,
1231
+ show_progress: bool = False,
1232
+ ) -> dict:
1233
+ """
1234
+ Creates a GeoJSON file with the shortest path between the origin and destination of each route.
1235
+
1236
+ Required Parameters:
1237
+
1238
+ - `routes`: list[dict]
1239
+ - List of dictionaries with the following keys:
1240
+ - geograph: GeoGraph
1241
+ - Geograph object to use for the shortest path calculation.
1242
+ - origin: dict[float|float]
1243
+ - Origin coordinates
1244
+ - EG: {"latitude":39.2904, "longitude":-76.6122}
1245
+ - destination: dict[float|int]
1246
+ - Destination coordinates
1247
+ - EG: {"latitude":39.2904, "longitude":-76.6122}
1248
+ - properties: dict
1249
+ - Dictionary with the properties of the route
1250
+ - Everything in this dictionary will be included in the output GeoJSON file as properties of the route.
1251
+ - EG: {"id":"route_1", "name":"Route 1", "color":"#ff0000"}
1252
+
1253
+ Optional Parameters:
1254
+
1255
+ - `filename`: str | None
1256
+ - Name of the output GeoJSON file.
1257
+ - If None, the function will not save the file
1258
+ - Default: None
1259
+ - `show_progress`: bool
1260
+ - Whether to show basic progress information
1261
+ - Default: False
1262
+
1263
+ Returns
1264
+
1265
+ - `output`: dict
1266
+ - Dictionary with the GeoJSON file content.
1267
+ """
1268
+ assert isinstance(routes, list), "Routes must be a list"
1269
+ assert all(
1270
+ [isinstance(i, dict) for i in routes]
1271
+ ), "Routes must be a list of dictionaries"
1272
+ assert all(
1273
+ [isinstance(i.get("geograph"), GeoGraph) for i in routes]
1274
+ ), "All routes must have a 'geograph' key with a GeoGraph object"
1275
+ assert all(
1276
+ [isinstance(i.get("origin"), dict) for i in routes]
1277
+ ), "All routes must have an 'origin' key with a dictionary"
1278
+ assert all(
1279
+ [isinstance(i.get("destination"), dict) for i in routes]
1280
+ ), "All routes must have a 'destination' key with a dictionary"
1281
+ assert all(
1282
+ [isinstance(i.get("properties"), dict) for i in routes]
1283
+ ), "All routes must have a 'properties' key with a dictionary"
1284
+ assert all(
1285
+ [isinstance(i["origin"].get("latitude"), (int, float)) for i in routes]
1286
+ ), "All origins must have a 'latitude' key with a number"
1287
+ assert all(
1288
+ [isinstance(i["origin"].get("longitude"), (int, float)) for i in routes]
1289
+ ), "All origins must have a 'longitude' key with a number"
1290
+ assert all(
1291
+ [
1292
+ isinstance(i["destination"].get("latitude"), (int, float))
1293
+ for i in routes
1294
+ ]
1295
+ ), "All destinations must have a 'latitude' key with a number"
1296
+ assert all(
1297
+ [
1298
+ isinstance(i["destination"].get("longitude"), (int, float))
1299
+ for i in routes
1300
+ ]
1301
+ ), "All destinations must have a 'longitude' key with a number"
1302
+ assert all(
1303
+ [
1304
+ (
1305
+ i["origin"].get("latitude") >= -90
1306
+ and i["origin"].get("latitude") <= 90
1307
+ )
1308
+ for i in routes
1309
+ ]
1310
+ ), "All origin latitudes must be between -90 and 90"
1311
+ assert all(
1312
+ [
1313
+ (
1314
+ i["origin"].get("longitude") >= -180
1315
+ and i["origin"].get("longitude") <= 180
1316
+ )
1317
+ for i in routes
1318
+ ]
1319
+ ), "All origin longitudes must be between -180 and 180"
1320
+ assert all(
1321
+ [
1322
+ (
1323
+ i["destination"].get("latitude") >= -90
1324
+ and i["destination"].get("latitude") <= 90
1325
+ )
1326
+ for i in routes
1327
+ ]
1328
+ ), "All destination latitudes must be between -90 and 90"
1329
+ assert all(
1330
+ [
1331
+ (
1332
+ i["destination"].get("longitude") >= -180
1333
+ and i["destination"].get("longitude") <= 180
1334
+ )
1335
+ for i in routes
1336
+ ]
1337
+ ), "All destination longitudes must be between -180 and 180"
1338
+
1339
+ output = {"type": "FeatureCollection", "features": []}
1340
+ len_routes = len(routes)
1341
+ for idx, route in enumerate(routes):
1342
+ shortest_path = route["geograph"].get_shortest_path(
1343
+ route["origin"], route["destination"]
1344
+ )
1345
+ shortest_line_path = get_line_path(shortest_path)
1346
+ output["features"].append(
1347
+ {
1348
+ "type": "Feature",
1349
+ "properties": route["properties"],
1350
+ "geometry": shortest_line_path,
1351
+ }
1352
+ )
1353
+ if show_progress:
1354
+ print(
1355
+ f"[{'='*(int((idx+1)/len_routes*20))}>{' '*(20-int((idx+1)/len_routes*20))}] {idx+1}/{len_routes}",
1356
+ end="\r",
1357
+ )
1358
+ if show_progress:
1359
+ print()
1360
+ if filename is not None:
1361
+ json.dump(output, open(filename, "w"))
1362
+ return output
@@ -2,10 +2,10 @@ import math, json
2
2
 
3
3
 
4
4
  def haversine(
5
- origin: list[float, int],
6
- destination: list[float, int],
5
+ origin: list[float|int],
6
+ destination: list[float|int],
7
7
  units: str = "km",
8
- circuity: [int, float] = 1,
8
+ circuity: [float|int] = 1,
9
9
  ):
10
10
  """
11
11
  Function:
@@ -16,10 +16,10 @@ def haversine(
16
16
 
17
17
  - `origin`:
18
18
  - Type: list of two floats | ints
19
- - What: The origin point as a list of "longitude" and "latitude"
19
+ - What: The origin point as a list of "latitude" and "longitude"
20
20
  - `destination`:
21
21
  - Type: list of two floats | ints
22
- - What: The destination point as a list of "longitude" and "latitude"
22
+ - What: The destination point as a list of "latitude" and "longitude"
23
23
 
24
24
  Optional Arguments:
25
25
 
@@ -69,7 +69,7 @@ def haversine(
69
69
  raise Exception()
70
70
 
71
71
 
72
- def hard_round(decimal_places: int, a: [int, float]):
72
+ def hard_round(decimal_places: int, a: [float|int]):
73
73
  """
74
74
  Function:
75
75
 
@@ -90,7 +90,7 @@ def hard_round(decimal_places: int, a: [int, float]):
90
90
 
91
91
 
92
92
  def distance_converter(
93
- distance: [int, float], input_units: str, output_units: str
93
+ distance: [float|int], input_units: str, output_units: str
94
94
  ):
95
95
  """
96
96
  Function:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: scgraph
3
- Version: 2.1.2
3
+ Version: 2.3.0
4
4
  Summary: Determine an approximate route between two points on earth.
5
5
  Author-email: Connor Makowski <conmak@mit.edu>
6
6
  Project-URL: Homepage, https://github.com/connor-makowski/scgraph
@@ -9,7 +9,7 @@ Project-URL: Documentation, https://connor-makowski.github.io/scgraph/core.html
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.6
12
+ Requires-Python: >=3.10
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
15
 
@@ -57,7 +57,9 @@ Low Level: https://connor-makowski.github.io/scgraph/scgraph/core.html
57
57
 
58
58
  ## Setup
59
59
 
60
- Make sure you have Python 3.6.x (or higher) installed on your system. You can download it [here](https://www.python.org/downloads/).
60
+ Make sure you have Python 3.10.x (or higher) installed on your system. You can download it [here](https://www.python.org/downloads/).
61
+
62
+ Support for python3.6-3.9 is available up to version 2.2.0
61
63
 
62
64
  ## Installation
63
65
 
@@ -67,7 +69,10 @@ pip install scgraph
67
69
 
68
70
  ## Use with Google Colab
69
71
 
70
- See the example [here](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/example.ipynb)
72
+ - [Getting Started](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/getting_started.ipynb)
73
+ - [Creating A Multi Path Geojson](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/multi_path_geojson.ipynb)
74
+ - [Modifying A Geograph](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/geograph_modifications.ipynb)
75
+
71
76
 
72
77
  # Getting Started
73
78
 
@@ -101,7 +106,7 @@ In the above example, the `output` variable is a dictionary with three keys: `le
101
106
  - `ft` (feet)
102
107
  - `coordinate_path`: A list of lists [`latitude`,`longitude`] that make up the shortest path
103
108
 
104
- For more examples including viewing the output on a map, see the [example notebook](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/example.ipynb).
109
+ For more examples including viewing the output on a map, see the [example notebook](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/getting_started.ipynb).
105
110
 
106
111
  ## Included GeoGraphs
107
112
 
@@ -165,15 +170,17 @@ output = marnet_geograph.get_shortest_path(
165
170
  get_line_path(output, filename='output.geojson')
166
171
  ```
167
172
 
173
+ Modify an existing geograph: See the notebook [here](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/geograph_modifications.ipynb)
174
+
168
175
 
169
176
  You can specify your own custom graphs for direct access to the solving algorithms. This requires the use of the low level `Graph` class
170
177
 
171
178
  ```py
172
179
  from scgraph import Graph
173
180
 
174
- # Define a graph
181
+ # Define an arbitrary graph
175
182
  # See the graph definitions here:
176
- # https://connor-makowski.github.io/scgraph/scgraph/core.html
183
+ # https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
177
184
  graph = [
178
185
  {1: 5, 2: 1},
179
186
  {0: 5, 2: 2, 3: 1},
@@ -198,25 +205,67 @@ from scgraph import GeoGraph
198
205
 
199
206
  # Define nodes
200
207
  # See the nodes definitions here:
201
- # https://connor-makowski.github.io/scgraph/scgraph/core.html
208
+ # https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
202
209
  nodes = [
203
- [0,0],
204
- [0,1],
205
- [1,0],
206
- [1,1],
207
- [1,2],
208
- [2,1]
210
+ # London
211
+ [51.5074, -0.1278],
212
+ # Paris
213
+ [48.8566, 2.3522],
214
+ # Berlin
215
+ [52.5200, 13.4050],
216
+ # Rome
217
+ [41.9028, 12.4964],
218
+ # Madrid
219
+ [40.4168, -3.7038],
220
+ # Lisbon
221
+ [38.7223, -9.1393]
209
222
  ]
210
223
  # Define a graph
211
224
  # See the graph definitions here:
212
- # https://connor-makowski.github.io/scgraph/scgraph/core.html
225
+ # https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
213
226
  graph = [
214
- {1: 5, 2: 1},
215
- {0: 5, 2: 2, 3: 1},
216
- {0: 1, 1: 2, 3: 4, 4: 8},
217
- {1: 1, 2: 4, 4: 3, 5: 6},
218
- {2: 8, 3: 3},
219
- {3: 6}
227
+ # From London
228
+ {
229
+ # To Paris
230
+ 1: 311,
231
+ },
232
+ # From Paris
233
+ {
234
+ # To London
235
+ 0: 311,
236
+ # To Berlin
237
+ 2: 878,
238
+ # To Rome
239
+ 3: 1439,
240
+ # To Madrid
241
+ 4: 1053
242
+ },
243
+ # From Berlin
244
+ {
245
+ # To Paris
246
+ 1: 878,
247
+ # To Rome
248
+ 3: 1181,
249
+ },
250
+ # From Rome
251
+ {
252
+ # To Paris
253
+ 1: 1439,
254
+ # To Berlin
255
+ 2: 1181,
256
+ },
257
+ # From Madrid
258
+ {
259
+ # To Paris
260
+ 1: 1053,
261
+ # To Lisbon
262
+ 5: 623
263
+ },
264
+ # From Lisbon
265
+ {
266
+ # To Madrid
267
+ 4: 623
268
+ }
220
269
  ]
221
270
 
222
271
  # Create a GeoGraph object
@@ -229,23 +278,31 @@ my_geograph.validate_graph()
229
278
  my_geograph.validate_nodes()
230
279
 
231
280
  # Get the shortest path between two points
281
+ # In this case, Birmingham England and Zaragoza Spain
282
+ # Since Birmingham and Zaragoza are not in the graph,
283
+ # the algorithm will add them into the graph.
284
+ # See: https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph.get_shortest_path
285
+ # Expected output would be to go from
286
+ # Birmingham -> London -> Paris -> Madrid -> Zaragoza
287
+
232
288
  output = my_geograph.get_shortest_path(
233
- origin_node = {'latitude': 0, 'longitude': 0},
234
- destination_node = {'latitude': 2, 'longitude': 1}
289
+ # Birmingham England
290
+ origin_node = {'latitude': 52.4862, 'longitude': -1.8904},
291
+ # Zaragoza Spain
292
+ destination_node = {'latitude': 41.6488, 'longitude': -0.8891}
235
293
  )
236
- #=>
294
+ print(output)
237
295
  # {
238
- # "coordinate_path": [
239
- # [0,0],
240
- # [0,0],
241
- # [1,0],
242
- # [0,1],
243
- # [1,1],
244
- # [2,1],
245
- # [2,1]
246
- # ],
247
- # "length": 10
296
+ # 'length': 1799.4323,
297
+ # 'coordinate_path': [
298
+ # [52.4862, -1.8904],
299
+ # [51.5074, -0.1278],
300
+ # [48.8566, 2.3522],
301
+ # [40.4168, -3.7038],
302
+ # [41.6488, -0.8891]
303
+ # ]
248
304
  # }
305
+
249
306
  ```
250
307
 
251
308
  ## Attributions and Thanks
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = scgraph
3
- version = 2.1.2
3
+ version = 2.3.0
4
4
  description_file = README.md
5
5
 
6
6
  [options]
File without changes
File without changes