r5py 0.1.1.dev2__py3-none-any.whl → 1.0.0.dev0__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.

@@ -4,25 +4,82 @@
4
4
 
5
5
  import copy
6
6
 
7
- import joblib
7
+ try:
8
+ from warnings import deprecated
9
+ except ImportError: # Python<=3.12
10
+ from typing_extensions import deprecated
11
+
8
12
  import pandas
9
13
 
10
- from .base_travel_time_matrix_computer import BaseTravelTimeMatrixComputer
14
+ from .base_travel_time_matrix import BaseTravelTimeMatrix
11
15
  from ..util import start_jvm
12
16
 
13
17
  import com.conveyal.r5
14
18
 
15
19
 
16
- __all__ = ["TravelTimeMatrixComputer"]
20
+ __all__ = ["TravelTimeMatrix", "TravelTimeMatrixComputer"]
17
21
 
18
22
 
19
23
  start_jvm()
20
24
 
21
25
 
22
- class TravelTimeMatrixComputer(BaseTravelTimeMatrixComputer):
26
+ class TravelTimeMatrix(BaseTravelTimeMatrix):
23
27
  """Compute travel times between many origins and destinations."""
24
28
 
25
- def compute_travel_times(self):
29
+ def __init__(
30
+ self,
31
+ transport_network,
32
+ origins=None,
33
+ destinations=None,
34
+ snap_to_network=False,
35
+ **kwargs,
36
+ ):
37
+ """
38
+ Compute travel times between many origins and destinations.
39
+
40
+ ``r5py.TravelTimeMatrix`` are child classes of ``pandas.DataFrame`` and
41
+ support all of their methods and properties,
42
+ see https://pandas.pydata.org/docs/
43
+
44
+ Arguments
45
+ ---------
46
+ transport_network : r5py.TransportNetwork | tuple(str, list(str), dict)
47
+ The transport network to route on. This can either be a readily
48
+ initialised r5py.TransportNetwork or a tuple of the parameters
49
+ passed to ``TransportNetwork.__init__()``: the path to an OpenStreetMap
50
+ extract in PBF format, a list of zero of more paths to GTFS transport
51
+ schedule files, and a dict with ``build_config`` options.
52
+ origins : geopandas.GeoDataFrame
53
+ Places to find a route _from_
54
+ Has to have a point geometry, and at least an `id` column
55
+ destinations : geopandas.GeoDataFrame (optional)
56
+ Places to find a route _to_
57
+ Has to have a point geometry, and at least an `id` column
58
+ If omitted, use same data set as for origins
59
+ snap_to_network : bool or int, default False
60
+ Should origin an destination points be snapped to the street network
61
+ before routing? If `True`, the default search radius (defined in
62
+ `com.conveyal.r5.streets.StreetLayer.LINK_RADIUS_METERS`) is used,
63
+ if `int`, use `snap_to_network` meters as the search radius.
64
+ **kwargs : mixed
65
+ Any arguments than can be passed to r5py.RegionalTask:
66
+ ``departure``, ``departure_time_window``, ``percentiles``, ``transport_modes``,
67
+ ``access_modes``, ``egress_modes``, ``max_time``, ``max_time_walking``,
68
+ ``max_time_cycling``, ``max_time_driving``, ``speed_cycling``, ``speed_walking``,
69
+ ``max_public_transport_rides``, ``max_bicycle_traffic_stress``
70
+ """
71
+ super().__init__(
72
+ transport_network,
73
+ origins,
74
+ destinations,
75
+ snap_to_network,
76
+ **kwargs,
77
+ )
78
+ data = self._compute()
79
+ for column in data.columns:
80
+ self[column] = data[column]
81
+
82
+ def _compute(self):
26
83
  """
27
84
  Compute travel times from all origins to all destinations.
28
85
 
@@ -40,20 +97,10 @@ class TravelTimeMatrixComputer(BaseTravelTimeMatrixComputer):
40
97
  self._prepare_origins_destinations()
41
98
  self.request.destinations = self.destinations
42
99
 
43
- # loop over all origins, modify the request, and compute the times
44
- # to all destinations.
45
- with joblib.Parallel(
46
- prefer="threads",
47
- verbose=(10 * self.verbose), # joblib has a funny verbosity scale
48
- n_jobs=self.NUM_THREADS,
49
- ) as parallel:
50
- od_matrix = pandas.concat(
51
- parallel(
52
- joblib.delayed(self._travel_times_per_origin)(from_id)
53
- for from_id in self.origins.id
54
- ),
55
- ignore_index=True,
56
- )
100
+ od_matrix = pandas.concat(
101
+ [self._travel_times_per_origin(from_id) for from_id in self.origins.id],
102
+ ignore_index=True,
103
+ )
57
104
 
58
105
  try:
59
106
  od_matrix = od_matrix.to_crs(self._origins_crs)
@@ -132,3 +179,31 @@ class TravelTimeMatrixComputer(BaseTravelTimeMatrixComputer):
132
179
  od_matrix = self._parse_results(from_id, results)
133
180
 
134
181
  return od_matrix
182
+
183
+
184
+ @deprecated(
185
+ "Use `TravelTimeMatrix` instead, `TravelTimeMatrixComputer will be deprecated in a future release."
186
+ )
187
+ class TravelTimeMatrixComputer:
188
+ """Compute travel times between many origins and destinations."""
189
+
190
+ def __init__(self, *args, **kwargs):
191
+ """Compute travel times between many origins and destinations."""
192
+ self._ttm = TravelTimeMatrix(*args, **kwargs)
193
+
194
+ def compute_travel_times(self):
195
+ """
196
+ Compute travel times from all origins to all destinations.
197
+
198
+ Returns
199
+ -------
200
+ pandas.DataFrame
201
+ A data frame containing the columns ``from_id``, ``to_id``, and
202
+ ``travel_time``, where ``travel_time`` is the median calculated
203
+ travel time between ``from_id`` and ``to_id`` or ``numpy.nan``
204
+ if no connection with the given parameters was found.
205
+ If non-default ``percentiles`` were requested: one or more columns
206
+ ``travel_time_p{:02d}`` representing the particular percentile of
207
+ travel time.
208
+ """
209
+ return self._ttm
r5py/r5/trip.py CHANGED
@@ -15,9 +15,7 @@ __all__ = ["Trip"]
15
15
 
16
16
 
17
17
  class Trip:
18
- """
19
- Represent one trip, consisting of one or more `TripLeg`s.
20
- """
18
+ """Represent one trip, consisting of one or more `r5py.r5.TripLeg`."""
21
19
 
22
20
  COLUMNS = [
23
21
  "segment",
@@ -25,7 +23,7 @@ class Trip:
25
23
 
26
24
  def __init__(self, legs=[]):
27
25
  """
28
- Represent one trip, consisting of one of more `TripLeg`s.
26
+ Represent one trip, consisting of one of more `r5py.r5.TripLeg`.
29
27
 
30
28
  Arguments
31
29
  =========
@@ -34,7 +32,13 @@ class Trip:
34
32
  """
35
33
  self.legs = legs
36
34
 
35
+ def __eq__(self, other):
36
+ """Check whether `self` and `other` are equal."""
37
+ if isinstance(other, self.__class__):
38
+ return self.legs == other.legs
39
+
37
40
  def __repr__(self):
41
+ """Return a string representation of `self`."""
38
42
  legs = ", ".join([str(leg) for leg in self.legs])
39
43
  return (
40
44
  f"<{self.__class__.__name__}: "
@@ -50,8 +54,9 @@ class Trip:
50
54
  Returns
51
55
  =======
52
56
  list : detailed information about this trip and its legs (segments):
53
- ``segment``, ``transport_mode``, ``departure_time``, ``distance``,
54
- ``travel_time``, ``wait_time``, ``route``, ``geometry``
57
+ ``segment``, ``transport_mode``, ``departure_time``, ``distance``,
58
+ ``travel_time``, ``wait_time``, ``feed``, ``agency_id``, ``route_id``,
59
+ ``start_stop_id``, ``end_stop_id``, ``geometry``
55
60
  """
56
61
  return [[segment] + leg.as_table_row() for segment, leg in enumerate(self.legs)]
57
62
 
@@ -72,9 +77,9 @@ class Trip:
72
77
  )
73
78
 
74
79
  @property
75
- def routes(self):
80
+ def route_ids(self):
76
81
  """The public transport route(s) used on this trip."""
77
- return [leg.route for leg in self.legs]
82
+ return [leg.route_id for leg in self.legs]
78
83
 
79
84
  @property
80
85
  def transport_modes(self):
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
- "route",
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=None,
40
+ departure_time=numpy.datetime64("NaT"),
32
41
  distance=None,
33
- travel_time=None,
34
- wait_time=None,
35
- route=None,
36
- geometry=None,
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
- e.g., TransitLeg, or DirectLeg
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
- route : str
56
- public transport route used for this trip leg
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.route = route
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
- ``departure_time``, ``distance``, ``travel_time``, ``wait_time``,
142
- ``route``, ``geometry``
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
@@ -43,9 +43,7 @@ ZERO_SECONDS = datetime.timedelta(seconds=0)
43
43
 
44
44
 
45
45
  class TripPlanner:
46
- """
47
- Find detailed routes between two points.
48
- """
46
+ """Find detailed routes between two points."""
49
47
 
50
48
  MAX_ACCESS_TIME = datetime.timedelta(hours=1)
51
49
  MAX_EGRESS_TIME = MAX_ACCESS_TIME
@@ -72,7 +70,7 @@ class TripPlanner:
72
70
  @property
73
71
  def trips(self):
74
72
  """
75
- Find detailed routes between two points.
73
+ Detailed routes between two points.
76
74
 
77
75
  Returns
78
76
  =======
@@ -84,6 +82,15 @@ class TripPlanner:
84
82
 
85
83
  @property
86
84
  def direct_paths(self):
85
+ """
86
+ Detailed routes between two points using direct modes.
87
+
88
+ Returns
89
+ =======
90
+ list[r5py.r5.Trip]
91
+ Detailed routes that meet the requested parameters, using direct
92
+ modes (walking, cycling, driving).
93
+ """
87
94
  direct_paths = []
88
95
  request = copy.copy(self.request)
89
96
 
@@ -143,11 +150,16 @@ class TripPlanner:
143
150
  ]
144
151
  )
145
152
  )
146
- except java.lang.NullPointerException:
153
+ except (
154
+ java.lang.NullPointerException,
155
+ java.util.NoSuchElementException,
156
+ ):
147
157
  warnings.warn(
148
158
  f"Could not find route between origin "
149
- f"({self.request.fromLon}, {self.request.fromLat}) "
150
- f"and destination ({self.request.toLon}, {self.request.toLat})",
159
+ f"({self.request._regional_task.fromLon}, "
160
+ f"{self.request._regional_task.fromLat}) "
161
+ f"and destination ({self.request._regional_task.toLon}, "
162
+ f"{self.request._regional_task.toLat})",
151
163
  RuntimeWarning,
152
164
  )
153
165
  return direct_paths
@@ -168,6 +180,15 @@ class TripPlanner:
168
180
 
169
181
  @functools.cached_property
170
182
  def transit_paths(self):
183
+ """
184
+ Detailed routes between two points on public transport.
185
+
186
+ Returns
187
+ =======
188
+ list[r5py.r5.Trip]
189
+ Detailed routes that meet the requested parameters, on public
190
+ transport.
191
+ """
171
192
  transit_paths = []
172
193
 
173
194
  # if any transit mode requested:
@@ -195,7 +216,6 @@ class TripPlanner:
195
216
  distance=0.0,
196
217
  travel_time=ZERO_SECONDS,
197
218
  wait_time=ZERO_SECONDS,
198
- route=None,
199
219
  geometry=shapely.LineString(((lon, lat), (lon, lat))),
200
220
  )
201
221
  ]
@@ -214,8 +234,8 @@ class TripPlanner:
214
234
  com.conveyal.r5.profile.McRaptorSuboptimalPathProfileRouter(
215
235
  self.transport_network,
216
236
  request,
217
- self.transit_access_times,
218
- self.transit_egress_times,
237
+ self._transit_access_times,
238
+ self._transit_egress_times,
219
239
  list_supplier_callback,
220
240
  None,
221
241
  True,
@@ -234,18 +254,26 @@ class TripPlanner:
234
254
  for state in list(states) # some departure times yield no results
235
255
  }
236
256
 
257
+ # keep another cache layer of shortest access and egress legs
258
+ access_legs_by_stop = {}
259
+ egress_legs_by_stop = {}
260
+
237
261
  for departure_time, state in final_states.items():
238
262
  trip = Trip()
239
263
  while state:
240
264
  if state.stop == -1: # EgressLeg
241
- leg = min(
242
- [
243
- self.transit_egress_paths[transport_mode][
244
- state.back.stop
265
+ try:
266
+ leg = egress_legs_by_stop[state.back.stop]
267
+ except KeyError:
268
+ leg = min(
269
+ [
270
+ self._transit_egress_paths[transport_mode][
271
+ state.back.stop
272
+ ]
273
+ for transport_mode in self._transit_egress_paths.keys()
245
274
  ]
246
- for transport_mode in self.transit_egress_paths.keys()
247
- ]
248
- )
275
+ )
276
+ egress_legs_by_stop[state.back.stop] = leg
249
277
  leg.wait_time = ZERO_SECONDS
250
278
  leg.departure_time = (
251
279
  midnight
@@ -255,14 +283,18 @@ class TripPlanner:
255
283
  leg.arrival_time = leg.departure_time + leg.travel_time
256
284
 
257
285
  elif state.back is None: # AccessLeg
258
- leg = min(
259
- [
260
- self.transit_access_paths[transport_mode][
261
- state.stop
286
+ try:
287
+ leg = access_legs_by_stop[state.stop]
288
+ except KeyError:
289
+ leg = min(
290
+ [
291
+ self._transit_access_paths[transport_mode][
292
+ state.stop
293
+ ]
294
+ for transport_mode in self._transit_access_paths.keys()
262
295
  ]
263
- for transport_mode in self.transit_access_paths.keys()
264
- ]
265
- )
296
+ )
297
+ access_legs_by_stop[state.stop] = leg
266
298
  leg.wait_time = ZERO_SECONDS
267
299
  leg.arrival_time = midnight + datetime.timedelta(
268
300
  seconds=state.time
@@ -274,7 +306,7 @@ class TripPlanner:
274
306
  departure_stop = state.back.stop
275
307
  arrival_stop = state.stop
276
308
 
277
- leg = self.transit_transfer_path(
309
+ leg = self._transit_transfer_path(
278
310
  departure_stop, arrival_stop
279
311
  )
280
312
 
@@ -294,6 +326,17 @@ class TripPlanner:
294
326
 
295
327
  else: # TransitLeg
296
328
  pattern = transit_layer.trip_patterns[state.pattern]
329
+
330
+ # Use the indices to look up the stop ids, which are scoped by the GTFS feed supplied
331
+ start_stop_id = transit_layer.get_stop_id_from_index(
332
+ state.back.stop
333
+ ).split(":")[1]
334
+ end_stop = transit_layer.get_stop_id_from_index(
335
+ state.stop
336
+ )
337
+ end_stop_id = end_stop.split(":")[1]
338
+ feed = end_stop.split(":")[0]
339
+
297
340
  route = transit_layer.routes[pattern.routeIndex]
298
341
  transport_mode = TransportMode(
299
342
  com.conveyal.r5.transit.TransitLayer.getTransitModes(
@@ -346,13 +389,17 @@ class TripPlanner:
346
389
  distance = None
347
390
 
348
391
  leg = TransitLeg(
349
- transport_mode,
350
- departure_time,
351
- distance,
352
- travel_time,
353
- wait_time,
354
- str(route.route_short_name),
355
- geometry,
392
+ transport_mode=transport_mode,
393
+ departure_time=departure_time,
394
+ distance=distance,
395
+ travel_time=travel_time,
396
+ wait_time=wait_time,
397
+ feed=str(feed),
398
+ agency_id=str(route.agency_id),
399
+ route_id=str(route.route_id),
400
+ start_stop_id=str(start_stop_id),
401
+ end_stop_id=str(end_stop_id),
402
+ geometry=geometry,
356
403
  )
357
404
 
358
405
  # we traverse in reverse order:
@@ -361,12 +408,14 @@ class TripPlanner:
361
408
  trip = leg + trip
362
409
  state = state.back
363
410
 
364
- transit_paths.append(trip)
411
+ # R5 sometimes reports the same path more than once, skip duplicates
412
+ if trip not in transit_paths:
413
+ transit_paths.append(trip)
365
414
 
366
415
  return transit_paths
367
416
 
368
417
  @functools.cached_property
369
- def transit_access_paths(self):
418
+ def _transit_access_paths(self):
370
419
  access_paths = {}
371
420
 
372
421
  request = copy.copy(self.request)
@@ -406,9 +455,12 @@ class TripPlanner:
406
455
  return access_paths
407
456
 
408
457
  @functools.cached_property
409
- def transit_access_times(self):
410
- """Times to reached stops in the format required by
411
- McRaptorSuboptimalPathProfileRouter."""
458
+ def _transit_access_times(self):
459
+ """
460
+ Times to reached stops.
461
+
462
+ In the format required by McRaptorSuboptimalPathProfileRouter.
463
+ """
412
464
  access_times = jpype.JObject(
413
465
  {
414
466
  com.conveyal.r5.api.util.LegMode
@@ -419,14 +471,14 @@ class TripPlanner:
419
471
  for transfer_leg in reached_stops.values()
420
472
  ],
421
473
  )
422
- for mode, reached_stops in self.transit_access_paths.items()
474
+ for mode, reached_stops in self._transit_access_paths.items()
423
475
  },
424
476
  "java.util.Map<com.conveyal.r5.LegMode, gnu.trove.map.TIntIntMap>",
425
477
  )
426
478
  return access_times
427
479
 
428
480
  @functools.cached_property
429
- def transit_egress_paths(self):
481
+ def _transit_egress_paths(self):
430
482
  egress_paths = {}
431
483
 
432
484
  request = copy.copy(self.request)
@@ -467,9 +519,12 @@ class TripPlanner:
467
519
  return egress_paths
468
520
 
469
521
  @functools.cached_property
470
- def transit_egress_times(self):
471
- """Times to reached stops in the format required by
472
- McRaptorSuboptimalPathProfileRouter."""
522
+ def _transit_egress_times(self):
523
+ """
524
+ Times to reached stops.
525
+
526
+ In the format required by McRaptorSuboptimalPathProfileRouter.
527
+ """
473
528
  egress_times = jpype.JObject(
474
529
  {
475
530
  com.conveyal.r5.api.util.LegMode
@@ -480,13 +535,14 @@ class TripPlanner:
480
535
  for transfer_leg in reached_stops.values()
481
536
  ],
482
537
  )
483
- for mode, reached_stops in self.transit_egress_paths.items()
538
+ for mode, reached_stops in self._transit_egress_paths.items()
484
539
  },
485
540
  "java.util.Map<com.conveyal.r5.LegMode, gnu.trove.map.TIntIntMap>",
486
541
  )
487
542
  return egress_times
488
543
 
489
- def transit_transfer_path(self, from_stop, to_stop):
544
+ def _transit_transfer_path(self, from_stop, to_stop):
545
+ """Find a transfer path between two transit stops."""
490
546
  self._transfer_paths = {}
491
547
  while True:
492
548
  try:
@@ -524,8 +580,8 @@ class TripPlanner:
524
580
  TransportMode.WALK,
525
581
  )
526
582
 
527
- transfer_path = self._transfer_paths[
528
- (from_stop, to_stop)
529
- ] = TransferLeg(TransportMode.WALK, street_segment)
583
+ transfer_path = self._transfer_paths[(from_stop, to_stop)] = (
584
+ TransferLeg(TransportMode.WALK, street_segment)
585
+ )
530
586
 
531
587
  return transfer_path