r5py 0.1.1.dev2__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of r5py might be problematic. Click here for more details.
- r5py/__init__.py +8 -1
- r5py/__main__.py +1 -14
- r5py/r5/__init__.py +20 -2
- r5py/r5/{base_travel_time_matrix_computer.py → base_travel_time_matrix.py} +28 -8
- r5py/r5/{detailed_itineraries_computer.py → detailed_itineraries.py} +82 -20
- r5py/r5/direct_leg.py +1 -3
- r5py/r5/isochrones.py +351 -0
- r5py/r5/regional_task.py +12 -9
- r5py/r5/street_layer.py +8 -3
- r5py/r5/street_segment.py +41 -0
- r5py/r5/transfer_leg.py +2 -6
- r5py/r5/transit_layer.py +6 -0
- r5py/r5/transit_leg.py +1 -5
- r5py/r5/transport_mode.py +5 -3
- r5py/r5/transport_network.py +60 -138
- r5py/r5/travel_time_matrix.py +209 -0
- r5py/r5/trip.py +13 -8
- r5py/r5/trip_leg.py +76 -15
- r5py/r5/trip_planner.py +109 -54
- r5py/util/__init__.py +8 -0
- r5py/util/classpath.py +9 -5
- r5py/util/config.py +32 -7
- r5py/util/environment.py +34 -0
- r5py/util/file_digest.py +42 -0
- r5py/util/good_enough_equidistant_crs.py +8 -4
- r5py/util/memory_footprint.py +3 -5
- r5py/util/sample_data_set.py +17 -6
- r5py/util/spatially_clustered_geodataframe.py +78 -0
- r5py/util/validating_requests_session.py +2 -2
- r5py/util/working_copy.py +44 -0
- {r5py-0.1.1.dev2.dist-info → r5py-1.0.0.dist-info}/METADATA +34 -33
- r5py-1.0.0.dist-info/RECORD +47 -0
- {r5py-0.1.1.dev2.dist-info → r5py-1.0.0.dist-info}/WHEEL +1 -1
- r5py/r5/travel_time_matrix_computer.py +0 -134
- r5py/sampledata/_keep/__init__.py +0 -3
- r5py-0.1.1.dev2.dist-info/RECORD +0 -42
- {r5py-0.1.1.dev2.dist-info → r5py-1.0.0.dist-info}/LICENSE +0 -0
- {r5py-0.1.1.dev2.dist-info → r5py-1.0.0.dist-info}/top_level.txt +0 -0
r5py/r5/trip_leg.py
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
"""Represent one leg of a trip."""
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
import datetime
|
|
8
|
+
import numpy
|
|
9
|
+
import shapely
|
|
10
|
+
|
|
11
|
+
|
|
7
12
|
__all__ = ["TripLeg"]
|
|
8
13
|
|
|
9
14
|
|
|
@@ -21,39 +26,56 @@ class TripLeg:
|
|
|
21
26
|
"distance",
|
|
22
27
|
"travel_time",
|
|
23
28
|
"wait_time",
|
|
24
|
-
"
|
|
29
|
+
"feed",
|
|
30
|
+
"agency_id",
|
|
31
|
+
"route_id",
|
|
32
|
+
"start_stop_id",
|
|
33
|
+
"end_stop_id",
|
|
25
34
|
"geometry",
|
|
26
35
|
]
|
|
27
36
|
|
|
28
37
|
def __init__(
|
|
29
38
|
self,
|
|
30
39
|
transport_mode=None,
|
|
31
|
-
departure_time=
|
|
40
|
+
departure_time=numpy.datetime64("NaT"),
|
|
32
41
|
distance=None,
|
|
33
|
-
travel_time=
|
|
34
|
-
wait_time=
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
travel_time=datetime.timedelta(seconds=0),
|
|
43
|
+
wait_time=datetime.timedelta(seconds=0),
|
|
44
|
+
feed=None,
|
|
45
|
+
agency_id=None,
|
|
46
|
+
route_id=None,
|
|
47
|
+
start_stop_id=None,
|
|
48
|
+
end_stop_id=None,
|
|
49
|
+
geometry=shapely.LineString(),
|
|
37
50
|
):
|
|
38
51
|
"""
|
|
39
52
|
Represent one leg of a trip.
|
|
40
53
|
|
|
41
|
-
This is a base class, use one the specific classes,
|
|
42
|
-
|
|
54
|
+
This is a base class, use one of the more specific classes, e.g.,
|
|
55
|
+
TransitLeg, or DirectLeg
|
|
43
56
|
|
|
44
57
|
Arguments
|
|
45
58
|
=========
|
|
46
59
|
transport_mode : r5py.TransportMode
|
|
47
60
|
mode of transport this trip leg was travelled
|
|
48
|
-
departure_time : datetime.datetime
|
|
61
|
+
departure_time : datetime.datetime
|
|
62
|
+
departure time of this trip leg
|
|
49
63
|
distance : float
|
|
50
64
|
distance covered by this trip leg, in metres
|
|
51
65
|
travel_time : datetime.timedelta
|
|
52
66
|
time spent travelling on this trip leg
|
|
53
67
|
wait_time : datetime.timedelta
|
|
54
68
|
time spent waiting for a connection on this trip leg
|
|
55
|
-
|
|
56
|
-
|
|
69
|
+
feed : str
|
|
70
|
+
the GTFS feed identifier used for this trip leg
|
|
71
|
+
agency_id : str
|
|
72
|
+
the GTFS id the agency used for this trip leg
|
|
73
|
+
route_id : str
|
|
74
|
+
the GTFS id of the public transport route used for this trip leg
|
|
75
|
+
start_stop_id : str
|
|
76
|
+
the GTFS stop_id of the boarding stop used for this trip leg
|
|
77
|
+
end_stop_id : str
|
|
78
|
+
the GTFS stop_id of the aligning stop used for this trip leg
|
|
57
79
|
geometry : shapely.LineString
|
|
58
80
|
spatial representation of this trip leg
|
|
59
81
|
"""
|
|
@@ -62,10 +84,15 @@ class TripLeg:
|
|
|
62
84
|
self.distance = distance
|
|
63
85
|
self.travel_time = travel_time
|
|
64
86
|
self.wait_time = wait_time
|
|
65
|
-
self.
|
|
87
|
+
self.feed = feed
|
|
88
|
+
self.agency_id = agency_id
|
|
89
|
+
self.route_id = route_id
|
|
90
|
+
self.start_stop_id = start_stop_id
|
|
91
|
+
self.end_stop_id = end_stop_id
|
|
66
92
|
self.geometry = geometry
|
|
67
93
|
|
|
68
94
|
def __add__(self, other):
|
|
95
|
+
"""Trip-chain `other` to `self`."""
|
|
69
96
|
from .trip import Trip
|
|
70
97
|
|
|
71
98
|
if isinstance(other, self.__class__):
|
|
@@ -80,6 +107,7 @@ class TripLeg:
|
|
|
80
107
|
)
|
|
81
108
|
|
|
82
109
|
def __radd__(self, other):
|
|
110
|
+
"""Trip-chain `self` to `other`."""
|
|
83
111
|
from .trip import Trip
|
|
84
112
|
|
|
85
113
|
if other == 0: # first iteration of sum()
|
|
@@ -90,31 +118,43 @@ class TripLeg:
|
|
|
90
118
|
else:
|
|
91
119
|
return self.__add__(other)
|
|
92
120
|
|
|
121
|
+
def __eq__(self, other):
|
|
122
|
+
"""Check if `other` is an equal `TripLeg`."""
|
|
123
|
+
if isinstance(other, self.__class__):
|
|
124
|
+
return False not in [
|
|
125
|
+
self._are_columns_equal(other, column) for column in self.COLUMNS
|
|
126
|
+
]
|
|
127
|
+
|
|
93
128
|
def __gt__(self, other):
|
|
129
|
+
"""Check if `other` has a longer travel time."""
|
|
94
130
|
if isinstance(other, TripLeg):
|
|
95
131
|
return (self.travel_time + self.wait_time) > (
|
|
96
132
|
other.travel_time + other.wait_time
|
|
97
133
|
)
|
|
98
134
|
|
|
99
135
|
def __ge__(self, other):
|
|
136
|
+
"""Check if `other` has a longer or equal travel time."""
|
|
100
137
|
if isinstance(other, TripLeg):
|
|
101
138
|
return (self.travel_time + self.wait_time) >= (
|
|
102
139
|
other.travel_time + other.wait_time
|
|
103
140
|
)
|
|
104
141
|
|
|
105
142
|
def __lt__(self, other):
|
|
143
|
+
"""Check if `other` has a shorter travel time."""
|
|
106
144
|
if isinstance(other, TripLeg):
|
|
107
145
|
return (self.travel_time + self.wait_time) < (
|
|
108
146
|
other.travel_time + other.wait_time
|
|
109
147
|
)
|
|
110
148
|
|
|
111
149
|
def __le__(self, other):
|
|
150
|
+
"""Check if `other` has a shorter or equal travel time."""
|
|
112
151
|
if isinstance(other, TripLeg):
|
|
113
152
|
return (self.travel_time + self.wait_time) <= (
|
|
114
153
|
other.travel_time + other.wait_time
|
|
115
154
|
)
|
|
116
155
|
|
|
117
156
|
def __repr__(self):
|
|
157
|
+
"""Return a string representation."""
|
|
118
158
|
try:
|
|
119
159
|
first_point = self.geometry.coords[0]
|
|
120
160
|
last_point = self.geometry.coords[-1]
|
|
@@ -127,10 +167,30 @@ class TripLeg:
|
|
|
127
167
|
f"{first_point} -> {last_point}"
|
|
128
168
|
">"
|
|
129
169
|
)
|
|
130
|
-
except AttributeError:
|
|
170
|
+
except (AttributeError, IndexError):
|
|
131
171
|
_repr = f"<{self.__class__.__name__}>"
|
|
132
172
|
return _repr
|
|
133
173
|
|
|
174
|
+
def _are_columns_equal(self, other, column):
|
|
175
|
+
"""
|
|
176
|
+
Check if a column equals the same column of a different `Trip`.
|
|
177
|
+
|
|
178
|
+
Compare if attribute `column` of self equals attribute `column` of
|
|
179
|
+
other. Also True if both values are None or NaN or NaT.
|
|
180
|
+
"""
|
|
181
|
+
self_column = getattr(self, column)
|
|
182
|
+
other_column = getattr(other, column)
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
self_column == other_column
|
|
186
|
+
or (self_column is None and other_column is None)
|
|
187
|
+
or (self_column == numpy.nan and other_column == numpy.nan)
|
|
188
|
+
or (
|
|
189
|
+
self_column == numpy.datetime64("NaT")
|
|
190
|
+
and other_column == numpy.datetime64("NaT")
|
|
191
|
+
)
|
|
192
|
+
)
|
|
193
|
+
|
|
134
194
|
def as_table_row(self):
|
|
135
195
|
"""
|
|
136
196
|
Return a table row (list) of this trip leg’s details.
|
|
@@ -138,7 +198,8 @@ class TripLeg:
|
|
|
138
198
|
Returns
|
|
139
199
|
=======
|
|
140
200
|
list : detailed information about this trip leg: ``transport_mode``,
|
|
141
|
-
|
|
142
|
-
|
|
201
|
+
``departure_time``, ``distance``, ``travel_time``, ``wait_time``,
|
|
202
|
+
``feed``, ``agency_id`` ``route_id``, ``start_stop_id``,
|
|
203
|
+
``end_stop_id``, ``geometry``
|
|
143
204
|
"""
|
|
144
205
|
return [getattr(self, column) for column in self.COLUMNS]
|
r5py/r5/trip_planner.py
CHANGED
|
@@ -17,6 +17,7 @@ import shapely
|
|
|
17
17
|
from .access_leg import AccessLeg
|
|
18
18
|
from .direct_leg import DirectLeg
|
|
19
19
|
from .egress_leg import EgressLeg
|
|
20
|
+
from .street_segment import StreetSegment
|
|
20
21
|
from .transfer_leg import TransferLeg
|
|
21
22
|
from .transit_leg import TransitLeg
|
|
22
23
|
from .transport_mode import TransportMode
|
|
@@ -43,9 +44,7 @@ ZERO_SECONDS = datetime.timedelta(seconds=0)
|
|
|
43
44
|
|
|
44
45
|
|
|
45
46
|
class TripPlanner:
|
|
46
|
-
"""
|
|
47
|
-
Find detailed routes between two points.
|
|
48
|
-
"""
|
|
47
|
+
"""Find detailed routes between two points."""
|
|
49
48
|
|
|
50
49
|
MAX_ACCESS_TIME = datetime.timedelta(hours=1)
|
|
51
50
|
MAX_EGRESS_TIME = MAX_ACCESS_TIME
|
|
@@ -66,13 +65,15 @@ class TripPlanner:
|
|
|
66
65
|
|
|
67
66
|
EQUIDISTANT_CRS = GoodEnoughEquidistantCrs(self.transport_network.extent)
|
|
68
67
|
self._crs_transformer_function = pyproj.Transformer.from_crs(
|
|
69
|
-
R5_CRS,
|
|
68
|
+
R5_CRS,
|
|
69
|
+
EQUIDISTANT_CRS,
|
|
70
|
+
always_xy=True,
|
|
70
71
|
).transform
|
|
71
72
|
|
|
72
73
|
@property
|
|
73
74
|
def trips(self):
|
|
74
75
|
"""
|
|
75
|
-
|
|
76
|
+
Detailed routes between two points.
|
|
76
77
|
|
|
77
78
|
Returns
|
|
78
79
|
=======
|
|
@@ -84,6 +85,15 @@ class TripPlanner:
|
|
|
84
85
|
|
|
85
86
|
@property
|
|
86
87
|
def direct_paths(self):
|
|
88
|
+
"""
|
|
89
|
+
Detailed routes between two points using direct modes.
|
|
90
|
+
|
|
91
|
+
Returns
|
|
92
|
+
=======
|
|
93
|
+
list[r5py.r5.Trip]
|
|
94
|
+
Detailed routes that meet the requested parameters, using direct
|
|
95
|
+
modes (walking, cycling, driving).
|
|
96
|
+
"""
|
|
87
97
|
direct_paths = []
|
|
88
98
|
request = copy.copy(self.request)
|
|
89
99
|
|
|
@@ -143,31 +153,41 @@ class TripPlanner:
|
|
|
143
153
|
]
|
|
144
154
|
)
|
|
145
155
|
)
|
|
146
|
-
except
|
|
156
|
+
except (
|
|
157
|
+
java.lang.NullPointerException,
|
|
158
|
+
java.util.NoSuchElementException,
|
|
159
|
+
):
|
|
147
160
|
warnings.warn(
|
|
148
161
|
f"Could not find route between origin "
|
|
149
|
-
f"({self.request.fromLon},
|
|
150
|
-
f"
|
|
162
|
+
f"({self.request._regional_task.fromLon}, "
|
|
163
|
+
f"{self.request._regional_task.fromLat}) "
|
|
164
|
+
f"and destination ({self.request._regional_task.toLon}, "
|
|
165
|
+
f"{self.request._regional_task.toLat})",
|
|
151
166
|
RuntimeWarning,
|
|
152
167
|
)
|
|
153
168
|
return direct_paths
|
|
154
169
|
|
|
155
170
|
def _street_segment_from_router_state(self, router_state, transport_mode):
|
|
156
|
-
"""Retrieve a
|
|
171
|
+
"""Retrieve a StreetSegment for a route."""
|
|
157
172
|
street_path = com.conveyal.r5.profile.StreetPath(
|
|
158
173
|
router_state,
|
|
159
174
|
self.transport_network,
|
|
160
175
|
False,
|
|
161
176
|
)
|
|
162
|
-
street_segment =
|
|
163
|
-
street_path,
|
|
164
|
-
transport_mode,
|
|
165
|
-
self.transport_network.street_layer,
|
|
166
|
-
)
|
|
177
|
+
street_segment = StreetSegment(street_path)
|
|
167
178
|
return street_segment
|
|
168
179
|
|
|
169
180
|
@functools.cached_property
|
|
170
181
|
def transit_paths(self):
|
|
182
|
+
"""
|
|
183
|
+
Detailed routes between two points on public transport.
|
|
184
|
+
|
|
185
|
+
Returns
|
|
186
|
+
=======
|
|
187
|
+
list[r5py.r5.Trip]
|
|
188
|
+
Detailed routes that meet the requested parameters, on public
|
|
189
|
+
transport.
|
|
190
|
+
"""
|
|
171
191
|
transit_paths = []
|
|
172
192
|
|
|
173
193
|
# if any transit mode requested:
|
|
@@ -195,7 +215,6 @@ class TripPlanner:
|
|
|
195
215
|
distance=0.0,
|
|
196
216
|
travel_time=ZERO_SECONDS,
|
|
197
217
|
wait_time=ZERO_SECONDS,
|
|
198
|
-
route=None,
|
|
199
218
|
geometry=shapely.LineString(((lon, lat), (lon, lat))),
|
|
200
219
|
)
|
|
201
220
|
]
|
|
@@ -214,8 +233,8 @@ class TripPlanner:
|
|
|
214
233
|
com.conveyal.r5.profile.McRaptorSuboptimalPathProfileRouter(
|
|
215
234
|
self.transport_network,
|
|
216
235
|
request,
|
|
217
|
-
self.
|
|
218
|
-
self.
|
|
236
|
+
self._transit_access_times,
|
|
237
|
+
self._transit_egress_times,
|
|
219
238
|
list_supplier_callback,
|
|
220
239
|
None,
|
|
221
240
|
True,
|
|
@@ -234,18 +253,26 @@ class TripPlanner:
|
|
|
234
253
|
for state in list(states) # some departure times yield no results
|
|
235
254
|
}
|
|
236
255
|
|
|
256
|
+
# keep another cache layer of shortest access and egress legs
|
|
257
|
+
access_legs_by_stop = {}
|
|
258
|
+
egress_legs_by_stop = {}
|
|
259
|
+
|
|
237
260
|
for departure_time, state in final_states.items():
|
|
238
261
|
trip = Trip()
|
|
239
262
|
while state:
|
|
240
263
|
if state.stop == -1: # EgressLeg
|
|
241
|
-
|
|
242
|
-
[
|
|
243
|
-
|
|
244
|
-
|
|
264
|
+
try:
|
|
265
|
+
leg = egress_legs_by_stop[state.back.stop]
|
|
266
|
+
except KeyError:
|
|
267
|
+
leg = min(
|
|
268
|
+
[
|
|
269
|
+
self._transit_egress_paths[transport_mode][
|
|
270
|
+
state.back.stop
|
|
271
|
+
]
|
|
272
|
+
for transport_mode in self._transit_egress_paths.keys()
|
|
245
273
|
]
|
|
246
|
-
|
|
247
|
-
]
|
|
248
|
-
)
|
|
274
|
+
)
|
|
275
|
+
egress_legs_by_stop[state.back.stop] = leg
|
|
249
276
|
leg.wait_time = ZERO_SECONDS
|
|
250
277
|
leg.departure_time = (
|
|
251
278
|
midnight
|
|
@@ -255,14 +282,18 @@ class TripPlanner:
|
|
|
255
282
|
leg.arrival_time = leg.departure_time + leg.travel_time
|
|
256
283
|
|
|
257
284
|
elif state.back is None: # AccessLeg
|
|
258
|
-
|
|
259
|
-
[
|
|
260
|
-
|
|
261
|
-
|
|
285
|
+
try:
|
|
286
|
+
leg = access_legs_by_stop[state.stop]
|
|
287
|
+
except KeyError:
|
|
288
|
+
leg = min(
|
|
289
|
+
[
|
|
290
|
+
self._transit_access_paths[transport_mode][
|
|
291
|
+
state.stop
|
|
292
|
+
]
|
|
293
|
+
for transport_mode in self._transit_access_paths.keys()
|
|
262
294
|
]
|
|
263
|
-
|
|
264
|
-
]
|
|
265
|
-
)
|
|
295
|
+
)
|
|
296
|
+
access_legs_by_stop[state.stop] = leg
|
|
266
297
|
leg.wait_time = ZERO_SECONDS
|
|
267
298
|
leg.arrival_time = midnight + datetime.timedelta(
|
|
268
299
|
seconds=state.time
|
|
@@ -274,7 +305,7 @@ class TripPlanner:
|
|
|
274
305
|
departure_stop = state.back.stop
|
|
275
306
|
arrival_stop = state.stop
|
|
276
307
|
|
|
277
|
-
leg = self.
|
|
308
|
+
leg = self._transit_transfer_path(
|
|
278
309
|
departure_stop, arrival_stop
|
|
279
310
|
)
|
|
280
311
|
|
|
@@ -294,6 +325,17 @@ class TripPlanner:
|
|
|
294
325
|
|
|
295
326
|
else: # TransitLeg
|
|
296
327
|
pattern = transit_layer.trip_patterns[state.pattern]
|
|
328
|
+
|
|
329
|
+
# Use the indices to look up the stop ids, which are scoped by the GTFS feed supplied
|
|
330
|
+
start_stop_id = transit_layer.get_stop_id_from_index(
|
|
331
|
+
state.back.stop
|
|
332
|
+
).split(":")[1]
|
|
333
|
+
end_stop = transit_layer.get_stop_id_from_index(
|
|
334
|
+
state.stop
|
|
335
|
+
)
|
|
336
|
+
end_stop_id = end_stop.split(":")[1]
|
|
337
|
+
feed = end_stop.split(":")[0]
|
|
338
|
+
|
|
297
339
|
route = transit_layer.routes[pattern.routeIndex]
|
|
298
340
|
transport_mode = TransportMode(
|
|
299
341
|
com.conveyal.r5.transit.TransitLayer.getTransitModes(
|
|
@@ -346,13 +388,17 @@ class TripPlanner:
|
|
|
346
388
|
distance = None
|
|
347
389
|
|
|
348
390
|
leg = TransitLeg(
|
|
349
|
-
transport_mode,
|
|
350
|
-
departure_time,
|
|
351
|
-
distance,
|
|
352
|
-
travel_time,
|
|
353
|
-
wait_time,
|
|
354
|
-
str(
|
|
355
|
-
|
|
391
|
+
transport_mode=transport_mode,
|
|
392
|
+
departure_time=departure_time,
|
|
393
|
+
distance=distance,
|
|
394
|
+
travel_time=travel_time,
|
|
395
|
+
wait_time=wait_time,
|
|
396
|
+
feed=str(feed),
|
|
397
|
+
agency_id=str(route.agency_id),
|
|
398
|
+
route_id=str(route.route_id),
|
|
399
|
+
start_stop_id=str(start_stop_id),
|
|
400
|
+
end_stop_id=str(end_stop_id),
|
|
401
|
+
geometry=geometry,
|
|
356
402
|
)
|
|
357
403
|
|
|
358
404
|
# we traverse in reverse order:
|
|
@@ -361,12 +407,14 @@ class TripPlanner:
|
|
|
361
407
|
trip = leg + trip
|
|
362
408
|
state = state.back
|
|
363
409
|
|
|
364
|
-
|
|
410
|
+
# R5 sometimes reports the same path more than once, skip duplicates
|
|
411
|
+
if trip not in transit_paths:
|
|
412
|
+
transit_paths.append(trip)
|
|
365
413
|
|
|
366
414
|
return transit_paths
|
|
367
415
|
|
|
368
416
|
@functools.cached_property
|
|
369
|
-
def
|
|
417
|
+
def _transit_access_paths(self):
|
|
370
418
|
access_paths = {}
|
|
371
419
|
|
|
372
420
|
request = copy.copy(self.request)
|
|
@@ -406,9 +454,12 @@ class TripPlanner:
|
|
|
406
454
|
return access_paths
|
|
407
455
|
|
|
408
456
|
@functools.cached_property
|
|
409
|
-
def
|
|
410
|
-
"""
|
|
411
|
-
|
|
457
|
+
def _transit_access_times(self):
|
|
458
|
+
"""
|
|
459
|
+
Times to reached stops.
|
|
460
|
+
|
|
461
|
+
In the format required by McRaptorSuboptimalPathProfileRouter.
|
|
462
|
+
"""
|
|
412
463
|
access_times = jpype.JObject(
|
|
413
464
|
{
|
|
414
465
|
com.conveyal.r5.api.util.LegMode
|
|
@@ -419,14 +470,14 @@ class TripPlanner:
|
|
|
419
470
|
for transfer_leg in reached_stops.values()
|
|
420
471
|
],
|
|
421
472
|
)
|
|
422
|
-
for mode, reached_stops in self.
|
|
473
|
+
for mode, reached_stops in self._transit_access_paths.items()
|
|
423
474
|
},
|
|
424
475
|
"java.util.Map<com.conveyal.r5.LegMode, gnu.trove.map.TIntIntMap>",
|
|
425
476
|
)
|
|
426
477
|
return access_times
|
|
427
478
|
|
|
428
479
|
@functools.cached_property
|
|
429
|
-
def
|
|
480
|
+
def _transit_egress_paths(self):
|
|
430
481
|
egress_paths = {}
|
|
431
482
|
|
|
432
483
|
request = copy.copy(self.request)
|
|
@@ -467,9 +518,12 @@ class TripPlanner:
|
|
|
467
518
|
return egress_paths
|
|
468
519
|
|
|
469
520
|
@functools.cached_property
|
|
470
|
-
def
|
|
471
|
-
"""
|
|
472
|
-
|
|
521
|
+
def _transit_egress_times(self):
|
|
522
|
+
"""
|
|
523
|
+
Times to reached stops.
|
|
524
|
+
|
|
525
|
+
In the format required by McRaptorSuboptimalPathProfileRouter.
|
|
526
|
+
"""
|
|
473
527
|
egress_times = jpype.JObject(
|
|
474
528
|
{
|
|
475
529
|
com.conveyal.r5.api.util.LegMode
|
|
@@ -480,13 +534,14 @@ class TripPlanner:
|
|
|
480
534
|
for transfer_leg in reached_stops.values()
|
|
481
535
|
],
|
|
482
536
|
)
|
|
483
|
-
for mode, reached_stops in self.
|
|
537
|
+
for mode, reached_stops in self._transit_egress_paths.items()
|
|
484
538
|
},
|
|
485
539
|
"java.util.Map<com.conveyal.r5.LegMode, gnu.trove.map.TIntIntMap>",
|
|
486
540
|
)
|
|
487
541
|
return egress_times
|
|
488
542
|
|
|
489
|
-
def
|
|
543
|
+
def _transit_transfer_path(self, from_stop, to_stop):
|
|
544
|
+
"""Find a transfer path between two transit stops."""
|
|
490
545
|
self._transfer_paths = {}
|
|
491
546
|
while True:
|
|
492
547
|
try:
|
|
@@ -524,8 +579,8 @@ class TripPlanner:
|
|
|
524
579
|
TransportMode.WALK,
|
|
525
580
|
)
|
|
526
581
|
|
|
527
|
-
transfer_path = self._transfer_paths[
|
|
528
|
-
(
|
|
529
|
-
|
|
582
|
+
transfer_path = self._transfer_paths[(from_stop, to_stop)] = (
|
|
583
|
+
TransferLeg(TransportMode.WALK, street_segment)
|
|
584
|
+
)
|
|
530
585
|
|
|
531
586
|
return transfer_path
|
r5py/util/__init__.py
CHANGED
|
@@ -2,22 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
"""Utility functions, e.g., starting a JVM, and accessing configuration."""
|
|
4
4
|
|
|
5
|
+
from . import environment # noqa: F401
|
|
6
|
+
|
|
5
7
|
from .camel_to_snake_case import camel_to_snake_case
|
|
6
8
|
from .config import Config
|
|
7
9
|
from .contains_gtfs_data import contains_gtfs_data
|
|
8
10
|
from .data_validation import check_od_data_set
|
|
11
|
+
from .file_digest import FileDigest
|
|
9
12
|
from .good_enough_equidistant_crs import GoodEnoughEquidistantCrs
|
|
10
13
|
from .jvm import start_jvm
|
|
11
14
|
from .parse_int_date import parse_int_date
|
|
12
15
|
from .snake_to_camel_case import snake_to_camel_case
|
|
16
|
+
from .spatially_clustered_geodataframe import SpatiallyClusteredGeoDataFrame
|
|
17
|
+
from .working_copy import WorkingCopy
|
|
13
18
|
|
|
14
19
|
__all__ = [
|
|
15
20
|
"camel_to_snake_case",
|
|
16
21
|
"check_od_data_set",
|
|
17
22
|
"Config",
|
|
18
23
|
"contains_gtfs_data",
|
|
24
|
+
"FileDigest",
|
|
19
25
|
"GoodEnoughEquidistantCrs",
|
|
20
26
|
"parse_int_date",
|
|
21
27
|
"snake_to_camel_case",
|
|
28
|
+
"SpatiallyClusteredGeoDataFrame",
|
|
22
29
|
"start_jvm",
|
|
30
|
+
"WorkingCopy",
|
|
23
31
|
]
|
r5py/util/classpath.py
CHANGED
|
@@ -17,8 +17,10 @@ from .warnings import R5pyWarning
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
# update these to use a newer R5 version if no R5 available locally
|
|
20
|
-
R5_JAR_URL =
|
|
21
|
-
|
|
20
|
+
R5_JAR_URL = (
|
|
21
|
+
"https://github.com/r5py/r5/releases/download/v7.3-r5py/r5-v7.3-r5py-all.jar"
|
|
22
|
+
)
|
|
23
|
+
R5_JAR_SHA256 = "cb1ccad370757ba229cf17f1bedc9549ff5d77fdbb44b7a3058104fe1f243f53"
|
|
22
24
|
# ---
|
|
23
25
|
|
|
24
26
|
|
|
@@ -73,9 +75,11 @@ def find_r5_classpath(arguments):
|
|
|
73
75
|
"Could not find R5 jar, trying to download it from upstream",
|
|
74
76
|
R5pyWarning,
|
|
75
77
|
)
|
|
76
|
-
with
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
with (
|
|
79
|
+
ValidatingRequestsSession() as session,
|
|
80
|
+
session.get(R5_JAR_URL, R5_JAR_SHA256) as response,
|
|
81
|
+
open(r5_classpath, "wb") as jar,
|
|
82
|
+
):
|
|
79
83
|
jar.write(response.content)
|
|
80
84
|
if arguments.verbose:
|
|
81
85
|
warnings.warn(
|