r5py 1.1.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.
Files changed (49) hide show
  1. r5py/__init__.py +27 -0
  2. r5py/__main__.py +3 -0
  3. r5py/r5/__init__.py +39 -0
  4. r5py/r5/access_leg.py +12 -0
  5. r5py/r5/base_travel_time_matrix.py +255 -0
  6. r5py/r5/detailed_itineraries.py +226 -0
  7. r5py/r5/direct_leg.py +38 -0
  8. r5py/r5/egress_leg.py +12 -0
  9. r5py/r5/elevation_cost_function.py +50 -0
  10. r5py/r5/elevation_model.py +89 -0
  11. r5py/r5/file_storage.py +82 -0
  12. r5py/r5/isochrones.py +345 -0
  13. r5py/r5/regional_task.py +600 -0
  14. r5py/r5/scenario.py +36 -0
  15. r5py/r5/street_layer.py +90 -0
  16. r5py/r5/street_segment.py +39 -0
  17. r5py/r5/transfer_leg.py +12 -0
  18. r5py/r5/transit_layer.py +87 -0
  19. r5py/r5/transit_leg.py +12 -0
  20. r5py/r5/transport_mode.py +148 -0
  21. r5py/r5/transport_network.py +299 -0
  22. r5py/r5/travel_time_matrix.py +186 -0
  23. r5py/r5/trip.py +97 -0
  24. r5py/r5/trip_leg.py +204 -0
  25. r5py/r5/trip_planner.py +576 -0
  26. r5py/util/__init__.py +31 -0
  27. r5py/util/camel_to_snake_case.py +25 -0
  28. r5py/util/classpath.py +95 -0
  29. r5py/util/config.py +176 -0
  30. r5py/util/contains_gtfs_data.py +46 -0
  31. r5py/util/data_validation.py +28 -0
  32. r5py/util/environment.py +32 -0
  33. r5py/util/exceptions.py +43 -0
  34. r5py/util/file_digest.py +40 -0
  35. r5py/util/good_enough_equidistant_crs.py +73 -0
  36. r5py/util/jvm.py +138 -0
  37. r5py/util/memory_footprint.py +178 -0
  38. r5py/util/parse_int_date.py +24 -0
  39. r5py/util/sample_data_set.py +76 -0
  40. r5py/util/snake_to_camel_case.py +16 -0
  41. r5py/util/spatially_clustered_geodataframe.py +66 -0
  42. r5py/util/validating_requests_session.py +58 -0
  43. r5py/util/warnings.py +7 -0
  44. r5py/util/working_copy.py +42 -0
  45. r5py-1.1.0.dist-info/METADATA +176 -0
  46. r5py-1.1.0.dist-info/RECORD +49 -0
  47. r5py-1.1.0.dist-info/WHEEL +5 -0
  48. r5py-1.1.0.dist-info/licenses/LICENSE +3 -0
  49. r5py-1.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env python3
2
+
3
+
4
+ """Wraps a com.conveyal.r5.streets.StreetLayer."""
5
+
6
+ import functools
7
+
8
+ import jpype
9
+ import jpype.types
10
+ import shapely
11
+
12
+ from .transport_mode import TransportMode
13
+ from ..util import start_jvm
14
+
15
+ import com.conveyal.r5
16
+
17
+ __all__ = ["StreetLayer"]
18
+
19
+
20
+ start_jvm()
21
+
22
+
23
+ EMPTY_POINT = shapely.Point()
24
+
25
+
26
+ class StreetLayer:
27
+ """Wrap a com.conveyal.r5.streets.StreetLayer."""
28
+
29
+ @classmethod
30
+ def from_r5_street_layer(cls, street_layer):
31
+ """
32
+ Create a StreetLayer from a com.conveyal.r5.streets.StreetLayer.
33
+
34
+ Arguments
35
+ ---------
36
+ street_layer : com.conveyal.r5.streets.StreetLayer
37
+ """
38
+ instance = cls()
39
+ instance._street_layer = street_layer
40
+ return instance
41
+
42
+ @functools.cached_property
43
+ def extent(self):
44
+ """The geographic area covered, as a `shapely.box`."""
45
+ envelope = self._street_layer.envelope
46
+ return shapely.box(
47
+ envelope.getMinX(),
48
+ envelope.getMinY(),
49
+ envelope.getMaxX(),
50
+ envelope.getMaxY(),
51
+ )
52
+
53
+ def find_split(
54
+ self,
55
+ point,
56
+ radius=com.conveyal.r5.streets.StreetLayer.LINK_RADIUS_METERS,
57
+ street_mode=TransportMode.WALK,
58
+ ):
59
+ """
60
+ Find a location on an existing street near `point`.
61
+
62
+ Arguments
63
+ ---------
64
+ point : shapely.Point
65
+ Find a location close to this point
66
+ radius : float
67
+ Search radius around `point`
68
+ street_mode : travel mode that the snapped-to street should allow
69
+
70
+ Returns
71
+ -------
72
+ shapely.Point
73
+ Closest location on the street network or `POINT EMPTY` if no
74
+ such location could be found within `radius`
75
+ """
76
+ try:
77
+ split = self._street_layer.findSplit(point.y, point.x, radius, street_mode)
78
+ return shapely.Point(
79
+ split.fixedLon / com.conveyal.r5.streets.VertexStore.FIXED_FACTOR,
80
+ split.fixedLat / com.conveyal.r5.streets.VertexStore.FIXED_FACTOR,
81
+ )
82
+ except (AttributeError, TypeError):
83
+ return EMPTY_POINT
84
+
85
+
86
+ @jpype._jcustomizer.JConversion(
87
+ "com.conveyal.r5.streets.StreetLayer", exact=StreetLayer
88
+ )
89
+ def _cast_StreetLayer(java_class, object_):
90
+ return object_._street_layer
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """A less complex representation of com.conveyal.r5.api.util.StreetSegment."""
4
+
5
+ import datetime
6
+
7
+ import shapely
8
+
9
+ __all__ = ["StreetSegment"]
10
+
11
+
12
+ class StreetSegment:
13
+ """A less complex representation of com.conveyal.r5.api.util.StreetSegment."""
14
+
15
+ distance = 0
16
+ duration = datetime.timedelta()
17
+ geometry = shapely.LineString()
18
+
19
+ def __init__(self, street_path):
20
+ """
21
+ Initialise a less complex StreetSegment.
22
+
23
+ Arguments
24
+ ---------
25
+ street_path : com.conveyal.r5.profile.StreetPath
26
+ StreetPath, obtained, e.g., from StreetRouter state
27
+ """
28
+ self.distance = street_path.getDistance()
29
+ self.duration = street_path.getDuration()
30
+ self.geometry = shapely.line_merge(
31
+ shapely.MultiLineString(
32
+ [
33
+ shapely.from_wkt(
34
+ str(street_path.getEdge(edge).getGeometry().toText())
35
+ )
36
+ for edge in street_path.getEdges()
37
+ ]
38
+ )
39
+ )
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env python3
2
+
3
+
4
+ """Represent one leg of a trip: transfers between public transport vehicles."""
5
+
6
+ from .direct_leg import DirectLeg
7
+
8
+ __all__ = ["TransferLeg"]
9
+
10
+
11
+ class TransferLeg(DirectLeg):
12
+ """Represent one leg of a trip: transfers between public transport vehicles."""
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env python3
2
+
3
+
4
+ """Wraps a com.conveyal.r5.transit.TransitLayer."""
5
+
6
+ import functools
7
+
8
+ import jpype
9
+ import jpype.types
10
+
11
+ import java.time
12
+
13
+ __all__ = ["TransitLayer"]
14
+
15
+
16
+ class TransitLayer:
17
+ """Wrap a com.conveyal.r5.transit.TransitLayer."""
18
+
19
+ @classmethod
20
+ def from_r5_transit_layer(cls, transit_layer):
21
+ """
22
+ Create a TransitLayer from a com.conveyal.r5.transit.TransitLayer.
23
+
24
+ Arguments
25
+ ---------
26
+ transit_layer : com.conveyal.r5.transit.TransitLayer
27
+ """
28
+ instance = cls()
29
+ instance._transit_layer = transit_layer
30
+ return instance
31
+
32
+ def covers(self, date):
33
+ """
34
+ Check whether `date` is covered by GTFS data sets.
35
+
36
+ Arguments:
37
+ ----------
38
+ date : datetime.date
39
+ date for which to check whether a GTFS service exists.
40
+
41
+ Returns:
42
+ --------
43
+ bool
44
+ Whether or not any services exist on `date`.
45
+ """
46
+ date = java.time.LocalDate.of(date.year, date.month, date.day)
47
+ return True in set(
48
+ [service.activeOn(date) for service in self._transit_layer.services]
49
+ )
50
+
51
+ def get_street_vertex_for_stop(self, stop):
52
+ """
53
+ Get the street layer’s vertex corresponding to `stop`.
54
+
55
+ Arguments
56
+ ---------
57
+ stop : int
58
+ ID of the public transport stop for which to find a vertex
59
+
60
+ Returns
61
+ -------
62
+ int
63
+ ID of the vertex corresponding to the public transport stop
64
+ """
65
+ street_vertex = self._transit_layer.streetVertexForStop.get(stop)
66
+ return street_vertex
67
+
68
+ @functools.cached_property
69
+ def routes(self):
70
+ """Return a list of GTFS routes."""
71
+ return list(self._transit_layer.routes)
72
+
73
+ @functools.cached_property
74
+ def trip_patterns(self):
75
+ """Return a list of GTFS trip patterns."""
76
+ return list(self._transit_layer.tripPatterns)
77
+
78
+ def get_stop_id_from_index(self, stop_index):
79
+ """Get the GTFS stop id for the `stop_index`-th stop of this transit layer."""
80
+ return self._transit_layer.stopIdForIndex[stop_index]
81
+
82
+
83
+ @jpype._jcustomizer.JConversion(
84
+ "com.conveyal.r5.transit.TransitLayer", exact=TransitLayer
85
+ )
86
+ def _cast_TransitLayer(java_class, object_):
87
+ return object_._transit_layer
r5py/r5/transit_leg.py ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env python3
2
+
3
+
4
+ """Represent one leg of a public transport trip."""
5
+
6
+ from .trip_leg import TripLeg
7
+
8
+ __all__ = ["TransitLeg"]
9
+
10
+
11
+ class TransitLeg(TripLeg):
12
+ """Represent one leg of a public transport trip."""
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env python3
2
+
3
+
4
+ """The transport modes supported by R5 (Leg, Street, Transit, combined)."""
5
+
6
+ import enum
7
+
8
+ import jpype
9
+
10
+ from ..util import start_jvm
11
+
12
+ import com.conveyal.r5
13
+
14
+ __all__ = ["TransportMode"]
15
+
16
+
17
+ start_jvm()
18
+
19
+
20
+ TRANSIT_MODES = [
21
+ "AIR",
22
+ "BUS",
23
+ "CABLE_CAR",
24
+ "FERRY",
25
+ "FUNICULAR",
26
+ "GONDOLA",
27
+ "RAIL",
28
+ "SUBWAY",
29
+ "TRAM",
30
+ "TRANSIT",
31
+ ]
32
+
33
+ STREET_MODES = [
34
+ "BICYCLE",
35
+ "CAR",
36
+ "WALK",
37
+ ]
38
+
39
+ LEG_MODES = STREET_MODES + [
40
+ "BICYCLE_RENT",
41
+ "CAR_PARK",
42
+ ]
43
+
44
+
45
+ class TransportMode(enum.Enum):
46
+ """
47
+ Transport modes.
48
+
49
+ TransportMode.AIR, TransportMode.TRAM, TransportMode.SUBWAY,
50
+ TransportMode.RAIL, TransportMode.BUS, TransportMode.FERRY,
51
+ TransportMode.CABLE_CAR, TransportMode.GONDOLA, TransportMode.FUNICULAR,
52
+ TransportMode.TRANSIT (translate into R5’s TransitMode)
53
+
54
+ TransportMode.WALK, TransportMode.BICYCLE, TransportMode.CAR (translate into
55
+ R5’s StreetMode or LegMode)
56
+
57
+ TransportMode.BICYCLE_RENT, TransportMode.CAR_PARK (translate into R5’s LegMode)
58
+ """
59
+
60
+ @classmethod
61
+ def _missing_(cls, value):
62
+ value = str(value).upper()
63
+ for member in cls:
64
+ if value == member.value:
65
+ return member
66
+ return None
67
+
68
+ def __add__(self, other):
69
+ """Combine two transport modes."""
70
+ if isinstance(other, self.__class__):
71
+ return [self, other]
72
+ elif isinstance(other, list):
73
+ return [self] + other
74
+ else:
75
+ raise TypeError(
76
+ "unsupported operand type(s) for '+': "
77
+ f"'{type(other)}' and '{type(self)}'"
78
+ )
79
+
80
+ def __radd__(self, other):
81
+ """Combine two transport modes."""
82
+ if other == 0: # first iteration of sum()
83
+ return self
84
+ elif isinstance(other, list):
85
+ return other + [self]
86
+ else:
87
+ return self.__add__(other)
88
+
89
+ @property
90
+ def is_leg_mode(self):
91
+ """Can this TransportMode function as a LegMode?."""
92
+ return self.name in LEG_MODES
93
+
94
+ @property
95
+ def is_street_mode(self):
96
+ """Can this TransportMode function as a StreetMode?."""
97
+ return self.name in STREET_MODES
98
+
99
+ @property
100
+ def is_transit_mode(self):
101
+ """Can this TransportMode function as a TransitMode?."""
102
+ return self.name in TRANSIT_MODES
103
+
104
+ AIR = "AIR"
105
+ BUS = "BUS"
106
+ CABLE_CAR = "CABLE_CAR"
107
+ FERRY = "FERRY"
108
+ FUNICULAR = "FUNICULAR"
109
+ GONDOLA = "GONDOLA"
110
+ RAIL = "RAIL"
111
+ SUBWAY = "SUBWAY"
112
+ TRAM = "TRAM"
113
+ TRANSIT = "TRANSIT"
114
+
115
+ BICYCLE = "BICYCLE"
116
+ CAR = "CAR"
117
+ WALK = "WALK"
118
+
119
+ BICYCLE_RENT = "BICYCLE_RENT"
120
+ CAR_PARK = "CAR_PARK"
121
+
122
+
123
+ @jpype._jcustomizer.JConversion("com.conveyal.r5.api.util.LegMode", exact=TransportMode)
124
+ def _cast_LegMode(java_class, object_):
125
+ if object_.name in LEG_MODES:
126
+ return com.conveyal.r5.api.util.LegMode.valueOf(object_.name)
127
+ else:
128
+ raise ValueError(f"{object_.name} is not a valid R5 LegMode")
129
+
130
+
131
+ @jpype._jcustomizer.JConversion(
132
+ "com.conveyal.r5.profile.StreetMode", exact=TransportMode
133
+ )
134
+ def _cast_StreetMode(java_class, object_):
135
+ if object_.name in STREET_MODES:
136
+ return com.conveyal.r5.profile.StreetMode.valueOf(object_.name)
137
+ else:
138
+ raise ValueError(f"{object_.name} is not a valid R5 StreetMode")
139
+
140
+
141
+ @jpype._jcustomizer.JConversion(
142
+ "com.conveyal.r5.api.util.TransitModes", exact=TransportMode
143
+ )
144
+ def _cast_TransitMode(java_class, object_):
145
+ if object_.name in TRANSIT_MODES:
146
+ return com.conveyal.r5.api.util.TransitModes.valueOf(object_.name)
147
+ else:
148
+ raise ValueError(f"{object_.name} is not a valid R5 TransitModes")
@@ -0,0 +1,299 @@
1
+ #!/usr/bin/env python3
2
+
3
+
4
+ """Wraps a com.conveyal.r5.transit.TransportNetwork."""
5
+
6
+ import functools
7
+ import hashlib
8
+ import pathlib
9
+ import warnings
10
+
11
+ import jpype
12
+ import jpype.types
13
+
14
+ from .elevation_cost_function import ElevationCostFunction
15
+ from .elevation_model import ElevationModel
16
+ from .street_layer import StreetLayer
17
+ from .transit_layer import TransitLayer
18
+ from .transport_mode import TransportMode
19
+ from ..util import Config, contains_gtfs_data, FileDigest, start_jvm, WorkingCopy
20
+ from ..util.exceptions import GtfsFileError
21
+
22
+ import com.conveyal.analysis
23
+ import com.conveyal.gtfs
24
+ import com.conveyal.osmlib
25
+ import com.conveyal.r5
26
+ import java.io
27
+
28
+ __all__ = ["TransportNetwork"]
29
+
30
+
31
+ PACKAGE = __package__.split(".", maxsplit=1)[0]
32
+
33
+
34
+ start_jvm()
35
+
36
+
37
+ class TransportNetwork:
38
+ """Wrap a com.conveyal.r5.transit.TransportNetwork."""
39
+
40
+ def __init__(
41
+ self,
42
+ osm_pbf,
43
+ gtfs=[], # noqa: B006
44
+ elevation_model=None,
45
+ elevation_cost_function=ElevationCostFunction.TOBLER,
46
+ allow_errors=False,
47
+ ):
48
+ """
49
+ Load a transport network.
50
+
51
+ Arguments
52
+ ---------
53
+ osm_pbf : str | pathlib.Path
54
+ file path of an OpenStreetMap extract in PBF format
55
+ gtfs : str | pathlib.Path | list[str] | list[pathlib.Path]
56
+ path(s) to public transport schedule information in GTFS format
57
+ elevation_model : str | pathlib.Path
58
+ file path to a digital elevation model in TIF format,
59
+ single-band, the value of which is the elevation in metres
60
+ elevation_cost_function : r5py.ElevationCostFunction
61
+ which algorithm to use to compute the added effort and travel time
62
+ of slopes
63
+ allow_errors : bool
64
+ try to proceed with loading the transport network even if input data
65
+ contain errors
66
+ """
67
+ osm_pbf = WorkingCopy(osm_pbf)
68
+ if isinstance(gtfs, (str, pathlib.Path)):
69
+ gtfs = [gtfs]
70
+ gtfs = [WorkingCopy(path) for path in gtfs]
71
+
72
+ # a hash representing all input files
73
+ digest = hashlib.sha256(
74
+ "".join(
75
+ [FileDigest(osm_pbf)]
76
+ + [FileDigest(path) for path in gtfs]
77
+ + [FileDigest(elevation_model) if elevation_model is not None else ""]
78
+ ).encode("utf-8")
79
+ ).hexdigest()
80
+
81
+ try:
82
+ transport_network = self._load_pickled_transport_network(
83
+ Config().CACHE_DIR / f"{digest}.transport_network"
84
+ )
85
+ except (FileNotFoundError, java.io.IOError, java.lang.RuntimeException):
86
+ transport_network = com.conveyal.r5.transit.TransportNetwork()
87
+ transport_network.scenarioId = PACKAGE
88
+
89
+ osm_mapdb = Config().CACHE_DIR / f"{digest}.mapdb"
90
+ osm_file = com.conveyal.osmlib.OSM(f"{osm_mapdb}")
91
+ osm_file.intersectionDetection = True
92
+ osm_file.readFromFile(f"{osm_pbf}")
93
+
94
+ transport_network.streetLayer = com.conveyal.r5.streets.StreetLayer()
95
+ transport_network.streetLayer.parentNetwork = transport_network
96
+ transport_network.streetLayer.loadFromOsm(osm_file)
97
+ transport_network.streetLayer.indexStreets()
98
+
99
+ transport_network.transitLayer = com.conveyal.r5.transit.TransitLayer()
100
+ transport_network.transitLayer.saveShapes = True
101
+ transport_network.transitLayer.parentNetwork = transport_network
102
+ # GtfsTransferLoader(transitLayer, OSM_ONLY)
103
+ gtfs_transfer_loader = com.conveyal.r5.transit.GtfsTransferLoader(
104
+ transport_network.transitLayer,
105
+ com.conveyal.r5.analyst.cluster.TransportNetworkConfig.TransferConfig.OSM_ONLY, # noqa: E501
106
+ )
107
+ for gtfs_file in gtfs:
108
+ gtfs_feed = com.conveyal.gtfs.GTFSFeed.writableTempFileFromGtfs(
109
+ f"{gtfs_file}"
110
+ )
111
+ if gtfs_feed.errors.size() > 0:
112
+ errors = [
113
+ f"{error.errorType}: {error.getMessageWithContext()}"
114
+ for error in gtfs_feed.errors
115
+ ]
116
+ if allow_errors:
117
+ warnings.warn(
118
+ (
119
+ "R5 reported the following issues with "
120
+ f"GTFS file {gtfs_file.name}: \n"
121
+ + ("\n- ".join(errors))
122
+ ),
123
+ RuntimeWarning,
124
+ stacklevel=1,
125
+ )
126
+ else:
127
+ raise GtfsFileError(
128
+ (
129
+ f"Could not load GTFS file {gtfs_file.name}. \n"
130
+ + ("\n- ".join(errors))
131
+ )
132
+ )
133
+
134
+ transport_network.transitLayer.loadFromGtfs(
135
+ gtfs_feed,
136
+ gtfs_transfer_loader,
137
+ )
138
+ gtfs_feed.close()
139
+
140
+ transport_network.streetLayer.associateStops(transport_network.transitLayer)
141
+ transport_network.streetLayer.buildEdgeLists()
142
+
143
+ transport_network.transitLayer.rebuildTransientIndexes()
144
+
145
+ transfer_finder = com.conveyal.r5.transit.TransferFinder(
146
+ transport_network,
147
+ gtfs_transfer_loader,
148
+ )
149
+ transfer_finder.findTransfers()
150
+ transfer_finder.findParkRideTransfer()
151
+
152
+ transport_network.transitLayer.buildDistanceTables(None)
153
+
154
+ if elevation_model is not None:
155
+ ElevationModel(
156
+ elevation_model,
157
+ elevation_cost_function,
158
+ ).apply_to(transport_network)
159
+
160
+ osm_file.close() # not needed after here?
161
+
162
+ self._save_pickled_transport_network(
163
+ transport_network, Config().CACHE_DIR / f"{digest}.transport_network"
164
+ )
165
+
166
+ self._transport_network = transport_network
167
+
168
+ @classmethod
169
+ def from_directory(cls, path):
170
+ """
171
+ Find input data in `path`, load an `r5py.TransportNetwork`.
172
+
173
+ This mimicks r5r’s behaviour to accept a directory path
174
+ as the only input to `setup_r5()`.
175
+
176
+ If more than one OpenStreetMap extract (`.osm.pbf`) is
177
+ found in `path`, the (alphabetically) first one is used.
178
+ In case *no* OpenStreetMap extract is found, a `FileNotFound`
179
+ exception is raised. Any and all GTFS data files are used.
180
+
181
+ Arguments
182
+ ---------
183
+ path : str
184
+ directory path in which to search for GTFS and .osm.pbf files
185
+
186
+ Returns
187
+ -------
188
+ TransportNetwork
189
+ A fully initialised r5py.TransportNetwork
190
+ """
191
+ path = pathlib.Path(path)
192
+ try:
193
+ potential_osm_pbf_files = sorted(path.glob("*.osm.pbf"))
194
+ osm_pbf = potential_osm_pbf_files[0]
195
+ if len(potential_osm_pbf_files) > 1:
196
+ warnings.warn(
197
+ (
198
+ f"Found more than one OpenStreetMap extract file (`.osm.pbf`), "
199
+ f"using alphabetically first one ({osm_pbf.name})"
200
+ ),
201
+ RuntimeWarning,
202
+ stacklevel=1,
203
+ )
204
+ except IndexError as exception:
205
+ raise FileNotFoundError(
206
+ "Could not find any OpenStreetMap extract file (`.osm.pbf`) "
207
+ f"in {path.absolute()}"
208
+ ) from exception
209
+ gtfs = [
210
+ potential_gtfs_file
211
+ for potential_gtfs_file in path.glob("*.zip")
212
+ if contains_gtfs_data(potential_gtfs_file)
213
+ ]
214
+
215
+ return cls(osm_pbf, gtfs)
216
+
217
+ def __enter__(self):
218
+ """Provide a context."""
219
+ return self
220
+
221
+ def __exit__(self, exception_type, exception_value, traceback):
222
+ """Exit context."""
223
+ return False
224
+
225
+ @property
226
+ def extent(self):
227
+ """The geographic area covered, as a `shapely.box`."""
228
+ # TODO: figure out how to get the extent of the GTFS schedule,
229
+ # then find the smaller extent of the two (or the larger one?)
230
+ return self.street_layer.extent
231
+
232
+ @property
233
+ def linkage_cache(self):
234
+ """Expose the `TransportNetwork`’s `linkageCache` to Python."""
235
+ return self._transport_network.linkageCache
236
+
237
+ def _load_pickled_transport_network(self, path):
238
+ try:
239
+ input_file = java.io.File(f"{path}")
240
+ return com.conveyal.r5.kryo.KryoNetworkSerializer.read(input_file)
241
+ except java.io.FileNotFoundException as exception:
242
+ raise FileNotFoundError from exception
243
+
244
+ def _save_pickled_transport_network(self, transport_network, path):
245
+ output_file = java.io.File(f"{path}")
246
+ com.conveyal.r5.kryo.KryoNetworkSerializer.write(transport_network, output_file)
247
+
248
+ def snap_to_network(
249
+ self,
250
+ points,
251
+ radius=com.conveyal.r5.streets.StreetLayer.LINK_RADIUS_METERS,
252
+ street_mode=TransportMode.WALK,
253
+ ):
254
+ """
255
+ Snap `points` to valid locations on the network.
256
+
257
+ Arguments
258
+ ---------
259
+ points : geopandas.GeoSeries
260
+ point geometries that will be snapped to the network
261
+ radius : float
262
+ Search radius around each `point`
263
+ street_mode : travel mode that the snapped-to street should allow
264
+
265
+ Returns
266
+ -------
267
+ geopandas.GeoSeries
268
+ point geometries that have been snapped to the network,
269
+ using the same index and order as the input `points`
270
+ """
271
+ return points.apply(
272
+ functools.partial(
273
+ self.street_layer.find_split,
274
+ radius=radius,
275
+ street_mode=street_mode,
276
+ )
277
+ )
278
+
279
+ @functools.cached_property
280
+ def street_layer(self):
281
+ """Expose the `TransportNetwork`’s `streetLayer` to Python."""
282
+ return StreetLayer.from_r5_street_layer(self._transport_network.streetLayer)
283
+
284
+ @property
285
+ def timezone(self):
286
+ """Determine the timezone of the GTFS data."""
287
+ return self._transport_network.getTimeZone()
288
+
289
+ @functools.cached_property
290
+ def transit_layer(self):
291
+ """Expose the `TransportNetwork`’s `transitLayer` to Python."""
292
+ return TransitLayer.from_r5_transit_layer(self._transport_network.transitLayer)
293
+
294
+
295
+ @jpype._jcustomizer.JConversion(
296
+ "com.conveyal.r5.transit.TransportNetwork", exact=TransportNetwork
297
+ )
298
+ def _cast_TransportNetwork(java_class, object_):
299
+ return object_._transport_network