r5py 0.1.1.dev2__py3-none-any.whl → 0.1.2__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 CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  """Python wrapper for the R5 routing analysis engine."""
4
4
 
5
- __version__ = "0.1.1.dev2"
5
+ __version__ = "0.1.2"
6
+
6
7
 
7
8
  from .r5 import (
8
9
  DetailedItinerariesComputer,
r5py/__main__.py CHANGED
@@ -1,16 +1,3 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
-
4
- """A Python wrapper for the R5 routing analysis engine."""
5
-
6
-
7
- from .util import Config
8
-
9
-
10
- def main():
11
- config = Config()
12
- arguments = config.get_arguments() # noqa F401
13
-
14
-
15
- if __name__ == "__main__":
16
- main()
3
+ """Python wrapper for the R5 routing analysis engine."""
r5py/r5/__init__.py CHANGED
@@ -2,23 +2,37 @@
2
2
 
3
3
  """R5 classes."""
4
4
 
5
+ from .access_leg import AccessLeg
5
6
  from .breakdown_stat import BreakdownStat
6
7
  from .detailed_itineraries_computer import DetailedItinerariesComputer
8
+ from .direct_leg import DirectLeg
9
+ from .egress_leg import EgressLeg
7
10
  from .regional_task import RegionalTask
8
11
  from .scenario import Scenario
9
12
  from .street_layer import StreetLayer
13
+ from .transfer_leg import TransferLeg
14
+ from .transit_leg import TransitLeg
10
15
  from .transport_mode import TransportMode
11
16
  from .transport_network import TransportNetwork
12
17
  from .travel_time_matrix_computer import TravelTimeMatrixComputer
18
+ from .trip import Trip
19
+ from .trip_planner import TripPlanner
13
20
 
14
21
  __all__ = [
22
+ "AccessLeg",
15
23
  "BreakdownStat",
16
24
  "DetailedItinerariesComputer",
25
+ "DirectLeg",
26
+ "EgressLeg",
17
27
  "RegionalTask",
18
28
  "Scenario",
19
29
  "SpeedConfig",
20
30
  "StreetLayer",
31
+ "TransferLeg",
32
+ "TransitLeg",
21
33
  "TransportMode",
22
34
  "TransportNetwork",
23
35
  "TravelTimeMatrixComputer",
36
+ "Trip",
37
+ "TripPlanner",
24
38
  ]
@@ -91,6 +91,7 @@ class BaseTravelTimeMatrixComputer:
91
91
 
92
92
  @property
93
93
  def destinations(self):
94
+ """The destinations of this travel time matrix (`geopandas.GeoDataFrame`)."""
94
95
  return self._destinations
95
96
 
96
97
  @destinations.setter
@@ -120,10 +121,7 @@ class BaseTravelTimeMatrixComputer:
120
121
  return data_set.map(lambda x: numpy.nan if x == MAX_INT32 else x)
121
122
 
122
123
  def _prepare_origins_destinations(self):
123
- """
124
- Make sure we received enough information to route from origins to
125
- destinations.
126
- """
124
+ """Make sure we received enough information to route from origins to destinations."""
127
125
  try:
128
126
  self.origins
129
127
  except AttributeError as exception:
@@ -165,6 +163,7 @@ class BaseTravelTimeMatrixComputer:
165
163
 
166
164
  @property
167
165
  def origins(self):
166
+ """The origins of this travel time matrix (`geopandas.GeoDataFrame`)."""
168
167
  return self._origins
169
168
 
170
169
  @origins.setter
@@ -124,7 +124,6 @@ class DetailedItinerariesComputer(BaseTravelTimeMatrixComputer):
124
124
  route number or name), `geometry` (`shapely.LineString`)
125
125
  TODO: Add description of output data frame columns and format
126
126
  """
127
-
128
127
  self._prepare_origins_destinations()
129
128
 
130
129
  # warn if public transport routes are requested, but R5 has been
@@ -150,11 +149,12 @@ class DetailedItinerariesComputer(BaseTravelTimeMatrixComputer):
150
149
  verbose=(10 * self.verbose), # joblib has a funny verbosity scale
151
150
  n_jobs=self.NUM_THREADS,
152
151
  ) as parallel:
152
+ matrices = parallel(
153
+ joblib.delayed(self._travel_details_per_od_pair)(from_id, to_id)
154
+ for _, (from_id, to_id) in self.od_pairs.iterrows()
155
+ )
153
156
  od_matrix = pandas.concat(
154
- parallel(
155
- joblib.delayed(self._travel_details_per_od_pair)(from_id, to_id)
156
- for _, (from_id, to_id) in self.od_pairs.iterrows()
157
- ),
157
+ [matrix.astype(matrices[0].dtypes) for matrix in matrices],
158
158
  ignore_index=True,
159
159
  )
160
160
 
@@ -162,11 +162,7 @@ class DetailedItinerariesComputer(BaseTravelTimeMatrixComputer):
162
162
  return od_matrix
163
163
 
164
164
  def _prepare_origins_destinations(self):
165
- """
166
- Make sure we received enough information to route from origins to
167
- destinations.
168
- """
169
-
165
+ """Make sure we received enough information to route from origins to destinations."""
170
166
  super()._prepare_origins_destinations()
171
167
 
172
168
  if self.all_to_all:
r5py/r5/direct_leg.py CHANGED
@@ -15,9 +15,7 @@ __all__ = ["DirectLeg"]
15
15
 
16
16
 
17
17
  class DirectLeg(TripLeg):
18
- """
19
- Represent one leg of a public transport trip.
20
- """
18
+ """Represent one leg of a public transport trip."""
21
19
 
22
20
  def __init__(self, transport_mode, street_segment):
23
21
  """
r5py/r5/regional_task.py CHANGED
@@ -200,12 +200,16 @@ class RegionalTask:
200
200
  # The value is a static property of com.conveyal.r5.analyst.cluster.PathResult;
201
201
  # static properites of Java classes can be modified in a singleton kind of way
202
202
  try:
203
- com.conveyal.r5.analyst.cluster.PathResult.maxDestinations = max(
204
- com.conveyal.r5.analyst.cluster.PathResult.maxDestinations,
205
- len(self.destinations) + 1,
206
- )
203
+ num_destinations = len(self.destinations)
207
204
  except AttributeError:
208
- pass
205
+ num_destinations = 0
206
+ if (
207
+ num_destinations
208
+ > com.conveyal.r5.analyst.cluster.PathResult.MAX_PATH_DESTINATIONS
209
+ ):
210
+ com.conveyal.r5.analyst.cluster.PathResult.MAX_PATH_DESTINATIONS = (
211
+ num_destinations + 1
212
+ )
209
213
 
210
214
  @property
211
215
  def departure(self):
@@ -246,8 +250,7 @@ class RegionalTask:
246
250
 
247
251
  @property
248
252
  def departure_time_window(self):
249
- """Find public transport connections leaving within
250
- ``departure_time_window`` after ``departure`` (datetime.timedelta).
253
+ """Find public transport connections leaving within ``departure_time_window`` after ``departure`` (datetime.timedelta).
251
254
 
252
255
  **Note:** The value of ``departure_time_window`` should be set with some
253
256
  caution. Specifically, setting values near or below the typical headways
r5py/r5/street_layer.py CHANGED
@@ -40,6 +40,7 @@ class StreetLayer:
40
40
 
41
41
  @functools.cached_property
42
42
  def extent(self):
43
+ """The geographic area covered, as a `shapely.box`."""
43
44
  envelope = self._street_layer.envelope
44
45
  return shapely.box(
45
46
  envelope.getMinX(),
r5py/r5/transfer_leg.py CHANGED
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
3
 
4
- """Represent one leg of a trip, specifically transfers between public transport
5
- vehicles."""
4
+ """Represent one leg of a trip, specifically transfers between public transport vehicles."""
6
5
 
7
6
 
8
7
  from .direct_leg import DirectLeg
@@ -12,7 +11,4 @@ __all__ = ["TransferLeg"]
12
11
 
13
12
 
14
13
  class TransferLeg(DirectLeg):
15
- """
16
- Represent one leg of a trip, specifically transfers between public transport
17
- vehicles.
18
- """
14
+ """Represent one leg of a trip, specifically transfers between public transport vehicles."""
r5py/r5/transit_layer.py CHANGED
@@ -90,10 +90,12 @@ class TransitLayer:
90
90
 
91
91
  @functools.cached_property
92
92
  def routes(self):
93
+ """Return a list of GTFS routes."""
93
94
  return list(self._transit_layer.routes)
94
95
 
95
96
  @functools.cached_property
96
97
  def trip_patterns(self):
98
+ """Return a list of GTFS trip patterns."""
97
99
  return list(self._transit_layer.tripPatterns)
98
100
 
99
101
 
r5py/r5/transit_leg.py CHANGED
@@ -11,8 +11,4 @@ __all__ = ["TransitLeg"]
11
11
 
12
12
 
13
13
  class TransitLeg(TripLeg):
14
- """
15
- Represent one leg of a public transport trip.
16
- """
17
-
18
- pass
14
+ """Represent one leg of a public transport trip."""
r5py/r5/transport_mode.py CHANGED
@@ -68,6 +68,7 @@ class TransportMode(enum.Enum):
68
68
  return None
69
69
 
70
70
  def __add__(self, other):
71
+ """Combine two transport modes."""
71
72
  if isinstance(other, self.__class__):
72
73
  return [self, other]
73
74
  elif isinstance(other, list):
@@ -78,6 +79,7 @@ class TransportMode(enum.Enum):
78
79
  )
79
80
 
80
81
  def __radd__(self, other):
82
+ """Combine two transport modes."""
81
83
  if other == 0: # first iteration of sum()
82
84
  return self
83
85
  elif isinstance(other, list):
@@ -87,17 +89,17 @@ class TransportMode(enum.Enum):
87
89
 
88
90
  @property
89
91
  def is_leg_mode(self):
90
- """Can this TransportMode function as a LegMode?"""
92
+ """Can this TransportMode function as a LegMode?."""
91
93
  return self.name in LEG_MODES
92
94
 
93
95
  @property
94
96
  def is_street_mode(self):
95
- """Can this TransportMode function as a StreetMode?"""
97
+ """Can this TransportMode function as a StreetMode?."""
96
98
  return self.name in STREET_MODES
97
99
 
98
100
  @property
99
101
  def is_transit_mode(self):
100
- """Can this TransportMode function as a TransitMode?"""
102
+ """Can this TransportMode function as a TransitMode?."""
101
103
  return self.name in TRANSIT_MODES
102
104
 
103
105
  AIR = "AIR"
@@ -89,9 +89,7 @@ class TransportNetwork:
89
89
  self._transport_network = transport_network
90
90
 
91
91
  def __del__(self):
92
- """
93
- Delete all temporary files upon destruction.
94
- """
92
+ """Delete all temporary files upon destruction."""
95
93
  MAX_TRIES = 10
96
94
 
97
95
  # first, close the open osm_file,
@@ -110,13 +108,23 @@ class TransportNetwork:
110
108
  del self.transit_layer
111
109
  except AttributeError:
112
110
  pass
113
- del self._transport_network
111
+ try:
112
+ del self._transport_network
113
+ except AttributeError:
114
+ pass
114
115
 
115
- time.sleep(0.5)
116
- jpype.java.lang.System.gc()
116
+ time.sleep(1.0)
117
+ try:
118
+ jpype.java.lang.System.gc()
119
+ except jpype.JVMNotRunning:
120
+ pass
117
121
 
118
122
  # then, try to delete all files in cache directory
119
- temporary_files = [child for child in self._cache_directory.iterdir()]
123
+ try:
124
+ temporary_files = [child for child in self._cache_directory.iterdir()]
125
+ except FileNotFoundError: # deleted in the meantime/race condition
126
+ temporary_files = []
127
+
120
128
  for _ in range(MAX_TRIES):
121
129
  for temporary_file in temporary_files:
122
130
  try:
@@ -206,6 +214,7 @@ class TransportNetwork:
206
214
 
207
215
  @property
208
216
  def extent(self):
217
+ """The geographic area covered, as a `shapely.box`."""
209
218
  # TODO: figure out how to get the extent of the GTFS schedule,
210
219
  # then find the smaller extent of the two (or the larger one?)
211
220
  return self.street_layer.extent
@@ -4,7 +4,6 @@
4
4
 
5
5
  import copy
6
6
 
7
- import joblib
8
7
  import pandas
9
8
 
10
9
  from .base_travel_time_matrix_computer import BaseTravelTimeMatrixComputer
@@ -40,20 +39,10 @@ class TravelTimeMatrixComputer(BaseTravelTimeMatrixComputer):
40
39
  self._prepare_origins_destinations()
41
40
  self.request.destinations = self.destinations
42
41
 
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
- )
42
+ od_matrix = pandas.concat(
43
+ [self._travel_times_per_origin(from_id) for from_id in self.origins.id],
44
+ ignore_index=True,
45
+ )
57
46
 
58
47
  try:
59
48
  od_matrix = od_matrix.to_crs(self._origins_crs)
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__}: "
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
 
@@ -28,12 +33,12 @@ class TripLeg:
28
33
  def __init__(
29
34
  self,
30
35
  transport_mode=None,
31
- departure_time=None,
36
+ departure_time=numpy.datetime64("NaT"),
32
37
  distance=None,
33
- travel_time=None,
34
- wait_time=None,
38
+ travel_time=datetime.timedelta(seconds=0),
39
+ wait_time=datetime.timedelta(seconds=0),
35
40
  route=None,
36
- geometry=None,
41
+ geometry=shapely.LineString(),
37
42
  ):
38
43
  """
39
44
  Represent one leg of a trip.
@@ -66,6 +71,7 @@ class TripLeg:
66
71
  self.geometry = geometry
67
72
 
68
73
  def __add__(self, other):
74
+ """Trip-chain `other` to `self`."""
69
75
  from .trip import Trip
70
76
 
71
77
  if isinstance(other, self.__class__):
@@ -80,6 +86,7 @@ class TripLeg:
80
86
  )
81
87
 
82
88
  def __radd__(self, other):
89
+ """Trip-chain `self` to `other`."""
83
90
  from .trip import Trip
84
91
 
85
92
  if other == 0: # first iteration of sum()
@@ -90,31 +97,43 @@ class TripLeg:
90
97
  else:
91
98
  return self.__add__(other)
92
99
 
100
+ def __eq__(self, other):
101
+ """Check if `other` is an equal `TripLeg`."""
102
+ if isinstance(other, self.__class__):
103
+ return False not in [
104
+ self._are_columns_equal(other, column) for column in self.COLUMNS
105
+ ]
106
+
93
107
  def __gt__(self, other):
108
+ """Check if `other` has a longer travel time."""
94
109
  if isinstance(other, TripLeg):
95
110
  return (self.travel_time + self.wait_time) > (
96
111
  other.travel_time + other.wait_time
97
112
  )
98
113
 
99
114
  def __ge__(self, other):
115
+ """Check if `other` has a longer or equal travel time."""
100
116
  if isinstance(other, TripLeg):
101
117
  return (self.travel_time + self.wait_time) >= (
102
118
  other.travel_time + other.wait_time
103
119
  )
104
120
 
105
121
  def __lt__(self, other):
122
+ """Check if `other` has a shorter travel time."""
106
123
  if isinstance(other, TripLeg):
107
124
  return (self.travel_time + self.wait_time) < (
108
125
  other.travel_time + other.wait_time
109
126
  )
110
127
 
111
128
  def __le__(self, other):
129
+ """Check if `other` has a shorter or equal travel time."""
112
130
  if isinstance(other, TripLeg):
113
131
  return (self.travel_time + self.wait_time) <= (
114
132
  other.travel_time + other.wait_time
115
133
  )
116
134
 
117
135
  def __repr__(self):
136
+ """Return a string representation."""
118
137
  try:
119
138
  first_point = self.geometry.coords[0]
120
139
  last_point = self.geometry.coords[-1]
@@ -127,10 +146,30 @@ class TripLeg:
127
146
  f"{first_point} -> {last_point}"
128
147
  ">"
129
148
  )
130
- except AttributeError:
149
+ except (AttributeError, IndexError):
131
150
  _repr = f"<{self.__class__.__name__}>"
132
151
  return _repr
133
152
 
153
+ def _are_columns_equal(self, other, column):
154
+ """
155
+ Check if a column equals the same column of a different `Trip`.
156
+
157
+ Compare if attribute `column` of self equals attribute `column` of
158
+ other. Also True if both values are None or NaN or NaT.
159
+ """
160
+ self_column = getattr(self, column)
161
+ other_column = getattr(other, column)
162
+
163
+ return (
164
+ self_column == other_column
165
+ or (self_column is None and other_column is None)
166
+ or (self_column == numpy.nan and other_column == numpy.nan)
167
+ or (
168
+ self_column == numpy.datetime64("NaT")
169
+ and other_column == numpy.datetime64("NaT")
170
+ )
171
+ )
172
+
134
173
  def as_table_row(self):
135
174
  """
136
175
  Return a table row (list) of this trip leg’s details.
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:
@@ -214,8 +235,8 @@ class TripPlanner:
214
235
  com.conveyal.r5.profile.McRaptorSuboptimalPathProfileRouter(
215
236
  self.transport_network,
216
237
  request,
217
- self.transit_access_times,
218
- self.transit_egress_times,
238
+ self._transit_access_times,
239
+ self._transit_egress_times,
219
240
  list_supplier_callback,
220
241
  None,
221
242
  True,
@@ -234,18 +255,26 @@ class TripPlanner:
234
255
  for state in list(states) # some departure times yield no results
235
256
  }
236
257
 
258
+ # keep another cache layer of shortest access and egress legs
259
+ access_legs_by_stop = {}
260
+ egress_legs_by_stop = {}
261
+
237
262
  for departure_time, state in final_states.items():
238
263
  trip = Trip()
239
264
  while state:
240
265
  if state.stop == -1: # EgressLeg
241
- leg = min(
242
- [
243
- self.transit_egress_paths[transport_mode][
244
- state.back.stop
266
+ try:
267
+ leg = egress_legs_by_stop[state.back.stop]
268
+ except KeyError:
269
+ leg = min(
270
+ [
271
+ self._transit_egress_paths[transport_mode][
272
+ state.back.stop
273
+ ]
274
+ for transport_mode in self._transit_egress_paths.keys()
245
275
  ]
246
- for transport_mode in self.transit_egress_paths.keys()
247
- ]
248
- )
276
+ )
277
+ egress_legs_by_stop[state.back.stop] = leg
249
278
  leg.wait_time = ZERO_SECONDS
250
279
  leg.departure_time = (
251
280
  midnight
@@ -255,14 +284,18 @@ class TripPlanner:
255
284
  leg.arrival_time = leg.departure_time + leg.travel_time
256
285
 
257
286
  elif state.back is None: # AccessLeg
258
- leg = min(
259
- [
260
- self.transit_access_paths[transport_mode][
261
- state.stop
287
+ try:
288
+ leg = access_legs_by_stop[state.stop]
289
+ except KeyError:
290
+ leg = min(
291
+ [
292
+ self._transit_access_paths[transport_mode][
293
+ state.stop
294
+ ]
295
+ for transport_mode in self._transit_access_paths.keys()
262
296
  ]
263
- for transport_mode in self.transit_access_paths.keys()
264
- ]
265
- )
297
+ )
298
+ access_legs_by_stop[state.stop] = leg
266
299
  leg.wait_time = ZERO_SECONDS
267
300
  leg.arrival_time = midnight + datetime.timedelta(
268
301
  seconds=state.time
@@ -274,7 +307,7 @@ class TripPlanner:
274
307
  departure_stop = state.back.stop
275
308
  arrival_stop = state.stop
276
309
 
277
- leg = self.transit_transfer_path(
310
+ leg = self._transit_transfer_path(
278
311
  departure_stop, arrival_stop
279
312
  )
280
313
 
@@ -361,12 +394,14 @@ class TripPlanner:
361
394
  trip = leg + trip
362
395
  state = state.back
363
396
 
364
- transit_paths.append(trip)
397
+ # R5 sometimes reports the same path more than once, skip duplicates
398
+ if trip not in transit_paths:
399
+ transit_paths.append(trip)
365
400
 
366
401
  return transit_paths
367
402
 
368
403
  @functools.cached_property
369
- def transit_access_paths(self):
404
+ def _transit_access_paths(self):
370
405
  access_paths = {}
371
406
 
372
407
  request = copy.copy(self.request)
@@ -406,9 +441,12 @@ class TripPlanner:
406
441
  return access_paths
407
442
 
408
443
  @functools.cached_property
409
- def transit_access_times(self):
410
- """Times to reached stops in the format required by
411
- McRaptorSuboptimalPathProfileRouter."""
444
+ def _transit_access_times(self):
445
+ """
446
+ Times to reached stops.
447
+
448
+ In the format required by McRaptorSuboptimalPathProfileRouter.
449
+ """
412
450
  access_times = jpype.JObject(
413
451
  {
414
452
  com.conveyal.r5.api.util.LegMode
@@ -419,14 +457,14 @@ class TripPlanner:
419
457
  for transfer_leg in reached_stops.values()
420
458
  ],
421
459
  )
422
- for mode, reached_stops in self.transit_access_paths.items()
460
+ for mode, reached_stops in self._transit_access_paths.items()
423
461
  },
424
462
  "java.util.Map<com.conveyal.r5.LegMode, gnu.trove.map.TIntIntMap>",
425
463
  )
426
464
  return access_times
427
465
 
428
466
  @functools.cached_property
429
- def transit_egress_paths(self):
467
+ def _transit_egress_paths(self):
430
468
  egress_paths = {}
431
469
 
432
470
  request = copy.copy(self.request)
@@ -467,9 +505,12 @@ class TripPlanner:
467
505
  return egress_paths
468
506
 
469
507
  @functools.cached_property
470
- def transit_egress_times(self):
471
- """Times to reached stops in the format required by
472
- McRaptorSuboptimalPathProfileRouter."""
508
+ def _transit_egress_times(self):
509
+ """
510
+ Times to reached stops.
511
+
512
+ In the format required by McRaptorSuboptimalPathProfileRouter.
513
+ """
473
514
  egress_times = jpype.JObject(
474
515
  {
475
516
  com.conveyal.r5.api.util.LegMode
@@ -480,13 +521,14 @@ class TripPlanner:
480
521
  for transfer_leg in reached_stops.values()
481
522
  ],
482
523
  )
483
- for mode, reached_stops in self.transit_egress_paths.items()
524
+ for mode, reached_stops in self._transit_egress_paths.items()
484
525
  },
485
526
  "java.util.Map<com.conveyal.r5.LegMode, gnu.trove.map.TIntIntMap>",
486
527
  )
487
528
  return egress_times
488
529
 
489
- def transit_transfer_path(self, from_stop, to_stop):
530
+ def _transit_transfer_path(self, from_stop, to_stop):
531
+ """Find a transfer path between two transit stops."""
490
532
  self._transfer_paths = {}
491
533
  while True:
492
534
  try:
@@ -524,8 +566,8 @@ class TripPlanner:
524
566
  TransportMode.WALK,
525
567
  )
526
568
 
527
- transfer_path = self._transfer_paths[
528
- (from_stop, to_stop)
529
- ] = TransferLeg(TransportMode.WALK, street_segment)
569
+ transfer_path = self._transfer_paths[(from_stop, to_stop)] = (
570
+ TransferLeg(TransportMode.WALK, street_segment)
571
+ )
530
572
 
531
573
  return transfer_path
r5py/util/__init__.py CHANGED
@@ -2,6 +2,8 @@
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
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 = "https://github.com/conveyal/r5/releases/download/v6.9/r5-v6.9-all.jar"
21
- R5_JAR_SHA256 = "a7e1c5ff8786a9fb9191073b8f31a6933b862f44b9ff85b2c00a68c85491274d"
20
+ R5_JAR_URL = (
21
+ "https://github.com/r5py/r5/releases/download/v7.1-r5py/r5-v7.1-r5py-all.jar"
22
+ )
23
+ R5_JAR_SHA256 = "cd697b50323fd99977c98039ea317698bcf5fbbdb12b59e3e094ae9443648db2"
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 ValidatingRequestsSession() as session, session.get(
77
- R5_JAR_URL, R5_JAR_SHA256
78
- ) as response, open(r5_classpath, "wb") as jar:
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(
r5py/util/config.py CHANGED
@@ -31,7 +31,6 @@ class Config:
31
31
 
32
32
  def __init__(self):
33
33
  """Load configuration from config files or command line arguments."""
34
-
35
34
  self.argparser.add(
36
35
  "-v",
37
36
  "--verbose",
@@ -40,6 +39,7 @@ class Config:
40
39
  )
41
40
 
42
41
  def __new__(cls):
42
+ """Load configuration from config files or command line arguments."""
43
43
  if cls._instance is None:
44
44
  cls._instance = super(Config, cls).__new__(cls)
45
45
  return cls._instance
@@ -55,6 +55,7 @@ class Config:
55
55
 
56
56
  @property
57
57
  def argparser(self):
58
+ """Return a singleton instance of a `configargparse.ArgumentParser`."""
58
59
  try:
59
60
  argparser = configargparse.get_argument_parser(
60
61
  prog=PACKAGE,
@@ -67,6 +68,7 @@ class Config:
67
68
 
68
69
  @functools.cached_property
69
70
  def CACHE_DIR(self):
71
+ """Save persistent cache files into this directory."""
70
72
  cache_dir = (
71
73
  pathlib.Path(
72
74
  os.environ.get("LOCALAPPDATA")
@@ -80,6 +82,7 @@ class Config:
80
82
 
81
83
  @functools.cached_property
82
84
  def CONFIG_FILES(self):
85
+ """List locations of potential configuration files."""
83
86
  config_files = [
84
87
  pathlib.Path(f"/etc/{PACKAGE}.yml"),
85
88
  pathlib.Path(
@@ -130,8 +133,14 @@ class Config:
130
133
 
131
134
  @functools.cached_property
132
135
  def TEMP_DIR(self):
136
+ """
137
+ Save temporary files to this directory.
138
+
139
+ read-only property,
140
+ use command-line option `--temporary-directory` to change.
141
+ """
133
142
  parent_dir = self.arguments.temporary_directory
134
- temp_dir = pathlib.Path(tempfile.mkdtemp(prefix=self.PACKAGE), dir=parent_dir)
143
+ temp_dir = pathlib.Path(tempfile.mkdtemp(prefix=self.PACKAGE, dir=parent_dir))
135
144
  return temp_dir
136
145
 
137
146
  PACKAGE = PACKAGE
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env python3
2
+
3
+
4
+ """Normalise some environment variables that might not always get set."""
5
+
6
+
7
+ import os
8
+ import pathlib
9
+
10
+
11
+ # if a readthedocs runner uses a conda environment, it fails to
12
+ # properly initialise the JAVA_HOME and PROJ_LIB environment variables
13
+ #
14
+ # this might happen on other installation, so let’s keep this as general
15
+ # as possible.
16
+ #
17
+ # As readthedocs also does not export CONDA_PREFIX, we first reconstruct
18
+ # it from CONDA_ENVS_PATH and CONDA_DEFAULT_ENV
19
+ if (
20
+ "CONDA_PREFIX" not in os.environ
21
+ and "CONDA_DEFAULT_ENV" in os.environ
22
+ and "CONDA_ENVS_PATH" in os.environ
23
+ ):
24
+ os.environ["CONDA_PREFIX"] = str(
25
+ pathlib.Path(os.environ["CONDA_ENVS_PATH"]) / os.environ["CONDA_DEFAULT_ENV"]
26
+ )
27
+ if "JAVA_HOME" not in os.environ and "CONDA_PREFIX" in os.environ:
28
+ os.environ["JAVA_HOME"] = str(
29
+ pathlib.Path(os.environ["CONDA_PREFIX"]) / "lib" / "jvm"
30
+ )
31
+ if "PROJ_LIB" not in os.environ and "CONDA_PREFIX" in os.environ:
32
+ os.environ["PROJ_LIB"] = str(
33
+ pathlib.Path(os.environ["CONDA_PREFIX"]) / "share" / "proj"
34
+ )
@@ -31,7 +31,6 @@ class GoodEnoughEquidistantCrs(pyproj.CRS):
31
31
  The geographical extent for which to find an equidistant reference
32
32
  system, in `EPSG:4326`
33
33
  """
34
-
35
34
  if GoodEnoughEquidistantCrs._is_plausible_in_epsg4326(extent):
36
35
  # default CRS in case we do not find any better match
37
36
  crs = pyproj.CRS.from_epsg(3857)
@@ -62,9 +62,9 @@ def _share_of_ram(share=0.8, leave_at_least=(2 * 1024**3)):
62
62
 
63
63
  def _parse_value_and_unit(value_and_unit, max_unit_length=1):
64
64
  """
65
- Extract value and unit from a string containing a possible
66
- (non-numeric) unit suffix.
65
+ Extract value and unit from a string.
67
66
 
67
+ The string is allowed to contain a (non-numeric) unit suffix.
68
68
  For instance, input values of `'1M'` or `3.732G` would yield return
69
69
  values `(1, 'M')` or `(3.732, 'G')`, respectively.
70
70
 
@@ -112,11 +112,10 @@ def _interpret_power_of_two_units(value, unit):
112
112
  int:
113
113
  interpreted value in bytes
114
114
  """
115
-
116
- SUFFIXES = " KMGTPEZY"
117
115
  # the position of each suffix in this string is the unit’s exponent
118
116
  # over 1024.
119
117
  # Compare https://en.wikipedia.org/wiki/ISO%2FIEC_80000#Part_13:_Information_science_and_technology
118
+ SUFFIXES = " KMGTPEZY"
120
119
 
121
120
  if unit is None:
122
121
  unit = " "
@@ -152,7 +151,6 @@ def _get_max_memory(max_memory):
152
151
  int
153
152
  Maximum amount of memory allocated for R5 in bytes.
154
153
  """
155
-
156
154
  try:
157
155
  value, unit = _parse_value_and_unit(max_memory)
158
156
  except TypeError:
@@ -25,6 +25,7 @@ class SampleDataSet(pathlib.Path):
25
25
  _CACHE_DIR = pathlib.Path(config.CACHE_DIR) / "sampledata"
26
26
 
27
27
  def __new__(cls, remote_url, sha256_checksum):
28
+ """Define a data set that is downloaded and cached on demand."""
28
29
  # pathlib.Path does everything in __new__, rather than __init__
29
30
  cached_path = cls._CACHE_DIR / pathlib.Path(remote_url).name
30
31
  return super().__new__(cls, cached_path)
@@ -40,10 +41,16 @@ class SampleDataSet(pathlib.Path):
40
41
  sha256_checksum : str
41
42
  checksum for this data set, using an SHA256 algorithm
42
43
  """
43
- super().__init__()
44
+ cached_path = self._CACHE_DIR / pathlib.Path(remote_url).name
45
+
46
+ try: # Python>=3.12
47
+ super().__init__(cached_path)
48
+ except TypeError:
49
+ super().__init__()
50
+
44
51
  self.remote_url = remote_url
45
52
  self.checksum = sha256_checksum
46
- self.cached_path = self._CACHE_DIR / pathlib.Path(remote_url).name
53
+ self.cached_path = cached_path
47
54
  self._download_remote_file()
48
55
 
49
56
  def _download_remote_file(self):
@@ -60,7 +67,8 @@ class SampleDataSet(pathlib.Path):
60
67
  RuntimeWarning,
61
68
  )
62
69
  self.cached_path.parent.mkdir(exist_ok=True)
63
- with ValidatingRequestsSession() as session, session.get(
64
- self.remote_url, self.checksum
65
- ) as response:
70
+ with (
71
+ ValidatingRequestsSession() as session,
72
+ session.get(self.remote_url, self.checksum) as response,
73
+ ):
66
74
  self.cached_path.write_bytes(response.content)
@@ -30,12 +30,12 @@ class ValidatingRequestsSession(requests.Session):
30
30
  self._algorithm = checksum_algorithm
31
31
 
32
32
  def get(self, url, checksum, **kwargs):
33
- """Sends a GET request, tests checksum."""
33
+ """Send a GET request, tests checksum."""
34
34
  kwargs.setdefault("allow_redirects", True)
35
35
  return self.request("GET", url, checksum, **kwargs)
36
36
 
37
37
  def post(self, url, checksum, **kwargs):
38
- """Sends a POST request, tests checksum."""
38
+ """Send a POST request, tests checksum."""
39
39
  return self.request("POST", url, checksum, **kwargs)
40
40
 
41
41
  # delete, put, head don’t return data,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: r5py
3
- Version: 0.1.1.dev2
3
+ Version: 0.1.2
4
4
  Summary: Python wrapper for the R5 routing analysis engine
5
5
  Author: Willem Klumpenhouwer, Marcus Sairava, Rafael Pereira, Henrikki Tenkanen
6
6
  Author-email: Christoph Fink <christoph.fink@helsinki.fi>
@@ -41,8 +41,8 @@ Requires-Dist: matplotlib ; extra == 'docs'
41
41
  Requires-Dist: myst-nb ; extra == 'docs'
42
42
  Requires-Dist: nbsphinx ; extra == 'docs'
43
43
  Requires-Dist: pybtex-apa7-style ; extra == 'docs'
44
- Requires-Dist: r5py.sampledata.helsinki >=0.1.1.dev2 ; extra == 'docs'
45
- Requires-Dist: r5py.sampledata.sao-paulo >=0.1.1.dev2 ; extra == 'docs'
44
+ Requires-Dist: r5py.sampledata.helsinki >=0.1.1 ; extra == 'docs'
45
+ Requires-Dist: r5py.sampledata.sao-paulo >=0.1.1 ; extra == 'docs'
46
46
  Requires-Dist: shapely ; extra == 'docs'
47
47
  Requires-Dist: sphinx ; extra == 'docs'
48
48
  Requires-Dist: sphinx-book-theme ; extra == 'docs'
@@ -50,12 +50,13 @@ Requires-Dist: sphinx-design ; extra == 'docs'
50
50
  Requires-Dist: sphinxcontrib-bibtex ; extra == 'docs'
51
51
  Requires-Dist: sphinxcontrib-images ; extra == 'docs'
52
52
  Provides-Extra: tests
53
+ Requires-Dist: pyarrow ; extra == 'tests'
53
54
  Requires-Dist: pytest ; extra == 'tests'
54
55
  Requires-Dist: pytest-asyncio ; extra == 'tests'
55
56
  Requires-Dist: pytest-cov ; extra == 'tests'
56
- Requires-Dist: pytest-lazy-fixture ; extra == 'tests'
57
- Requires-Dist: r5py.sampledata.helsinki >=0.1.1.dev2 ; extra == 'tests'
58
- Requires-Dist: r5py.sampledata.sao-paulo >=0.1.1.dev2 ; extra == 'tests'
57
+ Requires-Dist: pytest-lazy-fixtures ; extra == 'tests'
58
+ Requires-Dist: r5py.sampledata.helsinki >=0.1.1 ; extra == 'tests'
59
+ Requires-Dist: r5py.sampledata.sao-paulo >=0.1.1 ; extra == 'tests'
59
60
 
60
61
  <img class="r5py_logo" align="right" src="https://github.com/r5py/r5py/raw/main/docs/_static/images/r5py_blue.svg" alt="r5py logo" style="width:180px; max-width:30vW;">
61
62
 
@@ -0,0 +1,43 @@
1
+ r5py/__init__.py,sha256=CvtwufBhIEjDtiNGgSjbvb6mtHtmDAmzOmRYtdOm49M,414
2
+ r5py/__main__.py,sha256=Wvn0ChD7E-dCSZ8b8k_HhHG0KMOk0qMNFkijGuSH3-0,81
3
+ r5py/r5/__init__.py,sha256=xwk-xAck8osVgIW7sSML04c5JuB5y9a23Gym0G6cV4w,999
4
+ r5py/r5/access_leg.py,sha256=W3GfPEpqmWD1c4xipd6UcVIaBC-yb6srGCZV30E2dPY,293
5
+ r5py/r5/base_travel_time_matrix_computer.py,sha256=qxaNyw4AuzLfCi7Yll8M_Y37v6pCoZsQPEZ3WKLhpkc,6454
6
+ r5py/r5/breakdown_stat.py,sha256=ZQkWA0hXlcRH3KVgtxPSNHP0FUDri8MWqdFk8EUdDMU,533
7
+ r5py/r5/detailed_itineraries_computer.py,sha256=6PzypGs5bOJv_NJ2AIm77u7uECzhm6BCF0lUPKlCXrs,8525
8
+ r5py/r5/direct_leg.py,sha256=T7wX8puhOVIssCpflXthYs-G9OA8pasFbdz9p8k8teg,1054
9
+ r5py/r5/egress_leg.py,sha256=9rsCIcwlZUzoZE6q4imNY3VWpjJepO1IJvheVrlPi90,297
10
+ r5py/r5/regional_task.py,sha256=33PptFZziTPuwAlbBsyTqpAtl1K6HT9sjKLc_CxzRzY,23250
11
+ r5py/r5/scenario.py,sha256=nUNAlN3cO7E_b4sMpNqdL0FD7WQaQ49iIvh-k8l4YRM,763
12
+ r5py/r5/street_layer.py,sha256=iGlAWftzmwzaRUpXngis7prVuH3Oq8i-AXS8-pnVXMk,2259
13
+ r5py/r5/transfer_leg.py,sha256=_IpzQJAyW4hDPO5V4k-ZjIPd3uyxhHPa4U6_b8UbKt4,311
14
+ r5py/r5/transit_layer.py,sha256=Z2quvw19IqmB6eeCFbavYp8TFmo3YVWhlk0ifCWbDng,3023
15
+ r5py/r5/transit_leg.py,sha256=R0Qc9YLMEXYu51NIdo7Q0bdmpYIJf5irEDXWrW6pZWE,221
16
+ r5py/r5/transport_mode.py,sha256=zHSqXb0R4oyjTp069CzO69IgoCKt0nmOAwsSy272rGo,3675
17
+ r5py/r5/transport_network.py,sha256=d4PPBEBk3t2QbUI5KMS9zM-a4s4E4zEYOHIV6txCnYg,10777
18
+ r5py/r5/travel_time_matrix_computer.py,sha256=H_zsCfDlBX7LKJcTJZH92TY96P1nTCl9zDdCs78d7Wo,4463
19
+ r5py/r5/trip.py,sha256=ZsEshy4BflMHZQoq-LIFauvYbn4T7gLD-Aa91rV45m8,2854
20
+ r5py/r5/trip_leg.py,sha256=63DVgIYKc_X4nC7oYF6OMRkFeaL5kQy2zRnfkGX3ALc,5642
21
+ r5py/r5/trip_planner.py,sha256=hwarUMO29zw4-nPLbGtA6sxXUovNxVu8KBvo_Bkay54,22937
22
+ r5py/sampledata/_keep/__init__.py,sha256=Dd14TxWipq66sLK3ponMl09SbtzWoqmD-dhbTuS889M,114
23
+ r5py/util/__init__.py,sha256=S-agt-08twU7hFIH1_x_VjuNC-WHfP6844n0xM0E8t8,714
24
+ r5py/util/camel_to_snake_case.py,sha256=zj5F3PNBvsuS6vqN4USeeo8NI-3hnscGhwun0G95AK0,673
25
+ r5py/util/classpath.py,sha256=lqyoJaL2VI3Gu3s9sspKS8BDlmzH2dqA7k4VjJoNwdA,2773
26
+ r5py/util/config.py,sha256=TpT67KZoHj17USjmGgiyH7HKa757CFXVk5nnBEio8E0,4667
27
+ r5py/util/contains_gtfs_data.py,sha256=ooX4hfVDKK0aqX1MI46jSFZ7dZ6riyXaORrgF6PUFrk,1211
28
+ r5py/util/data_validation.py,sha256=H5Mcp2nS4vu5RKym20mPnGpl-8d0SDchzDRJBrrL6WE,1039
29
+ r5py/util/environment.py,sha256=cbSM8TKTuhbXsTIIB06pMtydBOiqLkitF2Lj2asVTho,1082
30
+ r5py/util/exceptions.py,sha256=r65XUg_AJ_bTw8ARNj7A2-GbFZlSTrOAjDynx1pSD2Y,1049
31
+ r5py/util/good_enough_equidistant_crs.py,sha256=1aqJLghNwcd2FbLfODcht_6pyOEqhsrE2KPaC3NLoek,2354
32
+ r5py/util/jvm.py,sha256=NCwoYLDznXydcIRAZl2kzUQA6D6NCvzjVG74pm6ioR0,5027
33
+ r5py/util/memory_footprint.py,sha256=p8efCUs4UXRg6P1GrRxVs71m7SpEw2mASoz6PVTRvgQ,4672
34
+ r5py/util/parse_int_date.py,sha256=JmnV8TwdUdUp3kSp2e73ZSxCbRyqv2FmQzNt0I_MsM0,667
35
+ r5py/util/sample_data_set.py,sha256=SRTFmhnhtwNCYtXRGPNKkh_lh3OEvgcyHS4Fz8N9uM8,2394
36
+ r5py/util/snake_to_camel_case.py,sha256=uJ5hTCVDUEmIxTyy4LGFTbpGC_rtnjDZVQ2vmVRTQ4k,485
37
+ r5py/util/validating_requests_session.py,sha256=sH5FgpS9eGax5DG2qA2GrGuiwgTJgh8tKsZ9OiXKmvk,1807
38
+ r5py/util/warnings.py,sha256=CvxKWKlNO_p3riB4SkNqbU5AGPsaY_3-OzqaBObE3B8,139
39
+ r5py-0.1.2.dist-info/LICENSE,sha256=VAnuGDX1TPylSN9G2xLa-urDpj_SQwn-qqs068dx4tk,51
40
+ r5py-0.1.2.dist-info/METADATA,sha256=8JDgZ6ZNX6a8hvLO7FPahfQxvN59DNz3hoTBHAw9suE,10015
41
+ r5py-0.1.2.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
42
+ r5py-0.1.2.dist-info/top_level.txt,sha256=fOH1R85dkNDOI7jkg-lIsl5CQIO4fE5X868K9dTqs9U,5
43
+ r5py-0.1.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.2)
2
+ Generator: setuptools (71.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,42 +0,0 @@
1
- r5py/__init__.py,sha256=eWYsVlnfyYPcIveYA3jP_niDewEPlrWau81hem9WybM,418
2
- r5py/__main__.py,sha256=5p_QWx8hsNbz34OwMKl9mY644bfqwXRFXkFSlTP06AM,239
3
- r5py/r5/__init__.py,sha256=gq-1XbdrHE4v3XAvyV-Amod-Vcgp_c1Sbs13mgxOD08,643
4
- r5py/r5/access_leg.py,sha256=W3GfPEpqmWD1c4xipd6UcVIaBC-yb6srGCZV30E2dPY,293
5
- r5py/r5/base_travel_time_matrix_computer.py,sha256=lnyI70dIGq0e9E1cvLJfF17hIw8eG8Qnt8WfXOwNFBo,6313
6
- r5py/r5/breakdown_stat.py,sha256=ZQkWA0hXlcRH3KVgtxPSNHP0FUDri8MWqdFk8EUdDMU,533
7
- r5py/r5/detailed_itineraries_computer.py,sha256=kRV8OBpvdsgYHcNGQzBi6Y3WyAlPCTSQn0HrWoBqvus,8483
8
- r5py/r5/direct_leg.py,sha256=opX575iuByoy8WORIsSvIgVqAwglBVCl15ZCo1pv_Mk,1064
9
- r5py/r5/egress_leg.py,sha256=9rsCIcwlZUzoZE6q4imNY3VWpjJepO1IJvheVrlPi90,297
10
- r5py/r5/regional_task.py,sha256=uzciavh9qWrMPYtixIdSR8hMxg4tyC98FnTYqpN4FME,23136
11
- r5py/r5/scenario.py,sha256=nUNAlN3cO7E_b4sMpNqdL0FD7WQaQ49iIvh-k8l4YRM,763
12
- r5py/r5/street_layer.py,sha256=aA8cBXvV60wH7WlEnwqbPXPpPxwQQsPPbW59HDUJrc0,2196
13
- r5py/r5/transfer_leg.py,sha256=m_9t1Kr8pq5rmtJz4XZSvRpog4_WpMtF2nKeybJ0v8U,325
14
- r5py/r5/transit_layer.py,sha256=znQcJmtFpqVcsvZziPDHxAcRS0OXvyn3JdWE_lXZv0A,2928
15
- r5py/r5/transit_leg.py,sha256=zd8QnMiOHCw3hS6WO5uwegxROdwDpqNDqvZwVJ2MlKo,241
16
- r5py/r5/transport_mode.py,sha256=YJj2CzZ0cz4hAu48udYtX8MnFuSx4he703xcP3BV_7I,3586
17
- r5py/r5/transport_network.py,sha256=ZWKHEhRkmpy_lQLvMjjjRqp_8ciP5kea7263Y4WwhG0,10470
18
- r5py/r5/travel_time_matrix_computer.py,sha256=ns4DnWXfyPpslwQlHGM5Vsmpdzvr8NF-hWaRgc8HYGc,4880
19
- r5py/r5/trip.py,sha256=SvXXQdvkCZzXcAbLyxKjCEwdbft_Jt7SPmPDzdw0K9w,2617
20
- r5py/r5/trip_leg.py,sha256=gto-VRo_AdUxdRDRlIey6f8jSZ021NI91BODTowp-wQ,4131
21
- r5py/r5/trip_planner.py,sha256=298IvJioFz4uj1L8qXJE5Ehrqcy51IP_2FoQfo3GEDc,21293
22
- r5py/sampledata/_keep/__init__.py,sha256=Dd14TxWipq66sLK3ponMl09SbtzWoqmD-dhbTuS889M,114
23
- r5py/util/__init__.py,sha256=moUcR_oxpITt2vdNQxuej6haFywjA62UcUY4T5TMDns,673
24
- r5py/util/camel_to_snake_case.py,sha256=zj5F3PNBvsuS6vqN4USeeo8NI-3hnscGhwun0G95AK0,673
25
- r5py/util/classpath.py,sha256=os4UmHEs2GGk0jnHSD_1wJHEjmpYQU3VAb6T_iUdFEw,2724
26
- r5py/util/config.py,sha256=M3eT4B2mB0CaFHQYn5DmtFTtSc4_RCK8fS3UuO7X2D8,4216
27
- r5py/util/contains_gtfs_data.py,sha256=ooX4hfVDKK0aqX1MI46jSFZ7dZ6riyXaORrgF6PUFrk,1211
28
- r5py/util/data_validation.py,sha256=H5Mcp2nS4vu5RKym20mPnGpl-8d0SDchzDRJBrrL6WE,1039
29
- r5py/util/exceptions.py,sha256=r65XUg_AJ_bTw8ARNj7A2-GbFZlSTrOAjDynx1pSD2Y,1049
30
- r5py/util/good_enough_equidistant_crs.py,sha256=VuzPGPTyL7T0Cl3SZba7atBTyyt9uUTLkq7JVMxjGsI,2355
31
- r5py/util/jvm.py,sha256=NCwoYLDznXydcIRAZl2kzUQA6D6NCvzjVG74pm6ioR0,5027
32
- r5py/util/memory_footprint.py,sha256=FlOLEAz7yI3YOv3wJe_tejJPh0y640QlDd0Z3Ce5i2s,4660
33
- r5py/util/parse_int_date.py,sha256=JmnV8TwdUdUp3kSp2e73ZSxCbRyqv2FmQzNt0I_MsM0,667
34
- r5py/util/sample_data_set.py,sha256=YQcDjcywria2hiR9A3cS8y3LmGsEAraQMDaGXFPg4JU,2165
35
- r5py/util/snake_to_camel_case.py,sha256=uJ5hTCVDUEmIxTyy4LGFTbpGC_rtnjDZVQ2vmVRTQ4k,485
36
- r5py/util/validating_requests_session.py,sha256=nkgOsZ_fbaP19R8l0ImZLFo5zUdw9-B289w0DEygtW0,1809
37
- r5py/util/warnings.py,sha256=CvxKWKlNO_p3riB4SkNqbU5AGPsaY_3-OzqaBObE3B8,139
38
- r5py-0.1.1.dev2.dist-info/LICENSE,sha256=VAnuGDX1TPylSN9G2xLa-urDpj_SQwn-qqs068dx4tk,51
39
- r5py-0.1.1.dev2.dist-info/METADATA,sha256=ZFy_vU_mdoQtSMduASSOhF8uIIL0Yq2PG0KxnuBVnCY,9997
40
- r5py-0.1.1.dev2.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
41
- r5py-0.1.1.dev2.dist-info/top_level.txt,sha256=fOH1R85dkNDOI7jkg-lIsl5CQIO4fE5X868K9dTqs9U,5
42
- r5py-0.1.1.dev2.dist-info/RECORD,,