A service that works out road journeys, compares how long each takes and fits location traces onto the road network.
This routing and travel time API case study answers three related questions for one planning application. It works out how to travel between two locations, how long chosen journeys take, and which road path best fits a GPS trace. HTTP and library interfaces expose these calculations. They read prepared road datasets, and a vehicle profile sets how roads and turns are treated.
The challenge
Snapping a coordinate to the nearest road is only a start. The vehicle may not be allowed on that road. A turn may be barred, or a noisy GPS reading may fit several roads at once. We split the preparation of those rules from the live request. When a caller asks for a route, the server searches a network that is already prepared.
What we built
Route requests return paths, alternatives and turn guidance. Matrix requests compare durations and distances between chosen origins and destinations. Nearest-road queries find candidates, and GPS matching reads a sequence of points to estimate the path taken. A trip operation suggests a stop order from a heuristic.
Capacity, delivery windows and the split of work across vehicles stay with the surrounding application. Oversized requests and unmatched coordinates come back as defined errors.
Technical approach
Map preparation applies a Lua vehicle profile, builds the network and creates its spatial index. Turn restrictions and penalties join the path calculation alongside the road segments. Changing the profile means preparing the relevant dataset again.
Contraction Hierarchies prepares shortcuts for the searches. Multi-Level Dijkstra offers an alternative way to prepare them for the same searches. It divides the network into nested areas and prepares the costs between their boundaries. Its customisation stage can update travel weights without rerunning the whole preparation pipeline, so how often the weights change can guide that choice.
At query time, the spatial index snaps coordinates to road positions. Matrices run a many-to-many search across the points. GPS matching uses a hidden Markov model to weigh the plausible paths across a trace.
Datasets can sit in process memory, in memory-mapped files or in shared memory. The shared-memory path can take prepared updates with no restart. Each vehicle profile needs its own prepared data and its own dedicated serving process to answer queries. A gateway in front of the server supplies authentication, TLS and rate limits.
Project success
The planning application can compare road journeys and read location traces with the same vehicle and turn rules. Preparation stays apart from the live queries. An update path covers the prepared weights. Routes and travel-time matrices then feed the application's own scheduling, including the constraints the routing engine does not solve.
Technology stack
Routing engine
C++20
Build system
CMake
Networking and algorithms
Boost, Intel TBB
Vehicle profiles
Lua
Map processing
libosmium
Integration interfaces
HTTP, C++, Node.js and Python bindings
Other engagements
Shipping API Integration: Multi-Carrier Rates, Labels and Tracking
Read the case studyVisual Workflow Automation and Application Integration
Read the case studyFHIR Integration for Clinical Data: Access Policies and a Local Message Queue
Read the case studyRecognize the pattern?
If one of these looks like your situation, the discovery call is where we find out how close.