Skip to content

Commit 41eae6a

Browse files
Merge pull request #130 from traveltime-dev/traveltime-sdk-version-4
Update to `traveltimepy==4.1.0`
2 parents 93f25ac + 10741de commit 41eae6a

File tree

2 files changed

+99
-42
lines changed

2 files changed

+99
-42
lines changed

python-nextroute-traveltime/main.py

Lines changed: 98 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,45 @@
1-
import asyncio
21
import os
32

43
import nextmv
54
import nextmv.cloud
65
import nextroute.schema as nextrouteSchema
76
import numpy as np
87
from nextpipe import FlowSpec, app, needs, step
9-
from traveltimepy import Coordinates, Location, Property, Transportation, TravelTimeSdk
8+
from traveltimepy import Client
9+
from traveltimepy.requests.common import Location, Coordinates, Property
10+
from traveltimepy.requests.time_filter_fast import TimeFilterFastArrivalSearches, TimeFilterFastOneToMany
11+
from traveltimepy.requests.transportation import TransportationFast
1012

1113

12-
async def async_part(input_data: dict):
14+
def create_traveltime_client():
1315
import os
14-
16+
1517
# Check if API credentials are available
1618
app_id = os.getenv("TT_APP_ID")
1719
api_key = os.getenv("TT_API_KEY")
18-
20+
1921
if not app_id or not api_key:
2022
raise ValueError(
2123
"TravelTime API credentials not found. Please set the following environment variables:\n"
2224
"- TT_APP_ID: Your TravelTime application ID\n"
2325
"- TT_API_KEY: Your TravelTime API key\n\n"
2426
"You can get these credentials from: https://docs.traveltime.com/api/overview/getting-keys"
2527
)
26-
27-
sdk = TravelTimeSdk(app_id=app_id, api_key=api_key)
28-
nextroute_input = nextrouteSchema.Input.from_dict(input_data)
29-
# Create locations for all stops
28+
29+
return Client(app_id=app_id, api_key=api_key)
30+
31+
32+
def build_locations_list(nextroute_input):
33+
"""
34+
Build locations list from Nextroute input data.
35+
36+
Args:
37+
nextroute_input: Parsed Nextroute input schema
38+
39+
Returns:
40+
List of TravelTime Location objects
41+
"""
3042
locations = []
31-
location_ids = []
3243

3344
# Add stops first
3445
for stop in nextroute_input.stops:
@@ -38,7 +49,6 @@ async def async_part(input_data: dict):
3849
coords=Coordinates(lat=stop.location.lat, lng=stop.location.lon)
3950
)
4051
)
41-
location_ids.append(stop.id)
4252

4353
# Add vehicle start/end locations for each vehicle
4454
for vehicle in nextroute_input.vehicles:
@@ -53,7 +63,6 @@ async def async_part(input_data: dict):
5363
)
5464
)
5565
)
56-
location_ids.append(start_id)
5766

5867
# Add end location
5968
end_id = f"{vehicle.id}-end"
@@ -66,23 +75,21 @@ async def async_part(input_data: dict):
6675
)
6776
)
6877
)
69-
location_ids.append(end_id)
7078

71-
# Prepare the search_ids dictionary - each location to all other locations
72-
search_ids = {}
73-
for origin in location_ids:
74-
# Each origin should search for all destinations except itself
75-
search_ids[origin] = [dest for dest in location_ids if dest != origin]
79+
return locations
7680

77-
# Execute the API call
78-
results = await sdk.time_filter_fast_async(
79-
locations=locations,
80-
search_ids=search_ids,
81-
transportation=Transportation(type="driving"),
82-
properties=[Property("distance"), Property("travel_time")],
83-
one_to_many=False,
84-
)
8581

82+
def build_travel_matrices(results, location_ids):
83+
"""
84+
Build travel time and distance matrices from TravelTime API results.
85+
86+
Args:
87+
results: TravelTime API response
88+
location_ids: List of location IDs in matrix order
89+
90+
Returns:
91+
Tuple of (duration_matrix, distance_matrix) as numpy arrays
92+
"""
8693
# Initialize matrices with zeros or infinity where appropriate
8794
n = len(location_ids)
8895
duration_matrix = np.full((n, n), np.inf)
@@ -93,31 +100,81 @@ async def async_part(input_data: dict):
93100
np.fill_diagonal(distance_matrix, 0)
94101

95102
# Process results to fill in the matrix
96-
for result in results:
97-
origin_idx = location_ids.index(result.search_id)
103+
for result in results.results:
104+
origin_id = result.search_id
105+
origin_idx = location_ids.index(origin_id)
106+
107+
for location_result in result.locations:
108+
destination_idx = location_ids.index(location_result.id)
109+
# Extract travel time and distance
110+
props = location_result.properties
111+
duration_matrix[origin_idx][destination_idx] = props.travel_time
112+
distance_matrix[origin_idx][destination_idx] = props.distance
98113

99-
for location in result.locations:
100-
destination_idx = location_ids.index(location.id)
101-
# Convert travel time to seconds if needed
102-
duration_matrix[origin_idx][destination_idx] = location.properties.travel_time
103-
distance_matrix[origin_idx][destination_idx] = location.properties.distance
114+
return duration_matrix, distance_matrix
104115

105-
# Convert numpy arrays to lists for JSON serialization
106-
duration_matrix_list = duration_matrix.tolist()
107-
distance_matrix_list = distance_matrix.tolist()
116+
117+
def sync_part(input_data: dict, client):
118+
"""
119+
Calculate travel matrices using TravelTime API client.
120+
121+
Args:
122+
input_data: Nextroute input data dictionary
123+
client: TravelTime API client instance
124+
125+
Returns:
126+
Dictionary containing duration_matrix, distance_matrix, and location_ids
127+
"""
128+
nextroute_input = nextrouteSchema.Input.from_dict(input_data)
129+
130+
# Build locations list from input data
131+
locations = build_locations_list(nextroute_input)
132+
location_ids = [loc.id for loc in locations]
133+
134+
one_to_many_searches = []
135+
for origin_id in location_ids:
136+
# Get all destinations except the origin itself
137+
destinations = location_ids.copy()
138+
destinations.remove(origin_id)
139+
140+
if destinations: # Only create search if there are destinations
141+
one_to_many_searches.append(
142+
TimeFilterFastOneToMany(
143+
id=origin_id,
144+
departure_location_id=origin_id,
145+
arrival_location_ids=destinations,
146+
transportation=TransportationFast.DRIVING,
147+
travel_time=7200, # 2 hours maximum
148+
properties=[Property.TRAVEL_TIME, Property.DISTANCE]
149+
)
150+
)
151+
152+
# Execute the API call
153+
results = client.time_filter_fast(
154+
locations=locations,
155+
arrival_searches=TimeFilterFastArrivalSearches(
156+
one_to_many=one_to_many_searches,
157+
many_to_one=[]
158+
)
159+
)
160+
161+
# Build travel matrices from API results
162+
duration_matrix, distance_matrix = build_travel_matrices(results, location_ids)
108163

109164
return {
110-
"duration_matrix": duration_matrix_list,
111-
"distance_matrix": distance_matrix_list,
165+
"duration_matrix": duration_matrix.tolist(),
166+
"distance_matrix": distance_matrix.tolist(),
112167
"location_ids": location_ids # Include location IDs for reference
113168
}
114169

170+
115171
# >>> Workflow definition
116172
class Flow(FlowSpec):
117173
@step
118174
def get_durations_distances(input: dict) -> nextrouteSchema.Input:
119175
"""Get distances and durations from TravelTime."""
120-
results = asyncio.run(async_part(input))
176+
with create_traveltime_client() as client:
177+
results = sync_part(input, client)
121178
nextroute_input = nextrouteSchema.Input.from_dict(input)
122179
nextroute_input.duration_matrix = results["duration_matrix"]
123180
nextroute_input.distance_matrix = results["distance_matrix"]
@@ -129,10 +186,10 @@ def get_durations_distances(input: dict) -> nextrouteSchema.Input:
129186
# Check routes from vehicle start to first few stops
130187
start_idx = location_ids.index("vehicle-1-start")
131188
for i in range(3): # Check first 3 stops
132-
stop_idx = location_ids.index(f"location-{i+1}")
189+
stop_idx = location_ids.index(f"location-{i + 1}")
133190
duration = results["duration_matrix"][start_idx][stop_idx]
134191
distance = results["distance_matrix"][start_idx][stop_idx]
135-
nextmv.log(f"Vehicle start to location-{i+1}: {duration} seconds, {distance} meters")
192+
nextmv.log(f"Vehicle start to location-{i + 1}: {duration} seconds, {distance} meters")
136193

137194
return nextroute_input.to_dict()
138195

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Define the packages required by your project here.
22
nextmv==0.28.0
33
plotly==6.0.0
4-
traveltimepy==3.10.2
4+
traveltimepy==4.1.0
55
nextpipe==0.1.3
66
nextroute

0 commit comments

Comments
 (0)