Routing guide

Practical guide with examples: route calculation, via points, geometry and output formats.

Live examples. Requests were made against https://pump.elemroot.com/LogismeRouting.
Contents

Endpoint and method

MethodURL
GEThttps://pump.elemroot.com/LogismeRouting/SURRouting

The road network covers Estonia, Latvia and Lithuania (one Baltic graph); e.g. Riga β†’ Vilnius works. GET only; POST returns 400.

Parameter reference

ParamMeaningAllowed values / exampleDefault
pref *Route preference (case-insensitive; unknown β†’ 400)Fastest, Shortest, Pedestrian (= same as Shortest: road network by distance)β€”
start *Start point E,N (east,north = lon,lat); alias Start22.513265,58.265563β€”
end *End point E,N; alias End23.233129,58.605584β€”
viaVia points E,N, space-separated; alias Via22.8,58.45 22.9,58.5β€”
srsCoordinate system (EPSG); bBox uses the sameonly EPSG:4326 or EPSG:3301 (L-EST97; the EPSG: prefix is required, case-insensitive); any other value returns 500EPSG:4326
outputOutput format (case-insensitive; unknown β†’ 400)xml, jsonxml
noRouteGeomOmit geometryexactly false (lowercase) = include geometry; FALSE/0 = default (no geometry)β€”
separateViaResultsEach via leg separatetrue / exactly false (merges the legs: total length and time are sums, one continuous LineString); default trueβ€”
maxDevGeneralisation max deviation (m)e.g. 1, 10; 0 = ungeneralised full geometry; non-numeric β†’ 500β€”
maxPointsGeometry point limit (overrides maxDev)integer, e.g. 100; non-numeric β†’ 500β€”
bBoxClipping window (two E,N points, space-separated; outside the window the line collapses to a straight line)22.5,58.2 23.3,58.7; malformed β†’ 400β€”
callbackJSONP callback (only with output=json)function name, e.g. cb β†’ response cb && cb([…]) (cb({…}) on /ESRIRouting)β€”
key *API key (missing/invalid β†’ 403 JSON {"error":"invalid_api_key"}); on POST always in the URLdocs_382d376eaaa6d004b7b93b7d (demo, rate-limited)β€”
L-EST coordinates (EPSG:3301). Routing also supports Estonian projected coordinates: add srs=EPSG:3301 and pass start/end in L-EST form E,N (east,north in metres). Output coordinates (BoundingBox, LineString, gml:pos) use the same system. Example:
curl "https://pump.elemroot.com/LogismeRouting/SURRouting?pref=Fastest&start=660545.95,6476102.22&end=553474.28,6583771.55&srs=EPSG:3301&output=json&noRouteGeom=false&key=docs_382d376eaaa6d004b7b93b7d"

First request

Request
curl "https://pump.elemroot.com/LogismeRouting/SURRouting?pref=Fastest&start=22.513265,58.265563&end=23.233129,58.605584&output=json&key=docs_382d376eaaa6d004b7b93b7d"
Response (json: summary only)
[
  {
    "RouteSummary": {
      "BoundingBox": [
        { "e": 22.512399678574166, "n": 58.26509890506477 },
        { "e": 23.233128562435795, "n": 58.60563260378001 }
      ],
      "TotalTime": "P0Y0M0DT0H48M35.080S",
      "TotalDistance": "62973.600000000006"
    }
  }
]
FieldMeaning
TotalTimeTravel time as ISO 8601 duration (P0Y0M0DT0H48M35.080S = 48 min 35.08 s)
TotalDistanceTotal distance in metres (string)
BoundingBoxRoute bounding box (two corner points {e,n})

Route geometry

By default geometry is NOT returned: you get only the summary. To receive route points add noRouteGeom=false.
Request
curl "https://pump.elemroot.com/LogismeRouting/SURRouting?pref=Fastest&start=22.513265,58.265563&end=23.233129,58.605584&output=json&noRouteGeom=false&key=docs_382d376eaaa6d004b7b93b7d"
Response (RouteGeometry added)
[
  {
    "RouteSummary": { "TotalTime": "P0Y0M0DT0H48M35.080S", "TotalDistance": "62973.600000000006", "BoundingBox": [ ... ] },
    "RouteGeometry": {
      "LineString": [
        { "n": 58.265563, "e": 22.513265 },
        { "n": 58.265921552298856, "e": 22.512801561977046 },
        { "n": 58.2658073706609, "e": 22.51254892386472 }
      ]
    }
  }
]

LineString are route points in order from start to end ({n, e} = north, east).

Via points (via)

Add via points (space-separated E,N points):

curl "https://pump.elemroot.com/LogismeRouting/SURRouting?pref=Shortest&start=22.513265,58.265563&via=22.8,58.45&end=23.233129,58.605584&output=json&key=docs_382d376eaaa6d004b7b93b7d"
Separate legs by default. With via points each leg is returned separately as a RouteSummary object (startβ†’via1, via1β†’end, …). For one combined route add separateViaResults=false (exactly, lowercase); then TotalDistance/TotalTime are the sums of the legs and LineString is one continuous line.
Response (two legs)
[
  { "RouteSummary": { "TotalDistance": "28189.0", "TotalTime": "P0Y0M0DT0H22M17.770S", "BoundingBox": [ ... ] } },
  { "RouteSummary": { "TotalDistance": "34742.1", "TotalTime": "P0Y0M0DT0H31M47.620S", "BoundingBox": [ ... ] } }
]

Coordinate order

Note: input E,N is the opposite of geocoding/reverse geo q (lat,lon).

XML (OpenLS XLS)

By default (output=xml) the service returns OpenLS XLS XML:

<?xml version='1.0' encoding='UTF-8'?>
<XLS version="1.1" xmlns="http://www.opengis.net/xls" xmlns:gml="http://www.opengis.net/gml">
  <ResponseHeader/>
  <Response version="" requestID="">
    <DetermineRouteResponse>
      <RouteSummary>
        <TotalTime>P0Y0M0DT0H48M35.080S</TotalTime>
        <TotalDistance value="62973.600000000006"/>
        <BoundingBox>
          <gml:pos>58.26509890506477 22.512399678574166</gml:pos>
          <gml:pos>58.60563260378001 23.233128562435795</gml:pos>
        </BoundingBox>
      </RouteSummary>
      <RouteGeometry>
        <gml:LineString>
          <gml:pos>58.265563 22.513265</gml:pos>
          <gml:pos>58.265921552298856 22.512801561977046</gml:pos>
        </gml:LineString>
      </RouteGeometry>
    </DetermineRouteResponse>
  </Response>
</XLS>

Geometry generalisation

Route geometry can be generalised (simplified):

ParamMeaning
maxDevMaximum allowed deviation in metres (default 1). Larger = coarser line; 0 = ungeneralised full geometry.
maxPointsGeometry point limit (when given, it overrides maxDev).
bBoxClipping window (two E,N points space-separated, in the same srs): precise inside, outside the line collapses to practically a straight line (only the window exit points and the end point remain).
A non-numeric maxDev/maxPoints returns 500; a malformed bBox 400.
curl "https://pump.elemroot.com/LogismeRouting/SURRouting?pref=Fastest&start=22.513265,58.265563&end=23.233129,58.605584&output=json&noRouteGeom=false&maxDev=10&maxPoints=100&key=docs_382d376eaaa6d004b7b93b7d"

ESRI interface (ArcGIS)

Endpoint /ESRIRouting is an ArcGIS Network Analyst style interface. Stop points are passed with stops (ArcGIS FeatureSet JSON or shorthand x1,y1;x2,y2); the response is ArcGIS Directions JSON. f=json is required.

ESRI interface. Endpoint /LogismeRouting/ESRIRouting supports e.g. impedanceAttributeName=Distance (shortest route length). The example below is a live working request.
Request (stops EPSG:3301, impedance Distance)
curl -G "https://pump.elemroot.com/LogismeRouting/ESRIRouting" \
  --data-urlencode 'stops={"features":[{"geometry":{"x":635541.4,"y":6583331.7,"spatialReference":{"wkid":3301}},"attributes":{}},{"geometry":{"x":638218.2,"y":6589326.7,"spatialReference":{"wkid":3301}},"attributes":{}}]}' \
  --data-urlencode "outSR=3301" \
  --data-urlencode "impedanceAttributeName=Distance" \
  --data-urlencode "directionsLanguage=et_EE" \
  --data-urlencode "directionsLengthUnits=esriNAUKilometers" \
  --data-urlencode "outputGeometryPrecisionUnits=esriMeters" \
  --data-urlencode "outputGeometryPrecision=0.7" \
  --data-urlencode "ignoreInvalidLocations=true" \
  --data-urlencode "returnDirections=true" \
  --data-urlencode "returnRoutes=false" \
  --data-urlencode "findBestSequence=false" \
  --data-urlencode "startTime=1307001600000" \
  --data-urlencode "key=docs_382d376eaaa6d004b7b93b7d" \
  --data-urlencode "f=json"
Response (ArcGIS Directions, truncated)
{
  "directions": [
    {
      "summary": {
        "envelope": { "xmin": 635541.4, "ymin": 6583322.45, "xmax": 638218.2, "ymax": 6589326.7,
                      "spatialReference": { "wkid": 3301 } },
        "totalDriveTime": 14.0415,
        "totalTime": 14.0415,
        "totalLength": 11.4213
      },
      "features": [
        { "compressedGeometry": "+3+1q5u0+iqn3b+u-s",
          "attributes": { "ETA": 1307001600000, "length": 0.0136, "maneuverType": "esriDMTDepart",
                          "time": 0.0817, "text": "1. sihtpunkt" } },
        { "compressedGeometry": "+3+1q5uu+iqn2f-p7-r1+o-d…",
          "attributes": { "ETA": 1307001604900, "length": 11.3988, "maneuverType": "esriDMTTurnRight",
                          "time": 13.9063, "text": "SΓ΅ida 11,4 km." } }
      ]
    }
  ],
  "messages": []
}
FieldMeaning
summary.totalLengthTotal length always in kilometres (independent of directionsLengthUnits)
routeId / routeNameRoute ID (1) and name ("Location 1 - Location N", or from the stops' attributes.Name)
summary.totalDriveTime / totalTimeTime in minutes
summary.envelopeRoute bounding box (xmin/ymin/xmax/ymax + wkid)
features[].compressedGeometryESRI compressed geometry (leg line)
features[].attributes.textTurn instruction text
features[].attributes.maneuverTypeManeuver type (esriDMTDepart, esriDMTTurnRight, …)
features[].attributes.length / timeLeg length (unit = directionsLengthUnits: km or m) / time in minutes
features[].attributes.ETAArrival time (Unix ms; computed from startTime)

Parameters (* = required; the table is generated from the OpenAPI YAML):

ParamMeaningAllowed values / exampleDefault
stops *Stop points, at least 2; a FeatureSet attributes.Name = stop name (routeName, direction texts); RouteName β†’ 400FeatureSet JSON {"features":[{"geometry":{"x":…,"y":…,"spatialReference":{"wkid":3301}},"attributes":{}},…]} or shorthand x1,y1;x2,y2β€”
f *Output formatmust be json (if missing: 400 without a message)β€”
outSROutput and input WKID; when given every stop's spatialReference.wkid must match; when omitted the stops must have NO spatialReference at all (degrees, EPSG:4326)3301 or 4326; other β†’ 400β€”
directionsLanguage *Directions language (Java Locale); unknown β†’ 400et_EE, en_US, de_DE, fi_FI, ru_RU, sv_SE, ja_JP (short form et also works)β€”
directionsLengthUnits *Length unit of the features[].attributes.length field; summary.totalLength and the direction texts are always kmesriNAUKilometers / esriNAUMetersβ€”
outputGeometryPrecisionUnits *Geometry precision unitmust be esriMetersβ€”
outputGeometryPrecisionGeometry generalisation precision in metres (default β‰ˆ0.05: the example's 0.7 is coarser)number, e.g. 0.7; non-numeric β†’ 400β€”
impedanceAttributeNameImpedance attributeDistance = shortest; DrivingMinutes or omitted = fastest; other β†’ 400β€”
ignoreInvalidLocations *Required compatibility parameter (value not used)e.g. trueβ€”
returnDirectionsReturn directionsdefault true; false gives an empty response {"messages":[]}β€”
returnRoutes *Return route objectsrequired, must be false (true/missing β†’ 400)β€”
findBestSequence *Stop orderrequired; false = given order, true = stop order optimisation (TSP; first and last stay in place)β€”
returnStopsStops FeatureSet in the response (ObjectID, Sequence, Cumul_Time s, Status, geometry)true / false (default)β€”
startTimeStart time Unix ms (for ETA); when omitted the ETA is elapsed ms from 01307001600000; non-numeric β†’ 400β€”
key *API key (missing/invalid β†’ 403 JSON {"error":"invalid_api_key"}); on POST always in the URLdocs_382d376eaaa6d004b7b93b7d (demo, rate-limited)β€”

Some ArcGIS parameters are ignored (accumulateAttributeNames, useHierarchy, outputLines, preserveFirstStop, preserveLastStop, directionsStyleName, useTimeWindows, the returnBarriers family with value false); you may keep them in requests for compatibility. In contrast barriers, polylineBarriers, restrictUTurns, restrictionAttributeNames, attributeParameterValues, directionsTimeAttributeName return 400 (β€œisn't supported”).

Stop order optimisation (findBestSequence=true)

On a multi-stop route the service can optimise the stop order itself (travelling-salesman problem, TSP): the first and last stops stay in place, the intermediate ones are reordered so that the whole tour is shortest/fastest (according to impedanceAttributeName). The response (directions, length, time) describes the optimised order; to get the new order itself add returnStops=true: stops[].ObjectID is the input order, Sequence the new order and Cumul_Time the cumulative drive time in seconds.

Request (4 stops in Tartu, EPSG:3301; same parameter set as above, only findBestSequence=true&returnStops=true)
curl -G "https://pump.elemroot.com/LogismeRouting/ESRIRouting" \
  --data-urlencode 'stops={"features":[{"geometry":{"x":659000,"y":6473000,"spatialReference":{"wkid":3301}},"attributes":{}},{"geometry":{"x":664500,"y":6470500,"spatialReference":{"wkid":3301}},"attributes":{}},{"geometry":{"x":661500,"y":6472500,"spatialReference":{"wkid":3301}},"attributes":{}},{"geometry":{"x":658000,"y":6469500,"spatialReference":{"wkid":3301}},"attributes":{}}]}' \
  --data-urlencode "outSR=3301" --data-urlencode "impedanceAttributeName=Distance" \
  --data-urlencode "directionsLanguage=et_EE" --data-urlencode "directionsLengthUnits=esriNAUKilometers" \
  --data-urlencode "outputGeometryPrecisionUnits=esriMeters" --data-urlencode "ignoreInvalidLocations=true" \
  --data-urlencode "returnRoutes=false" --data-urlencode "findBestSequence=true" --data-urlencode "returnStops=true" \
  --data-urlencode "key=docs_382d376eaaa6d004b7b93b7d" --data-urlencode "f=json"
Response (abridged: the same request with findBestSequence=false gave 22.62 km / 38.3 min)
{
  "directions": [ { "routeId": 1, "routeName": "Location 1 - Location 4",
                    "summary": { "totalLength": 21.1842, "totalTime": 33.7405, … }, "features": [ … ] } ],
  "stops": {
    "features": [
      { "attributes": { "ObjectID": 1, "Sequence": 1, "Cumul_Time": 0,       "Status": 0 }, "geometry": { "x": 659000, "y": 6473000 } },
      { "attributes": { "ObjectID": 2, "Sequence": 3, "Cumul_Time": 1067.39, "Status": 0 }, "geometry": { "x": 664500, "y": 6470500 } },
      { "attributes": { "ObjectID": 3, "Sequence": 2, "Cumul_Time": 495.46,  "Status": 0 }, "geometry": { "x": 661500, "y": 6472500 } },
      { "attributes": { "ObjectID": 4, "Sequence": 4, "Cumul_Time": 2024.43, "Status": 0 }, "geometry": { "x": 658000, "y": 6469500 } }
    ],
    "spatialReference": { "wkid": 3301 }
  }
}

The stop order changed from 1β†’2β†’3β†’4 to 1β†’3β†’2β†’4 and the tour got 1.4 km shorter. Without returnStops the optimised order is only implicit in the response (direction texts β€œstop N”).

Other interfaces

Besides short-URL REST and ESRI interfaces the service offers:

PathInterface
/SURRoutingShort-URL REST (query parameters): topic of this guide
/routingXML input (POST XML body)
/ESRIRoutingESRI-compatible interface (incl. trajectory description)
/services/*SOAP (Apache Axis)

When to use what

The most common tasks and the interface/parameter combination for each. All rows are verified live.

TaskCombinationWhy
Fastest/shortest route Aβ†’B: length and time only/SURRouting?pref=Fastest&start=…&end=…&output=jsonsummary only by default, smallest response
Route line on a mapnoRouteGeom=false (+ maxDev=10 or maxPoints=100 for a lighter line)geometry is omitted by default; generalisation reduces points
Several stops in the given ordervia=E,N E,N (+ separateViaResults=false for one route)legs separately or summed
Stop order optimisation (TSP)/ESRIRouting … findBestSequence=true&returnStops=trueESRI interface only; see above
Turn-by-turn directions (text, ETA)/ESRIRouting … returnDirections=true&directionsLanguage=en_US/SURRouting gives no directions
L-EST97 coordinates in metressrs=EPSG:3301 (SUR) Β· outSR=3301 + wkid: 3301 (ESRI)input and output in the same system
ArcGIS client (Network Analyst style)/ESRIRouting with the full parameter set (see example)compatible Directions JSON
OpenLS XML (legacy integration)output=xml (default)XLS 1.1
From a browser without CORS (JSONP)output=json&callback=cbresponse cb && cb([…]); CORS headers are present anyway
Five pitfalls. (1) Input is E,N = lon,lat: the reverse of the geocoding q. (2) Geometry only comes with noRouteGeom=false (exactly lowercase). (3) β€œNo route” is 500 (HTML) on /SURRouting but 400 (JSON) on /ESRIRouting; handle both. (4) On POST the key must be in the URL, not the body. (5) The same parameter given twice β†’ the first value counts.

Error handling

SituationCodeMessage
Missing required parameter400Missing parameter "pref"
Invalid coordinates400Invalid point coordinates … / Non-numeric coordinates …
Unknown output / pref; malformed bBox400Unknown output format: … / Unsupported cost function: … / Invalid bBox value: …
POST request (key in URL)400Sorry, HTTP POST not supported (without the key in the URL a 403 comes first)
Missing or wrong API key403JSON {"error":"invalid_api_key"}
Too many requests (demo key)429Too Many Requests (HTML)
Route not found; unknown srs; non-numeric maxDev/maxPoints500No route exists from location (id=1; e=…; n=…) to location (id=2; …). / Unknown EPSG code: …

The /SURRouting error body (400/500) is a Tomcat HTML error page with the message in the heading (<h1>HTTP Status 400 - Missing parameter "pref"</h1>). /ESRIRouting errors are JSON {"error":{"message":"…","status":400}}; β€œroute not found” and a stop outside the road-network rectangle (Coordinates of destination … are out of range) are 400 on ESRI, not 500.

A point outside the road network returns 500 (400 on /ESRIRouting). When a point is too far from the road network (e.g. at sea or in open terrain), no route is found and the service answers 500. The failure can be direction-dependent: the same point may work as start (it snaps to the nearest road) yet return 500 as end. On multi-stop routes, retry the legs one by one after a 500: that pinpoints the problematic stop.

API reference (Swagger UI) β†’ Interactive try-it-out. OpenAPI spec (YAML file) β†’ Machine-readable API description for tooling. Note: opens as a raw YAML file.