Skip to content

OSRM Multi-Segment Path Building

Builds a complete route polyline from an ordered list of waypoints by chaining consecutive OSRM road-snapped segments. Used in the admin map editor when drawing a line's road geometry.


Where It Is Used

apps/web-admin/src/app/services/osrm-routing.service.ts

Called from the line-form component when an admin places waypoints on the Leaflet map. OSRM snaps each waypoint to the nearest drivable road and returns the road-following path between them.


Why Not One OSRM Call?

OSRM's /route endpoint can take multiple waypoints in one call, but the admin workflow places waypoints one at a time and needs to show the partial route as it is being drawn. Chaining pair-by-pair allows incremental display and lets the admin correct mistakes before committing.


Algorithm

Given waypoints [W0, W1, W2, ..., Wn]:

  1. Initialize accumulated = [].
  2. For each consecutive pair $(W_i, W_{i+1})$:
  3. Fetch OSRM segment: GET /route/v1/driving/{lng1},{lat1};{lng2},{lat2}?geometries=polyline
  4. Decode the returned polyline into coordinate array segment[].
  5. If accumulated is empty, append the full segment.
  6. Otherwise, skip segment[0] (duplicate of the last accumulated point) and append the rest.
  7. Return accumulated as the final connected polyline.
async function buildMultiSegmentPath(
  waypoints: [number, number][]
): Promise<[number, number][]> {
  const accumulated: [number, number][] = [];

  for (let i = 0; i < waypoints.length - 1; i++) {
    const segment = await fetchOsrmSegment(waypoints[i], waypoints[i + 1]);
    if (accumulated.length === 0) {
      accumulated.push(...segment);
    } else {
      accumulated.push(...segment.slice(1)); // skip duplicate first point
    }
  }
  return accumulated;
}

Complexity: O(n) network calls for n waypoints. Each call is sequential (next call needs the previous endpoint), so this cannot be parallelized.


OSRM Endpoint

https://router.project-osrm.org/route/v1/driving/{lng},{lat};{lng},{lat}?geometries=polyline

Called directly from the browser — no backend proxy. The response's routes[0].geometry is a Google-encoded polyline at 5-decimal precision.


Output

The resulting polyline is encoded with Google Encoded Polyline and saved to Line.polyline via PATCH /lines/:id/polyline.