Skip to content

Commit f63519c

Browse files
authored
Add osrm (#11)
* Adding OSRM * formatting * fix * readme * fix
1 parent 4c6cd06 commit f63519c

File tree

6 files changed

+114
-5
lines changed

6 files changed

+114
-5
lines changed

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
This tool compares the travel times obtained from [TravelTime Routes API](https://docs.traveltime.com/api/reference/routes),
44
[Google Maps Directions API](https://developers.google.com/maps/documentation/directions/get-directions),
55
[TomTom Routing API](https://developer.tomtom.com/routing-api/documentation/tomtom-maps/routing-service),
6-
[HERE Routing API](https://www.here.com/docs/bundle/routing-api-v8-api-reference)
7-
and [Mapbox Directions API](https://docs.mapbox.com/api/navigation/directions/).
6+
[HERE Routing API](https://www.here.com/docs/bundle/routing-api-v8-api-reference),
7+
[Mapbox Directions API](https://docs.mapbox.com/api/navigation/directions/),
8+
and [OSRM Routes API](https://project-osrm.org/docs/v5.5.1/api/?language=cURL#route-service).
89
Source code is available on [GitHub](https://github.com/traveltime-dev/traveltime-google-comparison).
910

1011
## Features
1112

12-
- Get travel times from TravelTime API, Google Maps API, TomTom API, HERE API and Mapbox API in parallel, for provided origin/destination pairs and a set
13+
- Get travel times from TravelTime API, Google Maps API, TomTom API, HERE API, Mapbox API and OSRM API in parallel, for provided origin/destination pairs and a set
1314
of departure times.
1415
- Departure times are calculated based on user provided start time, end time and interval.
1516
- Analyze the differences between the results and print out the average error percentage.
@@ -61,6 +62,8 @@ For Mapbox API:
6162
export MAPBOX_API_KEY=[Your Mapbox API Key]
6263
```
6364

65+
For OSRM API: OSRM does not require a key.
66+
6467
For TravelTime API:
6568
```bash
6669
export TRAVELTIME_APP_ID=[Your TravelTime App ID]
@@ -105,6 +108,8 @@ Optional arguments:
105108
It is enforced on per-second basis, to avoid bursts.
106109
- `--here-max-rpm [int]`: Set max number of parallel requests sent to HERE API per minute. Default is 60.
107110
It is enforced on per-second basis, to avoid bursts.
111+
- `--osrm-max-rpm [int]`: Set max number of parallel requests sent to HERE API per minute. Default is 60.
112+
It is enforced on per-second basis, to avoid bursts.
108113
- `--traveltime-max-rpm [int]`: Set max number of parallel requests sent to TravelTime API per minute. Default is 60.
109114
It is enforced on per-second basis, to avoid bursts.
110115

src/traveltime_google_comparison/collect.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
GOOGLE_API = "google"
1717
TOMTOM_API = "tomtom"
1818
HERE_API = "here"
19+
OSRM_API = "osrm"
1920
MAPBOX_API = "mapbox"
2021
TRAVELTIME_API = "traveltime"
2122

@@ -27,6 +28,8 @@ def get_capitalized_provider_name(provider: str) -> str:
2728
return "TomTom"
2829
elif provider == "here":
2930
return "HERE"
31+
elif provider == "osrm":
32+
return "OSRM"
3033
elif provider == "mapbox":
3134
return "Mapbox"
3235
elif provider == "traveltime":
@@ -44,6 +47,7 @@ class Fields:
4447
GOOGLE_API: "google_travel_time",
4548
TOMTOM_API: "tomtom_travel_time",
4649
HERE_API: "here_travel_time",
50+
OSRM_API: "osrm_travel_time",
4751
MAPBOX_API: "mapbox_travel_time",
4852
TRAVELTIME_API: "tt_travel_time",
4953
}
@@ -152,6 +156,7 @@ async def collect_travel_times(
152156
Fields.TRAVEL_TIME[GOOGLE_API]: "first",
153157
Fields.TRAVEL_TIME[TOMTOM_API]: "first",
154158
Fields.TRAVEL_TIME[HERE_API]: "first",
159+
Fields.TRAVEL_TIME[OSRM_API]: "first",
155160
Fields.TRAVEL_TIME[MAPBOX_API]: "first",
156161
Fields.TRAVEL_TIME[TRAVELTIME_API]: "first",
157162
}

src/traveltime_google_comparison/config.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
DEFAULT_GOOGLE_RPM = 60
1212
DEFAULT_TOMTOM_RPM = 60
1313
DEFAULT_HERE_RPM = 60
14+
DEFAULT_OSRM_RPM = 60
1415
DEFAULT_MAPBOX_RPM = 60
1516
DEFAULT_TRAVELTIME_RPM = 60
1617

@@ -68,6 +69,13 @@ def parse_args():
6869
default=DEFAULT_HERE_RPM,
6970
help="Maximum number of requests sent to HERE API per minute",
7071
)
72+
parser.add_argument(
73+
"--osrm-max-rpm",
74+
required=False,
75+
type=int,
76+
default=DEFAULT_OSRM_RPM,
77+
help="Maximum number of requests sent to HERE API per minute",
78+
)
7179
parser.add_argument(
7280
"--mapbox-max-rpm",
7381
required=False,

src/traveltime_google_comparison/main.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from traveltime_google_comparison import config
88
from traveltime_google_comparison.analysis import run_analysis
99
from traveltime_google_comparison.collect import (
10+
OSRM_API,
1011
HERE_API,
1112
MAPBOX_API,
1213
Fields,
@@ -26,7 +27,7 @@
2627

2728

2829
async def run():
29-
providers = [GOOGLE_API, TOMTOM_API, HERE_API, MAPBOX_API]
30+
providers = [GOOGLE_API, TOMTOM_API, HERE_API, MAPBOX_API, OSRM_API]
3031
args = config.parse_args()
3132
csv = pd.read_csv(
3233
args.input, usecols=[Fields.ORIGIN, Fields.DESTINATION]
@@ -40,6 +41,7 @@ async def run():
4041
args.google_max_rpm,
4142
args.tomtom_max_rpm,
4243
args.here_max_rpm,
44+
args.osrm_max_rpm,
4345
args.mapbox_max_rpm,
4446
args.traveltime_max_rpm,
4547
)
@@ -53,6 +55,7 @@ async def run():
5355
Fields.TRAVEL_TIME[GOOGLE_API],
5456
Fields.TRAVEL_TIME[TOMTOM_API],
5557
Fields.TRAVEL_TIME[HERE_API],
58+
Fields.TRAVEL_TIME[OSRM_API],
5659
Fields.TRAVEL_TIME[MAPBOX_API],
5760
Fields.TRAVEL_TIME[TRAVELTIME_API],
5861
],
@@ -65,6 +68,7 @@ async def run():
6568
travel_times_df[Fields.TRAVEL_TIME[GOOGLE_API]].notna()
6669
& travel_times_df[Fields.TRAVEL_TIME[TOMTOM_API]].notna()
6770
& travel_times_df[Fields.TRAVEL_TIME[HERE_API]].notna()
71+
& travel_times_df[Fields.TRAVEL_TIME[OSRM_API]].notna()
6872
& travel_times_df[Fields.TRAVEL_TIME[MAPBOX_API]].notna()
6973
& travel_times_df[Fields.TRAVEL_TIME[TRAVELTIME_API]].notna(),
7074
:,

src/traveltime_google_comparison/requests/factory.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
TOMTOM_API,
55
HERE_API,
66
MAPBOX_API,
7+
OSRM_API,
78
TRAVELTIME_API,
89
GOOGLE_API,
910
)
@@ -18,14 +19,20 @@
1819
from traveltime_google_comparison.requests.google_handler import GoogleRequestHandler
1920
from traveltime_google_comparison.requests.tomtom_handler import TomTomRequestHandler
2021
from traveltime_google_comparison.requests.here_handler import HereRequestHandler
22+
from traveltime_google_comparison.requests.osrm_handler import OSRMRequestHandler
2123
from traveltime_google_comparison.requests.mapbox_handler import MapboxRequestHandler
2224
from traveltime_google_comparison.requests.traveltime_handler import (
2325
TravelTimeRequestHandler,
2426
)
2527

2628

2729
def initialize_request_handlers(
28-
google_max_rpm, tomtom_max_rpm, here_max_rpm, mapbox_max_rpm, traveltime_max_rpm
30+
google_max_rpm,
31+
tomtom_max_rpm,
32+
here_max_rpm,
33+
osrm_max_rpm,
34+
mapbox_max_rpm,
35+
traveltime_max_rpm,
2936
) -> Dict[str, BaseRequestHandler]:
3037
google_api_key = retrieve_google_api_key()
3138
tomtom_api_key = retrieve_tomtom_api_key()
@@ -36,6 +43,7 @@ def initialize_request_handlers(
3643
GOOGLE_API: GoogleRequestHandler(google_api_key, google_max_rpm),
3744
TOMTOM_API: TomTomRequestHandler(tomtom_api_key, tomtom_max_rpm),
3845
HERE_API: HereRequestHandler(here_api_key, here_max_rpm),
46+
OSRM_API: OSRMRequestHandler("", osrm_max_rpm),
3947
MAPBOX_API: MapboxRequestHandler(mapbox_api_key, mapbox_max_rpm),
4048
TRAVELTIME_API: TravelTimeRequestHandler(
4149
credentials.app_id, credentials.api_key, traveltime_max_rpm
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import logging
2+
from datetime import datetime
3+
4+
import aiohttp
5+
from aiolimiter import AsyncLimiter
6+
from traveltimepy import Coordinates
7+
8+
from traveltime_google_comparison.config import Mode
9+
from traveltime_google_comparison.requests.base_handler import (
10+
BaseRequestHandler,
11+
RequestResult,
12+
)
13+
14+
logger = logging.getLogger(__name__)
15+
16+
17+
class OSRMApiError(Exception):
18+
pass
19+
20+
21+
class OSRMRequestHandler(BaseRequestHandler):
22+
OSRM_ROUTES_URL = "http://router.project-osrm.org/route/v1/"
23+
24+
default_timeout = aiohttp.ClientTimeout(total=60)
25+
26+
def __init__(self, api_key, max_rpm):
27+
self.api_key = api_key
28+
self._rate_limiter = AsyncLimiter(max_rpm // 60, 1)
29+
30+
async def send_request(
31+
self,
32+
origin: Coordinates,
33+
destination: Coordinates,
34+
departure_time: datetime,
35+
mode: Mode,
36+
) -> RequestResult:
37+
route = f"{origin.lng},{origin.lat};{destination.lng},{destination.lat}" # for OSRM lat/lng are flipped!
38+
transport_mode = get_osrm_specific_mode(mode)
39+
40+
params = {
41+
"overview": "false",
42+
}
43+
44+
try:
45+
async with aiohttp.ClientSession(
46+
timeout=self.default_timeout
47+
) as session, session.get(
48+
f"{self.OSRM_ROUTES_URL}{transport_mode}/{route}", params=params
49+
) as response:
50+
data = await response.json()
51+
if response.status == 200:
52+
first_route = data["routes"][0]
53+
54+
if not first_route:
55+
raise OSRMApiError(
56+
"No route found between origin and destination."
57+
)
58+
59+
total_duration = sum(leg["duration"] for leg in first_route["legs"])
60+
61+
return RequestResult(travel_time=int(total_duration))
62+
else:
63+
error_message = data.get("detailedError", "")
64+
logger.error(
65+
f"Error in OSRM API response: {response.status} - {error_message}"
66+
)
67+
return RequestResult(None)
68+
except Exception as e:
69+
logger.error(f"Exception during requesting OSRM API, {e}")
70+
return RequestResult(None)
71+
72+
73+
def get_osrm_specific_mode(mode: Mode) -> str:
74+
if mode == Mode.DRIVING:
75+
return "driving"
76+
elif mode == Mode.PUBLIC_TRANSPORT:
77+
raise ValueError("Public transport is not supported for OSRM requests")
78+
else:
79+
raise ValueError(f"Unsupported mode: `{mode.value}`")

0 commit comments

Comments
 (0)