r5py 0.1.1.dev2__tar.gz → 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of r5py might be problematic. Click here for more details.

Files changed (74) hide show
  1. {r5py-0.1.1.dev2/src/r5py.egg-info → r5py-0.1.2}/PKG-INFO +7 -6
  2. {r5py-0.1.1.dev2 → r5py-0.1.2}/pyproject.toml +6 -7
  3. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/__init__.py +2 -1
  4. r5py-0.1.2/src/r5py/__main__.py +3 -0
  5. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/__init__.py +14 -0
  6. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/base_travel_time_matrix_computer.py +3 -4
  7. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/detailed_itineraries_computer.py +6 -10
  8. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/direct_leg.py +1 -3
  9. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/regional_task.py +10 -7
  10. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/street_layer.py +1 -0
  11. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/transfer_leg.py +2 -6
  12. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/transit_layer.py +2 -0
  13. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/transit_leg.py +1 -5
  14. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/transport_mode.py +5 -3
  15. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/transport_network.py +16 -7
  16. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/travel_time_matrix_computer.py +4 -15
  17. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/trip.py +8 -4
  18. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/trip_leg.py +44 -5
  19. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/trip_planner.py +81 -39
  20. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/util/__init__.py +2 -0
  21. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/util/classpath.py +9 -5
  22. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/util/config.py +11 -2
  23. r5py-0.1.2/src/r5py/util/environment.py +34 -0
  24. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/util/good_enough_equidistant_crs.py +0 -1
  25. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/util/memory_footprint.py +3 -5
  26. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/util/sample_data_set.py +13 -5
  27. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/util/validating_requests_session.py +2 -2
  28. {r5py-0.1.1.dev2 → r5py-0.1.2/src/r5py.egg-info}/PKG-INFO +7 -6
  29. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py.egg-info/SOURCES.txt +1 -0
  30. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py.egg-info/requires.txt +6 -5
  31. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_breakdownstats.py +1 -1
  32. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_camel_to_snake_case.py +1 -1
  33. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_classpath.py +1 -1
  34. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_contains_gtfs_data.py +4 -3
  35. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_data_validation.py +5 -4
  36. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_detailed_itineraries_computer.py +36 -46
  37. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_java_casting.py +1 -1
  38. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_memory_footprint.py +1 -1
  39. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_regional_task.py +1 -1
  40. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_snake_to_camel_case.py +1 -1
  41. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_transport_mode.py +1 -1
  42. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_transport_network.py +13 -12
  43. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_travel_time_matrix_computer.py +36 -26
  44. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_validating_request_session.py +19 -13
  45. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_verbose_warnings.py +10 -4
  46. r5py-0.1.1.dev2/src/r5py/__main__.py +0 -16
  47. {r5py-0.1.1.dev2 → r5py-0.1.2}/LICENSE +0 -0
  48. {r5py-0.1.1.dev2 → r5py-0.1.2}/README.md +0 -0
  49. {r5py-0.1.1.dev2 → r5py-0.1.2}/setup.cfg +0 -0
  50. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/access_leg.py +0 -0
  51. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/breakdown_stat.py +0 -0
  52. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/egress_leg.py +0 -0
  53. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/r5/scenario.py +0 -0
  54. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/sampledata/_keep/__init__.py +0 -0
  55. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/util/camel_to_snake_case.py +0 -0
  56. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/util/contains_gtfs_data.py +0 -0
  57. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/util/data_validation.py +0 -0
  58. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/util/exceptions.py +0 -0
  59. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/util/jvm.py +0 -0
  60. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/util/parse_int_date.py +0 -0
  61. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/util/snake_to_camel_case.py +0 -0
  62. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py/util/warnings.py +0 -0
  63. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py.egg-info/dependency_links.txt +0 -0
  64. {r5py-0.1.1.dev2 → r5py-0.1.2}/src/r5py.egg-info/top_level.txt +0 -0
  65. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_config.py +0 -0
  66. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_deterministic_behaviour.py +0 -0
  67. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_good_enough_equidistant_crs.py +0 -0
  68. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_parse_int_date.py +0 -0
  69. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_sample_data_set.py +0 -0
  70. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_street_layer.py +0 -0
  71. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_transit_layer.py +0 -0
  72. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_trip.py +0 -0
  73. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_trip_leg.py +0 -0
  74. {r5py-0.1.1.dev2 → r5py-0.1.2}/tests/test_trip_planner.py +0 -0
@@ -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
 
@@ -46,13 +46,12 @@ dynamic = ["version"]
46
46
  [project.optional-dependencies]
47
47
  docs = ["contextily", "folium", "GitPython", "h3>=4.0.0b2", "jupyterlab_myst",
48
48
  "mapclassify", "matplotlib", "myst-nb", "nbsphinx", "pybtex-apa7-style",
49
- "r5py.sampledata.helsinki>=0.1.1.dev2",
50
- "r5py.sampledata.sao_paulo>=0.1.1.dev2", "shapely", "sphinx",
51
- "sphinx-book-theme", "sphinx-design", "sphinxcontrib-bibtex",
52
- "sphinxcontrib-images"]
53
- tests = ["pytest", "pytest-asyncio", "pytest-cov", "pytest-lazy-fixture",
54
- "r5py.sampledata.helsinki>=0.1.1.dev2",
55
- "r5py.sampledata.sao_paulo>=0.1.1.dev2"]
49
+ "r5py.sampledata.helsinki>=0.1.1", "r5py.sampledata.sao_paulo>=0.1.1",
50
+ "shapely", "sphinx", "sphinx-book-theme", "sphinx-design",
51
+ "sphinxcontrib-bibtex", "sphinxcontrib-images"]
52
+ tests = ["pyarrow", "pytest", "pytest-asyncio", "pytest-cov",
53
+ "pytest-lazy-fixtures", "r5py.sampledata.helsinki>=0.1.1",
54
+ "r5py.sampledata.sao_paulo>=0.1.1"]
56
55
 
57
56
 
58
57
  [project.urls]
@@ -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,
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """Python wrapper for the R5 routing analysis engine."""
@@ -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:
@@ -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
  """
@@ -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
@@ -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(),
@@ -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."""
@@ -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
 
@@ -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."""
@@ -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)
@@ -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__}: "
@@ -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.