From 0fc1aa271188699b996fbb3221c808f3e731b941 Mon Sep 17 00:00:00 2001 From: Daniel Patterson Date: Fri, 1 Sep 2017 15:08:22 -0700 Subject: [PATCH] Adds turn instructions to the turns layer in debug tiles. (#4460) Add turn types and modifiers to turn points in debug tiles. Also refactor some of the tile code to reduce some repetition. --- CHANGELOG.md | 2 + docs/http.md | 2 + include/engine/api/json_factory.hpp | 9 + .../engine/routing_algorithms/tile_turns.hpp | 1 + src/engine/api/json_factory.cpp | 59 +++- src/engine/plugins/tile.cpp | 260 +++++++++--------- src/engine/routing_algorithms/tile_turns.cpp | 9 +- test/nodejs/constants.js | 2 +- unit_tests/library/tile.cpp | 76 ++++- 9 files changed, 270 insertions(+), 150 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 704350f15..2d573ed5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ updating ETAs returned, without changing route selection (for example, in a distance-based profile with traffic data loaded). - Infrastructure: - New file `.osrm.cell_metrics` created by `osrm-customize`. + - Debug tiles: + - Added new properties `type` and `modifier` to `turns` layer, useful for viewing guidance calculated turn types on the map # 5.11.0 - Changes from 5.10: diff --git a/docs/http.md b/docs/http.md index 57810fbbb..53135080d 100644 --- a/docs/http.md +++ b/docs/http.md @@ -437,6 +437,8 @@ Vector tiles contain two layers: | `turn_angle` | `integer` | the angle of the turn, relative to the `bearing_in`. -180 to +180, 0 = straight ahead, 90 = 90-degrees to the right | | `cost` | `float` | the time we think it takes to make that turn, in seconds. May be negative, depending on how the data model is constructed (some turns get a "bonus"). | | `weight` | `float` | the weight we think it takes to make that turn. May be negative, depending on how the data model is constructed (some turns get a "bonus"). ACTUAL ROUTING USES THIS VALUE | +| `type` | `string` | the type of this turn - values like `turn`, `continue`, etc. See the `StepManeuver` for a partial list, this field also exposes internal turn types that are never returned with an API response | +| `modifier` | `string` | the direction modifier of the turn (`left`, `sharp left`, etc) | ## Result objects diff --git a/include/engine/api/json_factory.hpp b/include/engine/api/json_factory.hpp index 75ae2e51c..8754e4cf5 100644 --- a/include/engine/api/json_factory.hpp +++ b/include/engine/api/json_factory.hpp @@ -36,6 +36,15 @@ namespace detail std::string instructionTypeToString(extractor::guidance::TurnType::Enum type); std::string instructionModifierToString(extractor::guidance::DirectionModifier::Enum modifier); +/** + * Returns a string representing all instruction types (including internal types that + * are normally not exposed in route responses) + * + * @param type the TurnType value to convert into a string + * @return a string representing the turn type (e.g. `turn` or `continue`) + */ +std::string internalInstructionTypeToString(extractor::guidance::TurnType::Enum type); + util::json::Array coordinateToLonLat(const util::Coordinate coordinate); std::string modeToString(const extractor::TravelMode mode); diff --git a/include/engine/routing_algorithms/tile_turns.hpp b/include/engine/routing_algorithms/tile_turns.hpp index 2d58ef22e..cde773a0d 100644 --- a/include/engine/routing_algorithms/tile_turns.hpp +++ b/include/engine/routing_algorithms/tile_turns.hpp @@ -24,6 +24,7 @@ struct TurnData final const int turn_angle; const EdgeWeight weight; const EdgeWeight duration; + const extractor::guidance::TurnInstruction turn_instruction; }; using RTreeLeaf = datafacade::BaseDataFacade::RTreeLeaf; diff --git a/src/engine/api/json_factory.cpp b/src/engine/api/json_factory.cpp index f6b7ad538..581cfdb39 100644 --- a/src/engine/api/json_factory.cpp +++ b/src/engine/api/json_factory.cpp @@ -41,15 +41,47 @@ const constexpr char *modifier_names[] = {"uturn", "left", "sharp left"}; -// translations of TurnTypes. Not all types are exposed to the outside world. -// invalid types should never be returned as part of the API -const constexpr char *turn_type_names[] = { - "invalid", "new name", "continue", "turn", "merge", - "on ramp", "off ramp", "fork", "end of road", "notification", - "roundabout", "roundabout", "rotary", "rotary", "roundabout turn", - "roundabout turn", "use lane", "invalid", "invalid", "invalid", - "invalid", "invalid", "invalid", "invalid", "invalid", - "invalid", "invalid"}; +/** + * Human readable values for TurnType enum values + */ +struct TurnTypeName +{ + // String value we return with our API + const char *external_name; + // Internal only string name for the turn type - useful for debugging + // and used by debug tiles for visualizing hidden turn types + const char *internal_name; +}; + +// Indexes in this list correspond to the Enum values of osrm::extractor::guidance::TurnType +const constexpr TurnTypeName turn_type_names[] = { + {"invalid", "(not set)"}, + {"new name", "new name"}, + {"continue", "continue"}, + {"turn", "turn"}, + {"merge", "merge"}, + {"on ramp", "on ramp"}, + {"off ramp", "off ramp"}, + {"fork", "fork"}, + {"end of road", "end of road"}, + {"notification", "notification"}, + {"roundabout", "enter roundabout"}, + {"roundabout", "enter and exit roundabout"}, + {"rotary", "enter rotary"}, + {"rotary", "enter and exit rotary"}, + {"roundabout turn", "enter roundabout turn"}, + {"roundabout turn", "enter and exit roundabout turn"}, + {"use lane", "use lane"}, + {"invalid", "(noturn)"}, + {"invalid", "(suppressed)"}, + {"invalid", "(enter roundabout at exit)"}, + {"invalid", "(exit roundabout)"}, + {"invalid", "(enter rotary at exit)"}, + {"invalid", "(exit rotary)"}, + {"invalid", "(enter roundabout intersection at exit)"}, + {"invalid", "(exit roundabout intersection)"}, + {"invalid", "(stay on roundabout)"}, + {"invalid", "(sliproad)"}}; const constexpr char *waypoint_type_names[] = {"invalid", "arrive", "depart"}; @@ -69,7 +101,14 @@ std::string instructionTypeToString(const TurnType::Enum type) { static_assert(sizeof(turn_type_names) / sizeof(turn_type_names[0]) >= TurnType::MaxTurnType, "Some turn types has not string representation."); - return turn_type_names[static_cast(type)]; + return turn_type_names[static_cast(type)].external_name; +} + +std::string internalInstructionTypeToString(const TurnType::Enum type) +{ + static_assert(sizeof(turn_type_names) / sizeof(turn_type_names[0]) >= TurnType::MaxTurnType, + "Some turn types has not string representation."); + return turn_type_names[static_cast(type)].internal_name; } util::json::Array lanesFromIntersection(const guidance::IntermediateIntersection &intersection) diff --git a/src/engine/plugins/tile.cpp b/src/engine/plugins/tile.cpp index fc38f592b..8c283ee30 100644 --- a/src/engine/plugins/tile.cpp +++ b/src/engine/plugins/tile.cpp @@ -6,6 +6,8 @@ #include "util/vector_tile.hpp" #include "util/web_mercator.hpp" +#include "engine/api/json_factory.hpp" + #include #include #include @@ -37,6 +39,41 @@ constexpr const static int MIN_ZOOM_FOR_TURNS = 15; namespace { +// Creates an indexed lookup table for values - used to encoded the vector tile +// which uses a lookup table and index pointers for encoding +template struct ValueIndexer +{ + private: + std::vector used_values; + std::unordered_map value_offsets; + + public: + std::size_t add(const T &value) + { + const auto found = value_offsets.find(value); + std::size_t offset; + + if (found == value_offsets.end()) + { + used_values.push_back(value); + offset = used_values.size() - 1; + value_offsets[value] = offset; + } + else + { + offset = found->second; + } + + return offset; + } + + std::size_t indexOf(const T &value) { return value_offsets[value]; }; + + const std::vector &values() { return used_values; } + + std::size_t size() const { return used_values.size(); } +}; + using RTreeLeaf = datafacade::BaseDataFacade::RTreeLeaf; // TODO: Port all this encoding logic to https://github.com/mapbox/vector-tile, which wasn't // available when this code was originally written. @@ -281,87 +318,17 @@ void encodeVectorTile(const DataFacadeBase &facade, std::string &pbf_buffer) { - // Vector tiles encode properties as references to a common lookup table. - // When we add a property to a "feature", we actually attach the index of the value - // rather than the value itself. Thus, we need to keep a list of the unique - // values we need, and we add this list to the tile as a lookup table. This - // vector holds all the actual used values, the feature refernce offsets in - // this vector. - // for integer values - std::vector used_line_ints; - // While constructing the tile, we keep track of which integers we have in our table - // and their offsets, so multiple features can re-use the same values - std::unordered_map line_int_offsets; - - // Same idea for street names - one lookup table for names for all features - std::vector names; - std::unordered_map name_offsets; - - // And again for integer values used by points. - std::vector used_point_ints; - std::unordered_map point_int_offsets; - - // And again for float values used by points - std::vector used_point_floats; - std::unordered_map point_float_offsets; - std::uint8_t max_datasource_id = 0; - // This is where we accumulate information on turns - - // Helper function for adding a new value to the line_ints lookup table. Returns - // the index of the value in the table, adding the value if it doesn't already - // exist - const auto use_line_value = [&used_line_ints, &line_int_offsets](const int value) { - const auto found = line_int_offsets.find(value); - - if (found == line_int_offsets.end()) - { - used_line_ints.push_back(value); - line_int_offsets[value] = used_line_ints.size() - 1; - } - - return; - }; - - // Same again - const auto use_point_int_value = [&used_point_ints, &point_int_offsets](const int value) { - const auto found = point_int_offsets.find(value); - std::size_t offset; - - if (found == point_int_offsets.end()) - { - used_point_ints.push_back(value); - offset = used_point_ints.size() - 1; - point_int_offsets[value] = offset; - } - else - { - offset = found->second; - } - - return offset; - }; - - // And a third time, should probably template this.... - const auto use_point_float_value = [&used_point_floats, - &point_float_offsets](const float value) { - const auto found = point_float_offsets.find(value); - std::size_t offset; - - if (found == point_float_offsets.end()) - { - used_point_floats.push_back(value); - offset = used_point_floats.size() - 1; - point_float_offsets[value] = offset; - } - else - { - offset = found->second; - } - - return offset; - }; + // Vector tiles encode properties on features as indexes into a layer-specific + // lookup table. These ValueIndexer's act as memoizers for values as we discover + // them during edge explioration, and are then used to generate the lookup + // tables for each tile layer. + ValueIndexer line_int_index; + ValueIndexer line_string_index; + ValueIndexer point_int_index; + ValueIndexer point_float_index; + ValueIndexer point_string_index; const auto get_geometry_id = [&facade](auto edge) { return facade.GetGeometryIndex(edge.forward_segment_id.id).id; @@ -439,16 +406,16 @@ void encodeVectorTile(const DataFacadeBase &facade, const auto forward_weight = forward_weight_vector[edge.fwd_segment_position]; const auto reverse_weight = reverse_weight_vector[reverse_weight_vector.size() - edge.fwd_segment_position - 1]; - use_line_value(forward_weight); - use_line_value(reverse_weight); + line_int_index.add(forward_weight); + line_int_index.add(reverse_weight); std::uint32_t forward_rate = static_cast(round(length / forward_weight * 10.)); std::uint32_t reverse_rate = static_cast(round(length / reverse_weight * 10.)); - use_line_value(forward_rate); - use_line_value(reverse_rate); + line_int_index.add(forward_rate); + line_int_index.add(reverse_rate); // Duration values const auto forward_duration_vector = @@ -459,8 +426,8 @@ void encodeVectorTile(const DataFacadeBase &facade, const auto reverse_duration = reverse_duration_vector[reverse_duration_vector.size() - edge.fwd_segment_position - 1]; - use_line_value(forward_duration); - use_line_value(reverse_duration); + line_int_index.add(forward_duration); + line_int_index.add(reverse_duration); } // Begin the layer features block @@ -510,24 +477,14 @@ void encodeVectorTile(const DataFacadeBase &facade, const auto name_id = facade.GetNameIndex(edge.forward_segment_id.id); auto name = facade.GetNameForID(name_id); - const auto name_offset = [&name, &names, &name_offsets]() { - auto iter = name_offsets.find(name); - if (iter == name_offsets.end()) - { - auto offset = names.size(); - name_offsets[name] = offset; - names.push_back(name); - return offset; - } - return iter->second; - }(); + line_string_index.add(name); const auto encode_tile_line = [&line_layer_writer, &edge, &component_id, &id, &max_datasource_id, - &used_line_ints]( + &line_int_index]( const FixedLine &tile_line, const std::uint32_t speed_kmh_idx, const std::uint32_t rate_idx, @@ -575,12 +532,11 @@ void encodeVectorTile(const DataFacadeBase &facade, duration_idx); // duration value offset field.add_element(5); // "name" tag key offset - field.add_element(130 + max_datasource_id + 1 + used_line_ints.size() + - name_idx); // name value offset + field.add_element(130 + max_datasource_id + 1 + + line_int_index.values().size() + name_idx); field.add_element(6); // rate tag key offset - field.add_element(130 + max_datasource_id + 1 + - rate_idx); // rate goes in used_line_ints + field.add_element(130 + max_datasource_id + 1 + rate_idx); } { @@ -614,11 +570,11 @@ void encodeVectorTile(const DataFacadeBase &facade, { encode_tile_line(tile_line, speed_kmh_idx, - line_int_offsets[forward_rate], - line_int_offsets[forward_weight], - line_int_offsets[forward_duration], + line_int_index.indexOf(forward_rate), + line_int_index.indexOf(forward_weight), + line_int_index.indexOf(forward_duration), forward_datasource_idx, - name_offset, + line_string_index.indexOf(name), start_x, start_y); } @@ -648,11 +604,11 @@ void encodeVectorTile(const DataFacadeBase &facade, { encode_tile_line(tile_line, speed_kmh_idx, - line_int_offsets[reverse_rate], - line_int_offsets[reverse_weight], - line_int_offsets[reverse_duration], + line_int_index.indexOf(reverse_rate), + line_int_index.indexOf(reverse_weight), + line_int_index.indexOf(reverse_duration), reverse_datasource_idx, - name_offset, + line_string_index.indexOf(name), start_x, start_y); } @@ -703,7 +659,7 @@ void encodeVectorTile(const DataFacadeBase &facade, values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING, facade.GetDatasourceName(i).to_string()); } - for (auto value : used_line_ints) + for (auto value : line_int_index.values()) { // Writing field type 4 == variant type protozero::pbf_writer values_writer(line_layer_writer, @@ -714,7 +670,7 @@ void encodeVectorTile(const DataFacadeBase &facade, values_writer.add_double(util::vector_tile::VARIANT_TYPE_DOUBLE, value / 10.); } - for (const auto &name : names) + for (const auto &name : line_string_index.values()) { // Writing field type 4 == variant type protozero::pbf_writer values_writer(line_layer_writer, @@ -729,23 +685,46 @@ void encodeVectorTile(const DataFacadeBase &facade, // for tiles of z<16, and tiles that don't show any intersections) if (!all_turn_data.empty()) { + + struct EncodedTurnData + { + util::Coordinate coordinate; + std::size_t angle_index; + std::size_t turn_index; + std::size_t duration_index; + std::size_t weight_index; + std::size_t turntype_index; + std::size_t turnmodifier_index; + }; // we need to pre-encode all values here because we need the full offsets later // for encoding the actual features. - std::vector> - encoded_turn_data(all_turn_data.size()); - std::transform(all_turn_data.begin(), - all_turn_data.end(), - encoded_turn_data.begin(), - [&](const routing_algorithms::TurnData &t) { - auto angle_idx = use_point_int_value(t.in_angle); - auto turn_idx = use_point_int_value(t.turn_angle); - auto duration_idx = use_point_float_value( - t.duration / 10.0); // Note conversion to float here - auto weight_idx = use_point_float_value( - t.weight / 10.0); // Note conversion to float here - return std::make_tuple( - t.coordinate, angle_idx, turn_idx, duration_idx, weight_idx); - }); + std::vector encoded_turn_data(all_turn_data.size()); + std::transform( + all_turn_data.begin(), + all_turn_data.end(), + encoded_turn_data.begin(), + [&](const routing_algorithms::TurnData &t) { + auto angle_idx = point_int_index.add(t.in_angle); + auto turn_idx = point_int_index.add(t.turn_angle); + auto duration_idx = + point_float_index.add(t.duration / 10.0); // Note conversion to float here + auto weight_idx = + point_float_index.add(t.weight / 10.0); // Note conversion to float here + + auto turntype_idx = + point_string_index.add(api::json::detail::internalInstructionTypeToString( + t.turn_instruction.type)); + auto turnmodifier_idx = + point_string_index.add(api::json::detail::instructionModifierToString( + t.turn_instruction.direction_modifier)); + return EncodedTurnData{t.coordinate, + angle_idx, + turn_idx, + duration_idx, + weight_idx, + turntype_idx, + turnmodifier_idx}; + }); // Now write the points layer for turn penalty data: // Add a layer object to the PBF stream. 3=='layer' from the vector tile spec @@ -778,13 +757,19 @@ void encodeVectorTile(const DataFacadeBase &facade, protozero::packed_field_uint32 field( feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG); field.add_element(0); // "bearing_in" tag key offset - field.add_element(std::get<1>(point_turn_data)); + field.add_element(point_turn_data.angle_index); field.add_element(1); // "turn_angle" tag key offset - field.add_element(std::get<2>(point_turn_data)); + field.add_element(point_turn_data.turn_index); field.add_element(2); // "cost" tag key offset - field.add_element(used_point_ints.size() + std::get<3>(point_turn_data)); + field.add_element(point_int_index.size() + point_turn_data.duration_index); field.add_element(3); // "weight" tag key offset - field.add_element(used_point_ints.size() + std::get<4>(point_turn_data)); + field.add_element(point_int_index.size() + point_turn_data.weight_index); + field.add_element(4); // "type" tag key offset + field.add_element(point_int_index.size() + point_float_index.size() + + point_turn_data.turntype_index); + field.add_element(5); // "modifier" tag key offset + field.add_element(point_int_index.size() + point_float_index.size() + + point_turn_data.turnmodifier_index); } { // Add the geometry as the last field in this feature @@ -797,8 +782,7 @@ void encodeVectorTile(const DataFacadeBase &facade, // Loop over all the turns we found and add them as features to the layer for (const auto &turndata : encoded_turn_data) { - const auto tile_point = - coordinatesToTilePoint(std::get<0>(turndata), tile_bbox); + const auto tile_point = coordinatesToTilePoint(turndata.coordinate, tile_bbox); if (!boost::geometry::within(point_t(tile_point.x, tile_point.y), clip_box)) { continue; @@ -813,20 +797,28 @@ void encodeVectorTile(const DataFacadeBase &facade, point_layer_writer.add_string(util::vector_tile::KEY_TAG, "turn_angle"); point_layer_writer.add_string(util::vector_tile::KEY_TAG, "cost"); point_layer_writer.add_string(util::vector_tile::KEY_TAG, "weight"); + point_layer_writer.add_string(util::vector_tile::KEY_TAG, "type"); + point_layer_writer.add_string(util::vector_tile::KEY_TAG, "modifier"); // Now, save the lists of integers and floats that our features refer to. - for (const auto &value : used_point_ints) + for (const auto &value : point_int_index.values()) { protozero::pbf_writer values_writer(point_layer_writer, util::vector_tile::VARIANT_TAG); values_writer.add_sint64(util::vector_tile::VARIANT_TYPE_SINT64, value); } - for (const auto &value : used_point_floats) + for (const auto &value : point_float_index.values()) { protozero::pbf_writer values_writer(point_layer_writer, util::vector_tile::VARIANT_TAG); values_writer.add_float(util::vector_tile::VARIANT_TYPE_FLOAT, value); } + for (const auto &value : point_string_index.values()) + { + protozero::pbf_writer values_writer(point_layer_writer, + util::vector_tile::VARIANT_TAG); + values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING, value); + } } // OSM Node tile layer diff --git a/src/engine/routing_algorithms/tile_turns.cpp b/src/engine/routing_algorithms/tile_turns.cpp index 2af802be4..80939eb3e 100644 --- a/src/engine/routing_algorithms/tile_turns.cpp +++ b/src/engine/routing_algorithms/tile_turns.cpp @@ -183,6 +183,7 @@ std::vector generateTurns(const datafacade &facade, // treat the whole lot as the "turn cost" that we'll stick on the map. const auto turn_weight = data.weight - sum_node_weight; const auto turn_duration = data.duration - sum_node_duration; + const auto turn_instruction = facade.GetTurnInstructionForEdgeID(data.turn_id); // Find the three nodes that make up the turn movement) const auto node_from = startnode; @@ -214,8 +215,12 @@ std::vector generateTurns(const datafacade &facade, // Save everything we need to later add all the points to the tile. // We need the coordinate of the intersection, the angle in, the turn // angle and the turn cost. - all_turn_data.push_back( - TurnData{coord_via, angle_in, turn_angle, turn_weight, turn_duration}); + all_turn_data.push_back(TurnData{coord_via, + angle_in, + turn_angle, + turn_weight, + turn_duration, + turn_instruction}); } } } diff --git a/test/nodejs/constants.js b/test/nodejs/constants.js index 8750876bb..09cc86a83 100644 --- a/test/nodejs/constants.js +++ b/test/nodejs/constants.js @@ -10,7 +10,7 @@ exports.three_test_coordinates = [[7.41337, 43.72956], exports.two_test_coordinates = exports.three_test_coordinates.slice(0, 2) -exports.test_tile = {'at': [17059, 11948, 15], 'size': 162429}; +exports.test_tile = {'at': [17059, 11948, 15], 'size': 167178}; // Test files generated by the routing engine; check test/data diff --git a/unit_tests/library/tile.cpp b/unit_tests/library/tile.cpp index 9e216ed11..b804428f2 100644 --- a/unit_tests/library/tile.cpp +++ b/unit_tests/library/tile.cpp @@ -151,7 +151,7 @@ void validate_turn_layer(protozero::pbf_reader &layer_message) BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::FEATURE_ATTRIBUTES_TAG); // properties auto feature_iter_pair = feature_message.get_packed_uint32(); - BOOST_CHECK_EQUAL(std::distance(feature_iter_pair.begin(), feature_iter_pair.end()), 8); + BOOST_CHECK_EQUAL(std::distance(feature_iter_pair.begin(), feature_iter_pair.end()), 12); auto iter = feature_iter_pair.begin(); BOOST_CHECK_EQUAL(*iter++, 0); // bearing_in key *iter++; @@ -161,6 +161,10 @@ void validate_turn_layer(protozero::pbf_reader &layer_message) *iter++; // skip value check, can be valud uint32 BOOST_CHECK_EQUAL(*iter++, 3); // turn weight key *iter++; // skip value check, can be valud uint32 + BOOST_CHECK_EQUAL(*iter++, 4); // turn type key + *iter++; // skip value check, can be valud uint32 + BOOST_CHECK_EQUAL(*iter++, 5); // turn modifier + *iter++; // skip value check, can be valud uint32 BOOST_CHECK(iter == feature_iter_pair.end()); // geometry feature_message.next(); @@ -201,7 +205,7 @@ void validate_turn_layer(protozero::pbf_reader &layer_message) } } - BOOST_CHECK_EQUAL(number_of_turn_keys, 4); + BOOST_CHECK_EQUAL(number_of_turn_keys, 6); BOOST_CHECK(number_of_turns_found > 700); } @@ -337,6 +341,8 @@ void test_tile_turns(const osrm::OSRM &osrm) std::vector found_turn_angles_indexes; std::vector found_time_penalties_indexes; std::vector found_weight_penalties_indexes; + std::vector found_turn_type_indexes; + std::vector found_turn_modifier_indexes; const auto check_turn_feature = [&](protozero::pbf_reader feature_message) { feature_message.next(); // advance parser to first entry @@ -351,7 +357,7 @@ void test_tile_turns(const osrm::OSRM &osrm) BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::FEATURE_ATTRIBUTES_TAG); // properties auto feature_iter_pair = feature_message.get_packed_uint32(); - BOOST_CHECK_EQUAL(std::distance(feature_iter_pair.begin(), feature_iter_pair.end()), 8); + BOOST_CHECK_EQUAL(std::distance(feature_iter_pair.begin(), feature_iter_pair.end()), 12); auto iter = feature_iter_pair.begin(); BOOST_CHECK_EQUAL(*iter++, 0); // bearing_in key found_bearing_in_indexes.push_back(*iter++); @@ -361,6 +367,10 @@ void test_tile_turns(const osrm::OSRM &osrm) found_time_penalties_indexes.push_back(*iter++); // skip value check, can be valud uint32 BOOST_CHECK_EQUAL(*iter++, 3); // "weight" key found_weight_penalties_indexes.push_back(*iter++); // skip value check, can be valud uint32 + BOOST_CHECK_EQUAL(*iter++, 4); // "weight" key + found_turn_type_indexes.push_back(*iter++); // skip value check, can be valud uint32 + BOOST_CHECK_EQUAL(*iter++, 5); // "weight" key + found_turn_modifier_indexes.push_back(*iter++); // skip value check, can be valud uint32 BOOST_CHECK(iter == feature_iter_pair.end()); // geometry feature_message.next(); @@ -370,6 +380,7 @@ void test_tile_turns(const osrm::OSRM &osrm) std::unordered_map float_vals; std::unordered_map sint64_vals; + std::unordered_map string_vals; int kv_index = 0; @@ -384,6 +395,9 @@ void test_tile_turns(const osrm::OSRM &osrm) case util::vector_tile::VARIANT_TYPE_SINT64: sint64_vals[kv_index] = value.get_sint64(); break; + case util::vector_tile::VARIANT_TYPE_STRING: + string_vals[kv_index] = value.get_string(); + break; default: BOOST_CHECK(false); } @@ -448,6 +462,62 @@ void test_tile_turns(const osrm::OSRM &osrm) 0, 0, 0, 0, 0, 0, .1f, .1f, .3f, .4f, 1.2f, 1.9f, 5.3f, 5.5f, 5.8f, 7.1f, 7.2f, 7.2f}; CHECK_EQUAL_RANGE(actual_weight_turn_penalties, expected_weight_turn_penalties); + // Verify that we got the expected turn types + std::vector actual_turn_types; + for (const auto &i : found_turn_type_indexes) + { + BOOST_CHECK(string_vals.count(i) == 1); + actual_turn_types.push_back(string_vals[i]); + } + std::sort(actual_turn_types.begin(), actual_turn_types.end()); + const std::vector expected_turn_types = {"(noturn)", + "(noturn)", + "(noturn)", + "(noturn)", + "(suppressed)", + "(suppressed)", + "end of road", + "end of road", + "fork", + "fork", + "turn", + "turn", + "turn", + "turn", + "turn", + "turn", + "turn", + "turn"}; + CHECK_EQUAL_RANGE(actual_turn_types, expected_turn_types); + + // Verify that we got the expected turn modifiers + std::vector actual_turn_modifiers; + for (const auto &i : found_turn_modifier_indexes) + { + BOOST_CHECK(string_vals.count(i) == 1); + actual_turn_modifiers.push_back(string_vals[i]); + } + std::sort(actual_turn_modifiers.begin(), actual_turn_modifiers.end()); + const std::vector expected_turn_modifiers = {"left", + "left", + "left", + "left", + "right", + "right", + "right", + "right", + "sharp left", + "sharp right", + "slight left", + "slight left", + "slight right", + "slight right", + "straight", + "straight", + "straight", + "straight"}; + CHECK_EQUAL_RANGE(actual_turn_modifiers, expected_turn_modifiers); + // Verify the expected turn angles std::vector actual_turn_angles; for (const auto &i : found_turn_angles_indexes)