routx 1.0.2__py3-none-win_amd64.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.
- .routx.mesonpy.libs/libroutx.dll +0 -0
- routx/__init__.py +41 -0
- routx/wrapper.py +873 -0
- routx-1.0.2.dist-info/METADATA +7 -0
- routx-1.0.2.dist-info/RECORD +6 -0
- routx-1.0.2.dist-info/WHEEL +5 -0
|
Binary file
|
routx/__init__.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# (c) Copyright 2025 Mikołaj Kuranowski
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
from .wrapper import (
|
|
5
|
+
DEFAULT_STEP_LIMIT,
|
|
6
|
+
Edge,
|
|
7
|
+
Graph,
|
|
8
|
+
KDTree,
|
|
9
|
+
Node,
|
|
10
|
+
OsmCustomProfile,
|
|
11
|
+
OsmFormat,
|
|
12
|
+
OsmLoadingError,
|
|
13
|
+
OsmPenalty,
|
|
14
|
+
OsmProfile,
|
|
15
|
+
StepLimitExceeded,
|
|
16
|
+
earth_distance,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"DEFAULT_STEP_LIMIT",
|
|
21
|
+
"Edge",
|
|
22
|
+
"Graph",
|
|
23
|
+
"KDTree",
|
|
24
|
+
"Node",
|
|
25
|
+
"OsmCustomProfile",
|
|
26
|
+
"OsmFormat",
|
|
27
|
+
"OsmLoadingError",
|
|
28
|
+
"OsmPenalty",
|
|
29
|
+
"OsmProfile",
|
|
30
|
+
"StepLimitExceeded",
|
|
31
|
+
"earth_distance",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
__title__ = "routx"
|
|
35
|
+
__description__ = "Simple routing over OpenStreetMap data "
|
|
36
|
+
__url__ = "https://github.com/MKuranowski/routx-python"
|
|
37
|
+
__author__ = "Mikołaj Kuranowski"
|
|
38
|
+
__copyright__ = "© Copyright 2025 Mikołaj Kuranowski"
|
|
39
|
+
__license__ = "MIT"
|
|
40
|
+
__version__ = "1.0.2"
|
|
41
|
+
__email__ = "mkuranowski+pypackages@gmail.com"
|
routx/wrapper.py
ADDED
|
@@ -0,0 +1,873 @@
|
|
|
1
|
+
# (c) Copyright 2025 Mikołaj Kuranowski
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import sys
|
|
6
|
+
from collections.abc import Iterator, MutableMapping
|
|
7
|
+
from ctypes import (
|
|
8
|
+
CFUNCTYPE,
|
|
9
|
+
POINTER,
|
|
10
|
+
Structure,
|
|
11
|
+
Union,
|
|
12
|
+
byref,
|
|
13
|
+
c_bool,
|
|
14
|
+
c_char,
|
|
15
|
+
c_char_p,
|
|
16
|
+
c_float,
|
|
17
|
+
c_int,
|
|
18
|
+
c_int64,
|
|
19
|
+
c_size_t,
|
|
20
|
+
c_uint32,
|
|
21
|
+
c_void_p,
|
|
22
|
+
)
|
|
23
|
+
from ctypes import cast as c_cast
|
|
24
|
+
from ctypes import cdll, pointer
|
|
25
|
+
from dataclasses import dataclass
|
|
26
|
+
from enum import IntEnum
|
|
27
|
+
from os import PathLike
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import Any, Final, NamedTuple
|
|
30
|
+
|
|
31
|
+
from typing_extensions import Self
|
|
32
|
+
|
|
33
|
+
# Figure out where the shared library is and get a handle to it
|
|
34
|
+
|
|
35
|
+
if sys.platform.startswith("win32"):
|
|
36
|
+
lib_filename = "libroutx.dll"
|
|
37
|
+
else:
|
|
38
|
+
lib_filename = "libroutx.so"
|
|
39
|
+
|
|
40
|
+
wheel_lib_path = Path(__file__).parent.parent / ".routx.mesonpy.libs" / lib_filename
|
|
41
|
+
local_lib_path = Path(__file__).with_name(lib_filename)
|
|
42
|
+
lib_path = wheel_lib_path if wheel_lib_path.exists() else local_lib_path
|
|
43
|
+
lib = cdll.LoadLibrary(str(lib_path))
|
|
44
|
+
|
|
45
|
+
# C-level definitions
|
|
46
|
+
|
|
47
|
+
c_char_p_p = POINTER(c_char_p)
|
|
48
|
+
|
|
49
|
+
_Graph_p = c_void_p
|
|
50
|
+
_GraphIterator_p = c_void_p
|
|
51
|
+
_KDTree_p = c_void_p
|
|
52
|
+
_LoggingCallback = CFUNCTYPE(None, c_void_p, c_int, c_char_p, c_char_p)
|
|
53
|
+
_LoggingFlushCallback = CFUNCTYPE(None, c_void_p)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class _Node(Structure):
|
|
57
|
+
_fields_ = [
|
|
58
|
+
("id", c_int64),
|
|
59
|
+
("osm_id", c_int64),
|
|
60
|
+
("lat", c_float),
|
|
61
|
+
("lon", c_float),
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class _Edge(Structure):
|
|
66
|
+
_fields_ = [
|
|
67
|
+
("to", c_int64),
|
|
68
|
+
("cost", c_float),
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class _OsmProfilePenalty(Structure):
|
|
73
|
+
_fields_ = [
|
|
74
|
+
("key", c_char_p),
|
|
75
|
+
("value", c_char_p),
|
|
76
|
+
("penalty", c_float),
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class _OsmProfile(Structure):
|
|
81
|
+
_fields_ = [
|
|
82
|
+
("name", c_char_p),
|
|
83
|
+
("penalties", POINTER(_OsmProfilePenalty)),
|
|
84
|
+
("penalties_len", c_size_t),
|
|
85
|
+
("access", c_char_p_p),
|
|
86
|
+
("access_len", c_size_t),
|
|
87
|
+
("disallow_motorroad", c_bool),
|
|
88
|
+
("disable_restrictions", c_bool),
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class _OsmOptions(Structure):
|
|
93
|
+
_fields_ = [
|
|
94
|
+
("profile", POINTER(_OsmProfile)),
|
|
95
|
+
("file_format", c_int),
|
|
96
|
+
("bbox", c_float * 4),
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class _RouteResultOk(Structure):
|
|
101
|
+
_fields_ = [
|
|
102
|
+
("nodes", POINTER(c_int64)),
|
|
103
|
+
("len", c_uint32),
|
|
104
|
+
("capacity", c_uint32),
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class _RouteResultInvalidRef(Structure):
|
|
109
|
+
_fields_ = [
|
|
110
|
+
("invalid_node_id", c_int64),
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class _RouteResultUnion(Union):
|
|
115
|
+
_fields_ = [
|
|
116
|
+
("as_ok", _RouteResultOk),
|
|
117
|
+
("as_invalid_reference", _RouteResultInvalidRef),
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class _RouteResult(Structure):
|
|
122
|
+
_anonymous_ = ["u"]
|
|
123
|
+
_fields_ = [
|
|
124
|
+
("u", _RouteResultUnion),
|
|
125
|
+
("type", c_int),
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
lib.routx_set_logging_callback.argtypes = [
|
|
130
|
+
_LoggingCallback,
|
|
131
|
+
_LoggingFlushCallback,
|
|
132
|
+
c_void_p,
|
|
133
|
+
c_int,
|
|
134
|
+
]
|
|
135
|
+
lib.routx_set_logging_callback.restype = None
|
|
136
|
+
|
|
137
|
+
lib.routx_graph_new.argtypes = []
|
|
138
|
+
lib.routx_graph_new.restype = _Graph_p
|
|
139
|
+
|
|
140
|
+
lib.routx_graph_delete.argtypes = [_Graph_p]
|
|
141
|
+
lib.routx_graph_delete.restype = None
|
|
142
|
+
|
|
143
|
+
lib.routx_graph_get_nodes.argtypes = [_Graph_p, POINTER(_GraphIterator_p)]
|
|
144
|
+
lib.routx_graph_get_nodes.restype = c_size_t
|
|
145
|
+
|
|
146
|
+
lib.routx_graph_iterator_next.argtypes = [_GraphIterator_p]
|
|
147
|
+
lib.routx_graph_iterator_next.restype = _Node
|
|
148
|
+
|
|
149
|
+
lib.routx_graph_iterator_delete.argtypes = [_GraphIterator_p]
|
|
150
|
+
lib.routx_graph_iterator_delete.restype = None
|
|
151
|
+
|
|
152
|
+
lib.routx_graph_get_node.argtypes = [_Graph_p, c_int64]
|
|
153
|
+
lib.routx_graph_get_node.restype = _Node
|
|
154
|
+
|
|
155
|
+
lib.routx_graph_set_node.argtypes = [_Graph_p, _Node]
|
|
156
|
+
lib.routx_graph_set_node.restype = bool
|
|
157
|
+
|
|
158
|
+
lib.routx_graph_delete_node.argtypes = [_Graph_p, c_int64]
|
|
159
|
+
lib.routx_graph_delete_node.restype = bool
|
|
160
|
+
|
|
161
|
+
lib.routx_graph_find_nearest_node.argtypes = [_Graph_p, c_float, c_float]
|
|
162
|
+
lib.routx_graph_find_nearest_node.restype = _Node
|
|
163
|
+
|
|
164
|
+
lib.routx_graph_get_edges.argtypes = [_Graph_p, c_int64, POINTER(POINTER(_Edge))]
|
|
165
|
+
lib.routx_graph_get_edges.restype = c_size_t
|
|
166
|
+
|
|
167
|
+
lib.routx_graph_get_edge.argtypes = [_Graph_p, c_int64, c_int64]
|
|
168
|
+
lib.routx_graph_get_edge.restype = c_float
|
|
169
|
+
|
|
170
|
+
lib.routx_graph_set_edge.argtypes = [_Graph_p, c_int64, _Edge]
|
|
171
|
+
lib.routx_graph_set_edge.restype = c_bool
|
|
172
|
+
|
|
173
|
+
lib.routx_graph_delete_edge.argtypes = [_Graph_p, c_int64, c_int64]
|
|
174
|
+
lib.routx_graph_delete_edge.restype = c_bool
|
|
175
|
+
|
|
176
|
+
lib.routx_graph_add_from_osm_file.argtypes = [_Graph_p, POINTER(_OsmOptions), c_char_p]
|
|
177
|
+
lib.routx_graph_add_from_osm_file.restype = c_bool
|
|
178
|
+
|
|
179
|
+
lib.routx_graph_add_from_osm_memory.argtypes = [
|
|
180
|
+
_Graph_p,
|
|
181
|
+
POINTER(_OsmOptions),
|
|
182
|
+
c_char_p,
|
|
183
|
+
c_size_t,
|
|
184
|
+
]
|
|
185
|
+
lib.routx_graph_add_from_osm_memory.restype = c_bool
|
|
186
|
+
|
|
187
|
+
lib.routx_find_route.argtypes = [_Graph_p, c_int64, c_int64, c_size_t]
|
|
188
|
+
lib.routx_find_route.restype = _RouteResult
|
|
189
|
+
|
|
190
|
+
lib.routx_find_route_without_turn_around.argtypes = [_Graph_p, c_int64, c_int64, c_size_t]
|
|
191
|
+
lib.routx_find_route_without_turn_around.restype = _RouteResult
|
|
192
|
+
|
|
193
|
+
lib.routx_route_result_delete.argtypes = [_RouteResult]
|
|
194
|
+
lib.routx_route_result_delete.restype = None
|
|
195
|
+
|
|
196
|
+
lib.routx_kd_tree_new.argtypes = [_Graph_p]
|
|
197
|
+
lib.routx_kd_tree_new.restype = _KDTree_p
|
|
198
|
+
|
|
199
|
+
lib.routx_kd_tree_delete.argtypes = [_KDTree_p]
|
|
200
|
+
lib.routx_kd_tree_delete.restype = None
|
|
201
|
+
|
|
202
|
+
lib.routx_kd_tree_find_nearest_node.argtypes = [_KDTree_p, c_float, c_float]
|
|
203
|
+
lib.routx_kd_tree_find_nearest_node.restype = _Node
|
|
204
|
+
|
|
205
|
+
lib.routx_earth_distance.argtypes = [c_float, c_float, c_float, c_float]
|
|
206
|
+
lib.routx_earth_distance.restype = c_float
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# Wire up logging
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@_LoggingCallback
|
|
213
|
+
def _builtin_log_handler(_: Any, level: int, target_b: bytes, message_b: bytes) -> None:
|
|
214
|
+
target = target_b.decode("utf-8").replace("::", ".")
|
|
215
|
+
message = message_b.decode("utf-8")
|
|
216
|
+
logging.getLogger(target).log(level, message)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
lib.routx_set_logging_callback(_builtin_log_handler, _LoggingFlushCallback(), None, 10)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
# High-level Python definitions
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
DEFAULT_STEP_LIMIT: Final[int] = 1000000
|
|
226
|
+
"""Recommended A* step limit for Graph.find_route."""
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class StepLimitExceeded(ValueError):
|
|
230
|
+
"""Graph.find_route exceeded its step limit."""
|
|
231
|
+
|
|
232
|
+
pass
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class OsmLoadingError(ValueError):
|
|
236
|
+
"""Raised with the underlying library has failed to load OSM data data. See logs for details."""
|
|
237
|
+
|
|
238
|
+
pass
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class Node(NamedTuple):
|
|
242
|
+
"""
|
|
243
|
+
An element of a Graph.
|
|
244
|
+
|
|
245
|
+
Due to turn restriction processing, one OpenStreetMap node may be represented by
|
|
246
|
+
multiple nodes in the graph. If that is the case, a "canonical" node
|
|
247
|
+
(not bound by any turn restriction) will have `id == osm_id`.
|
|
248
|
+
|
|
249
|
+
Nodes with `id == 0` are used by the underlying library to signify the absence of a node,
|
|
250
|
+
are considered false-y and must not be used by consumers.
|
|
251
|
+
"""
|
|
252
|
+
|
|
253
|
+
id: int
|
|
254
|
+
osm_id: int
|
|
255
|
+
lat: float
|
|
256
|
+
lon: float
|
|
257
|
+
|
|
258
|
+
@property
|
|
259
|
+
def is_canonical(self) -> bool:
|
|
260
|
+
return self.id == self.osm_id
|
|
261
|
+
|
|
262
|
+
def __bool__(self) -> bool:
|
|
263
|
+
return self.id != 0
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class Edge(NamedTuple):
|
|
267
|
+
"""
|
|
268
|
+
Outgoing (one-way) connection from a Node.
|
|
269
|
+
|
|
270
|
+
`cost` must be greater than the crow-flies distance between the two nodes.
|
|
271
|
+
"""
|
|
272
|
+
|
|
273
|
+
to: int
|
|
274
|
+
cost: float
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class OsmPenalty(NamedTuple):
|
|
278
|
+
"""Numeric multiplier for OSM ways with specific keys and values."""
|
|
279
|
+
|
|
280
|
+
key: str
|
|
281
|
+
"""
|
|
282
|
+
Key of an OSM way for which this penalty applies,
|
|
283
|
+
used for `value` comparison (e.g. "highway" or "railway").
|
|
284
|
+
"""
|
|
285
|
+
|
|
286
|
+
value: str
|
|
287
|
+
"""
|
|
288
|
+
Value under `key` of an OSM way for which this penalty applies.
|
|
289
|
+
E.g. "motorway", "residential", or "rail".
|
|
290
|
+
"""
|
|
291
|
+
|
|
292
|
+
penalty: float
|
|
293
|
+
"""
|
|
294
|
+
Multiplier of the length, to express preference for a specific way.
|
|
295
|
+
Must be not less than one and be finite.
|
|
296
|
+
"""
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
class OsmProfile(IntEnum):
|
|
300
|
+
"""Predefined OSM conversion profiles."""
|
|
301
|
+
|
|
302
|
+
CAR = 1
|
|
303
|
+
"""
|
|
304
|
+
Car routing profile.
|
|
305
|
+
|
|
306
|
+
Penalties:
|
|
307
|
+
| Tag | Penalty |
|
|
308
|
+
|------------------------|---------|
|
|
309
|
+
| highway=motorway | 1.0 |
|
|
310
|
+
| highway=motorway_link | 1.0 |
|
|
311
|
+
| highway=trunk | 2.0 |
|
|
312
|
+
| highway=trunk_link | 2.0 |
|
|
313
|
+
| highway=primary | 5.0 |
|
|
314
|
+
| highway=primary_link | 5.0 |
|
|
315
|
+
| highway=secondary | 6.5 |
|
|
316
|
+
| highway=secondary_link | 6.5 |
|
|
317
|
+
| highway=tertiary | 10.0 |
|
|
318
|
+
| highway=tertiary_link | 10.0 |
|
|
319
|
+
| highway=unclassified | 10.0 |
|
|
320
|
+
| highway=minor | 10.0 |
|
|
321
|
+
| highway=residential | 15.0 |
|
|
322
|
+
| highway=living_street | 20.0 |
|
|
323
|
+
| highway=track | 20.0 |
|
|
324
|
+
| highway=service | 20.0 |
|
|
325
|
+
|
|
326
|
+
Access tags: `access`, `vehicle`, `motor_vehicle`, `motorcar`.
|
|
327
|
+
|
|
328
|
+
Allows [motorroads](https://wiki.openstreetmap.org/wiki/Key:motorroad) and considers turn restrictions.
|
|
329
|
+
"""
|
|
330
|
+
|
|
331
|
+
BUS = 2
|
|
332
|
+
"""
|
|
333
|
+
Bus routing profile.
|
|
334
|
+
|
|
335
|
+
Penalties:
|
|
336
|
+
| Tag | Penalty |
|
|
337
|
+
|------------------------|---------|
|
|
338
|
+
| highway=motorway | 1.0 |
|
|
339
|
+
| highway=motorway_link | 1.0 |
|
|
340
|
+
| highway=trunk | 1.0 |
|
|
341
|
+
| highway=trunk_link | 1.0 |
|
|
342
|
+
| highway=primary | 1.1 |
|
|
343
|
+
| highway=primary_link | 1.1 |
|
|
344
|
+
| highway=secondary | 1.15 |
|
|
345
|
+
| highway=secondary_link | 1.15 |
|
|
346
|
+
| highway=tertiary | 1.15 |
|
|
347
|
+
| highway=tertiary_link | 1.15 |
|
|
348
|
+
| highway=unclassified | 1.5 |
|
|
349
|
+
| highway=minor | 1.5 |
|
|
350
|
+
| highway=residential | 2.5 |
|
|
351
|
+
| highway=living_street | 2.5 |
|
|
352
|
+
| highway=track | 5.0 |
|
|
353
|
+
| highway=service | 5.0 |
|
|
354
|
+
|
|
355
|
+
Access tags: `access`, `vehicle`, `motor_vehicle`, `psv`, `bus`, `routing:ztm`.
|
|
356
|
+
|
|
357
|
+
Allows [motorroads](https://wiki.openstreetmap.org/wiki/Key:motorroad) and considers turn restrictions.
|
|
358
|
+
"""
|
|
359
|
+
|
|
360
|
+
BICYCLE = 3
|
|
361
|
+
"""
|
|
362
|
+
Bicycle routing profile.
|
|
363
|
+
|
|
364
|
+
Penalties:
|
|
365
|
+
| Tag | Penalty |
|
|
366
|
+
|------------------------|---------|
|
|
367
|
+
| highway=trunk | 50.0 |
|
|
368
|
+
| highway=trunk_link | 50.0 |
|
|
369
|
+
| highway=primary | 10.0 |
|
|
370
|
+
| highway=primary_link | 10.0 |
|
|
371
|
+
| highway=secondary | 3.0 |
|
|
372
|
+
| highway=secondary_link | 3.0 |
|
|
373
|
+
| highway=tertiary | 2.5 |
|
|
374
|
+
| highway=tertiary_link | 2.5 |
|
|
375
|
+
| highway=unclassified | 2.5 |
|
|
376
|
+
| highway=minor | 2.5 |
|
|
377
|
+
| highway=cycleway | 1.0 |
|
|
378
|
+
| highway=residential | 1.0 |
|
|
379
|
+
| highway=living_street | 1.5 |
|
|
380
|
+
| highway=track | 2.0 |
|
|
381
|
+
| highway=service | 2.0 |
|
|
382
|
+
| highway=bridleway | 3.0 |
|
|
383
|
+
| highway=footway | 3.0 |
|
|
384
|
+
| highway=steps | 5.0 |
|
|
385
|
+
| highway=path | 2.0 |
|
|
386
|
+
|
|
387
|
+
Access tags: `access`, `vehicle`, `bicycle`.
|
|
388
|
+
|
|
389
|
+
Disallows [motorroads](https://wiki.openstreetmap.org/wiki/Key:motorroad) and considers turn restrictions.
|
|
390
|
+
"""
|
|
391
|
+
|
|
392
|
+
FOOT = 4
|
|
393
|
+
"""
|
|
394
|
+
Pedestrian routing profile.
|
|
395
|
+
|
|
396
|
+
Penalties:
|
|
397
|
+
| Tag | Penalty |
|
|
398
|
+
|---------------------------|---------|
|
|
399
|
+
| highway=trunk | 4.0 |
|
|
400
|
+
| highway=trunk_link | 4.0 |
|
|
401
|
+
| highway=primary | 2.0 |
|
|
402
|
+
| highway=primary_link | 2.0 |
|
|
403
|
+
| highway=secondary | 1.3 |
|
|
404
|
+
| highway=secondary_link | 1.3 |
|
|
405
|
+
| highway=tertiary | 1.2 |
|
|
406
|
+
| highway=tertiary_link | 1.2 |
|
|
407
|
+
| highway=unclassified | 1.2 |
|
|
408
|
+
| highway=minor | 1.2 |
|
|
409
|
+
| highway=residential | 1.2 |
|
|
410
|
+
| highway=living_street | 1.2 |
|
|
411
|
+
| highway=track | 1.2 |
|
|
412
|
+
| highway=service | 1.2 |
|
|
413
|
+
| highway=bridleway | 1.2 |
|
|
414
|
+
| highway=footway | 1.05 |
|
|
415
|
+
| highway=path | 1.05 |
|
|
416
|
+
| highway=steps | 1.15 |
|
|
417
|
+
| highway=pedestrian | 1.0 |
|
|
418
|
+
| highway=platform | 1.1 |
|
|
419
|
+
| railway=platform | 1.1 |
|
|
420
|
+
| public_transport=platform | 1.1 |
|
|
421
|
+
|
|
422
|
+
Access tags: `access`, `foot`.
|
|
423
|
+
|
|
424
|
+
Disallows [motorroads](https://wiki.openstreetmap.org/wiki/Key:motorroad).
|
|
425
|
+
|
|
426
|
+
One-way is only considered when explicitly tagged with `oneway:foot` or on
|
|
427
|
+
`highway=footway`, `highway=path`, `highway=steps`, `highway/public_transport/railway=platform`.
|
|
428
|
+
|
|
429
|
+
Turn restrictions are only considered when explicitly tagged with `restriction:foot`.
|
|
430
|
+
"""
|
|
431
|
+
|
|
432
|
+
RAILWAY = 5
|
|
433
|
+
"""
|
|
434
|
+
Railway routing profile.
|
|
435
|
+
|
|
436
|
+
Penalties:
|
|
437
|
+
| Tag | Penalty |
|
|
438
|
+
|----------------------|---------|
|
|
439
|
+
| railway=rail | 1.0 |
|
|
440
|
+
| railway=light_rail | 1.0 |
|
|
441
|
+
| railway=subway | 1.0 |
|
|
442
|
+
| railway=narrow_gauge | 1.0 |
|
|
443
|
+
|
|
444
|
+
Access tags: `access`, `train`.
|
|
445
|
+
|
|
446
|
+
Allows [motorroads](https://wiki.openstreetmap.org/wiki/Key:motorroad) and considers turn restrictions.
|
|
447
|
+
"""
|
|
448
|
+
|
|
449
|
+
TRAM = 6
|
|
450
|
+
"""
|
|
451
|
+
Tram and light rail routing profile.
|
|
452
|
+
|
|
453
|
+
Penalties:
|
|
454
|
+
| Tag | Penalty |
|
|
455
|
+
|----------------------|---------|
|
|
456
|
+
| railway=tram | 1.0 |
|
|
457
|
+
| railway=light_rail | 1.0 |
|
|
458
|
+
|
|
459
|
+
Access tags: `access`, `tram`.
|
|
460
|
+
|
|
461
|
+
Allows [motorroads](https://wiki.openstreetmap.org/wiki/Key:motorroad) and considers turn restrictions.
|
|
462
|
+
"""
|
|
463
|
+
|
|
464
|
+
SUBWAY = 7
|
|
465
|
+
"""
|
|
466
|
+
Subway routing profile.
|
|
467
|
+
|
|
468
|
+
Penalties:
|
|
469
|
+
| Tag | Penalty |
|
|
470
|
+
|----------------|---------|
|
|
471
|
+
| railway=subway | 1.0 |
|
|
472
|
+
|
|
473
|
+
Access tags: `access`, `subway`.
|
|
474
|
+
|
|
475
|
+
Allows [motorroads](https://wiki.openstreetmap.org/wiki/Key:motorroad) and considers turn restrictions.
|
|
476
|
+
"""
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
@dataclass
|
|
480
|
+
class OsmCustomProfile:
|
|
481
|
+
"""
|
|
482
|
+
Describes how to convert OSM data into a Graph.
|
|
483
|
+
|
|
484
|
+
If possible, usage of pre-defined OsmProfiles should be preferred.
|
|
485
|
+
Using custom profile involves reallocation of all arrays and strings
|
|
486
|
+
two times to match ABIs (first from Python to C, then from C to Rust).
|
|
487
|
+
This is only a constant cost incurred on call to Graph.add_from_file or Graph.add_from_memory.
|
|
488
|
+
"""
|
|
489
|
+
|
|
490
|
+
name: str
|
|
491
|
+
"""Human readable name of the routing profile,
|
|
492
|
+
customary the most specific [access tag](https://wiki.openstreetmap.org/wiki/Key:access).
|
|
493
|
+
|
|
494
|
+
This value is not used for actual OSM data interpretation,
|
|
495
|
+
except when set to "foot", which adds the following logic:
|
|
496
|
+
- `oneway` tags are ignored - only `oneway:foot` tags are considered, except on:
|
|
497
|
+
- `highway=footway`,
|
|
498
|
+
- `highway=path`,
|
|
499
|
+
- `highway=steps`,
|
|
500
|
+
- `highway=platform`
|
|
501
|
+
- `public_transport=platform`,
|
|
502
|
+
- `railway=platform`;
|
|
503
|
+
- only `restriction:foot` turn restrictions are considered.
|
|
504
|
+
"""
|
|
505
|
+
|
|
506
|
+
penalties: list[OsmPenalty]
|
|
507
|
+
"""
|
|
508
|
+
Tags of OSM ways which can be used for routing.
|
|
509
|
+
|
|
510
|
+
A way is matched against all OsmPenalty objects in order, and once an exact key and value match
|
|
511
|
+
is found; the way is used for routing, and each connection between two nodes gets
|
|
512
|
+
a resulting cost equal to the distance between nodes multiplied the penalty.
|
|
513
|
+
|
|
514
|
+
All penalties must be normal and not less than zero.
|
|
515
|
+
|
|
516
|
+
For example, if there are two penalties:
|
|
517
|
+
1. highway=motorway, penalty=1
|
|
518
|
+
2. highway=trunk, penalty=1.5
|
|
519
|
+
|
|
520
|
+
This will result in:
|
|
521
|
+
- a highway=motorway stretch of 100 meters will be used for routing with a cost of 100.
|
|
522
|
+
- a highway=trunk motorway of 100 meters will be used for routing with a cost of 150.
|
|
523
|
+
- a highway=motorway_link or highway=primary won't be used for routing, as they do not any penalty.
|
|
524
|
+
"""
|
|
525
|
+
|
|
526
|
+
access: list[str]
|
|
527
|
+
"""
|
|
528
|
+
List of OSM [access tags](https://wiki.openstreetmap.org/wiki/Key:access#Land-based_transportation)
|
|
529
|
+
(in order from least to most specific) to consider when checking for road prohibitions.
|
|
530
|
+
|
|
531
|
+
This list is used mainly to follow the access tags, but also to follow mode-specific one-way
|
|
532
|
+
and turn restrictions.
|
|
533
|
+
"""
|
|
534
|
+
|
|
535
|
+
disallow_motorroad: bool = False
|
|
536
|
+
"""Force no routing over [motorroad=yes](https://wiki.openstreetmap.org/wiki/Key:motorroad) ways."""
|
|
537
|
+
|
|
538
|
+
disable_restrictions: bool = False
|
|
539
|
+
"""Force ignoring of [turn restrictions](https://wiki.openstreetmap.org/wiki/Turn_restriction)."""
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
class OsmFormat(IntEnum):
|
|
543
|
+
"""Format of the input OSM file."""
|
|
544
|
+
|
|
545
|
+
UNKNOWN = 0
|
|
546
|
+
"""Unknown format - guess based on content."""
|
|
547
|
+
|
|
548
|
+
XML = 1
|
|
549
|
+
"""Force uncompressed [OSM XML](https://wiki.openstreetmap.org/wiki/OSM_XML)"""
|
|
550
|
+
|
|
551
|
+
XML_GZ = 2
|
|
552
|
+
"""
|
|
553
|
+
Force [OSM XML](https://wiki.openstreetmap.org/wiki/OSM_XML)
|
|
554
|
+
with [gzip](https://en.wikipedia.org/wiki/Gzip) compression
|
|
555
|
+
"""
|
|
556
|
+
|
|
557
|
+
XML_BZ2 = 3
|
|
558
|
+
"""
|
|
559
|
+
Force [OSM XML](https://wiki.openstreetmap.org/wiki/OSM_XML)
|
|
560
|
+
with [bzip2](https://en.wikipedia.org/wiki/Bzip2) compression
|
|
561
|
+
"""
|
|
562
|
+
|
|
563
|
+
PBF = 4
|
|
564
|
+
"""Force [OSM PBF](https://wiki.openstreetmap.org/wiki/PBF_Format)"""
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
class Graph(MutableMapping[int, Node]):
|
|
568
|
+
"""
|
|
569
|
+
OpenStreetMap-based network representation as a set of nodes and edges between them.
|
|
570
|
+
|
|
571
|
+
Node access is implemented through the standard
|
|
572
|
+
[MutableMapping interface](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)
|
|
573
|
+
from ids (integers) to nodes.
|
|
574
|
+
|
|
575
|
+
Note that overwriting existing nodes preserves all outgoing and incoming edges. Thus updating
|
|
576
|
+
a node position might result in violation of the Edge invariant and break route finding.
|
|
577
|
+
It **is discouraged** to update nodes, and it is the caller's responsibility not to break
|
|
578
|
+
this invariant.
|
|
579
|
+
|
|
580
|
+
Edge access is implemented through custom get_edges, get_edge, set_edge and delete_edge methods.
|
|
581
|
+
"""
|
|
582
|
+
|
|
583
|
+
handle: _Graph_p
|
|
584
|
+
|
|
585
|
+
def __init__(self) -> None:
|
|
586
|
+
self.handle = lib.routx_graph_new()
|
|
587
|
+
|
|
588
|
+
def __del__(self) -> None:
|
|
589
|
+
lib.routx_graph_delete(self.handle)
|
|
590
|
+
|
|
591
|
+
def __getitem__(self, key: int) -> Node:
|
|
592
|
+
n = _node_from_c(lib.routx_graph_get_node(self.handle, key))
|
|
593
|
+
if n.id == 0:
|
|
594
|
+
raise KeyError(key)
|
|
595
|
+
return n
|
|
596
|
+
|
|
597
|
+
def __setitem__(self, key: int, value: Node) -> None:
|
|
598
|
+
if key != value.id:
|
|
599
|
+
raise ValueError(f"attempt to save node with id {value.id} under different id, {key}")
|
|
600
|
+
lib.routx_graph_set_node(self.handle, _node_to_c(value))
|
|
601
|
+
|
|
602
|
+
def __delitem__(self, key: int) -> None:
|
|
603
|
+
deleted = lib.routx_graph_delete_node(self.handle, key)
|
|
604
|
+
if not deleted:
|
|
605
|
+
raise KeyError(key)
|
|
606
|
+
|
|
607
|
+
def __iter__(self) -> Iterator[int]:
|
|
608
|
+
it_handle = _GraphIterator_p()
|
|
609
|
+
try:
|
|
610
|
+
lib.routx_graph_get_nodes(self.handle, byref(it_handle))
|
|
611
|
+
while n := _node_from_c(lib.routx_graph_iterator_next(it_handle)):
|
|
612
|
+
yield n.id
|
|
613
|
+
finally:
|
|
614
|
+
lib.routx_graph_iterator_delete(it_handle)
|
|
615
|
+
|
|
616
|
+
def __len__(self) -> int:
|
|
617
|
+
return lib.routx_graph_get_nodes(self.handle, None)
|
|
618
|
+
|
|
619
|
+
def find_nearest_node(self, lat: float, lon: float) -> Node:
|
|
620
|
+
"""
|
|
621
|
+
Find the closest canonical (`id == osm_id`) Node to the given position.
|
|
622
|
+
|
|
623
|
+
This function requires computing distance to every Node in the graph
|
|
624
|
+
and is not suitable for large graphs or for multiple searches.
|
|
625
|
+
Use KDTree for faster NN finding.
|
|
626
|
+
|
|
627
|
+
If the graph is empty, raises KeyError.
|
|
628
|
+
"""
|
|
629
|
+
n = _node_from_c(lib.routx_graph_find_nearest_node(self.handle, lat, lon))
|
|
630
|
+
if not n:
|
|
631
|
+
raise KeyError("find_nearest_node on empty Graph")
|
|
632
|
+
return n
|
|
633
|
+
|
|
634
|
+
def get_edges(self, from_: int) -> list[Edge]:
|
|
635
|
+
"""Gets all outgoing edges from a node with a given id."""
|
|
636
|
+
c_ptr = POINTER(_Edge)()
|
|
637
|
+
c_ptr_len = lib.routx_graph_get_edges(self.handle, from_, byref(c_ptr))
|
|
638
|
+
return [_edge_from_c(c_ptr[i]) for i in range(c_ptr_len)]
|
|
639
|
+
|
|
640
|
+
def get_edge(self, from_: int, to: int) -> float:
|
|
641
|
+
"""
|
|
642
|
+
Gets the cost of traversing an edge between nodes with provided ids.
|
|
643
|
+
Returns positive infinity when the provided edge does not exist.
|
|
644
|
+
"""
|
|
645
|
+
return lib.routx_graph_get_edge(self.handle, from_, to)
|
|
646
|
+
|
|
647
|
+
def set_edge(self, from_: int, to: int, cost: float) -> bool:
|
|
648
|
+
"""
|
|
649
|
+
Creates or updates an Edge from one node to another.
|
|
650
|
+
|
|
651
|
+
The `cost` must not be smaller than the crow-flies distance between nodes,
|
|
652
|
+
as this would violate the A* invariant and break route finding. It is the
|
|
653
|
+
caller's responsibility to uphold this invariant.
|
|
654
|
+
|
|
655
|
+
Returns True if an existing edge was overwritten, False otherwise.
|
|
656
|
+
|
|
657
|
+
Note that given an `Edge` object, this method may be called with `g.set_edge(from_, *edge)`.
|
|
658
|
+
"""
|
|
659
|
+
return lib.routx_graph_set_edge(self.handle, from_, _Edge(to=to, cost=cost))
|
|
660
|
+
|
|
661
|
+
def delete_edge(self, from_: int, to: int, /, missing_ok: bool = True) -> None:
|
|
662
|
+
"""
|
|
663
|
+
Ensures an Edge from one node to another does not exist.
|
|
664
|
+
|
|
665
|
+
If no such edge exists and `missing_ok` is set to `False`, raises KeyError.
|
|
666
|
+
"""
|
|
667
|
+
removed = lib.routx_graph_delete_edge(self.handle, from_, to)
|
|
668
|
+
if not removed and not missing_ok:
|
|
669
|
+
raise KeyError((from_, to))
|
|
670
|
+
|
|
671
|
+
def find_route(
|
|
672
|
+
self,
|
|
673
|
+
from_: int,
|
|
674
|
+
to: int,
|
|
675
|
+
/,
|
|
676
|
+
without_turn_around: bool = True,
|
|
677
|
+
step_limit: int = DEFAULT_STEP_LIMIT,
|
|
678
|
+
) -> list[int]:
|
|
679
|
+
"""
|
|
680
|
+
Finds the cheapest way between two nodes using the [A* algorithm](https://en.wikipedia.org/wiki/A*_search_algorithm).
|
|
681
|
+
Returns a list node IDs of such route. The list may be empty if no route exists.
|
|
682
|
+
|
|
683
|
+
`without_turn_around` defaults to `True` and prevents the algorithm from circumventing
|
|
684
|
+
turn restrictions by suppressing unrealistic turn-around instructions (A-B-A).
|
|
685
|
+
This introduces an extra dimension to the search space, so if the graph doesn't contain
|
|
686
|
+
any turn restriction, this parameter should be set to `False`.
|
|
687
|
+
|
|
688
|
+
`step_limit` limits how many nodes can be expanded during search before raising StepLimitExceeded.
|
|
689
|
+
Concluding that no route exists requires expanding all nodes accessible from the start,
|
|
690
|
+
which is usually very time consuming, especially on large datasets.
|
|
691
|
+
"""
|
|
692
|
+
func = (
|
|
693
|
+
lib.routx_find_route_without_turn_around
|
|
694
|
+
if without_turn_around
|
|
695
|
+
else lib.routx_find_route
|
|
696
|
+
)
|
|
697
|
+
res = func(self.handle, from_, to, step_limit)
|
|
698
|
+
try:
|
|
699
|
+
if res.type == 0:
|
|
700
|
+
# TODO: Could we return a memoryview with type "q"?
|
|
701
|
+
return [res.as_ok.nodes[i] for i in range(res.as_ok.len)]
|
|
702
|
+
elif res.type == 1:
|
|
703
|
+
raise KeyError(res.as_invalid_reference.invalid_node_id)
|
|
704
|
+
elif res.type == 2:
|
|
705
|
+
raise StepLimitExceeded()
|
|
706
|
+
else:
|
|
707
|
+
raise RuntimeError(f"routx_find_route returned unexpected result type: {res.type}")
|
|
708
|
+
finally:
|
|
709
|
+
lib.routx_route_result_delete(res)
|
|
710
|
+
|
|
711
|
+
def add_from_osm_file(
|
|
712
|
+
self,
|
|
713
|
+
filename: str | bytes | PathLike[str] | PathLike[bytes],
|
|
714
|
+
profile: OsmProfile | OsmCustomProfile,
|
|
715
|
+
/,
|
|
716
|
+
format: OsmFormat = OsmFormat.UNKNOWN,
|
|
717
|
+
bbox: tuple[float, float, float, float] = (0.0, 0.0, 0.0, 0.0),
|
|
718
|
+
) -> None:
|
|
719
|
+
"""
|
|
720
|
+
Parses OSM data from the provided file and adds them to this graph.
|
|
721
|
+
|
|
722
|
+
`profile` describes how the OSM data should be interpreted.
|
|
723
|
+
|
|
724
|
+
`format` describes the file format of the input OSM data. Defaults to auto-detection.
|
|
725
|
+
|
|
726
|
+
`bbox` filters features by a specific bounding box, in order:
|
|
727
|
+
left (min lon), bottom (min lat), right (max lon), top (max lat).
|
|
728
|
+
Ignored if all values are zero (default).
|
|
729
|
+
"""
|
|
730
|
+
if isinstance(filename, PathLike):
|
|
731
|
+
filename = filename.__fspath__()
|
|
732
|
+
if isinstance(filename, str):
|
|
733
|
+
filename = filename.encode("utf-8")
|
|
734
|
+
|
|
735
|
+
ok = lib.routx_graph_add_from_osm_file(
|
|
736
|
+
self.handle,
|
|
737
|
+
_osm_options_to_c(profile, format, bbox),
|
|
738
|
+
filename,
|
|
739
|
+
)
|
|
740
|
+
if not ok:
|
|
741
|
+
raise OsmLoadingError()
|
|
742
|
+
|
|
743
|
+
def add_from_osm_memory(
|
|
744
|
+
self,
|
|
745
|
+
mv: memoryview,
|
|
746
|
+
profile: OsmProfile | OsmCustomProfile,
|
|
747
|
+
/,
|
|
748
|
+
format: OsmFormat = OsmFormat.UNKNOWN,
|
|
749
|
+
bbox: tuple[float, float, float, float] = (0.0, 0.0, 0.0, 0.0),
|
|
750
|
+
) -> None:
|
|
751
|
+
"""
|
|
752
|
+
Parses OSM data from the provided contents of a memory file.
|
|
753
|
+
The buffer must be contiguous and also mutable (for reasons only known to
|
|
754
|
+
[ctypes](https://docs.python.org/3/library/ctypes.html#ctypes._CData.from_buffer),
|
|
755
|
+
because the underlying library takes a const pointer).
|
|
756
|
+
|
|
757
|
+
`profile` describes how the OSM data should be interpreted.
|
|
758
|
+
|
|
759
|
+
`format` describes the file format of the input OSM data. Defaults to auto-detection.
|
|
760
|
+
|
|
761
|
+
`bbox` filters features by a specific bounding box, in order:
|
|
762
|
+
left (min lon), bottom (min lat), right (max lon), top (max lat).
|
|
763
|
+
Ignored if all values are zero (default).
|
|
764
|
+
"""
|
|
765
|
+
mv = mv.cast("B")
|
|
766
|
+
ptr = (c_char * len(mv)).from_buffer(mv)
|
|
767
|
+
ok = lib.routx_graph_add_from_osm_memory(
|
|
768
|
+
self.handle,
|
|
769
|
+
_osm_options_to_c(profile, format, bbox),
|
|
770
|
+
ptr,
|
|
771
|
+
len(mv),
|
|
772
|
+
)
|
|
773
|
+
if not ok:
|
|
774
|
+
raise OsmLoadingError()
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
class KDTree:
|
|
778
|
+
"""
|
|
779
|
+
[k-d tree data structure](https://en.wikipedia.org/wiki/K-d_tree) which can be used to
|
|
780
|
+
speed up nearest-neighbor search for large datasets.
|
|
781
|
+
|
|
782
|
+
Create with `KDTree.build`, as the constructors takes a raw C handler.
|
|
783
|
+
"""
|
|
784
|
+
|
|
785
|
+
_handle: _KDTree_p
|
|
786
|
+
|
|
787
|
+
def __init__(self, handle: _KDTree_p) -> None:
|
|
788
|
+
self._handle = handle
|
|
789
|
+
|
|
790
|
+
def __del__(self) -> None:
|
|
791
|
+
lib.routx_kd_tree_delete(self._handle)
|
|
792
|
+
|
|
793
|
+
@classmethod
|
|
794
|
+
def build(cls, graph: Graph) -> Self:
|
|
795
|
+
"""
|
|
796
|
+
Builds a k-d tree with all canonical (`id == osm_id`) nodes contained in the provided graph.
|
|
797
|
+
"""
|
|
798
|
+
return cls(lib.routx_kd_tree_new(graph.handle))
|
|
799
|
+
|
|
800
|
+
def find_nearest_node(self, lat: float, lon: float) -> Node:
|
|
801
|
+
"""
|
|
802
|
+
Find the closest node to the provided position and returns its id.
|
|
803
|
+
|
|
804
|
+
Raises KeyError when the k-d tree contains no nodes.
|
|
805
|
+
"""
|
|
806
|
+
nd = _node_from_c(lib.routx_kd_tree_find_nearest_node(self._handle, lat, lon))
|
|
807
|
+
if nd.id == 0:
|
|
808
|
+
raise KeyError("find_nearest_node on empty KDTree")
|
|
809
|
+
return nd
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
def earth_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
|
|
813
|
+
"""
|
|
814
|
+
Calculates the great-circle distance between two positions using the
|
|
815
|
+
[haversine formula](https://en.wikipedia.org/wiki/Haversine_formula).
|
|
816
|
+
|
|
817
|
+
Returns the result in kilometers.
|
|
818
|
+
"""
|
|
819
|
+
return lib.routx_earth_distance(lat1, lon1, lat2, lon2)
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
def _node_to_c(o: Node) -> _Node:
|
|
823
|
+
return _Node(id=o.id, osm_id=o.osm_id, lat=o.lat, lon=o.lon)
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
def _node_from_c(o: _Node) -> Node:
|
|
827
|
+
return Node(id=o.id, osm_id=o.osm_id, lat=o.lat, lon=o.lon)
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
def _edge_from_c(o: _Edge) -> Edge:
|
|
831
|
+
return Edge(to=o.to, cost=o.cost)
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
def _osm_profile_to_c(profile: OsmProfile | OsmCustomProfile):
|
|
835
|
+
if isinstance(profile, OsmProfile):
|
|
836
|
+
return c_cast(c_void_p(profile.value), POINTER(_OsmProfile))
|
|
837
|
+
|
|
838
|
+
c_profile = _OsmProfile()
|
|
839
|
+
c_profile.name = profile.name.encode("utf-8")
|
|
840
|
+
|
|
841
|
+
c_profile.penalties = (_OsmProfilePenalty * len(profile.penalties))()
|
|
842
|
+
for i, (key, value, penalty) in enumerate(profile.penalties):
|
|
843
|
+
c_profile.penalties[i] = _OsmProfilePenalty(
|
|
844
|
+
key=key.encode("utf-8"),
|
|
845
|
+
value=value.encode("utf-8"),
|
|
846
|
+
penalty=penalty,
|
|
847
|
+
)
|
|
848
|
+
c_profile.penalties_len = len(profile.penalties)
|
|
849
|
+
|
|
850
|
+
c_profile.access = (c_char_p * len(profile.access))()
|
|
851
|
+
for i, access_key in enumerate(profile.access):
|
|
852
|
+
c_profile.access[i] = access_key.encode("utf-8")
|
|
853
|
+
c_profile.access_len = len(profile.access)
|
|
854
|
+
|
|
855
|
+
c_profile.disallow_motorroad = profile.disallow_motorroad
|
|
856
|
+
c_profile.disable_restrictions = profile.disable_restrictions
|
|
857
|
+
|
|
858
|
+
return pointer(c_profile)
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
def _osm_options_to_c(
|
|
862
|
+
profile: OsmProfile | OsmCustomProfile,
|
|
863
|
+
format: OsmFormat,
|
|
864
|
+
bbox: tuple[float, float, float, float],
|
|
865
|
+
):
|
|
866
|
+
o = _OsmOptions()
|
|
867
|
+
o.profile = _osm_profile_to_c(profile)
|
|
868
|
+
o.file_format = format.value
|
|
869
|
+
o.bbox[0] = bbox[0]
|
|
870
|
+
o.bbox[1] = bbox[1]
|
|
871
|
+
o.bbox[2] = bbox[2]
|
|
872
|
+
o.bbox[3] = bbox[3]
|
|
873
|
+
return pointer(o)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
routx-1.0.2.dist-info/METADATA,sha256=1fabSUghlXWXiFm1_i36-cVyW1AIRv2dPJ9qBUlGDMU,187
|
|
2
|
+
routx-1.0.2.dist-info/WHEEL,sha256=eGbHOVnVkNk5N_A188Q13-gtiwyWWQjeSFRh9oxazXU,84
|
|
3
|
+
.routx.mesonpy.libs/libroutx.dll,sha256=EwUPrw8mi1WyOo8IsehAVAIE2ovuj8KIagIjVgIwToY,740864
|
|
4
|
+
routx/__init__.py,sha256=shuZ01RoaXzL9TLKS6samxJ_1Maz_NszA5B1WtZM8oo,854
|
|
5
|
+
routx/wrapper.py,sha256=nDACNt37uHsamKlalKaCcDvDQGzhitFB_71NbiKtYJA,28068
|
|
6
|
+
routx-1.0.2.dist-info/RECORD,,
|