From c8eb2b2d11eb6122abc6b1618f18f22a0d3aab7c Mon Sep 17 00:00:00 2001 From: Daniel Patterson Date: Mon, 22 Aug 2016 23:26:48 -0700 Subject: [PATCH] Refactor edge unpacking so that it's CH indepenent and we don't repeat ourselves so much. --- include/engine/datafacade/datafacade_base.hpp | 4 + .../engine/datafacade/internal_datafacade.hpp | 7 + .../engine/datafacade/shared_datafacade.hpp | 7 + include/engine/edge_unpacker.hpp | 104 ++++++++ include/engine/plugins/tile.hpp | 7 +- .../routing_algorithms/routing_base.hpp | 224 +++--------------- include/util/static_graph.hpp | 23 +- src/engine/plugins/tile.cpp | 221 +++++++++-------- 8 files changed, 301 insertions(+), 296 deletions(-) create mode 100644 include/engine/edge_unpacker.hpp diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp index e837e0730..9dc5f5f7b 100644 --- a/include/engine/datafacade/datafacade_base.hpp +++ b/include/engine/datafacade/datafacade_base.hpp @@ -66,6 +66,10 @@ class BaseDataFacade virtual EdgeID FindEdgeIndicateIfReverse(const NodeID from, const NodeID to, bool &result) const = 0; + virtual EdgeID FindSmallestEdge(const NodeID from, + const NodeID to, + const std::function filter) const = 0; + // node and edge information access virtual util::Coordinate GetCoordinateOfNode(const unsigned id) const = 0; virtual OSMNodeID GetOSMNodeIDOfNode(const unsigned id) const = 0; diff --git a/include/engine/datafacade/internal_datafacade.hpp b/include/engine/datafacade/internal_datafacade.hpp index 20dfe89a5..9cf9df259 100644 --- a/include/engine/datafacade/internal_datafacade.hpp +++ b/include/engine/datafacade/internal_datafacade.hpp @@ -472,6 +472,13 @@ class InternalDataFacade final : public BaseDataFacade return m_query_graph->FindEdgeIndicateIfReverse(from, to, result); } + EdgeID FindSmallestEdge(const NodeID from, + const NodeID to, + std::function filter) const override final + { + return m_query_graph->FindSmallestEdge(from, to, filter); + } + // node and edge information access util::Coordinate GetCoordinateOfNode(const unsigned id) const override final { diff --git a/include/engine/datafacade/shared_datafacade.hpp b/include/engine/datafacade/shared_datafacade.hpp index eb58c5743..ef2545d8e 100644 --- a/include/engine/datafacade/shared_datafacade.hpp +++ b/include/engine/datafacade/shared_datafacade.hpp @@ -507,6 +507,13 @@ class SharedDataFacade final : public BaseDataFacade return m_query_graph->FindEdgeIndicateIfReverse(from, to, result); } + EdgeID FindSmallestEdge(const NodeID from, + const NodeID to, + std::function filter) const override final + { + return m_query_graph->FindSmallestEdge(from, to, filter); + } + // node and edge information access util::Coordinate GetCoordinateOfNode(const NodeID id) const override final { diff --git a/include/engine/edge_unpacker.hpp b/include/engine/edge_unpacker.hpp new file mode 100644 index 000000000..8d1f131ba --- /dev/null +++ b/include/engine/edge_unpacker.hpp @@ -0,0 +1,104 @@ +#ifndef EDGE_UNPACKER_H +#define EDGE_UNPACKER_H + +#include "extractor/guidance/turn_instruction.hpp" +#include "extractor/travel_mode.hpp" +#include "engine/phantom_node.hpp" +#include "osrm/coordinate.hpp" +#include "util/guidance/turn_lanes.hpp" +#include "util/typedefs.hpp" + +#include +#include + +namespace osrm +{ +namespace engine +{ + +/** + * Given a sequence of connected `NodeID`s in the CH graph, performs a depth-first unpacking of + * the shortcut + * edges. For every "original" edge found, it calls the `callback` with the two NodeIDs for the + * edge, and the EdgeData + * for that edge. + * + * The primary purpose of this unpacking is to expand a path through the CH into the original + * route through the + * pre-contracted graph. + * + * Because of the depth-first-search, the `callback` will effectively be called in sequence for + * the original route + * from beginning to end. + * + * @param packed_path_begin iterator pointing to the start of the NodeID list + * @param packed_path_end iterator pointing to the end of the NodeID list + * @param callback void(const std::pair, const EdgeData &) called for each + * original edge found. + */ + +template +inline void UnpackCHEdge(DataFacadeT *facade, + RandomIter packed_path_begin, + RandomIter packed_path_end, + const Callback &callback) +{ + + using EdgeData = typename DataFacadeT::EdgeData; + + std::stack> recursion_stack; + + // We have to push the path in reverse order onto the stack because it's LIFO. + for (auto current = std::prev(packed_path_end); current != packed_path_begin; + current = std::prev(current)) + { + recursion_stack.emplace(*std::prev(current), *current); + } + + std::pair edge; + while (!recursion_stack.empty()) + { + edge = recursion_stack.top(); + recursion_stack.pop(); + + // Look for an edge on the forward CH graph (.forward) + EdgeID smaller_edge_id = facade->FindSmallestEdge( + edge.first, edge.second, [](const EdgeData &data) { return data.forward; }); + + // If we didn't find one there, the we might be looking at a part of the path that + // was found using the backward search. Here, we flip the node order (.second, .first) + // and only consider edges with the `.backward` flag. + if (SPECIAL_EDGEID == smaller_edge_id) + { + smaller_edge_id = facade->FindSmallestEdge( + edge.second, edge.first, [](const EdgeData &data) { return data.backward; }); + } + + // If we didn't find anything *still*, then something is broken and someone has + // called this function with bad values. + BOOST_ASSERT_MSG(smaller_edge_id != SPECIAL_EDGEID, "Invalid smaller edge ID"); + + const auto &data = facade->GetEdgeData(smaller_edge_id); + BOOST_ASSERT_MSG(data.distance != std::numeric_limits::max(), + "edge weight invalid"); + + // If the edge is a shortcut, we need to add the two halfs to the stack. + if (data.shortcut) + { // unpack + const NodeID middle_node_id = data.id; + // Note the order here - we're adding these to a stack, so we + // want the first->middle to get visited before middle->second + recursion_stack.emplace(middle_node_id, edge.second); + recursion_stack.emplace(edge.first, middle_node_id); + } + else + { + // We found an original edge, call our callback. + callback(edge, data); + } + } +} +} +} + +#endif // EDGE_UNPACKER_H diff --git a/include/engine/plugins/tile.hpp b/include/engine/plugins/tile.hpp index a8f4cd8c6..fa7ff2678 100644 --- a/include/engine/plugins/tile.hpp +++ b/include/engine/plugins/tile.hpp @@ -25,13 +25,8 @@ namespace plugins class TilePlugin final : public BasePlugin { - private: - routing_algorithms::BasicRoutingInterface< - datafacade::BaseDataFacade, - routing_algorithms::ShortestPathRouting> routing_base; - public: - TilePlugin(datafacade::BaseDataFacade &facade) : BasePlugin(facade), routing_base(&facade) {} + TilePlugin(datafacade::BaseDataFacade &facade) : BasePlugin(facade) {} Status HandleRequest(const api::TileParameters ¶meters, std::string &pbf_buffer); }; diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp index 8fca648e8..615d174c9 100644 --- a/include/engine/routing_algorithms/routing_base.hpp +++ b/include/engine/routing_algorithms/routing_base.hpp @@ -4,6 +4,7 @@ #include "extractor/guidance/turn_instruction.hpp" #include "engine/internal_route_result.hpp" #include "engine/search_engine_data.hpp" +#include "engine/edge_unpacker.hpp" #include "util/coordinate_calculation.hpp" #include "util/typedefs.hpp" @@ -221,14 +222,6 @@ template class BasicRoutingInterface (*std::prev(packed_path_end) != phantom_node_pair.target_phantom.forward_segment_id.id); BOOST_ASSERT(std::distance(packed_path_begin, packed_path_end) > 0); - std::stack> recursion_stack; - - // We have to push the path in reverse order onto the stack because it's LIFO. - for (auto current = std::prev(packed_path_end); current != packed_path_begin; - current = std::prev(current)) - { - recursion_stack.emplace(*std::prev(current), *current); - } BOOST_ASSERT(*packed_path_begin == phantom_node_pair.source_phantom.forward_segment_id.id || *packed_path_begin == phantom_node_pair.source_phantom.reverse_segment_id.id); @@ -236,69 +229,26 @@ template class BasicRoutingInterface *std::prev(packed_path_end) == phantom_node_pair.target_phantom.forward_segment_id.id || *std::prev(packed_path_end) == phantom_node_pair.target_phantom.reverse_segment_id.id); - std::pair edge; - while (!recursion_stack.empty()) - { - // edge.first edge.second - // *------------------>* - // edge_id - edge = recursion_stack.top(); - recursion_stack.pop(); + UnpackCHEdge( + facade, + packed_path_begin, + packed_path_end, + [this, + &unpacked_path, + &phantom_node_pair, + &start_traversed_in_reverse, + &target_traversed_in_reverse](std::pair & /* edge */, + const EdgeData &data) { - // Contraction might introduce double edges by inserting shortcuts - // this searching for the smallest upwards edge found by the forward search - EdgeID smaller_edge_id = SPECIAL_EDGEID; - EdgeWeight edge_weight = std::numeric_limits::max(); - for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.first)) - { - const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; - if ((facade->GetTarget(edge_id) == edge.second) && (weight < edge_weight) && - facade->GetEdgeData(edge_id).forward) - { - smaller_edge_id = edge_id; - edge_weight = weight; - } - } - - // edge.first edge.second - // *<------------------* - // edge_id - // if we don't find a forward edge, this edge must have been an downwards edge - // found by the reverse search. - if (SPECIAL_EDGEID == smaller_edge_id) - { - for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.second)) - { - const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; - if ((facade->GetTarget(edge_id) == edge.first) && (weight < edge_weight) && - facade->GetEdgeData(edge_id).backward) - { - smaller_edge_id = edge_id; - edge_weight = weight; - } - } - } - BOOST_ASSERT_MSG(edge_weight != INVALID_EDGE_WEIGHT, "edge id invalid"); - - const EdgeData &ed = facade->GetEdgeData(smaller_edge_id); - if (ed.shortcut) - { // unpack - const NodeID middle_node_id = ed.id; - // again, we need to this in reversed order - recursion_stack.emplace(middle_node_id, edge.second); - recursion_stack.emplace(edge.first, middle_node_id); - } - else - { - BOOST_ASSERT_MSG(!ed.shortcut, "original edge flagged as shortcut"); - unsigned name_index = facade->GetNameIndexFromEdgeID(ed.id); - const auto turn_instruction = facade->GetTurnInstructionForEdgeID(ed.id); + BOOST_ASSERT_MSG(!data.shortcut, "original edge flagged as shortcut"); + unsigned name_index = facade->GetNameIndexFromEdgeID(data.id); + const auto turn_instruction = facade->GetTurnInstructionForEdgeID(data.id); const extractor::TravelMode travel_mode = (unpacked_path.empty() && start_traversed_in_reverse) ? phantom_node_pair.source_phantom.backward_travel_mode - : facade->GetTravelModeForEdgeID(ed.id); + : facade->GetTravelModeForEdgeID(data.id); - const auto geometry_index = facade->GetGeometryIndexForEdgeID(ed.id); + const auto geometry_index = facade->GetGeometryIndexForEdgeID(data.id); std::vector id_vector; facade->GetUncompressedGeometry(geometry_index, id_vector); BOOST_ASSERT(id_vector.size() > 0); @@ -339,14 +289,14 @@ template class BasicRoutingInterface datasource_vector[i]}); } BOOST_ASSERT(unpacked_path.size() > 0); - if (facade->hasLaneData(ed.id)) - unpacked_path.back().lane_data = facade->GetLaneData(ed.id); + if (facade->hasLaneData(data.id)) + unpacked_path.back().lane_data = facade->GetLaneData(data.id); - unpacked_path.back().entry_classid = facade->GetEntryClassID(ed.id); + unpacked_path.back().entry_classid = facade->GetEntryClassID(data.id); unpacked_path.back().turn_instruction = turn_instruction; - unpacked_path.back().duration_until_turn += (ed.distance - total_weight); - } - } + unpacked_path.back().duration_until_turn += (data.distance - total_weight); + }); + std::size_t start_index = 0, end_index = 0; std::vector id_vector; std::vector weight_vector; @@ -455,124 +405,24 @@ template class BasicRoutingInterface } } - void UnpackEdge(const NodeID s, const NodeID t, std::vector &unpacked_path) const - { - std::stack> recursion_stack; - recursion_stack.emplace(s, t); - - std::pair edge; - while (!recursion_stack.empty()) - { - edge = recursion_stack.top(); - recursion_stack.pop(); - - EdgeID smaller_edge_id = SPECIAL_EDGEID; - EdgeWeight edge_weight = std::numeric_limits::max(); - for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.first)) - { - const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; - if ((facade->GetTarget(edge_id) == edge.second) && (weight < edge_weight) && - facade->GetEdgeData(edge_id).forward) - { - smaller_edge_id = edge_id; - edge_weight = weight; - } - } - - if (SPECIAL_EDGEID == smaller_edge_id) - { - for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.second)) - { - const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; - if ((facade->GetTarget(edge_id) == edge.first) && (weight < edge_weight) && - facade->GetEdgeData(edge_id).backward) - { - smaller_edge_id = edge_id; - edge_weight = weight; - } - } - } - BOOST_ASSERT_MSG(edge_weight != std::numeric_limits::max(), - "edge weight invalid"); - - const EdgeData &ed = facade->GetEdgeData(smaller_edge_id); - if (ed.shortcut) - { // unpack - const NodeID middle_node_id = ed.id; - // again, we need to this in reversed order - recursion_stack.emplace(middle_node_id, edge.second); - recursion_stack.emplace(edge.first, middle_node_id); - } - else - { - BOOST_ASSERT_MSG(!ed.shortcut, "edge must be shortcut"); - unpacked_path.emplace_back(edge.first); - } - } - unpacked_path.emplace_back(t); - } - /** - * A duplicate of the above `UnpackEdge` function, but returning full EdgeData - * objects for the unpacked path. Used in the tile plugin to find outgoing - * edges from a given turn. + * Unpacks a single edge (NodeID->NodeID) from the CH graph down to it's original non-shortcut + * route. + * @param from the node the CH edge starts at + * @param to the node the CH edge finishes at + * @param unpacked_path the sequence of original NodeIDs that make up the expanded CH edge */ - - void - UnpackEdgeToEdges(const NodeID s, const NodeID t, std::vector &unpacked_path) const + void UnpackEdge(const NodeID from, const NodeID to, std::vector &unpacked_path) const { - std::stack> recursion_stack; - recursion_stack.emplace(s, t); - - std::pair edge; - while (!recursion_stack.empty()) - { - edge = recursion_stack.top(); - recursion_stack.pop(); - - EdgeID smaller_edge_id = SPECIAL_EDGEID; - EdgeWeight edge_weight = std::numeric_limits::max(); - for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.first)) - { - const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; - if ((facade->GetTarget(edge_id) == edge.second) && (weight < edge_weight) && - facade->GetEdgeData(edge_id).forward) - { - smaller_edge_id = edge_id; - edge_weight = weight; - } - } - - if (SPECIAL_EDGEID == smaller_edge_id) - { - for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.second)) - { - const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; - if ((facade->GetTarget(edge_id) == edge.first) && (weight < edge_weight) && - facade->GetEdgeData(edge_id).backward) - { - smaller_edge_id = edge_id; - edge_weight = weight; - } - } - } - BOOST_ASSERT_MSG(edge_weight != std::numeric_limits::max(), - "edge weight invalid"); - - const EdgeData &ed = facade->GetEdgeData(smaller_edge_id); - if (ed.shortcut) - { // unpack - const NodeID middle_node_id = ed.id; - // again, we need to this in reversed order - recursion_stack.emplace(middle_node_id, edge.second); - recursion_stack.emplace(edge.first, middle_node_id); - } - else - { - BOOST_ASSERT_MSG(!ed.shortcut, "edge must be shortcut"); - unpacked_path.emplace_back(ed); - } - } + std::array path{{from, to}}; + UnpackCHEdge( + facade, + path.begin(), + path.end(), + [&unpacked_path](const std::pair &edge, const EdgeData & /* data */) { + unpacked_path.emplace_back(edge.first); + }); + unpacked_path.emplace_back(to); } void RetrievePackedPathFromHeap(const SearchEngineData::QueryHeap &forward_heap, diff --git a/include/util/static_graph.hpp b/include/util/static_graph.hpp index 7b4fa8ecb..86ea85b17 100644 --- a/include/util/static_graph.hpp +++ b/include/util/static_graph.hpp @@ -148,19 +148,32 @@ template class StaticGraph return SPECIAL_EDGEID; } - // searches for a specific edge - EdgeIterator FindSmallestEdge(const NodeIterator from, const NodeIterator to) const + /** + * Finds the edge with the smallest `.distance` going from `from` to `to` + * @param from the source node ID + * @param to the target node ID + * @param filter a functor that returns a `bool` that determines whether an edge should be + * tested or not. + * Takes `EdgeData` as a parameter. + * @return the ID of the smallest edge if any were found that satisfied *filter*, or + * `SPECIAL_EDGEID` if no + * matching edge is found. + */ + template + EdgeIterator FindSmallestEdge(const NodeIterator from, + const NodeIterator to, + const FilterFunction filter) const { EdgeIterator smallest_edge = SPECIAL_EDGEID; EdgeWeight smallest_weight = INVALID_EDGE_WEIGHT; for (auto edge : GetAdjacentEdgeRange(from)) { const NodeID target = GetTarget(edge); - const EdgeWeight weight = GetEdgeData(edge).distance; - if (target == to && weight < smallest_weight) + const auto data = GetEdgeData(edge); + if (target == to && data.distance < smallest_weight && filter(data)) { smallest_edge = edge; - smallest_weight = weight; + smallest_weight = data.distance; } } return smallest_edge; diff --git a/src/engine/plugins/tile.cpp b/src/engine/plugins/tile.cpp index 2595cae2a..f6af57880 100644 --- a/src/engine/plugins/tile.cpp +++ b/src/engine/plugins/tile.cpp @@ -1,5 +1,6 @@ #include "engine/plugins/tile.hpp" #include "engine/plugins/plugin_base.hpp" +#include "engine/edge_unpacker.hpp" #include "util/coordinate_calculation.hpp" #include "util/vector_tile.hpp" @@ -13,11 +14,11 @@ #include #include +#include +#include #include #include #include -#include -#include #include #include @@ -211,6 +212,27 @@ FixedPoint coordinatesToTilePoint(const util::Coordinate point, const detail::BB return FixedPoint{px, py}; } + +/** + * Unpacks a single CH edge (NodeID->NodeID) down to the original edges, and returns a list of the edge data + * @param from the node the CH edge starts at + * @param to the node the CH edge finishes at + * @param unpacked_path the sequence of EdgeData objects along the unpacked path + */ +void UnpackEdgeToEdges(const datafacade::BaseDataFacade &facade, + const NodeID from, + const NodeID to, + std::vector &unpacked_path) +{ + std::array path{{from, to}}; + UnpackCHEdge( + &facade, + path.begin(), + path.end(), + [&unpacked_path](const std::pair & /* edge */, const datafacade::BaseDataFacade::EdgeData &data) { + unpacked_path.emplace_back(data); + }); +} } Status TilePlugin::HandleRequest(const api::TileParameters ¶meters, std::string &pbf_buffer) @@ -236,13 +258,11 @@ Status TilePlugin::HandleRequest(const api::TileParameters ¶meters, std::str std::vector names; std::unordered_map name_offsets; - std::vector used_point_ints; std::unordered_map point_int_offsets; std::vector> all_turn_data; - const auto use_line_value = [&used_line_ints, &line_int_offsets](const int &value) - { + 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()) @@ -254,8 +274,7 @@ Status TilePlugin::HandleRequest(const api::TileParameters ¶meters, std::str return; }; - const auto use_point_value = [&used_point_ints, &point_int_offsets](const int &value) - { + const auto use_point_value = [&used_point_ints, &point_int_offsets](const int &value) { const auto found = point_int_offsets.find(value); std::size_t offset; @@ -336,9 +355,10 @@ Status TilePlugin::HandleRequest(const api::TileParameters ¶meters, std::str continue; } - routing_base.UnpackEdgeToEdges(edge.forward_segment_id.id, - facade.GetTarget(adj_shortcut), - unpacked_shortcut); + detail::UnpackEdgeToEdges(facade, + edge.forward_segment_id.id, + facade.GetTarget(adj_shortcut), + unpacked_shortcut); // Sometimes a "shortcut" is just an edge itself: this will not return a turn if (unpacked_shortcut.size() < 2) @@ -376,8 +396,14 @@ Status TilePlugin::HandleRequest(const api::TileParameters ¶meters, std::str util::coordinate_calculation::bearing(coord_b, coord_c)); auto turn_angle = c_bearing - angle_in; - while (turn_angle > 180) { turn_angle -= 360; } - while (turn_angle < -180) { turn_angle += 360; } + while (turn_angle > 180) + { + turn_angle -= 360; + } + while (turn_angle < -180) + { + turn_angle += 360; + } const auto turn_angle_offset = use_point_value(turn_angle); const auto angle_weight_offset = use_point_value(possible_next_node.second); @@ -414,7 +440,6 @@ Status TilePlugin::HandleRequest(const api::TileParameters ¶meters, std::str max_datasource_id = std::max(max_datasource_id, forward_datasource); max_datasource_id = std::max(max_datasource_id, reverse_datasource); - std::string name = facade.GetNameForID(edge.name_id); if (name_offsets.find(name) == name_offsets.end()) { @@ -507,65 +532,64 @@ Status TilePlugin::HandleRequest(const api::TileParameters ¶meters, std::str max_datasource_id = std::max(max_datasource_id, forward_datasource); max_datasource_id = std::max(max_datasource_id, reverse_datasource); - const auto encode_tile_line = [&line_layer_writer, - &edge, - &id, - &max_datasource_id, - &used_line_ints](const detail::FixedLine &tile_line, - const std::uint32_t speed_kmh, - const std::size_t duration, - const DatasourceID datasource, - const std::size_t name, - std::int32_t &start_x, - std::int32_t &start_y) - { - // Here, we save the two attributes for our feature: the speed and the - // is_small - // boolean. We only serve up speeds from 0-139, so all we do is save the - // first - protozero::pbf_writer feature_writer(line_layer_writer, - util::vector_tile::FEATURE_TAG); - // Field 3 is the "geometry type" field. Value 2 is "line" - feature_writer.add_enum( - util::vector_tile::GEOMETRY_TAG, - util::vector_tile::GEOMETRY_TYPE_LINE); // geometry type - // Field 1 for the feature is the "id" field. - feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id - { - // When adding attributes to a feature, we have to write - // pairs of numbers. The first value is the index in the - // keys array (written later), and the second value is the - // index into the "values" array (also written later). We're - // not writing the actual speed or bool value here, we're saving - // an index into the "values" array. This means many features - // can share the same value data, leading to smaller tiles. - protozero::packed_field_uint32 field( - feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG); + const auto encode_tile_line = + [&line_layer_writer, &edge, &id, &max_datasource_id, &used_line_ints]( + const detail::FixedLine &tile_line, + const std::uint32_t speed_kmh, + const std::size_t duration, + const DatasourceID datasource, + const std::size_t name, + std::int32_t &start_x, + std::int32_t &start_y) { + // Here, we save the two attributes for our feature: the speed and the + // is_small + // boolean. We only serve up speeds from 0-139, so all we do is save + // the + // first + protozero::pbf_writer feature_writer(line_layer_writer, + util::vector_tile::FEATURE_TAG); + // Field 3 is the "geometry type" field. Value 2 is "line" + feature_writer.add_enum( + util::vector_tile::GEOMETRY_TAG, + util::vector_tile::GEOMETRY_TYPE_LINE); // geometry type + // Field 1 for the feature is the "id" field. + feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id + { + // When adding attributes to a feature, we have to write + // pairs of numbers. The first value is the index in the + // keys array (written later), and the second value is the + // index into the "values" array (also written later). We're + // not writing the actual speed or bool value here, we're saving + // an index into the "values" array. This means many features + // can share the same value data, leading to smaller tiles. + protozero::packed_field_uint32 field( + feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG); - field.add_element(0); // "speed" tag key offset - field.add_element( - std::min(speed_kmh, 127u)); // save the speed value, capped at 127 - field.add_element(1); // "is_small" tag key offset - field.add_element(128 + - (edge.component.is_tiny ? 0 : 1)); // is_small feature - field.add_element(2); // "datasource" tag key offset - field.add_element(130 + datasource); // datasource value offset - field.add_element(3); // "duration" tag key offset - field.add_element(130 + max_datasource_id + 1 + - duration); // duration value offset - field.add_element(4); // "name" tag key offset + field.add_element(0); // "speed" tag key offset + field.add_element(std::min( + speed_kmh, 127u)); // save the speed value, capped at 127 + field.add_element(1); // "is_small" tag key offset + field.add_element( + 128 + (edge.component.is_tiny ? 0 : 1)); // is_small feature + field.add_element(2); // "datasource" tag key offset + field.add_element(130 + datasource); // datasource value offset + field.add_element(3); // "duration" tag key offset + field.add_element(130 + max_datasource_id + 1 + + duration); // duration value offset + field.add_element(4); // "name" tag key offset - field.add_element(130 + max_datasource_id + 1 + used_line_ints.size() + - name); // name value offset - } - { + field.add_element(130 + max_datasource_id + 1 + + used_line_ints.size() + + name); // name value offset + } + { - // Encode the geometry for the feature - protozero::packed_field_uint32 geometry( - feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG); - encodeLinestring(tile_line, geometry, start_x, start_y); - } - }; + // Encode the geometry for the feature + protozero::packed_field_uint32 geometry( + feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG); + encodeLinestring(tile_line, geometry, start_x, start_y); + } + }; // If this is a valid forward edge, go ahead and add it to the tile if (forward_weight != 0 && edge.forward_segment_id.enabled) @@ -668,9 +692,11 @@ Status TilePlugin::HandleRequest(const api::TileParameters ¶meters, std::str values_writer.add_double(util::vector_tile::VARIANT_TYPE_DOUBLE, value / 10.); } - for (const auto &name : names) { + for (const auto &name : names) + { // Writing field type 4 == variant type - protozero::pbf_writer values_writer(line_layer_writer, util::vector_tile::VARIANT_TAG); + protozero::pbf_writer values_writer(line_layer_writer, + util::vector_tile::VARIANT_TAG); // Attribute value 1 == string type values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING, name); } @@ -716,34 +742,33 @@ Status TilePlugin::HandleRequest(const api::TileParameters ¶meters, std::str const auto encode_tile_point = [&point_layer_writer, &edge, &id](const detail::FixedPoint &tile_point, - const detail::TurnData &point_turn_data) - { - protozero::pbf_writer feature_writer(point_layer_writer, - util::vector_tile::FEATURE_TAG); - // Field 3 is the "geometry type" field. Value 1 is "point" - feature_writer.add_enum( - util::vector_tile::GEOMETRY_TAG, - util::vector_tile::GEOMETRY_TYPE_POINT); // geometry type - // Field 1 for the feature is the "id" field. - feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id - { - // See above for explanation - protozero::packed_field_uint32 field( - feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG); + const detail::TurnData &point_turn_data) { + protozero::pbf_writer feature_writer(point_layer_writer, + util::vector_tile::FEATURE_TAG); + // Field 3 is the "geometry type" field. Value 1 is "point" + feature_writer.add_enum( + util::vector_tile::GEOMETRY_TAG, + util::vector_tile::GEOMETRY_TYPE_POINT); // geometry type + // Field 1 for the feature is the "id" field. + feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id + { + // See above for explanation + 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(point_turn_data.in_angle_offset); - field.add_element(1); // "turn_angle" tag key offset - field.add_element(point_turn_data.turn_angle_offset); - field.add_element(2); // "weight" tag key offset - field.add_element(point_turn_data.weight_offset); - } - { - protozero::packed_field_uint32 geometry( - feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG); - encodePoint(tile_point, geometry); - } - }; + field.add_element(0); // "bearing_in" tag key offset + field.add_element(point_turn_data.in_angle_offset); + field.add_element(1); // "turn_angle" tag key offset + field.add_element(point_turn_data.turn_angle_offset); + field.add_element(2); // "weight" tag key offset + field.add_element(point_turn_data.weight_offset); + } + { + protozero::packed_field_uint32 geometry( + feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG); + encodePoint(tile_point, geometry); + } + }; const auto turn_coordinate = facade.GetCoordinateOfNode(edge.v); const auto tile_point = coordinatesToTilePoint(turn_coordinate, tile_bbox);