diff --git a/docs/http.md b/docs/http.md index ce03b22470e..34e20277586 100644 --- a/docs/http.md +++ b/docs/http.md @@ -209,7 +209,7 @@ In addition to the [general options](#general-options) the following options are |------------|---------------------------------------------|-------------------------------------------------------------------------------| |alternatives|`true`, `false` (default), or Number |Search for alternative routes. Passing a number `alternatives=n` searches for up to `n` alternative routes.\* | |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. | +|annotations |`true`, `false` (default), `nodes`, `distance`, `duration`, `datasources`, `weight`, `speed`, `way_ids` |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`, `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. | @@ -431,7 +431,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. | +|annotations |`true`, `false` (default), `nodes`, `distance`, `duration`, `datasources`, `weight`, `speed`, `way_ids` |Returns additional metadata for each coordinate along the route geometry. | |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.| @@ -487,7 +487,7 @@ In addition to the [general options](#general-options) the following options are |source |`any` (default), `first` |Returned route starts at `any` or `first` coordinate | |destination |`any` (default), `last` |Returned route ends at `any` or `last` coordinate | |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. | +|annotations |`true`, `false` (default), `nodes`, `distance`, `duration`, `datasources`, `weight`, `speed`, `way_ids` |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`, `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.| @@ -667,7 +667,7 @@ Represents a route between two waypoints. | annotations | | |--------------|-------------------------------------------------------------------------------| -| true | An `Annotation` object containing node ids, durations, distances, and weights. | +| true | An `Annotation` object containing the requested per-segment values, including node ids, way ids, durations, distances, and weights. | | false | `undefined` | #### Example @@ -686,7 +686,8 @@ With `steps=false` and `annotations=true`: "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] + "speed": [0.3, 0.3, 0.3, 0.3, 0.3], + "way_ids": [111,111,222,222,333] } } ``` @@ -703,6 +704,7 @@ Annotation of the whole route leg with fine-grained information about each segme - `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 +- `way_ids`: The OSM way ID for each traversed segment along the route leg - `metadata`: Metadata related to other annotations - `datasource_names`: The names of the data sources used for the speed between each pair of coordinates. `lua profile` is the default profile, other values are the filenames supplied via `--segment-speed-file` to `osrm-contract` or `osrm-customize` @@ -715,7 +717,8 @@ Annotation of the whole route leg with fine-grained information about each segme "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] + "weight": [15,15,40,15,15], + "way_ids": [111,111,222,222,333] } ``` diff --git a/docs/nodejs/api.md b/docs/nodejs/api.md index c7b7262d621..d50f04f8000 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.alternatives` **[Number][6]** Search for up to this many alternative routes. *Please note that even if alternative routes are requested, a result cannot be guaranteed.* (optional, default `0`) * `options.steps` **[Boolean][4]** Return route steps for each route leg. (optional, default `false`) - * `options.annotations` **([Array][5] | [Boolean][4])** An array with strings of `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed` or boolean for enabling/disabling all. (optional, default `false`) + * `options.annotations` **([Array][5] | [Boolean][4])** An array with strings of `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed`, `way_ids` or boolean for enabling/disabling all. (optional, default `false`) * `options.geometries` **[String][3]** Returned route geometry format (influences overview and per step). Can also be `geojson`. (optional, default `polyline`) * `options.overview` **[String][3]** Add overview geometry either `full`, `simplified` according to highest zoom level it could be displayed on, or not at all (`false`). If you want the overview for each leg, you can use `by_legs`. (optional, default `simplified`) * `options.continue_straight` **[Boolean][4]?** 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. @@ -227,7 +227,7 @@ if they can not be matched successfully. * `options.hints` **[Array][5]?** Hints for the coordinate snapping. Array of base64 encoded strings. * `options.generate_hints` **[Boolean][4]** Whether or not adds a Hint to the response which can be used in subsequent requests. (optional, default `true`) * `options.steps` **[Boolean][4]** Return route steps for each route. (optional, default `false`) - * `options.annotations` **([Array][5] | [Boolean][4])** An array with strings of `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed` or boolean for enabling/disabling all. (optional, default `false`) + * `options.annotations` **([Array][5] | [Boolean][4])** An array with strings of `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed`, `way_ids` or boolean for enabling/disabling all. (optional, default `false`) * `options.geometries` **[String][3]** Returned route geometry format (influences overview and per step). Can also be `geojson`. (optional, default `polyline`) * `options.overview` **[String][3]** Add overview geometry either `full`, `simplified` according to highest zoom level it could be display on, or not at all (`false`). (optional, default `simplified`) * `options.timestamps` **[Array][5]<[Number][6]>?** Timestamp of the input location (integers, UNIX-like timestamp). @@ -298,7 +298,7 @@ Right now, the following combinations are possible: * `options.hints` **[Array][5]?** Hints for the coordinate snapping. Array of base64 encoded strings. * `options.generate_hints` **[Boolean][4]** Whether or not adds a Hint to the response which can be used in subsequent requests. (optional, default `true`) * `options.steps` **[Boolean][4]** Return route steps for each route. (optional, default `false`) - * `options.annotations` **([Array][5] | [Boolean][4])** An array with strings of `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed` or boolean for enabling/disabling all. (optional, default `false`) + * `options.annotations` **([Array][5] | [Boolean][4])** An array with strings of `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed`, `way_ids` or boolean for enabling/disabling all. (optional, default `false`) * `options.geometries` **[String][3]** Returned route geometry format (influences overview and per step). Can also be `geojson`. (optional, default `polyline`) * `options.overview` **[String][3]** Add overview geometry either `full`, `simplified`, `false` or `by_legs`. (optional, default `simplified`) * `options.roundtrip` **[Boolean][4]** Return route is a roundtrip. (optional, default `true`) diff --git a/features/step_definitions/matching.js b/features/step_definitions/matching.js index 8c6e1adef86..6d20e4d010d 100644 --- a/features/step_definitions/matching.js +++ b/features/step_definitions/matching.js @@ -148,6 +148,8 @@ When(/^I match I should get$/, async function (table) { 'datasources', 'nodes', 'weight', + 'speed', + 'way_ids', ]; if (k.match(/^a:/)) { const a_type = k.slice(2); diff --git a/features/support/shared_steps.js b/features/support/shared_steps.js index e35fe70c295..e2d47feb0b1 100644 --- a/features/support/shared_steps.js +++ b/features/support/shared_steps.js @@ -211,6 +211,7 @@ export default class SharedSteps { 'nodes', 'weight', 'speed', + 'way_ids', ]; const metadata_whitelist = ['datasource_names']; if (k.match(/^a:/)) { diff --git a/features/testbot/way_ids.feature b/features/testbot/way_ids.feature new file mode 100644 index 00000000000..3e3f6840409 --- /dev/null +++ b/features/testbot/way_ids.feature @@ -0,0 +1,25 @@ +@routing @testbot +Feature: Route annotations - way ids + + Background: + Given the profile "testbot" + Given a grid size of 10 meters + Given the node map + """ + a--1-b--2-c--3-d + """ + And the ways + | nodes | + | ab | + | bc | + | cd | + + Scenario: Route annotations expose traversed OSM way ids per segment + Given the query options + | steps | false | + | annotations | way_ids | + + When I route I should get + | from | to | a:way_ids | + | 1 | 3 | 5:6:7 | + | 3 | 1 | 7:6:5 | diff --git a/include/engine/api/flatbuffers/route.fbs b/include/engine/api/flatbuffers/route.fbs index bbdd5fcd21c..dc008b37a60 100644 --- a/include/engine/api/flatbuffers/route.fbs +++ b/include/engine/api/flatbuffers/route.fbs @@ -12,6 +12,7 @@ table Annotation { nodes: [uint]; weight: [uint]; speed: [float]; + way_ids: [ulong]; metadata: Metadata; } @@ -109,4 +110,4 @@ table RouteObject { polyline: string; coordinates: [Position]; legs: [Leg]; -} \ No newline at end of file +} diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp index ab8d18aefee..8d7b5988d87 100644 --- a/include/engine/api/route_api.hpp +++ b/include/engine/api/route_api.hpp @@ -522,6 +522,15 @@ class RouteAPI : public BaseAPI [](const guidance::LegGeometry::Annotation &anno) { return anno.datasource; }); } + + flatbuffers::Offset> way_ids; + if (requested_annotations & RouteParameters::AnnotationsType::WayIds) + { + way_ids = GetAnnotations(fb_result, + leg_geometry, + [](const guidance::LegGeometry::Annotation &anno) + { return static_cast(anno.way_id); }); + } std::vector nodes; if (requested_annotations & RouteParameters::AnnotationsType::Nodes) { @@ -557,6 +566,7 @@ class RouteAPI : public BaseAPI annotation.add_weight(weight); annotation.add_datasources(datasources); annotation.add_nodes(nodes_vector); + annotation.add_way_ids(way_ids); if (use_metadata) { annotation.add_metadata(metadata_buffer); @@ -872,6 +882,14 @@ class RouteAPI : public BaseAPI [](const guidance::LegGeometry::Annotation &anno) { return anno.datasource; })); } + if (requested_annotations & RouteParameters::AnnotationsType::WayIds) + { + annotation.values.emplace( + "way_ids", + GetAnnotations(leg_geometry, + [](const guidance::LegGeometry::Annotation &anno) + { return static_cast(anno.way_id); })); + } if (requested_annotations & RouteParameters::AnnotationsType::Nodes) { util::json::Array nodes; diff --git a/include/engine/api/route_parameters.hpp b/include/engine/api/route_parameters.hpp index 9b5e163090c..eafa90b6d04 100644 --- a/include/engine/api/route_parameters.hpp +++ b/include/engine/api/route_parameters.hpp @@ -73,7 +73,8 @@ struct RouteParameters : public BaseParameters Weight = 0x08, Datasources = 0x10, Speed = 0x20, - All = Duration | Nodes | Distance | Weight | Datasources | Speed + WayIds = 0x40, + All = Duration | Nodes | Distance | Weight | Datasources | Speed | WayIds }; RouteParameters() = default; diff --git a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp index e47591604df..9ee9052a951 100644 --- a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp +++ b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp @@ -289,6 +289,11 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade return m_osmnodeid_list[node_based_node_id]; } + OSMWayID GetOSMWayID(const NodeID edge_based_node_id) const override final + { + return edge_based_node_data.GetWayID(edge_based_node_id); + } + NodeForwardRange GetUncompressedForwardGeometry(const PackedGeometryID id) const override final { return segment_data.GetForwardGeometry(id); @@ -337,6 +342,16 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade return segment_data.GetReverseDatasources(id); } + WayIDForwardRange GetUncompressedForwardWayIDs(const PackedGeometryID id) const override final + { + return segment_data.GetForwardWayIDs(id); + } + + WayIDReverseRange GetUncompressedReverseWayIDs(const PackedGeometryID id) const override final + { + return segment_data.GetReverseWayIDs(id); + } + TurnPenalty GetWeightPenaltyForEdgeID(const EdgeID edge_based_edge_id) const override final { BOOST_ASSERT(m_turn_weight_penalties.size() > edge_based_edge_id); diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp index 491e3983ec9..266ead32e9e 100644 --- a/include/engine/datafacade/datafacade_base.hpp +++ b/include/engine/datafacade/datafacade_base.hpp @@ -63,6 +63,10 @@ class BaseDataFacade std::ranges::subrange; using DatasourceReverseRange = std::ranges::reverse_view; + using WayIDForwardRange = + std::ranges::subrange; + using WayIDReverseRange = std::ranges::reverse_view; + BaseDataFacade() {} virtual ~BaseDataFacade() {} @@ -75,6 +79,8 @@ class BaseDataFacade virtual OSMNodeID GetOSMNodeIDOfNode(const NodeID node_based_node_id) const = 0; + virtual OSMWayID GetOSMWayID(const NodeID edge_based_node_id) const = 0; + virtual GeometryID GetGeometryIndex(const NodeID edge_based_node_id) const = 0; virtual ComponentID GetComponentID(const NodeID edge_based_node_id) const = 0; @@ -105,6 +111,9 @@ class BaseDataFacade virtual DatasourceReverseRange GetUncompressedReverseDatasources(const PackedGeometryID id) const = 0; + virtual WayIDForwardRange GetUncompressedForwardWayIDs(const PackedGeometryID id) const = 0; + virtual WayIDReverseRange GetUncompressedReverseWayIDs(const PackedGeometryID id) const = 0; + // Gets the name of a datasource virtual std::string_view GetDatasourceName(const DatasourceID id) const = 0; diff --git a/include/engine/guidance/assemble_geometry.hpp b/include/engine/guidance/assemble_geometry.hpp index 389765d20e9..46090b64824 100644 --- a/include/engine/guidance/assemble_geometry.hpp +++ b/include/engine/guidance/assemble_geometry.hpp @@ -102,7 +102,8 @@ inline LegGeometry assembleGeometry(const datafacade::BaseDataFacade &facade, 10., from_alias(path_point.weight_until_turn - path_point.weight_of_turn) / facade.GetWeightMultiplier(), - path_point.datasource_id}); + path_point.datasource_id, + path_point.way_id}); geometry.locations.push_back(coordinate); geometry.node_ids.push_back(node_id); } @@ -117,6 +118,7 @@ inline LegGeometry assembleGeometry(const datafacade::BaseDataFacade &facade, reversed_target ? target_node.reverse_segment_id.id : target_node.forward_segment_id.id; const auto target_geometry_id = facade.GetGeometryIndex(target_node_id).id; const auto forward_datasources = facade.GetUncompressedForwardDatasources(target_geometry_id); + const auto forward_way_ids = facade.GetUncompressedForwardWayIDs(target_geometry_id); // This happens when the source/target are on the same edge-based-node // There will be no entries in the unpacked path, thus no annotations. @@ -141,7 +143,8 @@ inline LegGeometry assembleGeometry(const datafacade::BaseDataFacade &facade, LegGeometry::Annotation{current_distance, duration, weight, - forward_datasources[target_node.fwd_segment_position]}); + forward_datasources[target_node.fwd_segment_position], + forward_way_ids[target_node.fwd_segment_position]}); } else { @@ -153,7 +156,8 @@ inline LegGeometry assembleGeometry(const datafacade::BaseDataFacade &facade, from_alias(reversed_target ? target_node.reverse_weight : target_node.forward_weight) / facade.GetWeightMultiplier(), - forward_datasources[target_node.fwd_segment_position]}); + forward_datasources[target_node.fwd_segment_position], + forward_way_ids[target_node.fwd_segment_position]}); } geometry.segment_offsets.push_back(geometry.locations.size()); diff --git a/include/engine/guidance/leg_geometry.hpp b/include/engine/guidance/leg_geometry.hpp index 55f8968d45c..195615b56b7 100644 --- a/include/engine/guidance/leg_geometry.hpp +++ b/include/engine/guidance/leg_geometry.hpp @@ -41,6 +41,7 @@ struct LegGeometry double weight; // weight value, NOT including the turn weight DatasourceID datasource; + OSMWayID way_id; }; std::vector annotations; diff --git a/include/engine/internal_route_result.hpp b/include/engine/internal_route_result.hpp index 78f5d11967d..2bcd396749f 100644 --- a/include/engine/internal_route_result.hpp +++ b/include/engine/internal_route_result.hpp @@ -23,6 +23,39 @@ namespace osrm::engine struct PathData { + PathData() = default; + + PathData(const NodeID from_edge_based_node, + const NodeID turn_via_node, + const EdgeWeight weight_until_turn, + const EdgeWeight weight_of_turn, + const EdgeDuration duration_until_turn, + const EdgeDuration duration_of_turn, + const DatasourceID datasource_id, + const std::optional turn_edge) + : from_edge_based_node(from_edge_based_node), turn_via_node(turn_via_node), + weight_until_turn(weight_until_turn), weight_of_turn(weight_of_turn), + duration_until_turn(duration_until_turn), duration_of_turn(duration_of_turn), + datasource_id(datasource_id), way_id(SPECIAL_OSM_WAYID), turn_edge(turn_edge) + { + } + + PathData(const NodeID from_edge_based_node, + const NodeID turn_via_node, + const EdgeWeight weight_until_turn, + const EdgeWeight weight_of_turn, + const EdgeDuration duration_until_turn, + const EdgeDuration duration_of_turn, + const DatasourceID datasource_id, + const OSMWayID way_id, + const std::optional turn_edge) + : from_edge_based_node(from_edge_based_node), turn_via_node(turn_via_node), + weight_until_turn(weight_until_turn), weight_of_turn(weight_of_turn), + duration_until_turn(duration_until_turn), duration_of_turn(duration_of_turn), + datasource_id(datasource_id), way_id(way_id), turn_edge(turn_edge) + { + } + // from edge-based-node id NodeID from_edge_based_node; // the internal OSRM id of the OSM node id that is the via node of the turn @@ -41,6 +74,7 @@ struct PathData EdgeDuration duration_of_turn; // Source of the speed value on this road segment DatasourceID datasource_id; + OSMWayID way_id; // If segment precedes a turn, ID of the turn itself std::optional turn_edge; }; diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp index 20821e6c97a..17d3b2c2b11 100644 --- a/include/engine/routing_algorithms/routing_base.hpp +++ b/include/engine/routing_algorithms/routing_base.hpp @@ -188,6 +188,7 @@ void annotatePath(const FacadeT &facade, std::vector weight_vector; std::vector duration_vector; std::vector datasource_vector; + std::vector way_id_vector; const auto get_segment_geometry = [&](const auto geometry_index) { @@ -203,6 +204,7 @@ void annotatePath(const FacadeT &facade, copy(weight_vector, facade.GetUncompressedForwardWeights(geometry_index.id)); copy(duration_vector, facade.GetUncompressedForwardDurations(geometry_index.id)); copy(datasource_vector, facade.GetUncompressedForwardDatasources(geometry_index.id)); + copy(way_id_vector, facade.GetUncompressedForwardWayIDs(geometry_index.id)); } else { @@ -210,6 +212,7 @@ void annotatePath(const FacadeT &facade, copy(weight_vector, facade.GetUncompressedReverseWeights(geometry_index.id)); copy(duration_vector, facade.GetUncompressedReverseDurations(geometry_index.id)); copy(datasource_vector, facade.GetUncompressedReverseDatasources(geometry_index.id)); + copy(way_id_vector, facade.GetUncompressedReverseWayIDs(geometry_index.id)); } }; @@ -225,8 +228,10 @@ void annotatePath(const FacadeT &facade, BOOST_ASSERT(!id_vector.empty()); BOOST_ASSERT(!datasource_vector.empty()); + BOOST_ASSERT(!way_id_vector.empty()); BOOST_ASSERT(weight_vector.size() + 1 == id_vector.size()); BOOST_ASSERT(duration_vector.size() + 1 == id_vector.size()); + BOOST_ASSERT(way_id_vector.size() + 1 == id_vector.size()); const bool is_first_segment = unpacked_path.empty(); @@ -254,6 +259,7 @@ void annotatePath(const FacadeT &facade, alias_cast(duration_vector[segment_idx]), {0}, datasource_vector[segment_idx], + way_id_vector[segment_idx], std::nullopt}); } BOOST_ASSERT(!unpacked_path.empty()); @@ -311,6 +317,7 @@ void annotatePath(const FacadeT &facade, alias_cast(duration_vector[segment_idx]), {0}, datasource_vector[segment_idx], + way_id_vector[segment_idx], std::nullopt}); } diff --git a/include/extractor/compressed_edge_container.hpp b/include/extractor/compressed_edge_container.hpp index 8e3fbe070cf..3224cfe49ba 100644 --- a/include/extractor/compressed_edge_container.hpp +++ b/include/extractor/compressed_edge_container.hpp @@ -22,6 +22,7 @@ class CompressedEdgeContainer NodeID node_id; // refers to an internal node-based-node SegmentWeight weight; // the weight of the edge leading to this node SegmentDuration duration; // the duration of the edge leading to this node + OSMWayID way_id; // the OSM way ID of the edge leading to this node }; using OnewayEdgeBucket = std::vector; @@ -35,6 +36,8 @@ class CompressedEdgeContainer const EdgeWeight weight2, const EdgeDuration duration1, const EdgeDuration duration2, + const OSMWayID way_id1 = SPECIAL_OSM_WAYID, + const OSMWayID way_id2 = SPECIAL_OSM_WAYID, // node-penalties can be added before/or after the traversal of an edge which // depends on whether we traverse the link forwards or backwards. const EdgeWeight node_weight_penalty = INVALID_EDGE_WEIGHT, @@ -43,7 +46,8 @@ class CompressedEdgeContainer void AddUncompressedEdge(const EdgeID edge_id, const NodeID target_node, const EdgeWeight weight, - const EdgeDuration duration); + const EdgeDuration duration, + const OSMWayID way_id = SPECIAL_OSM_WAYID); void InitializeBothwayVector(); unsigned ZipEdges(const unsigned f_edge_pos, const unsigned r_edge_pos); diff --git a/include/extractor/node_based_edge.hpp b/include/extractor/node_based_edge.hpp index f6388b4f841..b3ae892f104 100644 --- a/include/extractor/node_based_edge.hpp +++ b/include/extractor/node_based_edge.hpp @@ -68,9 +68,24 @@ struct NodeBasedEdgeAnnotation StringViewID string_view_id; // 32 4 LaneDescriptionID lane_description_id; // 16 2 ClassData classes; // 8 1 + OSMWayID way_id; // 64 8 TravelMode travel_mode : 4; // 4 bool is_left_hand_driving : 1; // 1 + NodeBasedEdgeAnnotation() = default; + + NodeBasedEdgeAnnotation(const StringViewID string_view_id, + const LaneDescriptionID lane_description_id, + const ClassData classes, + const OSMWayID way_id, + const TravelMode travel_mode, + const bool is_left_hand_driving) + : string_view_id(string_view_id), lane_description_id(lane_description_id), + classes(classes), way_id(way_id), travel_mode(travel_mode), + is_left_hand_driving(is_left_hand_driving) + { + } + bool CanCombineWith(const NodeBasedEdgeAnnotation &other) const { return (std::tie(string_view_id, classes, travel_mode, is_left_hand_driving) == @@ -80,16 +95,24 @@ struct NodeBasedEdgeAnnotation other.is_left_hand_driving)); } + bool CanCombineForGuidance(const NodeBasedEdgeAnnotation &other) const + { + return CanCombineWith(other); + } + bool operator<(const NodeBasedEdgeAnnotation &other) const { - return ( - std::tie( - string_view_id, lane_description_id, classes, travel_mode, is_left_hand_driving) < - std::tie(other.string_view_id, - other.lane_description_id, - other.classes, - other.travel_mode, - other.is_left_hand_driving)); + return (std::tie(string_view_id, + lane_description_id, + classes, + way_id, + travel_mode, + is_left_hand_driving) < std::tie(other.string_view_id, + other.lane_description_id, + other.classes, + other.way_id, + other.travel_mode, + other.is_left_hand_driving)); } }; diff --git a/include/extractor/node_data_container.hpp b/include/extractor/node_data_container.hpp index 72c2ebc7a48..96390ac2f90 100644 --- a/include/extractor/node_data_container.hpp +++ b/include/extractor/node_data_container.hpp @@ -89,6 +89,11 @@ template class EdgeBasedNodeDataContainerImpl return annotation_data[nodes[node_id].annotation_id].classes; } + OSMWayID GetWayID(const NodeID node_id) const + { + return annotation_data[nodes[node_id].annotation_id].way_id; + } + friend void serialization::read(storage::tar::FileReader &reader, const std::string &name, EdgeBasedNodeDataContainerImpl &ebn_data_container); diff --git a/include/extractor/segment_data_container.hpp b/include/extractor/segment_data_container.hpp index d012e5b9f61..1d04780d8cb 100644 --- a/include/extractor/segment_data_container.hpp +++ b/include/extractor/segment_data_container.hpp @@ -51,6 +51,7 @@ template class SegmentDataContainerImpl using SegmentWeightVector = PackedVector; using SegmentDurationVector = PackedVector; using SegmentDatasourceVector = Vector; + using SegmentWayIDVector = Vector; SegmentDataContainerImpl() = default; @@ -61,11 +62,14 @@ template class SegmentDataContainerImpl SegmentDurationVector fwd_durations_, SegmentDurationVector rev_durations_, SegmentDatasourceVector fwd_datasources_, - SegmentDatasourceVector rev_datasources_) + SegmentDatasourceVector rev_datasources_, + SegmentWayIDVector fwd_way_ids_, + SegmentWayIDVector rev_way_ids_) : index(std::move(index_)), nodes(std::move(nodes_)), fwd_weights(std::move(fwd_weights_)), rev_weights(std::move(rev_weights_)), fwd_durations(std::move(fwd_durations_)), rev_durations(std::move(rev_durations_)), fwd_datasources(std::move(fwd_datasources_)), - rev_datasources(std::move(rev_datasources_)) + rev_datasources(std::move(rev_datasources_)), fwd_way_ids(std::move(fwd_way_ids_)), + rev_way_ids(std::move(rev_way_ids_)) { } @@ -122,6 +126,14 @@ template class SegmentDataContainerImpl return std::ranges::subrange(begin, end); } + auto GetForwardWayIDs(const DirectionalGeometryID id) + { + const auto begin = fwd_way_ids.begin() + index[id] + 1; + const auto end = fwd_way_ids.begin() + index[id + 1]; + + return std::ranges::subrange(begin, end); + } + auto GetReverseDatasources(const DirectionalGeometryID id) { const auto begin = rev_datasources.begin() + index[id]; @@ -130,6 +142,14 @@ template class SegmentDataContainerImpl return std::ranges::subrange(begin, end) | std::views::reverse; } + auto GetReverseWayIDs(const DirectionalGeometryID id) + { + const auto begin = rev_way_ids.begin() + index[id]; + const auto end = rev_way_ids.begin() + index[id + 1] - 1; + + return std::ranges::subrange(begin, end) | std::views::reverse; + } + auto GetForwardGeometry(const DirectionalGeometryID id) const { const auto begin = nodes.cbegin() + index[id]; @@ -183,6 +203,14 @@ template class SegmentDataContainerImpl return std::ranges::subrange(begin, end); } + auto GetForwardWayIDs(const DirectionalGeometryID id) const + { + const auto begin = fwd_way_ids.cbegin() + index[id] + 1; + const auto end = fwd_way_ids.cbegin() + index[id + 1]; + + return std::ranges::subrange(begin, end); + } + auto GetReverseDatasources(const DirectionalGeometryID id) const { const auto begin = rev_datasources.cbegin() + index[id]; @@ -191,6 +219,14 @@ template class SegmentDataContainerImpl return std::ranges::subrange(begin, end) | std::views::reverse; } + auto GetReverseWayIDs(const DirectionalGeometryID id) const + { + const auto begin = rev_way_ids.cbegin() + index[id]; + const auto end = rev_way_ids.cbegin() + index[id + 1] - 1; + + return std::ranges::subrange(begin, end) | std::views::reverse; + } + auto GetNumberOfGeometries() const { return index.size() - 1; } auto GetNumberOfSegments() const { return fwd_weights.size(); } @@ -212,6 +248,8 @@ template class SegmentDataContainerImpl SegmentDurationVector rev_durations; SegmentDatasourceVector fwd_datasources; SegmentDatasourceVector rev_datasources; + SegmentWayIDVector fwd_way_ids; + SegmentWayIDVector rev_way_ids; }; } // namespace detail diff --git a/include/extractor/serialization.hpp b/include/extractor/serialization.hpp index f9e7ff29f35..2ee59e5b2f9 100644 --- a/include/extractor/serialization.hpp +++ b/include/extractor/serialization.hpp @@ -86,6 +86,8 @@ inline void read(storage::tar::FileReader &reader, reader, name + "/forward_data_sources", segment_data.fwd_datasources); storage::serialization::read( reader, name + "/reverse_data_sources", segment_data.rev_datasources); + storage::serialization::read(reader, name + "/forward_way_ids", segment_data.fwd_way_ids); + storage::serialization::read(reader, name + "/reverse_way_ids", segment_data.rev_way_ids); } template @@ -103,6 +105,8 @@ inline void write(storage::tar::FileWriter &writer, writer, name + "/forward_data_sources", segment_data.fwd_datasources); storage::serialization::write( writer, name + "/reverse_data_sources", segment_data.rev_datasources); + storage::serialization::write(writer, name + "/forward_way_ids", segment_data.fwd_way_ids); + storage::serialization::write(writer, name + "/reverse_way_ids", segment_data.rev_way_ids); } template diff --git a/include/nodejs/node_osrm_support.hpp b/include/nodejs/node_osrm_support.hpp index 510f0c6f0a3..eec749c9af4 100644 --- a/include/nodejs/node_osrm_support.hpp +++ b/include/nodejs/node_osrm_support.hpp @@ -934,6 +934,11 @@ inline bool parseCommonParameters(const Napi::Object &obj, ParamType ¶ms) params->annotations_type = params->annotations_type | osrm::RouteParameters::AnnotationsType::Speed; } + else if (annotations_str == "way_ids") + { + params->annotations_type = + params->annotations_type | osrm::RouteParameters::AnnotationsType::WayIds; + } else { ThrowError(obj.Env(), "this 'annotations' param is not supported"); diff --git a/include/server/api/route_parameters_grammar.hpp b/include/server/api/route_parameters_grammar.hpp index 9500e5808b4..a88945cdfc7 100644 --- a/include/server/api/route_parameters_grammar.hpp +++ b/include/server/api/route_parameters_grammar.hpp @@ -40,7 +40,8 @@ inline const auto annotations_type = []() x3::symbols sym; sym.add("duration", AnnotationsType::Duration)("nodes", AnnotationsType::Nodes)( "distance", AnnotationsType::Distance)("weight", AnnotationsType::Weight)( - "datasources", AnnotationsType::Datasources)("speed", AnnotationsType::Speed); + "datasources", AnnotationsType::Datasources)("speed", AnnotationsType::Speed)( + "way_ids", AnnotationsType::WayIds); return sym; }(); diff --git a/include/storage/view_factory.hpp b/include/storage/view_factory.hpp index 403b4c2b69a..f0f0c5f11e4 100644 --- a/include/storage/view_factory.hpp +++ b/include/storage/view_factory.hpp @@ -151,6 +151,10 @@ inline auto make_segment_data_view(const SharedDataIndex &index, const std::stri auto rev_datasources_list = make_vector_view(index, name + "/reverse_data_sources"); + auto fwd_way_ids_list = make_vector_view(index, name + "/forward_way_ids"); + + auto rev_way_ids_list = make_vector_view(index, name + "/reverse_way_ids"); + return extractor::SegmentDataView{geometry_begin_indices, node_list, fwd_weight_list, @@ -158,7 +162,9 @@ inline auto make_segment_data_view(const SharedDataIndex &index, const std::stri fwd_duration_list, rev_duration_list, fwd_datasources_list, - rev_datasources_list}; + rev_datasources_list, + fwd_way_ids_list, + rev_way_ids_list}; } inline auto make_coordinates_view(const SharedDataIndex &index, const std::string &name) diff --git a/src/extractor/compressed_edge_container.cpp b/src/extractor/compressed_edge_container.cpp index b1a220faf10..8ef2ad68d4d 100644 --- a/src/extractor/compressed_edge_container.cpp +++ b/src/extractor/compressed_edge_container.cpp @@ -105,6 +105,8 @@ void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1, const EdgeWeight weight2, const EdgeDuration duration1, const EdgeDuration duration2, + const OSMWayID way_id1, + const OSMWayID way_id2, const EdgeWeight node_weight_penalty, const EdgeDuration node_duration_penalty) { @@ -153,8 +155,8 @@ void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1, // weight1 is the distance to the (currently) last coordinate in the bucket if (was_empty) { - edge_bucket_list1.emplace_back( - OnewayCompressedEdge{via_node_id, ClipWeight(weight1), ClipDuration(duration1)}); + edge_bucket_list1.emplace_back(OnewayCompressedEdge{ + via_node_id, ClipWeight(weight1), ClipDuration(duration1), way_id1}); } BOOST_ASSERT(0 < edge_bucket_list1.size()); @@ -165,8 +167,10 @@ void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1, if (node_weight_penalty != INVALID_EDGE_WEIGHT && node_duration_penalty != MAXIMAL_EDGE_DURATION) { - edge_bucket_list1.emplace_back(OnewayCompressedEdge{ - via_node_id, ClipWeight(node_weight_penalty), ClipDuration(node_duration_penalty)}); + edge_bucket_list1.emplace_back(OnewayCompressedEdge{via_node_id, + ClipWeight(node_weight_penalty), + ClipDuration(node_duration_penalty), + SPECIAL_OSM_WAYID}); } if (HasEntryForID(edge_id_2)) @@ -193,15 +197,16 @@ void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1, else { // we are certain that the second edge is atomic. - edge_bucket_list1.emplace_back( - OnewayCompressedEdge{target_node_id, ClipWeight(weight2), ClipDuration(duration2)}); + edge_bucket_list1.emplace_back(OnewayCompressedEdge{ + target_node_id, ClipWeight(weight2), ClipDuration(duration2), way_id2}); } } void CompressedEdgeContainer::AddUncompressedEdge(const EdgeID edge_id, const NodeID target_node_id, const EdgeWeight weight, - const EdgeDuration duration) + const EdgeDuration duration, + const OSMWayID way_id) { // remove super-trivial geometries BOOST_ASSERT(SPECIAL_EDGEID != edge_id); @@ -238,8 +243,8 @@ void CompressedEdgeContainer::AddUncompressedEdge(const EdgeID edge_id, // Don't re-add this if it's already in there. if (edge_bucket_list.empty()) { - edge_bucket_list.emplace_back( - OnewayCompressedEdge{target_node_id, ClipWeight(weight), ClipDuration(duration)}); + edge_bucket_list.emplace_back(OnewayCompressedEdge{ + target_node_id, ClipWeight(weight), ClipDuration(duration), way_id}); } } @@ -254,6 +259,8 @@ void CompressedEdgeContainer::InitializeBothwayVector() segment_data->rev_durations.reserve(m_compressed_oneway_geometries.size()); segment_data->fwd_datasources.reserve(m_compressed_oneway_geometries.size()); segment_data->rev_datasources.reserve(m_compressed_oneway_geometries.size()); + segment_data->fwd_way_ids.reserve(m_compressed_oneway_geometries.size()); + segment_data->rev_way_ids.reserve(m_compressed_oneway_geometries.size()); } unsigned CompressedEdgeContainer::ZipEdges(const EdgeID f_edge_id, const EdgeID r_edge_id) @@ -283,6 +290,8 @@ unsigned CompressedEdgeContainer::ZipEdges(const EdgeID f_edge_id, const EdgeID segment_data->rev_durations.emplace_back(first_node.duration); segment_data->fwd_datasources.emplace_back(LUA_SOURCE); segment_data->rev_datasources.emplace_back(LUA_SOURCE); + segment_data->fwd_way_ids.emplace_back(SPECIAL_OSM_WAYID); + segment_data->rev_way_ids.emplace_back(first_node.way_id); for (std::size_t i = 0; i < forward_bucket.size() - 1; ++i) { @@ -298,6 +307,8 @@ unsigned CompressedEdgeContainer::ZipEdges(const EdgeID f_edge_id, const EdgeID segment_data->rev_durations.emplace_back(rev_node.duration); segment_data->fwd_datasources.emplace_back(LUA_SOURCE); segment_data->rev_datasources.emplace_back(LUA_SOURCE); + segment_data->fwd_way_ids.emplace_back(fwd_node.way_id); + segment_data->rev_way_ids.emplace_back(rev_node.way_id); } const auto &last_node = forward_bucket.back(); @@ -309,6 +320,8 @@ unsigned CompressedEdgeContainer::ZipEdges(const EdgeID f_edge_id, const EdgeID segment_data->rev_durations.emplace_back(INVALID_SEGMENT_DURATION); segment_data->fwd_datasources.emplace_back(LUA_SOURCE); segment_data->rev_datasources.emplace_back(LUA_SOURCE); + segment_data->fwd_way_ids.emplace_back(last_node.way_id); + segment_data->rev_way_ids.emplace_back(SPECIAL_OSM_WAYID); return zipped_geometry_id; } diff --git a/src/extractor/extractor_callbacks.cpp b/src/extractor/extractor_callbacks.cpp index 79fc524c6dc..03e51cf1a0b 100644 --- a/src/extractor/extractor_callbacks.cpp +++ b/src/extractor/extractor_callbacks.cpp @@ -397,12 +397,15 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti (turn_lane_id_forward != turn_lane_id_backward) || (forward_classes != backward_classes) || (parsed_way.forward_ref != parsed_way.backward_ref)); + const auto way_id = OSMWayID{static_cast(input_way.id())}; + if (in_forward_direction) { // add (forward) segments or (forward,backward) for non-split edges in backward direction const auto annotation_data_id = external_memory.all_edges_annotation_data_list.size(); external_memory.all_edges_annotation_data_list.push_back({forward_name_id, turn_lane_id_forward, forward_classes, + way_id, parsed_way.forward_travel_mode, parsed_way.is_left_hand_driving}); util::for_each_pair(nodes, @@ -438,6 +441,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti external_memory.all_edges_annotation_data_list.push_back({backward_name_id, turn_lane_id_backward, backward_classes, + way_id, parsed_way.backward_travel_mode, parsed_way.is_left_hand_driving}); util::for_each_pair(nodes, @@ -473,7 +477,6 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti [](const osmium::NodeRef &ref) { return OSMNodeID{static_cast(ref.ref())}; }); - auto way_id = OSMWayID{static_cast(input_way.id())}; external_memory.ways_list.push_back(way_id); external_memory.way_node_id_offsets.push_back(external_memory.used_node_id_list.size()); } diff --git a/src/extractor/graph_compressor.cpp b/src/extractor/graph_compressor.cpp index f755a729598..2f50340d49d 100644 --- a/src/extractor/graph_compressor.cpp +++ b/src/extractor/graph_compressor.cpp @@ -334,6 +334,8 @@ void GraphCompressor::Compress(ScriptingEnvironment &scripting_environment, forward_weight2, forward_duration1, forward_duration2, + fwd_annotation_data1.way_id, + fwd_annotation_data2.way_id, forward_penalties.weight, forward_penalties.duration); geometry_compressor.CompressEdge(reverse_e1, @@ -344,6 +346,8 @@ void GraphCompressor::Compress(ScriptingEnvironment &scripting_environment, reverse_weight2, reverse_duration1, reverse_duration2, + rev_annotation_data1.way_id, + rev_annotation_data2.way_id, backward_penalties.weight, backward_penalties.duration); } @@ -361,7 +365,12 @@ void GraphCompressor::Compress(ScriptingEnvironment &scripting_environment, { const EdgeData &data = graph.GetEdgeData(edge_id); const NodeID target = graph.GetTarget(edge_id); - geometry_compressor.AddUncompressedEdge(edge_id, target, data.weight, data.duration); + geometry_compressor.AddUncompressedEdge( + edge_id, + target, + data.weight, + data.duration, + node_data_container[data.annotation_data].way_id); } } } diff --git a/src/guidance/intersection_handler.cpp b/src/guidance/intersection_handler.cpp index 8b22133233e..b1ee3a4b539 100644 --- a/src/guidance/intersection_handler.cpp +++ b/src/guidance/intersection_handler.cpp @@ -36,7 +36,7 @@ inline bool requiresAnnouncement(const util::NodeBasedDynamicGraph &node_based_g const auto &annotation_from = node_data_container.GetAnnotation(from_edge.annotation_data); const auto &annotation_to = node_data_container.GetAnnotation(to_edge.annotation_data); - return !annotation_from.CanCombineWith(annotation_to); + return !annotation_from.CanCombineForGuidance(annotation_to); } } // namespace detail diff --git a/src/nodejs/node_osrm.cpp b/src/nodejs/node_osrm.cpp index 2a7f9da6633..b31d05c2661 100644 --- a/src/nodejs/node_osrm.cpp +++ b/src/nodejs/node_osrm.cpp @@ -283,7 +283,7 @@ inline void asyncForTiles(const Napi::CallbackInfo &info, * @param {Number} [options.alternatives=0] Search for up to this many alternative routes. * *Please note that even if alternative routes are requested, a result cannot be guaranteed.* * @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 {Array|Boolean} [options.annotations=false] An array with strings of `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed`, `way_ids` 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 displayed 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. @@ -474,7 +474,7 @@ Napi::Value Engine::tile(const Napi::CallbackInfo &info) * @param {Array} [options.hints] Hints for the coordinate snapping. Array of base64 encoded strings. * @param {Boolean} [options.generate_hints=true] Whether or not adds a Hint to the response which can be used in subsequent requests. * @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 {Array|Boolean} [options.annotations=false] An array with strings of `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed`, `way_ids` 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 {Array} [options.timestamps] Timestamp of the input location (integers, UNIX-like timestamp). @@ -553,7 +553,7 @@ Napi::Value Engine::match(const Napi::CallbackInfo &info) * @param {Array} [options.hints] Hints for the coordinate snapping. Array of base64 encoded strings. * @param {Boolean} [options.generate_hints=true] Whether or not adds a Hint to the response which can be used in subsequent requests. * @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 {Array|Boolean} [options.annotations=false] An array with strings of `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed`, `way_ids` 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`, `false` or `by_legs`. * @param {Function} callback diff --git a/test/nodejs/match.js b/test/nodejs/match.js index da2af06690a..c1605e25ad5 100644 --- a/test/nodejs/match.js +++ b/test/nodejs/match.js @@ -130,8 +130,8 @@ test('match: match in Monaco with speed annotations options', (assert) => { }); -test('match: match in Monaco with several (duration, distance, nodes) annotations options', (assert) => { - assert.plan(12); +test('match: match in Monaco with several (duration, distance, nodes, way_ids) annotations options', (assert) => { + assert.plan(13); const osrm = new OSRM(data_path); const options = { timestamps: [1424684612, 1424684616, 1424684620], @@ -139,7 +139,7 @@ test('match: match in Monaco with several (duration, distance, nodes) annotation timestamps: [1424684612, 1424684616, 1424684620], radiuses: [4.07, 4.07, 4.07], steps: true, - annotations: ['duration','distance','nodes'], + annotations: ['duration','distance','nodes','way_ids'], overview: 'false', geometries: 'geojson' }; @@ -152,6 +152,7 @@ test('match: match in Monaco with several (duration, distance, nodes) annotation assert.ok(response.matchings[0].legs.every((l) => {return l.annotation.distance; }), 'every leg has annotations for distance'); assert.ok(response.matchings[0].legs.every((l) => {return l.annotation.duration; }), 'every leg has annotations for durations'); assert.ok(response.matchings[0].legs.every((l) => {return l.annotation.nodes; }), 'every leg has annotations for nodes'); + assert.ok(response.matchings[0].legs.every((l) => {return l.annotation.way_ids; }), 'every leg has annotations for way ids'); assert.notOk(response.matchings[0].legs.every((l) => {return l.annotation.weight; }), 'has no annotations for weight'); assert.notOk(response.matchings[0].legs.every((l) => {return l.annotation.datasources; }), 'has no annotations for datasources'); assert.notOk(response.matchings[0].legs.every((l) => {return l.annotation.speed; }), 'has no annotations for speed'); @@ -160,7 +161,7 @@ test('match: match in Monaco with several (duration, distance, nodes) annotation }); test('match: match in Monaco with all options', (assert) => { - assert.plan(8); + assert.plan(9); const osrm = new OSRM(data_path); const options = { coordinates: three_test_coordinates, @@ -181,6 +182,7 @@ test('match: match in Monaco with all options', (assert) => { assert.ok(response.matchings[0].legs.every((l) => {return l.annotation; }), 'every leg has annotations'); assert.ok(response.matchings[0].legs.every((l) => {return l.annotation.distance; }), 'every leg has annotations for distance'); assert.ok(response.matchings[0].legs.every((l) => {return l.annotation.duration; }), 'every leg has annotations for durations'); + assert.ok(response.matchings[0].legs.every((l) => {return l.annotation.way_ids; }), 'every leg has annotations for way ids'); assert.equal(undefined, response.matchings[0].geometry); }); }); diff --git a/test/nodejs/route.js b/test/nodejs/route.js index 883b30d2dc0..25bf329504f 100644 --- a/test/nodejs/route.js +++ b/test/nodejs/route.js @@ -232,8 +232,8 @@ test('route: routes Monaco with speed annotations options', (assert) => { }); }); -test('route: routes Monaco with several (duration, distance, nodes) annotations options', (assert) => { - assert.plan(17); +test('route: routes Monaco with several (duration, distance, nodes, way_ids) annotations options', (assert) => { + assert.plan(18); const osrm = new OSRM(monaco_path); const options = { coordinates: two_test_coordinates, @@ -241,7 +241,7 @@ test('route: routes Monaco with several (duration, distance, nodes) annotations overview: 'false', geometries: 'polyline', steps: true, - annotations: ['duration', 'distance', 'nodes'] + annotations: ['duration', 'distance', 'nodes', 'way_ids'] }; osrm.route(options, (err, first) => { assert.ifError(err); @@ -255,6 +255,7 @@ test('route: routes Monaco with several (duration, distance, nodes) annotations assert.ok(first.routes[0].legs.every(l => { return l.annotation.distance;}), 'every leg has annotations for distance'); assert.ok(first.routes[0].legs.every(l => { return l.annotation.duration;}), 'every leg has annotations for durations'); assert.ok(first.routes[0].legs.every(l => { return l.annotation.nodes;}), 'every leg has annotations for nodes'); + assert.ok(first.routes[0].legs.every(l => { return l.annotation.way_ids;}), 'every leg has annotations for way ids'); assert.notOk(first.routes[0].legs.every(l => { return l.annotation.weight; }), 'has no annotations for weight'); assert.notOk(first.routes[0].legs.every(l => { return l.annotation.datasources; }), 'has no annotations for datasources'); assert.notOk(first.routes[0].legs.every(l => { return l.annotation.speed; }), 'has no annotations for speed'); @@ -272,7 +273,7 @@ test('route: routes Monaco with several (duration, distance, nodes) annotations }); test('route: routes Monaco with options', (assert) => { - assert.plan(17); + assert.plan(18); const osrm = new OSRM(monaco_path); const options = { coordinates: two_test_coordinates, @@ -297,6 +298,7 @@ test('route: routes Monaco with options', (assert) => { assert.ok(first.routes[0].legs.every(l => { return l.annotation.weight; }), 'every leg has annotations for weight'); assert.ok(first.routes[0].legs.every(l => { return l.annotation.datasources; }), 'every leg has annotations for datasources'); assert.ok(first.routes[0].legs.every(l => { return l.annotation.speed; }), 'every leg has annotations for speed'); + assert.ok(first.routes[0].legs.every(l => { return l.annotation.way_ids;}), 'every leg has annotations for way ids'); options.overview = 'full'; osrm.route(options, (err, full) => { diff --git a/test/nodejs/trip.js b/test/nodejs/trip.js index 46c4aebf71f..a8d1723f7b6 100644 --- a/test/nodejs/trip.js +++ b/test/nodejs/trip.js @@ -208,13 +208,13 @@ test('trip: trip through Monaco with speed annotations options', (assert) => { }); }); -test('trip: trip through Monaco with several (duration, distance, nodes) annotations options', (assert) => { - assert.plan(12); +test('trip: trip through Monaco with several (duration, distance, nodes, way_ids) annotations options', (assert) => { + assert.plan(13); const osrm = new OSRM(data_path); const options = { coordinates: two_test_coordinates, steps: true, - annotations: ['duration', 'distance', 'nodes'], + annotations: ['duration', 'distance', 'nodes', 'way_ids'], overview: 'false' }; osrm.trip(options, (err, trip) => { @@ -227,6 +227,7 @@ test('trip: trip through Monaco with several (duration, distance, nodes) annotat assert.ok(trip.trips[t].legs.every((l) => { return l.annotation.duration; }), 'every leg has annotations for duration'); assert.ok(trip.trips[t].legs.every((l) => { return l.annotation.distance; }), 'every leg has annotations for distance'); assert.ok(trip.trips[t].legs.every((l) => { return l.annotation.nodes; }), 'every leg has annotations for nodes'); + assert.ok(trip.trips[t].legs.every((l) => { return l.annotation.way_ids; }), 'every leg has annotations for way ids'); assert.notOk(trip.trips[t].legs.every((l) => { return l.annotation.weight; }), 'has no annotations for weight'); assert.notOk(trip.trips[t].legs.every((l) => { return l.annotation.datasources; }), 'has no annotations for datasources'); assert.notOk(trip.trips[t].legs.every((l) => { return l.annotation.speed; }), 'has no annotations for speed'); @@ -236,7 +237,7 @@ test('trip: trip through Monaco with several (duration, distance, nodes) annotat }); test('trip: trip through Monaco with options', (assert) => { - assert.plan(6); + assert.plan(7); const osrm = new OSRM(data_path); const options = { coordinates: two_test_coordinates, @@ -251,6 +252,7 @@ test('trip: trip through Monaco with options', (assert) => { assert.ok(trip.trips[t]); assert.ok(trip.trips[t].legs.every((l) => { return l.steps.length > 0; }), 'every leg has steps'); assert.ok(trip.trips[t].legs.every((l) => { return l.annotation; }), 'every leg has annotations'); + assert.ok(trip.trips[t].legs.every((l) => { return l.annotation.way_ids; }), 'every leg has annotations for way ids'); assert.notOk(trip.trips[t].geometry); } }); diff --git a/unit_tests/engine/guidance_assembly.cpp b/unit_tests/engine/guidance_assembly.cpp index 10ba1821dd6..7263a7a1ee8 100644 --- a/unit_tests/engine/guidance_assembly.cpp +++ b/unit_tests/engine/guidance_assembly.cpp @@ -93,7 +93,8 @@ BOOST_AUTO_TEST_CASE(trim_short_segments) geometry.segment_offsets = {0, 2}; geometry.segment_distances = {1.9076601161280742}; geometry.node_ids = {NodeID{0}, NodeID{1}, NodeID{2}}; - geometry.annotations = {{1.9076601161280742, 0.2, 0.2, 0}, {0, 0, 0, 0}}; + geometry.annotations = {{1.9076601161280742, 0.2, 0.2, 0, SPECIAL_OSM_WAYID}, + {0, 0, 0, 0, SPECIAL_OSM_WAYID}}; trimShortSegments(steps, geometry); diff --git a/unit_tests/engine/offline_facade.cpp b/unit_tests/engine/offline_facade.cpp index 2a62a687735..31b5c10fdb3 100644 --- a/unit_tests/engine/offline_facade.cpp +++ b/unit_tests/engine/offline_facade.cpp @@ -140,6 +140,7 @@ class ContiguousInternalMemoryDataFacade } OSMNodeID GetOSMNodeIDOfNode(const NodeID /*id*/) const override { return OSMNodeID(); } + OSMWayID GetOSMWayID(const NodeID /*id*/) const override { return SPECIAL_OSM_WAYID; } GeometryID GetGeometryIndex(const NodeID /*id*/) const override { return GeometryID{0, false}; } @@ -193,6 +194,16 @@ class ContiguousInternalMemoryDataFacade return DatasourceReverseRange(DatasourceForwardRange()); } + WayIDForwardRange GetUncompressedForwardWayIDs(const EdgeID /*id*/) const override + { + return {}; + } + + WayIDReverseRange GetUncompressedReverseWayIDs(const EdgeID /*id*/) const override + { + return WayIDReverseRange(WayIDForwardRange()); + } + std::string_view GetDatasourceName(const DatasourceID /*id*/) const override { return std::string_view{}; diff --git a/unit_tests/extractor/intersection_analysis_tests.cpp b/unit_tests/extractor/intersection_analysis_tests.cpp index b170fe13578..668a97ab0c7 100644 --- a/unit_tests/extractor/intersection_analysis_tests.cpp +++ b/unit_tests/extractor/intersection_analysis_tests.cpp @@ -19,8 +19,8 @@ using Graph = util::NodeBasedDynamicGraph; BOOST_AUTO_TEST_CASE(simple_intersection_connectivity) { std::vector annotations{ - {EMPTY_STRINGVIEWID, 0, INVALID_CLASS_DATA, TRAVEL_MODE_DRIVING, false}, - {EMPTY_STRINGVIEWID, 1, INVALID_CLASS_DATA, TRAVEL_MODE_DRIVING, false}}; + {EMPTY_STRINGVIEWID, 0, INVALID_CLASS_DATA, SPECIAL_OSM_WAYID, TRAVEL_MODE_DRIVING, false}, + {EMPTY_STRINGVIEWID, 1, INVALID_CLASS_DATA, SPECIAL_OSM_WAYID, TRAVEL_MODE_DRIVING, false}}; std::vector restrictions{TurnRestriction{{ViaNodePath{0, 2, 1}}, false}}; CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; @@ -146,7 +146,7 @@ BOOST_AUTO_TEST_CASE(simple_intersection_connectivity) BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity) { - std::vector annotations; + std::vector annotations(1); std::vector restrictions; CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; diff --git a/unit_tests/library/route.cpp b/unit_tests/library/route.cpp index 0da1e77ed85..1b3d1503ca8 100644 --- a/unit_tests/library/route.cpp +++ b/unit_tests/library/route.cpp @@ -589,7 +589,8 @@ void test_manual_setting_of_annotations_property(bool use_json_only_api) .values[0]) .values["annotation"]) .values; - BOOST_CHECK_EQUAL(annotations.size(), 7); + BOOST_CHECK_EQUAL(annotations.size(), 8); + BOOST_CHECK(annotations.contains("way_ids")); } BOOST_AUTO_TEST_CASE(test_manual_setting_of_annotations_property_old_api) { diff --git a/unit_tests/mocks/mock_datafacade.hpp b/unit_tests/mocks/mock_datafacade.hpp index cd5214bbc27..592670895f4 100644 --- a/unit_tests/mocks/mock_datafacade.hpp +++ b/unit_tests/mocks/mock_datafacade.hpp @@ -32,6 +32,7 @@ class MockBaseDataFacade : public engine::datafacade::BaseDataFacade return {util::FixedLongitude{0}, util::FixedLatitude{0}}; } OSMNodeID GetOSMNodeIDOfNode(const NodeID /* id */) const override { return OSMNodeID{0}; } + OSMWayID GetOSMWayID(const NodeID /* id */) const override { return SPECIAL_OSM_WAYID; } bool EdgeIsCompressed(const EdgeID /* id */) const { return false; } GeometryID GetGeometryIndex(const NodeID /* id */) const override { @@ -90,6 +91,16 @@ class MockBaseDataFacade : public engine::datafacade::BaseDataFacade { return DatasourceReverseRange(DatasourceForwardRange()); } + WayIDForwardRange GetUncompressedForwardWayIDs(const EdgeID /*id*/) const override + { + static OSMWayID data[] = {OSMWayID{1}, OSMWayID{2}, OSMWayID{3}}; + static const extractor::SegmentDataView::SegmentWayIDVector way_ids(data, 3); + return WayIDForwardRange(way_ids.cbegin(), way_ids.cend()); + } + WayIDReverseRange GetUncompressedReverseWayIDs(const EdgeID id) const override + { + return WayIDReverseRange(GetUncompressedForwardWayIDs(id)); + } std::string_view GetDatasourceName(const DatasourceID) const override final { return {}; }