From b06d1987cbe3f375e3b713c7fcb6720a0c23c26e Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Thu, 2 Oct 2025 11:10:06 +0200 Subject: [PATCH 1/2] Add new by_legs overview option to return overview split by legs instead of having one for the whole route --- include/engine/api/json_factory.hpp | 1 + include/engine/api/route_api.hpp | 56 +++++++++++++------ include/engine/api/route_parameters.hpp | 3 +- include/nodejs/node_osrm_support.hpp | 8 ++- .../server/api/route_parameters_grammar.hpp | 3 +- src/engine/api/json_factory.cpp | 35 +++++++----- src/nodejs/node_osrm.cpp | 4 +- 7 files changed, 72 insertions(+), 38 deletions(-) diff --git a/include/engine/api/json_factory.hpp b/include/engine/api/json_factory.hpp index 001f44ff851..49ad4ea2ca4 100644 --- a/include/engine/api/json_factory.hpp +++ b/include/engine/api/json_factory.hpp @@ -106,6 +106,7 @@ util::json::Object makeWaypoint(const util::Coordinate &location, util::json::Object makeRouteLeg(guidance::RouteLeg leg, util::json::Array steps); util::json::Array makeRouteLegs(std::vector legs, + std::vector leg_geometries, std::vector step_geometries, std::vector annotations); } // namespace api::json diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp index 08431a2cf5d..2940a587cf4 100644 --- a/include/engine/api/route_api.hpp +++ b/include/engine/api/route_api.hpp @@ -186,26 +186,34 @@ class RouteAPI : public BaseAPI } std::optional - MakeGeometry(std::optional> &&annotations) const + MakeGeometry(std::optional> &&geometry) const { std::optional json_geometry; - if (annotations) + if (geometry) { - auto begin = annotations->begin(); - auto end = annotations->end(); - if (parameters.geometries == RouteParameters::GeometriesType::Polyline) - { - json_geometry = json::makePolyline<100000>(begin, end); - } - else if (parameters.geometries == RouteParameters::GeometriesType::Polyline6) - { - json_geometry = json::makePolyline<1000000>(begin, end); - } - else - { - BOOST_ASSERT(parameters.geometries == RouteParameters::GeometriesType::GeoJSON); - json_geometry = json::makeGeoJSONGeometry(begin, end); - } + json_geometry = MakeGeometry(*geometry); + } + return json_geometry; + } + + util::json::Value + MakeGeometry(const std::vector &geometry) const + { + util::json::Value json_geometry; + auto begin = geometry.begin(); + auto end = geometry.end(); + if (parameters.geometries == RouteParameters::GeometriesType::Polyline) + { + json_geometry = json::makePolyline<100000>(begin, end); + } + else if (parameters.geometries == RouteParameters::GeometriesType::Polyline6) + { + json_geometry = json::makePolyline<1000000>(begin, end); + } + else + { + BOOST_ASSERT(parameters.geometries == RouteParameters::GeometriesType::GeoJSON); + json_geometry = json::makeGeoJSONGeometry(begin, end); } return json_geometry; } @@ -723,6 +731,16 @@ class RouteAPI : public BaseAPI auto route = guidance::assembleRoute(legs); std::optional json_overview = MakeGeometry(MakeOverview(leg_geometries)); + std::vector json_overview_by_legs; + if (parameters.overview == RouteParameters::OverviewType::ByLegs) + { + json_overview_by_legs.reserve(leg_geometries.size()); + for (const auto idx : util::irange(0UL, leg_geometries.size())) + { + json_overview_by_legs.emplace_back(MakeGeometry(leg_geometries[idx].locations)); + } + } + std::vector step_geometries; const auto total_step_count = std::accumulate(legs.begin(), @@ -871,6 +889,7 @@ class RouteAPI : public BaseAPI auto result = json::makeRoute(route, json::makeRouteLegs(std::move(legs), + std::move(json_overview_by_legs), std::move(step_geometries), std::move(annotations)), std::move(json_overview), @@ -1001,7 +1020,8 @@ class RouteAPI : public BaseAPI MakeOverview(const std::vector &leg_geometries) const { std::optional> overview; - if (parameters.overview != RouteParameters::OverviewType::False) + if (parameters.overview != RouteParameters::OverviewType::False && + parameters.overview != RouteParameters::OverviewType::ByLegs) { const auto use_simplification = parameters.overview == RouteParameters::OverviewType::Simplified; diff --git a/include/engine/api/route_parameters.hpp b/include/engine/api/route_parameters.hpp index a28f34b8c7c..7fb7659f4d4 100644 --- a/include/engine/api/route_parameters.hpp +++ b/include/engine/api/route_parameters.hpp @@ -61,7 +61,8 @@ struct RouteParameters : public BaseParameters { Simplified, Full, - False + False, + ByLegs }; enum class AnnotationsType { diff --git a/include/nodejs/node_osrm_support.hpp b/include/nodejs/node_osrm_support.hpp index 328a9985460..46c4ace33b5 100644 --- a/include/nodejs/node_osrm_support.hpp +++ b/include/nodejs/node_osrm_support.hpp @@ -992,7 +992,7 @@ inline bool parseCommonParameters(const Napi::Object &obj, ParamType ¶ms) if (!overview.IsString()) { - ThrowError(obj.Env(), "Overview must be a string: [simplified, full, false]"); + ThrowError(obj.Env(), "Overview must be a string: [simplified, full, false, by_legs]"); return false; } @@ -1010,9 +1010,13 @@ inline bool parseCommonParameters(const Napi::Object &obj, ParamType ¶ms) { params->overview = osrm::RouteParameters::OverviewType::False; } + else if (overview_str == "by_legs") + { + params->overview = osrm::RouteParameters::OverviewType::ByLegs; + } else { - ThrowError(obj.Env(), "'overview' param must be one of [simplified, full, false]"); + ThrowError(obj.Env(), "'overview' param must be one of [simplified, full, false, by_legs]"); return false; } } diff --git a/include/server/api/route_parameters_grammar.hpp b/include/server/api/route_parameters_grammar.hpp index 5ef668e6c09..11a9c131197 100644 --- a/include/server/api/route_parameters_grammar.hpp +++ b/include/server/api/route_parameters_grammar.hpp @@ -68,7 +68,8 @@ struct RouteParametersGrammar : public BaseParametersGrammar leg_geometry, + std::optional annotation) { util::json::Object route_leg; route_leg.values.reserve(5); @@ -267,18 +270,19 @@ util::json::Object makeRouteLeg(guidance::RouteLeg leg, util::json::Array steps) route_leg.values.emplace("weight", leg.weight); route_leg.values.emplace("summary", std::move(leg.summary)); route_leg.values.emplace("steps", std::move(steps)); - return route_leg; -} -util::json::Object -makeRouteLeg(guidance::RouteLeg leg, util::json::Array steps, util::json::Object annotation) -{ - util::json::Object route_leg = makeRouteLeg(std::move(leg), std::move(steps)); - route_leg.values.reserve(1); - route_leg.values.emplace("annotation", std::move(annotation)); + if (leg_geometry) + { + route_leg.values.emplace("geometry", std::move(*leg_geometry)); + } + if (annotation) + { + route_leg.values.emplace("annotation", std::move(*annotation)); + } return route_leg; } util::json::Array makeRouteLegs(std::vector legs, + std::vector leg_geometries, std::vector step_geometries, std::vector annotations) { @@ -295,15 +299,18 @@ util::json::Array makeRouteLegs(std::vector legs, [&step_geometry_iter](guidance::RouteStep step) { return makeRouteStep(std::move(step), std::move(*step_geometry_iter++)); }); - if (annotations.size() > 0) + std::optional annotation; + std::optional leg_geometry; + if (leg_geometries.size() > 0) { - json_legs.values.push_back( - makeRouteLeg(std::move(leg), std::move(json_steps), annotations[idx])); + leg_geometry = std::move(leg_geometries[idx]); } - else + if (annotations.size() > 0) { - json_legs.values.push_back(makeRouteLeg(std::move(leg), std::move(json_steps))); + annotation = std::move(annotations[idx]); } + json_legs.values.push_back( + makeRouteLeg(std::move(leg), std::move(json_steps), std::move(leg_geometry), std::move(annotation))); } return json_legs; } diff --git a/src/nodejs/node_osrm.cpp b/src/nodejs/node_osrm.cpp index f8cd7e4756f..f250ad53610 100644 --- a/src/nodejs/node_osrm.cpp +++ b/src/nodejs/node_osrm.cpp @@ -285,7 +285,7 @@ inline void asyncForTiles(const Napi::CallbackInfo &info, * @param {Boolean} [options.steps=false] Return route steps for each route leg. * @param {Array|Boolean} [options.annotations=false] An array with strings of `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed` or boolean for enabling/disabling all. * @param {String} [options.geometries=polyline] Returned route geometry format (influences overview and per step). Can also be `geojson`. - * @param {String} [options.overview=simplified] Add overview geometry either `full`, `simplified` according to highest zoom level it could be display on, or not at all (`false`). + * @param {String} [options.overview=simplified] Add overview geometry either `full`, `simplified` according to highest zoom level it could be display on, or not at all (`false`). If you want the overview for each leg, you can use `by_legs`. * @param {Boolean} [options.continue_straight] Forces the route to keep going straight at waypoints and don't do a uturn even if it would be faster. Default value depends on the profile. * @param {Array} [options.approaches] Restrict the direction on the road network at a waypoint, relative to the input coordinate. Can be `null` (unrestricted, default), `curb` or `opposite`. * `null`/`true`/`false` @@ -555,7 +555,7 @@ Napi::Value Engine::match(const Napi::CallbackInfo &info) * @param {Boolean} [options.steps=false] Return route steps for each route. * @param {Array|Boolean} [options.annotations=false] An array with strings of `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed` or boolean for enabling/disabling all. * @param {String} [options.geometries=polyline] Returned route geometry format (influences overview and per step). Can also be `geojson`. - * @param {String} [options.overview=simplified] Add overview geometry either `full`, `simplified` + * @param {String} [options.overview=simplified] Add overview geometry either `full`, `simplified`, `false` or `by_legs`. * @param {Function} callback * @param {Boolean} [options.roundtrip=true] Return route is a roundtrip. * @param {String} [options.source=any] Return route starts at `any` or `first` coordinate. From dcd81c1f8c5183963ca0d41a65dadc9a645fb6c7 Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Thu, 2 Oct 2025 11:26:01 +0200 Subject: [PATCH 2/2] Update documentation for by_legs overview option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update HTTP API docs for route, match, and trip services - Update Node.js API docs for route, match, and trip methods - Update test error messages to include by_legs option 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docs/http.md | 6 +++--- docs/nodejs/api.md | 6 +++--- test/nodejs/route.js | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/http.md b/docs/http.md index ca3dc20cfac..a9bcf1e7935 100644 --- a/docs/http.md +++ b/docs/http.md @@ -211,7 +211,7 @@ In addition to the [general options](#general-options) the following options are |steps |`true`, `false` (default) |Returned route steps for each route leg | |annotations |`true`, `false` (default), `nodes`, `distance`, `duration`, `datasources`, `weight`, `speed` |Returns additional metadata for each coordinate along the route geometry. | |geometries |`polyline` (default), `polyline6`, `geojson` |Returned route geometry format (influences overview and per step) | -|overview |`simplified` (default), `full`, `false` |Add overview geometry either full, simplified according to highest zoom level it could be displayed on, or not at all.| +|overview |`simplified` (default), `full`, `false`, `by_legs` |Add overview geometry either full, simplified according to highest zoom level it could be displayed on, not at all, or split by leg.| |continue\_straight |`default` (default), `true`, `false` |Forces the route to keep going straight at waypoints constraining uturns there even if it would be faster. Default value depends on the profile. | |waypoints | `{index};{index};{index}...` |Treats input coordinates indicated by given indices as waypoints in returned Match object. Default is to treat all input coordinates as waypoints. | @@ -432,7 +432,7 @@ In addition to the [general options](#general-options) the following options are |steps |`true`, `false` (default) |Returned route steps for each route | |geometries |`polyline` (default), `polyline6`, `geojson` |Returned route geometry format (influences overview and per step) | |annotations |`true`, `false` (default), `nodes`, `distance`, `duration`, `datasources`, `weight`, `speed` |Returns additional metadata for each coordinate along the route geometry. | -|overview |`simplified` (default), `full`, `false` |Add overview geometry either full, simplified according to highest zoom level it could be displayed on, or not at all.| +|overview |`simplified` (default), `full`, `false`, `by_legs` |Add overview geometry either full, simplified according to highest zoom level it could be displayed on, not at all, or split by leg.| |timestamps |`{timestamp};{timestamp}[;{timestamp} ...]` |Timestamps for the input locations in seconds since UNIX epoch. Timestamps need to be monotonically increasing. | |radiuses |`{radius};{radius}[;{radius} ...]` |Standard deviation of GPS precision used for map matching. If applicable use GPS accuracy.| |gaps |`split` (default), `ignore` |Allows the input track splitting based on huge timestamp gaps between points. | @@ -489,7 +489,7 @@ In addition to the [general options](#general-options) the following options are |steps |`true`, `false` (default) |Returned route instructions for each trip | |annotations |`true`, `false` (default), `nodes`, `distance`, `duration`, `datasources`, `weight`, `speed` |Returns additional metadata for each coordinate along the route geometry. | |geometries |`polyline` (default), `polyline6`, `geojson` |Returned route geometry format (influences overview and per step) | -|overview |`simplified` (default), `full`, `false` |Add overview geometry either full, simplified according to highest zoom level it could be displayed on, or not at all.| +|overview |`simplified` (default), `full`, `false`, `by_legs` |Add overview geometry either full, simplified according to highest zoom level it could be displayed on, not at all, or split by leg.| **Fixing Start and End Points** diff --git a/docs/nodejs/api.md b/docs/nodejs/api.md index d8c67eff7f5..489756b7b11 100644 --- a/docs/nodejs/api.md +++ b/docs/nodejs/api.md @@ -61,7 +61,7 @@ Returns the fastest route between two or more coordinates while visiting the way - `options.steps` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Return route steps for each route leg. (optional, default `false`) - `options.annotations` **([Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) \| [Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean))** An array with strings of `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed` or boolean for enabling/disabling all. (optional, default `false`) - `options.geometries` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Returned route geometry format (influences overview and per step). Can also be `geojson`. (optional, default `polyline`) - - `options.overview` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Add overview geometry either `full`, `simplified` according to highest zoom level it could be displayed on, or not at all (`false`). (optional, default `simplified`) + - `options.overview` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Add overview geometry either `full`, `simplified` according to highest zoom level it could be displayed on, not at all (`false`), or split by leg (`by_legs`). (optional, default `simplified`) - `options.continue_straight` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** Forces the route to keep going straight at waypoints and don't do a uturn even if it would be faster. Default value depends on the profile. - `options.approaches` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)?** Restrict the direction on the road network at a waypoint, relative to the input coordinate. Can be `null` (unrestricted, default), `curb` or `opposite`. `null`/`true`/`false` @@ -224,7 +224,7 @@ if they cannot be matched successfully. - `options.steps` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Return route steps for each route. (optional, default `false`) - `options.annotations` **([Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) \| [Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean))** An array with strings of `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed` or boolean for enabling/disabling all. (optional, default `false`) - `options.geometries` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Returned route geometry format (influences overview and per step). Can also be `geojson`. (optional, default `polyline`) - - `options.overview` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Add overview geometry either `full`, `simplified` according to highest zoom level it could be displayed on, or not at all (`false`). (optional, default `simplified`) + - `options.overview` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Add overview geometry either `full`, `simplified` according to highest zoom level it could be displayed on, not at all (`false`), or split by leg (`by_legs`). (optional, default `simplified`) - `options.timestamps` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)>?** Timestamp of the input location (integers, UNIX-like timestamp). - `options.radiuses` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)?** Standard deviation of GPS precision used for map matching. If applicable use GPS accuracy. Can be `null` for default value `5` meters or `double >= 0`. - `options.gaps` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Allows the input track splitting based on huge timestamp gaps between points. Either `split` or `ignore`. (optional, default `split`) @@ -294,7 +294,7 @@ Right now, the following combinations are possible: - `options.steps` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Return route steps for each route. (optional, default `false`) - `options.annotations` **([Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) \| [Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean))** An array with strings of `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed` or boolean for enabling/disabling all. (optional, default `false`) - `options.geometries` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Returned route geometry format (influences overview and per step). Can also be `geojson`. (optional, default `polyline`) - - `options.overview` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Add overview geometry either `full`, `simplified` (optional, default `simplified`) + - `options.overview` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Add overview geometry either `full`, `simplified` according to highest zoom level it could be displayed on, not at all (`false`), or split by leg (`by_legs`). (optional, default `simplified`) - `options.roundtrip` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Return route is a roundtrip. (optional, default `true`) - `options.source` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Return route starts at `any` or `first` coordinate. (optional, default `any`) - `options.destination` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Return route ends at `any` or `last` coordinate. (optional, default `any`) diff --git a/test/nodejs/route.js b/test/nodejs/route.js index 17d4de23a73..7c476758506 100644 --- a/test/nodejs/route.js +++ b/test/nodejs/route.js @@ -369,17 +369,17 @@ test('route: invalid route options', function(assert) { coordinates: two_test_coordinates, overview: false }, function(err, route) {}); }, - /Overview must be a string: \[simplified, full, false\]/); + /Overview must be a string: \[simplified, full, false, by_legs\]/); assert.throws(function() { osrm.route({ coordinates: two_test_coordinates, overview: false }, function(err, route) {}); }, - /Overview must be a string: \[simplified, full, false\]/); + /Overview must be a string: \[simplified, full, false, by_legs\]/); assert.throws(function() { osrm.route({ coordinates: two_test_coordinates, overview: 'maybe' }, function(err, route) {}); }, - /'overview' param must be one of \[simplified, full, false\]/); + /'overview' param must be one of \[simplified, full, false, by_legs\]/); assert.throws(function() { osrm.route({ coordinates: two_test_coordinates, geometries: 'maybe'