Routing guide
Practical guide with examples: route calculation, via points, geometry and output formats.
https://pump.elemroot.com/LogismeRouting.
Endpoint and method
| Method | URL |
|---|---|
GET | https://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
| Param | Meaning | Allowed values / example | Default |
|---|---|---|---|
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 Start | 22.513265,58.265563 | β |
end * | End point E,N; alias End | 23.233129,58.605584 | β |
via | Via points E,N, space-separated; alias Via | 22.8,58.45 22.9,58.5 | β |
srs | Coordinate system (EPSG); bBox uses the same | only EPSG:4326 or EPSG:3301 (L-EST97; the EPSG: prefix is required, case-insensitive); any other value returns 500 | EPSG:4326 |
output | Output format (case-insensitive; unknown β 400) | xml, json | xml |
noRouteGeom | Omit geometry | exactly false (lowercase) = include geometry; FALSE/0 = default (no geometry) | β |
separateViaResults | Each via leg separate | true / exactly false (merges the legs: total length and time are sums, one continuous LineString); default true | β |
maxDev | Generalisation max deviation (m) | e.g. 1, 10; 0 = ungeneralised full geometry; non-numeric β 500 | β |
maxPoints | Geometry point limit (overrides maxDev) | integer, e.g. 100; non-numeric β 500 | β |
bBox | Clipping 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 | β |
callback | JSONP 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 URL | docs_382d376eaaa6d004b7b93b7d (demo, rate-limited) | β |
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
Requestcurl "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"
}
}
]
| Field | Meaning |
|---|---|
TotalTime | Travel time as ISO 8601 duration (P0Y0M0DT0H48M35.080S = 48 min 35.08 s) |
TotalDistance | Total distance in metres (string) |
BoundingBox | Route bounding box (two corner points {e,n}) |
Route geometry
noRouteGeom=false.
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"
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.
[
{ "RouteSummary": { "TotalDistance": "28189.0", "TotalTime": "P0Y0M0DT0H22M17.770S", "BoundingBox": [ ... ] } },
{ "RouteSummary": { "TotalDistance": "34742.1", "TotalTime": "P0Y0M0DT0H31M47.620S", "BoundingBox": [ ... ] } }
]
Coordinate order
- Input (
start,end,via,bBox):E,N=longitude,latitude(e.g.22.513265,58.265563) - JSON output: points as objects
{n, e}(BoundingBoxuses the same) - XML output:
<gml:pos>=N E(latitude longitude, space-separated)
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):
| Param | Meaning |
|---|---|
maxDev | Maximum allowed deviation in metres (default 1). Larger = coarser line; 0 = ungeneralised full geometry. |
maxPoints | Geometry point limit (when given, it overrides maxDev). |
bBox | Clipping 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.
/LogismeRouting/ESRIRouting supports e.g.
impedanceAttributeName=Distance (shortest route length). The example below is a live working request.
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": []
}
| Field | Meaning |
|---|---|
summary.totalLength | Total length always in kilometres (independent of directionsLengthUnits) |
routeId / routeName | Route ID (1) and name ("Location 1 - Location N", or from the stops' attributes.Name) |
summary.totalDriveTime / totalTime | Time in minutes |
summary.envelope | Route bounding box (xmin/ymin/xmax/ymax + wkid) |
features[].compressedGeometry | ESRI compressed geometry (leg line) |
features[].attributes.text | Turn instruction text |
features[].attributes.maneuverType | Maneuver type (esriDMTDepart, esriDMTTurnRight, β¦) |
features[].attributes.length / time | Leg length (unit = directionsLengthUnits: km or m) / time in minutes |
features[].attributes.ETA | Arrival time (Unix ms; computed from startTime) |
Parameters (* = required; the table is generated from the OpenAPI YAML):
| Param | Meaning | Allowed values / example | Default |
|---|---|---|---|
stops * | Stop points, at least 2; a FeatureSet attributes.Name = stop name (routeName, direction texts); RouteName β 400 | FeatureSet JSON {"features":[{"geometry":{"x":β¦,"y":β¦,"spatialReference":{"wkid":3301}},"attributes":{}},β¦]} or shorthand x1,y1;x2,y2 | β |
f * | Output format | must be json (if missing: 400 without a message) | β |
outSR | Output 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 β 400 | et_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 km | esriNAUKilometers / esriNAUMeters | β |
outputGeometryPrecisionUnits * | Geometry precision unit | must be esriMeters | β |
outputGeometryPrecision | Geometry generalisation precision in metres (default β0.05: the example's 0.7 is coarser) | number, e.g. 0.7; non-numeric β 400 | β |
impedanceAttributeName | Impedance attribute | Distance = shortest; DrivingMinutes or omitted = fastest; other β 400 | β |
ignoreInvalidLocations * | Required compatibility parameter (value not used) | e.g. true | β |
returnDirections | Return directions | default true; false gives an empty response {"messages":[]} | β |
returnRoutes * | Return route objects | required, must be false (true/missing β 400) | β |
findBestSequence * | Stop order | required; false = given order, true = stop order optimisation (TSP; first and last stay in place) | β |
returnStops | Stops FeatureSet in the response (ObjectID, Sequence, Cumul_Time s, Status, geometry) | true / false (default) | β |
startTime | Start time Unix ms (for ETA); when omitted the ETA is elapsed ms from 0 | 1307001600000; non-numeric β 400 | β |
key * | API key (missing/invalid β 403 JSON {"error":"invalid_api_key"}); on POST always in the URL | docs_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.
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:
| Path | Interface |
|---|---|
/SURRouting | Short-URL REST (query parameters): topic of this guide |
/routing | XML input (POST XML body) |
/ESRIRouting | ESRI-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.
| Task | Combination | Why |
|---|---|---|
| Fastest/shortest route AβB: length and time only | /SURRouting?pref=Fastest&start=β¦&end=β¦&output=json | summary only by default, smallest response |
| Route line on a map | noRouteGeom=false (+ maxDev=10 or maxPoints=100 for a lighter line) | geometry is omitted by default; generalisation reduces points |
| Several stops in the given order | via=E,N E,N (+ separateViaResults=false for one route) | legs separately or summed |
| Stop order optimisation (TSP) | /ESRIRouting β¦ findBestSequence=true&returnStops=true | ESRI interface only; see above |
| Turn-by-turn directions (text, ETA) | /ESRIRouting β¦ returnDirections=true&directionsLanguage=en_US | /SURRouting gives no directions |
| L-EST97 coordinates in metres | srs=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=cb | response cb && cb([β¦]); CORS headers are present anyway |
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
| Situation | Code | Message |
|---|---|---|
| Missing required parameter | 400 | Missing parameter "pref" |
| Invalid coordinates | 400 | Invalid point coordinates β¦ / Non-numeric coordinates β¦ |
Unknown output / pref; malformed bBox | 400 | Unknown output format: β¦ / Unsupported cost function: β¦ / Invalid bBox value: β¦ |
POST request (key in URL) | 400 | Sorry, HTTP POST not supported (without the key in the URL a 403 comes first) |
| Missing or wrong API key | 403 | JSON {"error":"invalid_api_key"} |
| Too many requests (demo key) | 429 | Too Many Requests (HTML) |
Route not found; unknown srs; non-numeric maxDev/maxPoints | 500 | No 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.
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.