diff --git a/CHANGELOG.md b/CHANGELOG.md index 233e48a319c..93ac75a09b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ - ADDED: Add documentation about OSM node ids in nearest service response [#4436](https://github.com/Project-OSRM/osrm-backend/pull/4436) - Performance - FIXED: Speed up response time when lots of legs exist and geojson is used with `steps=true` [#4936](https://github.com/Project-OSRM/osrm-backend/pull/4936) + - Misc: + - ADDED: expose name for datasource annotations as metadata [#4973](https://github.com/Project-OSRM/osrm-backend/pull/4973) # 5.16.0 - Changes from 5.15.2: diff --git a/docs/http.md b/docs/http.md index 1f36fbcca36..b14b0a341db 100644 --- a/docs/http.md +++ b/docs/http.md @@ -547,6 +547,7 @@ With `steps=false` and `annotations=true`: "distance": [5,5,10,5,5], "duration": [15,15,40,15,15], "datasources": [1,0,0,0,1], + "metadata": { "datasource_names": ["traffic","lua profile","lua profile","lua profile","traffic"] }, "nodes": [49772551,49772552,49786799,49786800,49786801,49786802], "speed": [0.3, 0.3, 0.3, 0.3, 0.3] } @@ -561,10 +562,12 @@ Annotation of the whole route leg with fine-grained information about each segme - `distance`: The distance, in metres, between each pair of coordinates - `duration`: The duration between each pair of coordinates, in seconds. Does not include the duration of any turns. -- `datasources`: The index of the datasource for the speed between each pair of coordinates. `0` is the default profile, other values are supplied via `--segment-speed-file` to `osrm-contract` +- `datasources`: The index of the datasource for the speed between each pair of coordinates. `0` is the default profile, other values are supplied via `--segment-speed-file` to `osrm-contract` or `osrm-customize`. String-like names are in the `metadata.datasource_names` array. - `nodes`: The OSM node ID for each coordinate along the route, excluding the first/last user-supplied coordinates - `weight`: The weights between each pair of coordinates. Does not include any turn costs. - `speed`: Convenience field, calculation of `distance / duration` rounded to one decimal place +- `metadata`: Metadata related to other annotations + - `datasource_names`: The names of the datasources used for the speed between each pair of coordinates. `lua profile` is the default profile, other values arethe filenames supplied via `--segment-speed-file` to `osrm-contract` or `osrm-customize` #### Example @@ -573,6 +576,7 @@ Annotation of the whole route leg with fine-grained information about each segme "distance": [5,5,10,5,5], "duration": [15,15,40,15,15], "datasources": [1,0,0,0,1], + "metadata": { "datasource_names": ["traffic","lua profile","lua profile","lua profile","traffic"] }, "nodes": [49772551,49772552,49786799,49786800,49786801,49786802], "weight": [15,15,40,15,15] } diff --git a/features/support/route.js b/features/support/route.js index 739bc46af68..cd713372df9 100644 --- a/features/support/route.js +++ b/features/support/route.js @@ -199,14 +199,26 @@ module.exports = function () { var merged = {}; instructions.legs.map(l => { - Object.keys(l.annotation).forEach(a => { + Object.keys(l.annotation).filter(a => !a.match(/metadata/)).forEach(a => { if (!merged[a]) merged[a] = []; merged[a].push(l.annotation[a].join(':')); }); + if (l.annotation.metadata) { + merged.metadata = {}; + Object.keys(l.annotation.metadata).forEach(a => { + if (!merged.metadata[a]) merged.metadata[a] = []; + merged.metadata[a].push(l.annotation.metadata[a].join(':')); + }); + } }); - Object.keys(merged).map(a => { + Object.keys(merged).filter(k => !k.match(/metadata/)).map(a => { merged[a] = merged[a].join(','); }); + if (merged.metadata) { + Object.keys(merged.metadata).map(a => { + merged.metadata[a] = merged.metadata[a].join(','); + }); + } return merged; }; diff --git a/features/support/shared_steps.js b/features/support/shared_steps.js index fa98fe12033..b8743d2c18b 100644 --- a/features/support/shared_steps.js +++ b/features/support/shared_steps.js @@ -158,7 +158,8 @@ module.exports = function () { // if header matches 'a:*', parse out the values for * // and return in that header headers.forEach((k) => { - let whitelist = ['duration', 'distance', 'datasources', 'nodes', 'weight', 'speed']; + let whitelist = ['duration', 'distance', 'datasources', 'nodes', 'weight', 'speed' ]; + let metadata_whitelist = [ 'datasource_names' ]; if (k.match(/^a:/)) { let a_type = k.slice(2); if (whitelist.indexOf(a_type) == -1) @@ -166,6 +167,13 @@ module.exports = function () { if (annotation && !annotation[a_type]) return cb(new Error('Annotation not found in response', a_type)); got[k] = annotation && annotation[a_type] || ''; + } else if (k.match(/^am:/)) { + let a_type = k.slice(3); + if (metadata_whitelist.indexOf(a_type) == -1) + return cb(new Error('Unrecognized annotation field', a_type)); + if (annotation && (!annotation.metadata || !annotation.metadata[a_type])) + return cb(new Error('Annotation not found in response', a_type)); + got[k] = (annotation && annotation.metadata && annotation.metadata[a_type]) || ''; } }); diff --git a/features/testbot/annotations.feature b/features/testbot/annotations.feature index 27d6a7b7581..706eb95041d 100644 --- a/features/testbot/annotations.feature +++ b/features/testbot/annotations.feature @@ -60,6 +60,39 @@ Feature: Annotations | a | i | abcdefghi,abcdefghi | 1:0:1:0:1:0:0:0 | 50:10:50:10:50:10:10:10 | | i | a | abcdefghi,abcdefghi | 0:1:0:0:0:0:0:1 | 10:50:10:10:10:10:10:50 | + Scenario: datasource name annotations + Given the profile "testbot" + + And the node map + """ + a b c + """ + + And the ways + | nodes | + | abc | + + And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" + + And the speed file + """ + 1,2,180,1 + 2,1,180,1 + """ + + And the query options + | annotations | datasources | + + # Note - the source names here are specific to how the tests are constructed, + # so if this test is moved around (changes line number) or support code + # changes how the filenames are generated, this test will need to be updated + When I route I should get + | from | to | route | am:datasource_names | + | a | c | abc,abc | lua profile:63_datasource_name_annotations_speeds | + | c | a | abc,abc | lua profile:63_datasource_name_annotations_speeds | + + Scenario: Speed annotations should handle zero segments Given the profile "testbot" diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp index 1e6c9764743..a2427790132 100644 --- a/include/engine/api/route_api.hpp +++ b/include/engine/api/route_api.hpp @@ -324,6 +324,23 @@ class RouteAPI : public BaseAPI } annotation.values["nodes"] = std::move(nodes); } + // Add any supporting metadata, if needed + if (requested_annotations & RouteParameters::AnnotationsType::Datasources) + { + const auto MAX_DATASOURCE_ID = 255u; + util::json::Object metadata; + util::json::Array datasource_names; + for (auto i = 0u; i < MAX_DATASOURCE_ID; i++) + { + const auto name = facade.GetDatasourceName(i); + // Length of 0 indicates the first empty name, so we can stop here + if (name.size() == 0) + break; + datasource_names.values.push_back(std::string(facade.GetDatasourceName(i))); + } + metadata.values["datasource_names"] = datasource_names; + annotation.values["metadata"] = metadata; + } annotations.push_back(std::move(annotation)); } diff --git a/unit_tests/library/route.cpp b/unit_tests/library/route.cpp index 4e60e75fc7c..2ce75b0e015 100644 --- a/unit_tests/library/route.cpp +++ b/unit_tests/library/route.cpp @@ -453,7 +453,7 @@ BOOST_AUTO_TEST_CASE(test_manual_setting_of_annotations_property) .values["annotation"] .get() .values; - BOOST_CHECK_EQUAL(annotations.size(), 5); + BOOST_CHECK_EQUAL(annotations.size(), 6); } BOOST_AUTO_TEST_SUITE_END()