r5py 1.0.3.dev0__tar.gz → 1.0.5__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 (127) hide show
  1. {r5py-1.0.3.dev0/src/r5py.egg-info → r5py-1.0.5}/PKG-INFO +3 -2
  2. {r5py-1.0.3.dev0 → r5py-1.0.5}/pyproject.toml +2 -1
  3. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/__init__.py +3 -1
  4. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/__init__.py +2 -0
  5. r5py-1.0.5/src/r5py/r5/elevation_cost_function.py +50 -0
  6. r5py-1.0.5/src/r5py/r5/elevation_model.py +84 -0
  7. r5py-1.0.5/src/r5py/r5/file_storage.py +84 -0
  8. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/transport_network.py +29 -5
  9. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/classpath.py +2 -2
  10. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/config.py +7 -1
  11. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/jvm.py +1 -0
  12. {r5py-1.0.3.dev0 → r5py-1.0.5/src/r5py.egg-info}/PKG-INFO +3 -2
  13. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py.egg-info/SOURCES.txt +5 -0
  14. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py.egg-info/requires.txt +2 -1
  15. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/conftest_d/__init__.py +18 -0
  16. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/conftest_d/file_digest.py +3 -3
  17. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/conftest_d/r5_jar.py +2 -2
  18. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/conftest_d/transport_network.py +22 -0
  19. r5py-1.0.5/tests/data/test_isochrones_bicycle.gpkg.zip +0 -0
  20. r5py-1.0.5/tests/data/test_isochrones_car.gpkg.zip +0 -0
  21. r5py-1.0.5/tests/data/test_isochrones_transit.gpkg.zip +0 -0
  22. r5py-1.0.5/tests/data/test_isochrones_walk.gpkg.zip +0 -0
  23. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_config.py +20 -0
  24. r5py-1.0.5/tests/test_elevation_cost_function.py +31 -0
  25. r5py-1.0.5/tests/test_file_storage.py +51 -0
  26. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_java_casting.py +8 -0
  27. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_transport_network.py +54 -0
  28. r5py-1.0.3.dev0/tests/data/test_isochrones_bicycle.gpkg.zip +0 -0
  29. r5py-1.0.3.dev0/tests/data/test_isochrones_car.gpkg.zip +0 -0
  30. r5py-1.0.3.dev0/tests/data/test_isochrones_transit.gpkg.zip +0 -0
  31. r5py-1.0.3.dev0/tests/data/test_isochrones_walk.gpkg.zip +0 -0
  32. {r5py-1.0.3.dev0 → r5py-1.0.5}/LICENSE +0 -0
  33. {r5py-1.0.3.dev0 → r5py-1.0.5}/MANIFEST.in +0 -0
  34. {r5py-1.0.3.dev0 → r5py-1.0.5}/README.md +0 -0
  35. {r5py-1.0.3.dev0 → r5py-1.0.5}/setup.cfg +0 -0
  36. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/__main__.py +0 -0
  37. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/access_leg.py +0 -0
  38. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/base_travel_time_matrix.py +0 -0
  39. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/detailed_itineraries.py +0 -0
  40. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/direct_leg.py +0 -0
  41. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/egress_leg.py +0 -0
  42. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/isochrones.py +0 -0
  43. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/regional_task.py +0 -0
  44. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/scenario.py +0 -0
  45. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/street_layer.py +0 -0
  46. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/street_segment.py +0 -0
  47. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/transfer_leg.py +0 -0
  48. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/transit_layer.py +0 -0
  49. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/transit_leg.py +0 -0
  50. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/transport_mode.py +0 -0
  51. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/travel_time_matrix.py +0 -0
  52. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/trip.py +0 -0
  53. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/trip_leg.py +0 -0
  54. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/r5/trip_planner.py +0 -0
  55. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/__init__.py +0 -0
  56. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/camel_to_snake_case.py +0 -0
  57. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/contains_gtfs_data.py +0 -0
  58. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/data_validation.py +0 -0
  59. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/environment.py +0 -0
  60. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/exceptions.py +0 -0
  61. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/file_digest.py +0 -0
  62. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/good_enough_equidistant_crs.py +0 -0
  63. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/memory_footprint.py +0 -0
  64. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/parse_int_date.py +0 -0
  65. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/sample_data_set.py +0 -0
  66. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/snake_to_camel_case.py +0 -0
  67. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/spatially_clustered_geodataframe.py +0 -0
  68. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/validating_requests_session.py +0 -0
  69. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/warnings.py +0 -0
  70. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py/util/working_copy.py +0 -0
  71. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py.egg-info/dependency_links.txt +0 -0
  72. {r5py-1.0.3.dev0 → r5py-1.0.5}/src/r5py.egg-info/top_level.txt +0 -0
  73. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/__init__.py +0 -0
  74. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/conftest.py +0 -0
  75. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/conftest_d/data_directory.py +0 -0
  76. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/conftest_d/destinations.py +0 -0
  77. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/conftest_d/garbage_collection.py +0 -0
  78. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/conftest_d/origins.py +0 -0
  79. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/conftest_d/routing_parameters.py +0 -0
  80. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/conftest_d/routing_results.py +0 -0
  81. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/conftest_d/sample_data.py +0 -0
  82. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/conftest_d/upstream_r5.py +0 -0
  83. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_broken_gtfs.zip +0 -0
  84. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_detailed_itineraries_bicycle.gpkg.zip +0 -0
  85. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_detailed_itineraries_car.gpkg.zip +0 -0
  86. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_detailed_itineraries_transit.gpkg.zip +0 -0
  87. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_detailed_itineraries_walk.gpkg.zip +0 -0
  88. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_invalid_points_duplicate_ids.geojson +0 -0
  89. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_invalid_points_no_id_column.geojson +0 -0
  90. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_isochrones_from_multiple_origins.gpkg.zip +0 -0
  91. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_multiple_origins.geojson +0 -0
  92. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_snapped_population_grid_centroids.geojson +0 -0
  93. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_travel_times_bicycle.csv +0 -0
  94. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_travel_times_car.csv +0 -0
  95. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_travel_times_transit.csv +0 -0
  96. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_travel_times_walk.csv +0 -0
  97. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_valid_points_data.geojson +0 -0
  98. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_valid_single_point_data.geojson +0 -0
  99. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_walking_details_not_snapped.csv +0 -0
  100. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_walking_details_snapped.csv +0 -0
  101. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_walking_times_not_snapped.csv +0 -0
  102. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/data/test_walking_times_snapped.csv +0 -0
  103. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/temporary_directory.py +0 -0
  104. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_camel_to_snake_case.py +0 -0
  105. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_classpath.py +0 -0
  106. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_contains_gtfs_data.py +0 -0
  107. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_data_validation.py +0 -0
  108. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_detailed_itineraries.py +0 -0
  109. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_deterministic_behaviour.py +0 -0
  110. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_file_digest.py +0 -0
  111. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_good_enough_equidistant_crs.py +0 -0
  112. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_isochrones.py +0 -0
  113. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_memory_footprint.py +0 -0
  114. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_parse_int_date.py +0 -0
  115. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_regional_task.py +0 -0
  116. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_sample_data_set.py +0 -0
  117. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_snake_to_camel_case.py +0 -0
  118. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_street_layer.py +0 -0
  119. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_transit_layer.py +0 -0
  120. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_transport_mode.py +0 -0
  121. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_travel_time_matrix.py +0 -0
  122. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_trip.py +0 -0
  123. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_trip_leg.py +0 -0
  124. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_trip_planner.py +0 -0
  125. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_validating_request_session.py +0 -0
  126. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_verbose_warnings.py +0 -0
  127. {r5py-1.0.3.dev0 → r5py-1.0.5}/tests/test_working_directory.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: r5py
3
- Version: 1.0.3.dev0
3
+ Version: 1.0.5
4
4
  Summary: Python wrapper for the R5 routing analysis engine
5
5
  Author: Christoph Fink, Willem Klumpenhouwer, Marcus Sairava, Rafael Pereira, Henrikki Tenkanen
6
6
  License: GPL-3.0-or-later or MIT
@@ -26,6 +26,7 @@ Requires-Dist: numpy
26
26
  Requires-Dist: pandas>=2.1.0
27
27
  Requires-Dist: psutil
28
28
  Requires-Dist: pyproj
29
+ Requires-Dist: rasterio
29
30
  Requires-Dist: requests
30
31
  Requires-Dist: scikit-learn
31
32
  Requires-Dist: shapely>=2.0
@@ -55,7 +56,7 @@ Requires-Dist: pyarrow; extra == "tests"
55
56
  Requires-Dist: pytest; extra == "tests"
56
57
  Requires-Dist: pytest-cov; extra == "tests"
57
58
  Requires-Dist: pytest-lazy-fixtures; extra == "tests"
58
- Requires-Dist: r5py.sampledata.helsinki>=0.1.1; extra == "tests"
59
+ Requires-Dist: r5py.sampledata.helsinki>=1.0.3; extra == "tests"
59
60
  Requires-Dist: r5py.sampledata.sao_paulo>=0.1.1; extra == "tests"
60
61
  Requires-Dist: typing-extensions; extra == "tests"
61
62
  Dynamic: license-file
@@ -26,6 +26,7 @@ dependencies = [
26
26
  "pandas>=2.1.0",
27
27
  "psutil",
28
28
  "pyproj",
29
+ "rasterio",
29
30
  "requests",
30
31
  "scikit-learn",
31
32
  "shapely>=2.0",
@@ -52,7 +53,7 @@ docs = ["contextily", "folium", "GitPython", "h3>=4.0.0b2", "jupyterlab_myst",
52
53
  "shapely", "sphinx", "sphinx-book-theme", "sphinx-design",
53
54
  "sphinxcontrib-bibtex", "sphinxcontrib-images"]
54
55
  tests = ["pyarrow", "pytest", "pytest-cov", "pytest-lazy-fixtures",
55
- "r5py.sampledata.helsinki>=0.1.1", "r5py.sampledata.sao_paulo>=0.1.1",
56
+ "r5py.sampledata.helsinki>=1.0.3", "r5py.sampledata.sao_paulo>=0.1.1",
56
57
  "typing-extensions"]
57
58
 
58
59
 
@@ -2,12 +2,13 @@
2
2
 
3
3
  """Python wrapper for the R5 routing analysis engine."""
4
4
 
5
- __version__ = "1.0.3.dev0"
5
+ __version__ = "1.0.5"
6
6
 
7
7
 
8
8
  from .r5 import (
9
9
  DetailedItineraries,
10
10
  DetailedItinerariesComputer,
11
+ ElevationCostFunction,
11
12
  Isochrones,
12
13
  RegionalTask,
13
14
  TransportMode,
@@ -19,6 +20,7 @@ from .r5 import (
19
20
  __all__ = [
20
21
  "DetailedItineraries",
21
22
  "DetailedItinerariesComputer",
23
+ "ElevationCostFunction",
22
24
  "Isochrones",
23
25
  "RegionalTask",
24
26
  "TransportMode",
@@ -6,6 +6,7 @@ from .access_leg import AccessLeg
6
6
  from .detailed_itineraries import DetailedItineraries, DetailedItinerariesComputer
7
7
  from .direct_leg import DirectLeg
8
8
  from .egress_leg import EgressLeg
9
+ from .elevation_cost_function import ElevationCostFunction
9
10
  from .isochrones import Isochrones
10
11
  from .regional_task import RegionalTask
11
12
  from .scenario import Scenario
@@ -24,6 +25,7 @@ __all__ = [
24
25
  "DetailedItinerariesComputer",
25
26
  "DirectLeg",
26
27
  "EgressLeg",
28
+ "ElevationCostFunction",
27
29
  "Isochrones",
28
30
  "RegionalTask",
29
31
  "Scenario",
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env python3
2
+
3
+
4
+ """The elevation cost functions supported by R5 (Tobler, Minetti)."""
5
+
6
+
7
+ import enum
8
+
9
+ import jpype
10
+
11
+ from ..util import start_jvm
12
+
13
+ import com.conveyal.r5
14
+
15
+
16
+ __all__ = ["ElevationCostFunction"]
17
+
18
+
19
+ start_jvm()
20
+
21
+
22
+ class ElevationCostFunction(enum.Enum):
23
+ """
24
+ Elevation cost functions.
25
+
26
+ TOBLER: Waldo Tobler’s hiking function, cf. https://en.wikipedia.org/wiki/Tobler%27s_hiking_function
27
+ MINETTI: Minetti et al.’s perceived effort/energy consumption, cf.
28
+ https://doi.org/10.1152/japplphysiol.01177.2001
29
+ """
30
+
31
+ @classmethod
32
+ def _missing_(cls, value):
33
+ value = str(value).upper()
34
+ for member in cls:
35
+ if value == member.value:
36
+ return member
37
+ return None
38
+
39
+ TOBLER = "TOBLER"
40
+ MINETTI = "MINETTI"
41
+
42
+
43
+ @jpype._jcustomizer.JConversion(
44
+ "com.conveyal.r5.analyst.scenario.RasterCost.CostFunction",
45
+ exact=ElevationCostFunction,
46
+ )
47
+ def _cast_LegMode(java_class, object_):
48
+ return com.conveyal.r5.analyst.scenario.RasterCost.CostFunction.valueOf(
49
+ object_.name
50
+ )
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env python3
2
+
3
+
4
+ """Load a digital elevation model and apply it to an r5py.TransportNetwork."""
5
+
6
+
7
+ import rasterio
8
+
9
+ from .elevation_cost_function import ElevationCostFunction
10
+ from .file_storage import FileStorage
11
+ from ..util import WorkingCopy
12
+
13
+ import com.conveyal.analysis
14
+ import com.conveyal.r5
15
+
16
+
17
+ __all__ = ["ElevationModel"]
18
+
19
+
20
+ class ElevationModel:
21
+ """Load a digital elevation model and apply it to an r5py.TransportNetwork."""
22
+
23
+ def __init__(
24
+ self,
25
+ elevation_model,
26
+ elevation_cost_function=ElevationCostFunction.TOBLER,
27
+ ):
28
+ """
29
+ Load an elevation model.
30
+
31
+ Arguments
32
+ ---------
33
+ elevation_model : str | pathlib.Path
34
+ file path to a digital elevation model in TIF format,
35
+ single-band, the value of which is the elevation in metres
36
+ elevation_cost_function : r5py.ElevationCostFunction
37
+ which algorithm to use to compute the added effort and travel time
38
+ of slopes
39
+ """
40
+ elevation_model = WorkingCopy(elevation_model)
41
+ elevation_model = self._convert_tiff_to_format_readable_by_r5(elevation_model)
42
+
43
+ # instantiate an com.conveyal.file.FileStorage singleton
44
+ com.conveyal.analysis.components.WorkerComponents.fileStorage = FileStorage()
45
+
46
+ self._elevation_model = com.conveyal.r5.analyst.scenario.RasterCost()
47
+ self._elevation_model.dataSourceId = f"{elevation_model.with_suffix('')}"
48
+ self._elevation_model.costFunction = elevation_cost_function
49
+
50
+ def apply_to(self, transport_network):
51
+ """
52
+ Add the costs associated with elevation traversal to a transport network.
53
+
54
+ Arguments
55
+ ---------
56
+ transport_network : r5py.TransportNetwork
57
+ The transport network to which to add slope costs
58
+ """
59
+ self._elevation_model.resolve(transport_network)
60
+ self._elevation_model.apply(transport_network)
61
+
62
+ @staticmethod
63
+ def _convert_tiff_to_format_readable_by_r5(tiff):
64
+ # javax.imagio does not allow all compression/predictor
65
+ # combinations of TIFFs
66
+ # to work around it, convert the input to a format known to work.
67
+
68
+ input_tiff = tiff.with_stem(f".{tiff.stem}")
69
+ output_tiff = tiff.with_suffix(".tif")
70
+ tiff.rename(input_tiff)
71
+
72
+ with rasterio.open(input_tiff) as source:
73
+ metadata = source.profile
74
+ metadata.update(
75
+ {
76
+ "compress": "LZW",
77
+ "predictor": "2",
78
+ }
79
+ )
80
+ with rasterio.open(output_tiff, "w", **metadata) as destination:
81
+ destination.write(source.read())
82
+ input_tiff.unlink()
83
+
84
+ return output_tiff
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env python3
2
+
3
+
4
+ """A thin layer around com.conveyal.r5.file.FileStorage."""
5
+
6
+
7
+ import jpype
8
+
9
+ from ..util import start_jvm
10
+
11
+ import com.conveyal.file
12
+ import java.io.File
13
+
14
+
15
+ __all__ = ["FileStorage"]
16
+
17
+
18
+ start_jvm()
19
+
20
+
21
+ @jpype.JImplements(com.conveyal.file.FileStorage)
22
+ class FileStorage:
23
+ """A thin layer around com.conveyal.r5.file.FileStorage."""
24
+
25
+ @jpype.JOverride
26
+ def moveIntoStorage(self, *args):
27
+ """Not implemented."""
28
+ pass
29
+
30
+ @jpype.JOverride
31
+ def getFile(self, file_storage_key):
32
+ """
33
+ Return a java.io.File for the file identified by file_storage_key.
34
+
35
+ Arguments
36
+ ---------
37
+ file_storage_key : com.conveyal.r5.file.FileStorageKey
38
+ An R5 object referencing a certain file
39
+
40
+ Returns
41
+ -------
42
+ java.io.File
43
+ The file identified by file_storage_key
44
+ """
45
+ return java.io.File(file_storage_key.path)
46
+
47
+ @jpype.JOverride
48
+ def getURL(self, file_storage_key):
49
+ """
50
+ Return an URL for the file identified by file_storage_key.
51
+
52
+ Arguments
53
+ ---------
54
+ file_storage_key : com.conveyal.r5.file.FileStorageKey
55
+ An R5 object referencing a certain file
56
+
57
+ Returns
58
+ -------
59
+ str
60
+ An imaginary URL pointing to the file identified by file_storage_key
61
+ """
62
+ return f"file://{file_storage_key.path}"
63
+
64
+ @jpype.JOverride
65
+ def delete(self, *args):
66
+ """Not implemented."""
67
+ pass
68
+
69
+ @jpype.JOverride
70
+ def exists(self, file_storage_key):
71
+ """
72
+ Check whether the file identified by file_storage_key exists.
73
+
74
+ Arguments
75
+ ---------
76
+ file_storage_key : com.conveyal.r5.file.FileStorageKey
77
+ An R5 object referencing a certain file
78
+
79
+ Returns
80
+ -------
81
+ bool
82
+ Whether or not the file identified by file_storage_key exists
83
+ """
84
+ return self.getFile(file_storage_key).exists()
@@ -12,12 +12,15 @@ import warnings
12
12
  import jpype
13
13
  import jpype.types
14
14
 
15
+ from .elevation_cost_function import ElevationCostFunction
16
+ from .elevation_model import ElevationModel
15
17
  from .street_layer import StreetLayer
16
18
  from .transit_layer import TransitLayer
17
19
  from .transport_mode import TransportMode
18
20
  from ..util import Config, contains_gtfs_data, FileDigest, start_jvm, WorkingCopy
19
21
  from ..util.exceptions import GtfsFileError
20
22
 
23
+ import com.conveyal.analysis
21
24
  import com.conveyal.gtfs
22
25
  import com.conveyal.osmlib
23
26
  import com.conveyal.r5
@@ -36,7 +39,14 @@ start_jvm()
36
39
  class TransportNetwork:
37
40
  """Wrap a com.conveyal.r5.transit.TransportNetwork."""
38
41
 
39
- def __init__(self, osm_pbf, gtfs=[], allow_errors=False):
42
+ def __init__(
43
+ self,
44
+ osm_pbf,
45
+ gtfs=[],
46
+ elevation_model=None,
47
+ elevation_cost_function=ElevationCostFunction.TOBLER,
48
+ allow_errors=False,
49
+ ):
40
50
  """
41
51
  Load a transport network.
42
52
 
@@ -46,6 +56,12 @@ class TransportNetwork:
46
56
  file path of an OpenStreetMap extract in PBF format
47
57
  gtfs : str | pathlib.Path | list[str] | list[pathlib.Path]
48
58
  path(s) to public transport schedule information in GTFS format
59
+ elevation_model : str | pathlib.Path
60
+ file path to a digital elevation model in TIF format,
61
+ single-band, the value of which is the elevation in metres
62
+ elevation_cost_function : r5py.ElevationCostFunction
63
+ which algorithm to use to compute the added effort and travel time
64
+ of slopes
49
65
  allow_errors : bool
50
66
  try to proceed with loading the transport network even if input data
51
67
  contain errors
@@ -57,16 +73,18 @@ class TransportNetwork:
57
73
 
58
74
  # a hash representing all input files
59
75
  digest = hashlib.sha256(
60
- "".join([FileDigest(osm_pbf)] + [FileDigest(path) for path in gtfs]).encode(
61
- "utf-8"
62
- )
76
+ "".join(
77
+ [FileDigest(osm_pbf)]
78
+ + [FileDigest(path) for path in gtfs]
79
+ + [FileDigest(elevation_model) if elevation_model is not None else ""]
80
+ ).encode("utf-8")
63
81
  ).hexdigest()
64
82
 
65
83
  try:
66
84
  transport_network = self._load_pickled_transport_network(
67
85
  Config().CACHE_DIR / f"{digest}.transport_network"
68
86
  )
69
- except FileNotFoundError:
87
+ except (FileNotFoundError, java.io.IOError, java.lang.RuntimeException):
70
88
  transport_network = com.conveyal.r5.transit.TransportNetwork()
71
89
  transport_network.scenarioId = PACKAGE
72
90
 
@@ -122,6 +140,12 @@ class TransportNetwork:
122
140
 
123
141
  transport_network.transitLayer.buildDistanceTables(None)
124
142
 
143
+ if elevation_model is not None:
144
+ ElevationModel(
145
+ elevation_model,
146
+ elevation_cost_function,
147
+ ).apply_to(transport_network)
148
+
125
149
  osm_file.close() # not needed after here?
126
150
 
127
151
  self._save_pickled_transport_network(
@@ -18,9 +18,9 @@ from .warnings import R5pyWarning
18
18
 
19
19
  # update these to use a newer R5 version if no R5 available locally
20
20
  R5_JAR_URL = (
21
- "https://github.com/r5py/r5/releases/download/v7.3-r5py/r5-v7.3-r5py-all.jar"
21
+ "https://github.com/r5py/r5/releases/download/v7.4-r5py/r5-v7.4-r5py-all.jar"
22
22
  )
23
- R5_JAR_SHA256 = "cb1ccad370757ba229cf17f1bedc9549ff5d77fdbb44b7a3058104fe1f243f53"
23
+ R5_JAR_SHA256 = "c51b566671165d113add84e10e60fb9273014ca4a2e2a52591446954f68e7340"
24
24
  # ---
25
25
 
26
26
 
@@ -92,7 +92,13 @@ class Config:
92
92
  FileNotFoundError, # broken symlink
93
93
  PermissionError,
94
94
  ):
95
- cached_file.unlink()
95
+ try:
96
+ cached_file.unlink()
97
+ except (
98
+ IsADirectoryError, # only available on Linux kernels
99
+ PermissionError, # what’s raised instead on Win and MacOs
100
+ ):
101
+ pass
96
102
 
97
103
  return cache_dir
98
104
 
@@ -48,6 +48,7 @@ def start_jvm():
48
48
 
49
49
  jpype.startJVM(
50
50
  f"-Xmx{MAX_JVM_MEMORY:d}",
51
+ "-XX:+RestoreMXCSROnJNICalls", # https://github.com/r5py/r5py/issues/485
51
52
  "-Xcheck:jni",
52
53
  "-Xrs", # https://stackoverflow.com/q/34951812
53
54
  "-Duser.language=en", # Set a default locale, …
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: r5py
3
- Version: 1.0.3.dev0
3
+ Version: 1.0.5
4
4
  Summary: Python wrapper for the R5 routing analysis engine
5
5
  Author: Christoph Fink, Willem Klumpenhouwer, Marcus Sairava, Rafael Pereira, Henrikki Tenkanen
6
6
  License: GPL-3.0-or-later or MIT
@@ -26,6 +26,7 @@ Requires-Dist: numpy
26
26
  Requires-Dist: pandas>=2.1.0
27
27
  Requires-Dist: psutil
28
28
  Requires-Dist: pyproj
29
+ Requires-Dist: rasterio
29
30
  Requires-Dist: requests
30
31
  Requires-Dist: scikit-learn
31
32
  Requires-Dist: shapely>=2.0
@@ -55,7 +56,7 @@ Requires-Dist: pyarrow; extra == "tests"
55
56
  Requires-Dist: pytest; extra == "tests"
56
57
  Requires-Dist: pytest-cov; extra == "tests"
57
58
  Requires-Dist: pytest-lazy-fixtures; extra == "tests"
58
- Requires-Dist: r5py.sampledata.helsinki>=0.1.1; extra == "tests"
59
+ Requires-Dist: r5py.sampledata.helsinki>=1.0.3; extra == "tests"
59
60
  Requires-Dist: r5py.sampledata.sao_paulo>=0.1.1; extra == "tests"
60
61
  Requires-Dist: typing-extensions; extra == "tests"
61
62
  Dynamic: license-file
@@ -15,6 +15,9 @@ src/r5py/r5/base_travel_time_matrix.py
15
15
  src/r5py/r5/detailed_itineraries.py
16
16
  src/r5py/r5/direct_leg.py
17
17
  src/r5py/r5/egress_leg.py
18
+ src/r5py/r5/elevation_cost_function.py
19
+ src/r5py/r5/elevation_model.py
20
+ src/r5py/r5/file_storage.py
18
21
  src/r5py/r5/isochrones.py
19
22
  src/r5py/r5/regional_task.py
20
23
  src/r5py/r5/scenario.py
@@ -58,7 +61,9 @@ tests/test_contains_gtfs_data.py
58
61
  tests/test_data_validation.py
59
62
  tests/test_detailed_itineraries.py
60
63
  tests/test_deterministic_behaviour.py
64
+ tests/test_elevation_cost_function.py
61
65
  tests/test_file_digest.py
66
+ tests/test_file_storage.py
62
67
  tests/test_good_enough_equidistant_crs.py
63
68
  tests/test_isochrones.py
64
69
  tests/test_java_casting.py
@@ -8,6 +8,7 @@ numpy
8
8
  pandas>=2.1.0
9
9
  psutil
10
10
  pyproj
11
+ rasterio
11
12
  requests
12
13
  scikit-learn
13
14
  shapely>=2.0
@@ -41,6 +42,6 @@ pyarrow
41
42
  pytest
42
43
  pytest-cov
43
44
  pytest-lazy-fixtures
44
- r5py.sampledata.helsinki>=0.1.1
45
+ r5py.sampledata.helsinki>=1.0.3
45
46
  r5py.sampledata.sao_paulo>=0.1.1
46
47
  typing-extensions
@@ -3,6 +3,18 @@
3
3
 
4
4
  """Fixtures to be used in r5py tests."""
5
5
 
6
+ # geopandas 1.0.1 imports shapely.geos which raises
7
+ # a Deprecation warning in Shapely 2.1.0
8
+ import warnings
9
+
10
+ with warnings.catch_warnings():
11
+ warnings.filterwarnings(
12
+ "ignore",
13
+ category=DeprecationWarning,
14
+ message=".*shapely.geos.*deprecated.*",
15
+ )
16
+ import geopandas # noqa: F401
17
+
6
18
 
7
19
  from .destinations import (
8
20
  population_grid,
@@ -74,12 +86,15 @@ from .sample_data import (
74
86
 
75
87
  from .transport_network import (
76
88
  broken_gtfs_file_path,
89
+ cache_directory,
90
+ elevation_model_file_path,
77
91
  gtfs_file_path,
78
92
  gtfs_timezone_helsinki,
79
93
  helsinki_osm_pbf_file_path,
80
94
  not_a_gtfs_file,
81
95
  sao_paulo_osm_pbf_file_path,
82
96
  transport_network,
97
+ transport_network_checksum,
83
98
  transport_network_files_tuple,
84
99
  transport_network_from_test_directory,
85
100
  transport_network_from_test_files,
@@ -92,12 +107,14 @@ from .upstream_r5 import (
92
107
 
93
108
  __all__ = [
94
109
  "broken_gtfs_file_path",
110
+ "cache_directory",
95
111
  "can_compute_detailed_route_geometries",
96
112
  "departure_datetime",
97
113
  "detailed_itineraries_bicycle",
98
114
  "detailed_itineraries_car",
99
115
  "detailed_itineraries_transit",
100
116
  "detailed_itineraries_walk",
117
+ "elevation_model_file_path",
101
118
  "file_digest_blake2b",
102
119
  "file_digest_blake2s",
103
120
  "file_digest_sha256",
@@ -135,6 +152,7 @@ __all__ = [
135
152
  "sao_paulo_osm_pbf_file_path",
136
153
  "snapped_population_grid_points",
137
154
  "transport_network",
155
+ "transport_network_checksum",
138
156
  "transport_network_files_tuple",
139
157
  "transport_network_from_test_directory",
140
158
  "transport_network_from_test_files",
@@ -9,12 +9,12 @@ import pytest
9
9
  from .routing_results import ISOCHRONES_WALK
10
10
 
11
11
 
12
- ISOCHRONES_WALK_BLAKE2B = "163f6b2bc985e7eb347020320392a151ba1c156a09e82f87c0149273de2714516a098984e581bf99742c075232e18d69f5171370589cb5ec64d70f42f539f76c"
12
+ ISOCHRONES_WALK_BLAKE2B = "e5c0fe9d34b512363c876eec81439b2708a1171272dd721a40aefe502c6e0dc73811aab0f109a45ab83645b88dea38051112ede7fc4b99a1231d92e3b4492e11"
13
13
  ISOCHRONES_WALK_BLAKE2S = (
14
- "9f0af2c9946982b2e7d17a0b2205b596cbef01b1037874f5ad72348407ad381e"
14
+ "3d46b490841df409cbb962e25d4201bda0eea5b825fc1181cb0babfddf457299"
15
15
  )
16
16
  ISOCHRONES_WALK_SHA256 = (
17
- "b632d148ca5d9875482197461809f25fe790d71827e821983cefe2394841b417"
17
+ "1d899da67faed2f49a0dd870b1dc33b4c7bd695b18eafc08640a662b442e935c"
18
18
  )
19
19
 
20
20
 
@@ -10,9 +10,9 @@ import pytest
10
10
 
11
11
 
12
12
  R5_JAR_URL = (
13
- "https://github.com/r5py/r5/releases/download/v7.3-r5py/r5-v7.3-r5py-all.jar"
13
+ "https://github.com/r5py/r5/releases/download/v7.4-r5py/r5-v7.4-r5py-all.jar"
14
14
  )
15
- R5_JAR_SHA256 = "cb1ccad370757ba229cf17f1bedc9549ff5d77fdbb44b7a3058104fe1f243f53"
15
+ R5_JAR_SHA256 = "c51b566671165d113add84e10e60fb9273014ca4a2e2a52591446954f68e7340"
16
16
  R5_JAR_SHA256_INVALID = "adfadsfadsfadsfasdfasdf"
17
17
  R5_JAR_SHA256_GITHUB_ERROR_MESSAGE_WHEN_POSTING = (
18
18
  "14aa2347be79c280e4d0fd3a137fb8f5bf2863261a1e48e1a122df1a52a0f453"
@@ -26,6 +26,14 @@ def broken_gtfs_file_path():
26
26
  yield BROKEN_GTFS_FILE
27
27
 
28
28
 
29
+ @pytest.fixture
30
+ def elevation_model_file_path():
31
+ """Return the file path of a GTFS sample data set."""
32
+ import r5py.sampledata.helsinki
33
+
34
+ yield r5py.sampledata.helsinki.elevation_model
35
+
36
+
29
37
  @pytest.fixture
30
38
  def gtfs_file_path():
31
39
  """Return the file path of a GTFS sample data set."""
@@ -64,6 +72,20 @@ def transport_network_files_tuple():
64
72
  yield r5py.sampledata.helsinki.osm_pbf, [r5py.sampledata.helsinki.gtfs]
65
73
 
66
74
 
75
+ @pytest.fixture
76
+ def transport_network_checksum():
77
+ """Return the checksum of the default transport network (from files tuple)."""
78
+ yield "43bb097531f722016b26293d9e1cd11878d91e07e89846e1f5ba85c43a4b243c"
79
+
80
+
81
+ @pytest.fixture
82
+ def cache_directory():
83
+ """Return a pathlib.Path pointing to r5py’s cache directory."""
84
+ from r5py.util.config import Config
85
+
86
+ yield Config().CACHE_DIR
87
+
88
+
67
89
  @pytest.fixture
68
90
  def transport_network(transport_network_from_test_files):
69
91
  """Return an `r5py.TransportNetwork`."""
@@ -46,3 +46,23 @@ class TestConfig:
46
46
  _ = config.CACHE_DIR # re-evaluate cache dir contents
47
47
 
48
48
  assert not expired_file.exists()
49
+
50
+ def test_cache_clearing_skip_directory(self):
51
+ config = r5py.util.config.Config()
52
+
53
+ past_best_by_date = (
54
+ datetime.datetime.now() - CACHE_MAX_AGE - datetime.timedelta(seconds=1)
55
+ ).timestamp()
56
+
57
+ expired_directory = pathlib.Path(config.CACHE_DIR / "expired-dir")
58
+ expired_directory.mkdir()
59
+ os.utime(expired_directory, (past_best_by_date, past_best_by_date))
60
+
61
+ config.__dict__.pop("CACHE_DIR", None) # clear functools.cached_property
62
+ _ = config.CACHE_DIR # re-evaluate cache dir contents
63
+
64
+ assert expired_directory.exists()
65
+ try:
66
+ expired_directory.rmdir()
67
+ except PermissionError:
68
+ pass # Windows ...
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env python3
2
+
3
+
4
+ import pytest
5
+
6
+ import r5py
7
+
8
+
9
+ class TestElevationCostFunction:
10
+ def test_some_weird_combinations(self):
11
+ # not as a parametrized test as pytest has troubles with
12
+ # functions as parametrized fixtures (tries to evaluate?)
13
+ assert r5py.ElevationCostFunction("tobler") == r5py.ElevationCostFunction.TOBLER
14
+ assert r5py.ElevationCostFunction("Tobler") == r5py.ElevationCostFunction.TOBLER
15
+ assert r5py.ElevationCostFunction("tObLeR") == r5py.ElevationCostFunction.TOBLER
16
+ assert r5py.ElevationCostFunction("TOBLER") == r5py.ElevationCostFunction.TOBLER
17
+ assert (
18
+ r5py.ElevationCostFunction("minetti") == r5py.ElevationCostFunction.MINETTI
19
+ )
20
+ assert (
21
+ r5py.ElevationCostFunction("Minetti") == r5py.ElevationCostFunction.MINETTI
22
+ )
23
+ assert (
24
+ r5py.ElevationCostFunction("mInEtTi") == r5py.ElevationCostFunction.MINETTI
25
+ )
26
+ assert (
27
+ r5py.ElevationCostFunction("MINETTI") == r5py.ElevationCostFunction.MINETTI
28
+ )
29
+
30
+ with pytest.raises(ValueError):
31
+ _ = r5py.ElevationCostFunction("FooBarInvalidCostFunction")
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env python3
2
+
3
+
4
+ import pathlib
5
+
6
+ import r5py
7
+
8
+ import com.conveyal.file
9
+ import java.io
10
+
11
+
12
+ class TestFileStorage:
13
+ def test_void_methods(self):
14
+ file_storage = r5py.r5.file_storage.FileStorage()
15
+ assert file_storage.moveIntoStorage() is None
16
+ assert file_storage.delete() is None
17
+
18
+ def test_get_file(self):
19
+ file_storage = r5py.r5.file_storage.FileStorage()
20
+ fake_file_path = pathlib.Path("/home/foo/bar.txt")
21
+ gotten_file = file_storage.getFile(
22
+ com.conveyal.file.FileStorageKey(
23
+ com.conveyal.file.FileCategory.DATASOURCES,
24
+ f"{fake_file_path.with_suffix('')}",
25
+ )
26
+ )
27
+ assert isinstance(gotten_file, java.io.File)
28
+ assert str(gotten_file) == f"{fake_file_path.with_suffix('')}"
29
+
30
+ def test_get_url(self):
31
+ file_storage = r5py.r5.file_storage.FileStorage()
32
+ fake_file_path = pathlib.Path("/home/foo/bar.txt")
33
+ gotten_url = file_storage.getURL(
34
+ com.conveyal.file.FileStorageKey(
35
+ com.conveyal.file.FileCategory.DATASOURCES,
36
+ f"{fake_file_path.with_suffix('')}",
37
+ )
38
+ )
39
+ assert isinstance(gotten_url, str)
40
+ assert gotten_url == f"file://{fake_file_path.with_suffix('')}"
41
+
42
+ def test_exists(self):
43
+ file_storage = r5py.r5.file_storage.FileStorage()
44
+ fake_file_path = pathlib.Path("/home/foo/bar.txt")
45
+ exists = file_storage.exists(
46
+ com.conveyal.file.FileStorageKey(
47
+ com.conveyal.file.FileCategory.DATASOURCES,
48
+ f"{fake_file_path.with_suffix('')}",
49
+ )
50
+ )
51
+ assert not exists
@@ -28,6 +28,14 @@ class TestJavaCasting:
28
28
  (r5py.TransportMode.WALK, com.conveyal.r5.api.util.LegMode),
29
29
  (r5py.TransportMode.BICYCLE_RENT, com.conveyal.r5.api.util.LegMode),
30
30
  (r5py.TransportMode.CAR_PARK, com.conveyal.r5.api.util.LegMode),
31
+ (
32
+ r5py.ElevationCostFunction.TOBLER,
33
+ com.conveyal.r5.analyst.scenario.RasterCost.CostFunction,
34
+ ),
35
+ (
36
+ r5py.ElevationCostFunction.MINETTI,
37
+ com.conveyal.r5.analyst.scenario.RasterCost.CostFunction,
38
+ ),
31
39
  ],
32
40
  )
33
41
  def test_transport_modes_cast(self, python_object, java_class):
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
3
  import pathlib
4
+ import random
4
5
  import shutil
6
+ import string
5
7
 
6
8
  import geopandas
7
9
  import pytest
@@ -190,3 +192,55 @@ class Test_TransportNetwork:
190
192
  [broken_gtfs_file_path],
191
193
  allow_errors=True,
192
194
  )
195
+
196
+ def test_transport_network_with_elevation_model_tobler(
197
+ self,
198
+ helsinki_osm_pbf_file_path,
199
+ gtfs_file_path,
200
+ elevation_model_file_path,
201
+ ):
202
+ _ = r5py.TransportNetwork(
203
+ osm_pbf=helsinki_osm_pbf_file_path,
204
+ gtfs=[gtfs_file_path],
205
+ elevation_model=elevation_model_file_path,
206
+ elevation_cost_function=r5py.ElevationCostFunction.TOBLER,
207
+ )
208
+
209
+ def test_transport_network_with_elevation_model_minetti(
210
+ self,
211
+ helsinki_osm_pbf_file_path,
212
+ gtfs_file_path,
213
+ elevation_model_file_path,
214
+ ):
215
+ _ = r5py.TransportNetwork(
216
+ osm_pbf=helsinki_osm_pbf_file_path,
217
+ gtfs=[gtfs_file_path],
218
+ elevation_model=elevation_model_file_path,
219
+ elevation_cost_function=r5py.ElevationCostFunction.MINETTI,
220
+ )
221
+
222
+ def test_invalid_cache(
223
+ self,
224
+ transport_network_files_tuple,
225
+ cache_directory,
226
+ transport_network_checksum,
227
+ ):
228
+ _ = r5py.TransportNetwork(*transport_network_files_tuple)
229
+ del _
230
+
231
+ (
232
+ cache_directory / f"{transport_network_checksum}.transport_network"
233
+ ).write_text("".join(random.choices(string.printable, k=64)))
234
+
235
+ _ = r5py.TransportNetwork(*transport_network_files_tuple)
236
+
237
+ def test_cache_exists(
238
+ self,
239
+ transport_network,
240
+ cache_directory,
241
+ transport_network_checksum,
242
+ ):
243
+ del transport_network
244
+ assert (
245
+ cache_directory / f"{transport_network_checksum}.transport_network"
246
+ ).exists()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes