scgraph 2.2.0__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.
- {scgraph-2.2.0/scgraph.egg-info → scgraph-2.3.0}/PKG-INFO +90 -35
- {scgraph-2.2.0 → scgraph-2.3.0}/README.md +88 -33
- {scgraph-2.2.0 → scgraph-2.3.0}/pyproject.toml +2 -2
- {scgraph-2.2.0 → scgraph-2.3.0}/scgraph/core.py +311 -67
- {scgraph-2.2.0 → scgraph-2.3.0}/scgraph/utils.py +5 -5
- {scgraph-2.2.0 → scgraph-2.3.0/scgraph.egg-info}/PKG-INFO +90 -35
- {scgraph-2.2.0 → scgraph-2.3.0}/setup.cfg +1 -1
- {scgraph-2.2.0 → scgraph-2.3.0}/LICENSE +0 -0
- {scgraph-2.2.0 → scgraph-2.3.0}/scgraph/__init__.py +0 -0
- {scgraph-2.2.0 → scgraph-2.3.0}/scgraph/geographs/__init__.py +0 -0
- {scgraph-2.2.0 → scgraph-2.3.0}/scgraph/geographs/marnet.py +0 -0
- {scgraph-2.2.0 → scgraph-2.3.0}/scgraph/geographs/north_america_rail.py +0 -0
- {scgraph-2.2.0 → scgraph-2.3.0}/scgraph/geographs/oak_ridge_maritime.py +0 -0
- {scgraph-2.2.0 → scgraph-2.3.0}/scgraph/geographs/us_freeway.py +0 -0
- {scgraph-2.2.0 → scgraph-2.3.0}/scgraph.egg-info/SOURCES.txt +0 -0
- {scgraph-2.2.0 → scgraph-2.3.0}/scgraph.egg-info/dependency_links.txt +0 -0
- {scgraph-2.2.0 → scgraph-2.3.0}/scgraph.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: scgraph
|
|
3
|
-
Version: 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.
|
|
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.
|
|
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
|
-
|
|
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/
|
|
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,7 +170,7 @@ output = marnet_geograph.get_shortest_path(
|
|
|
165
170
|
get_line_path(output, filename='output.geojson')
|
|
166
171
|
```
|
|
167
172
|
|
|
168
|
-
Modify an existing geograph: See the notebook [here](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/
|
|
173
|
+
Modify an existing geograph: See the notebook [here](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/geograph_modifications.ipynb)
|
|
169
174
|
|
|
170
175
|
|
|
171
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
|
|
@@ -173,9 +178,9 @@ You can specify your own custom graphs for direct access to the solving algorith
|
|
|
173
178
|
```py
|
|
174
179
|
from scgraph import Graph
|
|
175
180
|
|
|
176
|
-
# Define
|
|
181
|
+
# Define an arbitrary graph
|
|
177
182
|
# See the graph definitions here:
|
|
178
|
-
# https://connor-makowski.github.io/scgraph/scgraph/core.html
|
|
183
|
+
# https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
|
|
179
184
|
graph = [
|
|
180
185
|
{1: 5, 2: 1},
|
|
181
186
|
{0: 5, 2: 2, 3: 1},
|
|
@@ -200,25 +205,67 @@ from scgraph import GeoGraph
|
|
|
200
205
|
|
|
201
206
|
# Define nodes
|
|
202
207
|
# See the nodes definitions here:
|
|
203
|
-
# https://connor-makowski.github.io/scgraph/scgraph/core.html
|
|
208
|
+
# https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
|
|
204
209
|
nodes = [
|
|
205
|
-
|
|
206
|
-
[0
|
|
207
|
-
|
|
208
|
-
[
|
|
209
|
-
|
|
210
|
-
[
|
|
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]
|
|
211
222
|
]
|
|
212
223
|
# Define a graph
|
|
213
224
|
# See the graph definitions here:
|
|
214
|
-
# https://connor-makowski.github.io/scgraph/scgraph/core.html
|
|
225
|
+
# https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
|
|
215
226
|
graph = [
|
|
216
|
-
|
|
217
|
-
{
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
+
}
|
|
222
269
|
]
|
|
223
270
|
|
|
224
271
|
# Create a GeoGraph object
|
|
@@ -231,23 +278,31 @@ my_geograph.validate_graph()
|
|
|
231
278
|
my_geograph.validate_nodes()
|
|
232
279
|
|
|
233
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
|
+
|
|
234
288
|
output = my_geograph.get_shortest_path(
|
|
235
|
-
|
|
236
|
-
|
|
289
|
+
# Birmingham England
|
|
290
|
+
origin_node = {'latitude': 52.4862, 'longitude': -1.8904},
|
|
291
|
+
# Zaragoza Spain
|
|
292
|
+
destination_node = {'latitude': 41.6488, 'longitude': -0.8891}
|
|
237
293
|
)
|
|
238
|
-
|
|
294
|
+
print(output)
|
|
239
295
|
# {
|
|
240
|
-
#
|
|
241
|
-
#
|
|
242
|
-
# [
|
|
243
|
-
# [
|
|
244
|
-
# [
|
|
245
|
-
# [
|
|
246
|
-
# [
|
|
247
|
-
#
|
|
248
|
-
# ],
|
|
249
|
-
# "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
|
+
# ]
|
|
250
304
|
# }
|
|
305
|
+
|
|
251
306
|
```
|
|
252
307
|
|
|
253
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.
|
|
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
|
-
|
|
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/
|
|
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,7 +155,7 @@ output = marnet_geograph.get_shortest_path(
|
|
|
150
155
|
get_line_path(output, filename='output.geojson')
|
|
151
156
|
```
|
|
152
157
|
|
|
153
|
-
Modify an existing geograph: See the notebook [here](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/
|
|
158
|
+
Modify an existing geograph: See the notebook [here](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/geograph_modifications.ipynb)
|
|
154
159
|
|
|
155
160
|
|
|
156
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
|
|
@@ -158,9 +163,9 @@ You can specify your own custom graphs for direct access to the solving algorith
|
|
|
158
163
|
```py
|
|
159
164
|
from scgraph import Graph
|
|
160
165
|
|
|
161
|
-
# Define
|
|
166
|
+
# Define an arbitrary graph
|
|
162
167
|
# See the graph definitions here:
|
|
163
|
-
# https://connor-makowski.github.io/scgraph/scgraph/core.html
|
|
168
|
+
# https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
|
|
164
169
|
graph = [
|
|
165
170
|
{1: 5, 2: 1},
|
|
166
171
|
{0: 5, 2: 2, 3: 1},
|
|
@@ -185,25 +190,67 @@ from scgraph import GeoGraph
|
|
|
185
190
|
|
|
186
191
|
# Define nodes
|
|
187
192
|
# See the nodes definitions here:
|
|
188
|
-
# https://connor-makowski.github.io/scgraph/scgraph/core.html
|
|
193
|
+
# https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
|
|
189
194
|
nodes = [
|
|
190
|
-
|
|
191
|
-
[0
|
|
192
|
-
|
|
193
|
-
[
|
|
194
|
-
|
|
195
|
-
[
|
|
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]
|
|
196
207
|
]
|
|
197
208
|
# Define a graph
|
|
198
209
|
# See the graph definitions here:
|
|
199
|
-
# https://connor-makowski.github.io/scgraph/scgraph/core.html
|
|
210
|
+
# https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
|
|
200
211
|
graph = [
|
|
201
|
-
|
|
202
|
-
{
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
+
}
|
|
207
254
|
]
|
|
208
255
|
|
|
209
256
|
# Create a GeoGraph object
|
|
@@ -216,23 +263,31 @@ my_geograph.validate_graph()
|
|
|
216
263
|
my_geograph.validate_nodes()
|
|
217
264
|
|
|
218
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
|
+
|
|
219
273
|
output = my_geograph.get_shortest_path(
|
|
220
|
-
|
|
221
|
-
|
|
274
|
+
# Birmingham England
|
|
275
|
+
origin_node = {'latitude': 52.4862, 'longitude': -1.8904},
|
|
276
|
+
# Zaragoza Spain
|
|
277
|
+
destination_node = {'latitude': 41.6488, 'longitude': -0.8891}
|
|
222
278
|
)
|
|
223
|
-
|
|
279
|
+
print(output)
|
|
224
280
|
# {
|
|
225
|
-
#
|
|
226
|
-
#
|
|
227
|
-
# [
|
|
228
|
-
# [
|
|
229
|
-
# [
|
|
230
|
-
# [
|
|
231
|
-
# [
|
|
232
|
-
#
|
|
233
|
-
# ],
|
|
234
|
-
# "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
|
+
# ]
|
|
235
289
|
# }
|
|
290
|
+
|
|
236
291
|
```
|
|
237
292
|
|
|
238
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.
|
|
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.
|
|
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
|
-
-
|
|
22
|
-
|
|
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
|
-
-
|
|
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
|
|
126
|
-
-
|
|
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
|
|
172
|
-
-
|
|
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
|
|
247
|
-
-
|
|
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
|
|
299
|
+
self, graph: list[dict], nodes: list[list[float|int]]
|
|
302
300
|
) -> None:
|
|
303
301
|
"""
|
|
304
302
|
Function:
|
|
@@ -311,10 +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
|
|
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
|
+
```
|
|
318
380
|
"""
|
|
319
381
|
self.graph = graph
|
|
320
382
|
self.nodes = nodes
|
|
@@ -394,17 +456,17 @@ class GeoGraph:
|
|
|
394
456
|
|
|
395
457
|
def get_shortest_path(
|
|
396
458
|
self,
|
|
397
|
-
origin_node: dict[int
|
|
398
|
-
destination_node: dict[int
|
|
459
|
+
origin_node: dict[float|int],
|
|
460
|
+
destination_node: dict[float|int],
|
|
399
461
|
output_units: str = "km",
|
|
400
462
|
algorithm_fn=Graph.dijkstra_makowski,
|
|
401
|
-
off_graph_circuity: [int
|
|
463
|
+
off_graph_circuity: [float|int] = 1,
|
|
402
464
|
node_addition_type: str = "quadrant",
|
|
403
|
-
node_addition_circuity: [int
|
|
465
|
+
node_addition_circuity: [float|int] = 4,
|
|
404
466
|
geograph_units: str = "km",
|
|
405
467
|
output_coordinate_path: str = "list_of_lists",
|
|
406
468
|
output_path: bool = False,
|
|
407
|
-
node_addition_lat_lon_bound: [int
|
|
469
|
+
node_addition_lat_lon_bound: [float|int] = 5,
|
|
408
470
|
node_addition_math: str = "euclidean",
|
|
409
471
|
**kwargs,
|
|
410
472
|
) -> dict:
|
|
@@ -447,6 +509,7 @@ class GeoGraph:
|
|
|
447
509
|
- 'Graph.dijkstra_makowski': A modified dijkstra algorithm that uses a sparse distance matrix to identify the shortest path
|
|
448
510
|
- Any user defined algorithm that takes the arguments:
|
|
449
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
|
|
450
513
|
- `origin`: The id of the origin node from the graph dictionary to start the shortest path from
|
|
451
514
|
- `destination`: The id of the destination node from the graph dictionary to end the shortest path at
|
|
452
515
|
- `off_graph_circuity`
|
|
@@ -579,9 +642,9 @@ class GeoGraph:
|
|
|
579
642
|
def adujust_circuity_length(
|
|
580
643
|
self,
|
|
581
644
|
output: dict,
|
|
582
|
-
node_addition_circuity: [float
|
|
583
|
-
off_graph_circuity: [float
|
|
584
|
-
) -> [float
|
|
645
|
+
node_addition_circuity: [float|int],
|
|
646
|
+
off_graph_circuity: [float|int],
|
|
647
|
+
) -> [float|int]:
|
|
585
648
|
"""
|
|
586
649
|
Function:
|
|
587
650
|
|
|
@@ -616,7 +679,7 @@ class GeoGraph:
|
|
|
616
679
|
4,
|
|
617
680
|
)
|
|
618
681
|
|
|
619
|
-
def get_coordinate_path(self, path: list[int]) -> list[dict[int
|
|
682
|
+
def get_coordinate_path(self, path: list[int]) -> list[dict[float|int]]:
|
|
620
683
|
"""
|
|
621
684
|
Function:
|
|
622
685
|
|
|
@@ -660,11 +723,11 @@ class GeoGraph:
|
|
|
660
723
|
def get_node_distances(
|
|
661
724
|
self,
|
|
662
725
|
node: list,
|
|
663
|
-
circuity: [int
|
|
726
|
+
circuity: [float|int],
|
|
664
727
|
node_addition_type: str,
|
|
665
728
|
node_addition_math: str,
|
|
666
|
-
lat_lon_bound: [int
|
|
667
|
-
) -> dict[int
|
|
729
|
+
lat_lon_bound: [float|int],
|
|
730
|
+
) -> dict[float|int]:
|
|
668
731
|
"""
|
|
669
732
|
Function:
|
|
670
733
|
|
|
@@ -764,11 +827,11 @@ class GeoGraph:
|
|
|
764
827
|
|
|
765
828
|
def add_node(
|
|
766
829
|
self,
|
|
767
|
-
node: dict[int
|
|
768
|
-
circuity: [int
|
|
830
|
+
node: dict[float|int],
|
|
831
|
+
circuity: [float|int],
|
|
769
832
|
node_addition_type: str = "quadrant",
|
|
770
833
|
node_addition_math: str = "euclidean",
|
|
771
|
-
lat_lon_bound: [int
|
|
834
|
+
lat_lon_bound: [float|int] = 5,
|
|
772
835
|
) -> int:
|
|
773
836
|
"""
|
|
774
837
|
Function:
|
|
@@ -923,14 +986,14 @@ class GeoGraph:
|
|
|
923
986
|
- Import as: 'from .custom import custom_geograph'
|
|
924
987
|
"""
|
|
925
988
|
self.validate_nodes()
|
|
926
|
-
self.validate_graph(
|
|
927
|
-
check_symmetry=True, check_connected=False
|
|
928
|
-
)
|
|
989
|
+
self.validate_graph(check_symmetry=True, check_connected=False)
|
|
929
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)"""
|
|
930
|
-
with open(name+".py", "w") as f:
|
|
991
|
+
with open(name + ".py", "w") as f:
|
|
931
992
|
f.write(out_string)
|
|
932
|
-
|
|
933
|
-
def mod_remove_arc(
|
|
993
|
+
|
|
994
|
+
def mod_remove_arc(
|
|
995
|
+
self, origin_idx: int, destination_idx: int, undirected: bool = True
|
|
996
|
+
) -> None:
|
|
934
997
|
"""
|
|
935
998
|
Function:
|
|
936
999
|
|
|
@@ -953,14 +1016,18 @@ class GeoGraph:
|
|
|
953
1016
|
- Default: True
|
|
954
1017
|
"""
|
|
955
1018
|
assert origin_idx < len(self.graph), "Origin node does not exist"
|
|
956
|
-
assert destination_idx < len(
|
|
1019
|
+
assert destination_idx < len(
|
|
1020
|
+
self.graph
|
|
1021
|
+
), "Destination node does not exist"
|
|
957
1022
|
assert destination_idx in self.graph[origin_idx], "Arc does not exist"
|
|
958
1023
|
del self.graph[origin_idx][destination_idx]
|
|
959
1024
|
if undirected:
|
|
960
1025
|
if origin_idx in self.graph[destination_idx]:
|
|
961
1026
|
del self.graph[destination_idx][origin_idx]
|
|
962
1027
|
|
|
963
|
-
def mod_add_node(
|
|
1028
|
+
def mod_add_node(
|
|
1029
|
+
self, latitude: [float|int], longitude: [float|int]
|
|
1030
|
+
) -> int:
|
|
964
1031
|
"""
|
|
965
1032
|
Function:
|
|
966
1033
|
|
|
@@ -982,8 +1049,15 @@ class GeoGraph:
|
|
|
982
1049
|
self.nodes.append([latitude, longitude])
|
|
983
1050
|
self.graph.append({})
|
|
984
1051
|
return len(self.graph) - 1
|
|
985
|
-
|
|
986
|
-
def mod_add_arc(
|
|
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:
|
|
987
1061
|
"""
|
|
988
1062
|
Function:
|
|
989
1063
|
|
|
@@ -1015,13 +1089,18 @@ class GeoGraph:
|
|
|
1015
1089
|
- Default: True
|
|
1016
1090
|
"""
|
|
1017
1091
|
assert origin_idx < len(self.graph), "Origin node does not exist"
|
|
1018
|
-
assert destination_idx < len(
|
|
1092
|
+
assert destination_idx < len(
|
|
1093
|
+
self.graph
|
|
1094
|
+
), "Destination node does not exist"
|
|
1019
1095
|
if use_haversine_distance:
|
|
1020
|
-
distance = haversine(
|
|
1096
|
+
distance = haversine(
|
|
1097
|
+
self.nodes[origin_idx], self.nodes[destination_idx]
|
|
1098
|
+
)
|
|
1021
1099
|
self.graph[origin_idx][destination_idx] = distance
|
|
1022
1100
|
if undirected:
|
|
1023
1101
|
self.graph[destination_idx][origin_idx] = distance
|
|
1024
1102
|
|
|
1103
|
+
|
|
1025
1104
|
def load_geojson_as_geograph(geojson_filename: str) -> GeoGraph:
|
|
1026
1105
|
"""
|
|
1027
1106
|
Function:
|
|
@@ -1066,9 +1145,9 @@ def load_geojson_as_geograph(geojson_filename: str) -> GeoGraph:
|
|
|
1066
1145
|
}
|
|
1067
1146
|
```
|
|
1068
1147
|
"""
|
|
1069
|
-
with open
|
|
1148
|
+
with open(geojson_filename, "r") as f:
|
|
1070
1149
|
geojson_features = json.load(f).get("features", [])
|
|
1071
|
-
|
|
1150
|
+
|
|
1072
1151
|
nodes_dict = {}
|
|
1073
1152
|
graph_dict = {}
|
|
1074
1153
|
for feature in geojson_features:
|
|
@@ -1080,14 +1159,30 @@ def load_geojson_as_geograph(geojson_filename: str) -> GeoGraph:
|
|
|
1080
1159
|
coordinates = geometry.get("coordinates", [])
|
|
1081
1160
|
|
|
1082
1161
|
# Validations
|
|
1083
|
-
assert
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
assert
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
assert
|
|
1090
|
-
|
|
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"
|
|
1091
1186
|
assert distance >= 0, "All distances must be greater than or equal to 0"
|
|
1092
1187
|
origin = coordinates[0]
|
|
1093
1188
|
destination = coordinates[1]
|
|
@@ -1095,24 +1190,173 @@ def load_geojson_as_geograph(geojson_filename: str) -> GeoGraph:
|
|
|
1095
1190
|
assert isinstance(destination, list), "All coordinates must be lists"
|
|
1096
1191
|
assert len(origin) == 2, "All coordinates must have a length of 2"
|
|
1097
1192
|
assert len(destination) == 2, "All coordinates must have a length of 2"
|
|
1098
|
-
assert all(
|
|
1099
|
-
|
|
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"
|
|
1100
1199
|
# assert all([origin[0] >= -90, origin[0] <= 90, origin[1] >= -180, origin[1] <= 180]), "All coordinates must be valid latitudes and longitudes"
|
|
1101
1200
|
# assert all([destination[0] >= -90, destination[0] <= 90, destination[1] >= -180, destination[1] <= 180]), "All coordinates must be valid latitudes and longitudes"
|
|
1102
|
-
|
|
1201
|
+
|
|
1103
1202
|
# Update the data
|
|
1104
1203
|
nodes_dict[origin_idx] = origin
|
|
1105
1204
|
nodes_dict[destination_idx] = destination
|
|
1106
|
-
graph_dict[origin_idx] = {
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
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
|
+
]
|
|
1110
1220
|
ordered_graph_tuple = sorted(graph_dict.items(), key=lambda x: x[0])
|
|
1111
1221
|
graph_map = {i[0]: idx for idx, i in enumerate(ordered_graph_tuple)}
|
|
1112
1222
|
graph = [
|
|
1113
1223
|
{graph_map[k]: v for k, v in i[1].items()} for i in ordered_graph_tuple
|
|
1114
1224
|
]
|
|
1115
|
-
return GeoGraph(
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
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
|
|
6
|
-
destination: list[float
|
|
5
|
+
origin: list[float|int],
|
|
6
|
+
destination: list[float|int],
|
|
7
7
|
units: str = "km",
|
|
8
|
-
circuity: [int
|
|
8
|
+
circuity: [float|int] = 1,
|
|
9
9
|
):
|
|
10
10
|
"""
|
|
11
11
|
Function:
|
|
@@ -69,7 +69,7 @@ def haversine(
|
|
|
69
69
|
raise Exception()
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
def hard_round(decimal_places: int, a: [int
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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/
|
|
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,7 +170,7 @@ output = marnet_geograph.get_shortest_path(
|
|
|
165
170
|
get_line_path(output, filename='output.geojson')
|
|
166
171
|
```
|
|
167
172
|
|
|
168
|
-
Modify an existing geograph: See the notebook [here](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/
|
|
173
|
+
Modify an existing geograph: See the notebook [here](https://colab.research.google.com/github/connor-makowski/scgraph/blob/main/examples/geograph_modifications.ipynb)
|
|
169
174
|
|
|
170
175
|
|
|
171
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
|
|
@@ -173,9 +178,9 @@ You can specify your own custom graphs for direct access to the solving algorith
|
|
|
173
178
|
```py
|
|
174
179
|
from scgraph import Graph
|
|
175
180
|
|
|
176
|
-
# Define
|
|
181
|
+
# Define an arbitrary graph
|
|
177
182
|
# See the graph definitions here:
|
|
178
|
-
# https://connor-makowski.github.io/scgraph/scgraph/core.html
|
|
183
|
+
# https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
|
|
179
184
|
graph = [
|
|
180
185
|
{1: 5, 2: 1},
|
|
181
186
|
{0: 5, 2: 2, 3: 1},
|
|
@@ -200,25 +205,67 @@ from scgraph import GeoGraph
|
|
|
200
205
|
|
|
201
206
|
# Define nodes
|
|
202
207
|
# See the nodes definitions here:
|
|
203
|
-
# https://connor-makowski.github.io/scgraph/scgraph/core.html
|
|
208
|
+
# https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
|
|
204
209
|
nodes = [
|
|
205
|
-
|
|
206
|
-
[0
|
|
207
|
-
|
|
208
|
-
[
|
|
209
|
-
|
|
210
|
-
[
|
|
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]
|
|
211
222
|
]
|
|
212
223
|
# Define a graph
|
|
213
224
|
# See the graph definitions here:
|
|
214
|
-
# https://connor-makowski.github.io/scgraph/scgraph/core.html
|
|
225
|
+
# https://connor-makowski.github.io/scgraph/scgraph/core.html#GeoGraph
|
|
215
226
|
graph = [
|
|
216
|
-
|
|
217
|
-
{
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
+
}
|
|
222
269
|
]
|
|
223
270
|
|
|
224
271
|
# Create a GeoGraph object
|
|
@@ -231,23 +278,31 @@ my_geograph.validate_graph()
|
|
|
231
278
|
my_geograph.validate_nodes()
|
|
232
279
|
|
|
233
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
|
+
|
|
234
288
|
output = my_geograph.get_shortest_path(
|
|
235
|
-
|
|
236
|
-
|
|
289
|
+
# Birmingham England
|
|
290
|
+
origin_node = {'latitude': 52.4862, 'longitude': -1.8904},
|
|
291
|
+
# Zaragoza Spain
|
|
292
|
+
destination_node = {'latitude': 41.6488, 'longitude': -0.8891}
|
|
237
293
|
)
|
|
238
|
-
|
|
294
|
+
print(output)
|
|
239
295
|
# {
|
|
240
|
-
#
|
|
241
|
-
#
|
|
242
|
-
# [
|
|
243
|
-
# [
|
|
244
|
-
# [
|
|
245
|
-
# [
|
|
246
|
-
# [
|
|
247
|
-
#
|
|
248
|
-
# ],
|
|
249
|
-
# "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
|
+
# ]
|
|
250
304
|
# }
|
|
305
|
+
|
|
251
306
|
```
|
|
252
307
|
|
|
253
308
|
## Attributions and Thanks
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|