diff --git a/include/util/vector_tile.hpp b/include/util/vector_tile.hpp index f3c05791ef7..d3d4978b11a 100644 --- a/include/util/vector_tile.hpp +++ b/include/util/vector_tile.hpp @@ -9,31 +9,6 @@ namespace util { namespace vector_tile { - -const constexpr std::uint32_t ID_TAG = 1; -const constexpr std::uint32_t NAME_TAG = 1; -const constexpr std::uint32_t FEATURE_TAG = 2; -const constexpr std::uint32_t LAYER_TAG = 3; -const constexpr std::uint32_t GEOMETRY_TAG = 3; -const constexpr std::uint32_t KEY_TAG = 3; -const constexpr std::uint32_t VARIANT_TAG = 4; -const constexpr std::uint32_t EXTENT_TAG = 5; -const constexpr std::uint32_t VERSION_TAG = 15; - -const constexpr std::uint32_t FEATURE_ATTRIBUTES_TAG = 2; -const constexpr std::uint32_t FEATURE_GEOMETRIES_TAG = 4; - -const constexpr std::uint32_t GEOMETRY_TYPE_POINT = 1; -const constexpr std::uint32_t GEOMETRY_TYPE_LINE = 2; - -const constexpr std::uint32_t VARIANT_TYPE_STRING = 1; -const constexpr std::uint32_t VARIANT_TYPE_FLOAT = 2; -const constexpr std::uint32_t VARIANT_TYPE_DOUBLE = 3; - -const constexpr std::uint32_t VARIANT_TYPE_UINT64 = 5; -const constexpr std::uint32_t VARIANT_TYPE_SINT64 = 6; -const constexpr std::uint32_t VARIANT_TYPE_BOOL = 7; - // Vector tiles are 4096 virtual pixels on each side const constexpr double EXTENT = 4096.0; const constexpr double BUFFER = 128.0; diff --git a/src/engine/plugins/tile.cpp b/src/engine/plugins/tile.cpp index 46a1493dd4b..dc561fd7cee 100644 --- a/src/engine/plugins/tile.cpp +++ b/src/engine/plugins/tile.cpp @@ -1,5 +1,6 @@ #include "guidance/turn_instruction.hpp" +#include "engine/plugins/plugin_base.hpp" #include "engine/plugins/plugin_base.hpp" #include "engine/plugins/tile.hpp" @@ -8,21 +9,19 @@ #include "util/vector_tile.hpp" #include "util/web_mercator.hpp" -#include "engine/api/json_factory.hpp" - #include #include #include #include -#include -#include +#include +#include +#include #include #include #include #include -#include #include #include @@ -41,45 +40,7 @@ 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. - // Simple container class for WGS84 coordinates template struct Point final { @@ -132,58 +93,15 @@ const static box_t clip_box(point_t(-util::vector_tile::BUFFER, -util::vector_ti point_t(util::vector_tile::EXTENT + util::vector_tile::BUFFER, util::vector_tile::EXTENT + util::vector_tile::BUFFER)); -// from mapnik-vector-tile -// Encodes a linestring using protobuf zigzag encoding -inline bool encodeLinestring(const FixedLine &line, - protozero::packed_field_uint32 &geometry, - std::int32_t &start_x, - std::int32_t &start_y) -{ - const std::size_t line_size = line.size(); - if (line_size < 2) - { - return false; - } - - const unsigned lineto_count = static_cast(line_size) - 1; - - auto pt = line.begin(); - const constexpr int MOVETO_COMMAND = 9; - geometry.add_element(MOVETO_COMMAND); // move_to | (1 << 3) - geometry.add_element(protozero::encode_zigzag32(pt->x - start_x)); - geometry.add_element(protozero::encode_zigzag32(pt->y - start_y)); - start_x = pt->x; - start_y = pt->y; - // This means LINETO repeated N times - // See: https://github.com/mapbox/vector-tile-spec/tree/master/2.1#example-command-integers - geometry.add_element((lineto_count << 3u) | 2u); - // Now that we've issued the LINETO REPEAT N command, we append - // N coordinate pairs immediately after the command. - for (++pt; pt != line.end(); ++pt) - { - const std::int32_t dx = pt->x - start_x; - const std::int32_t dy = pt->y - start_y; - geometry.add_element(protozero::encode_zigzag32(dx)); - geometry.add_element(protozero::encode_zigzag32(dy)); - start_x = pt->x; - start_y = pt->y; - } - return true; -} - -// from mapnik-vctor-tile -// Encodes a point -inline void encodePoint(const FixedPoint &pt, protozero::packed_field_uint32 &geometry) -{ - const constexpr int MOVETO_COMMAND = 9; - geometry.add_element(MOVETO_COMMAND); - const std::int32_t dx = pt.x; - const std::int32_t dy = pt.y; - // Manual zigzag encoding. - geometry.add_element(protozero::encode_zigzag32(dx)); - geometry.add_element(protozero::encode_zigzag32(dy)); -} - +/** + * Return the x1,y1,x2,y2 pixel coordinates of a line in a given + * tile. + * + * @param start the first coordinate of the line + * @param target the last coordinate of the line + * @param tile_bbox the boundaries of the tile, in mercator coordinates + * @return a FixedLine with coordinates relative to the tile_bbox. + */ linestring_t floatLineToTileLine(const FloatLine &geo_line, const BBox &tile_bbox) { linestring_t unclipped_line; @@ -361,6 +279,144 @@ std::vector getSegregatedNodes(const DataFacadeBase &facade, return result; } +struct SpeedLayer : public vtzero::layer_builder +{ + + vtzero::value_index_small_uint uint_index; + vtzero::value_index double_index; + vtzero::value_index_internal string_index; + vtzero::value_index_bool bool_index; + + vtzero::index_value key_speed; + vtzero::index_value key_is_small; + vtzero::index_value key_datasource; + vtzero::index_value key_weight; + vtzero::index_value key_duration; + vtzero::index_value key_name; + vtzero::index_value key_rate; + + SpeedLayer(vtzero::tile_builder &tile) + : layer_builder(tile, "speeds"), uint_index(*this), double_index(*this), + string_index(*this), bool_index(*this), key_speed(add_key_without_dup_check("speed")), + key_is_small(add_key_without_dup_check("is_small")), + key_datasource(add_key_without_dup_check("datasource")), + key_weight(add_key_without_dup_check("weight")), + key_duration(add_key_without_dup_check("duration")), + key_name(add_key_without_dup_check("name")), key_rate(add_key_without_dup_check("rate")) + { + } + +}; // struct SpeedLayer + +class SpeedLayerFeatureBuilder : public vtzero::linestring_feature_builder +{ + + SpeedLayer &m_layer; + + public: + SpeedLayerFeatureBuilder(SpeedLayer &layer, uint64_t id) + : vtzero::linestring_feature_builder(layer), m_layer(layer) + { + set_id(id); + } + + void set_speed(unsigned int value) + { + add_property(m_layer.key_speed, m_layer.uint_index(std::min(value, 127u))); + } + + void set_is_small(bool value) { add_property(m_layer.key_is_small, m_layer.bool_index(value)); } + + void set_datasource(const std::string &value) + { + add_property(m_layer.key_datasource, + m_layer.string_index(vtzero::encoded_property_value{value})); + } + + void set_weight(double value) { add_property(m_layer.key_weight, m_layer.double_index(value)); } + + void set_duration(double value) + { + add_property(m_layer.key_duration, m_layer.double_index(value)); + } + + void set_name(const boost::string_ref &value) + { + add_property( + m_layer.key_name, + m_layer.string_index(vtzero::encoded_property_value{value.data(), value.size()})); + } + + void set_rate(double value) { add_property(m_layer.key_rate, m_layer.double_index(value)); } + +}; // class SpeedLayerFeatureBuilder + +struct TurnsLayer : public vtzero::layer_builder +{ + + vtzero::value_index int_index; + vtzero::value_index float_index; + vtzero::value_index_internal string_index; + + vtzero::index_value key_bearing_in; + vtzero::index_value key_turn_angle; + vtzero::index_value key_cost; + vtzero::index_value key_weight; + vtzero::index_value key_turn_type; + vtzero::index_value key_turn_modifier; + + TurnsLayer(vtzero::tile_builder &tile) + : layer_builder(tile, "turns"), int_index(*this), float_index(*this), string_index(*this), + key_bearing_in(add_key_without_dup_check("bearing_in")), + key_turn_angle(add_key_without_dup_check("turn_angle")), + key_cost(add_key_without_dup_check("cost")), + key_weight(add_key_without_dup_check("weight")), + key_turn_type(add_key_without_dup_check("type")), + key_turn_modifier(add_key_without_dup_check("modifier")) + { + } + +}; // struct TurnsLayer + +class TurnsLayerFeatureBuilder : public vtzero::point_feature_builder +{ + + TurnsLayer &m_layer; + + public: + TurnsLayerFeatureBuilder(TurnsLayer &layer, uint64_t id) + : vtzero::point_feature_builder(layer), m_layer(layer) + { + set_id(id); + } + + void set_bearing_in(int value) + { + add_property(m_layer.key_bearing_in, m_layer.int_index(value)); + } + + void set_turn_angle(int value) + { + add_property(m_layer.key_turn_angle, m_layer.int_index(value)); + } + + void set_cost(float value) { add_property(m_layer.key_cost, m_layer.float_index(value)); } + + void set_weight(float value) { add_property(m_layer.key_weight, m_layer.float_index(value)); } + + void set_turn(osrm::guidance::TurnInstruction value) + { + const auto type = osrm::guidance::internalInstructionTypeToString(value.type); + const auto modifier = osrm::guidance::instructionModifierToString(value.direction_modifier); + add_property( + m_layer.key_turn_type, + m_layer.string_index(vtzero::encoded_property_value{type.data(), type.size()})); + add_property( + m_layer.key_turn_modifier, + m_layer.string_index(vtzero::encoded_property_value{modifier.data(), modifier.size()})); + } +}; // class TurnsLayerFeatureBuilder + void encodeVectorTile(const DataFacadeBase &facade, unsigned x, unsigned y, @@ -371,117 +427,25 @@ void encodeVectorTile(const DataFacadeBase &facade, const std::vector &segregated_nodes, std::string &pbf_buffer) { - - std::uint8_t max_datasource_id = 0; - - // 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; + vtzero::tile_builder tile; const auto get_geometry_id = [&facade](auto edge) { return facade.GetGeometryIndex(edge.forward_segment_id.id).id; }; - // Vector tiles encode feature properties as indexes into a lookup table. So, we need - // to "pre-loop" over all the edges to create the lookup tables. Once we have those, we - // can then encode the features, and we'll know the indexes that feature properties - // need to refer to. - for (const auto &edge_index : sorted_edge_indexes) - { - const auto &edge = edges[edge_index]; - - const auto geometry_id = get_geometry_id(edge); - const auto forward_datasource_range = facade.GetUncompressedForwardDatasources(geometry_id); - const auto reverse_datasource_range = facade.GetUncompressedReverseDatasources(geometry_id); - - BOOST_ASSERT(edge.fwd_segment_position < forward_datasource_range.size()); - const auto forward_datasource = forward_datasource_range(edge.fwd_segment_position); - BOOST_ASSERT(edge.fwd_segment_position < reverse_datasource_range.size()); - const auto reverse_datasource = reverse_datasource_range(reverse_datasource_range.size() - - edge.fwd_segment_position - 1); - - // Keep track of the highest datasource seen so that we don't write unnecessary - // data to the layer attribute values - max_datasource_id = std::max(max_datasource_id, forward_datasource); - max_datasource_id = std::max(max_datasource_id, reverse_datasource); - } - // Convert tile coordinates into mercator coordinates double min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat; util::web_mercator::xyzToMercator( x, y, z, min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat); const BBox tile_bbox{min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat}; - // Protobuf serializes blocks when objects go out of scope, hence - // the extra scoping below. - protozero::pbf_writer tile_writer{pbf_buffer}; + // XXX leaving in some superfluous scopes to make diff easier to read. { { - // Add a layer object to the PBF stream. 3=='layer' from the vector tile spec - // (2.1) - protozero::pbf_writer line_layer_writer(tile_writer, util::vector_tile::LAYER_TAG); - // TODO: don't write a layer if there are no features - - line_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version - // Field 1 is the "layer name" field, it's a string - line_layer_writer.add_string(util::vector_tile::NAME_TAG, "speeds"); // name - // Field 5 is the tile extent. It's a uint32 and should be set to 4096 - // for normal vector tiles. - line_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG, - util::vector_tile::EXTENT); // extent - - // Because we need to know the indexes into the vector tile lookup table, - // we need to do an initial pass over the data and create the complete - // index of used values. - for (const auto &edge_index : sorted_edge_indexes) - { - const auto &edge = edges[edge_index]; - const auto geometry_id = get_geometry_id(edge); - - // Get coordinates for start/end nodes of segment (NodeIDs u and v) - const auto a = facade.GetCoordinateOfNode(edge.u); - const auto b = facade.GetCoordinateOfNode(edge.v); - // Calculate the length in meters - const double length = osrm::util::coordinate_calculation::haversineDistance(a, b); - - // Weight values - const auto forward_weight_range = facade.GetUncompressedForwardWeights(geometry_id); - const auto reverse_weight_range = facade.GetUncompressedReverseWeights(geometry_id); - const auto forward_weight = forward_weight_range[edge.fwd_segment_position]; - const auto reverse_weight = reverse_weight_range[reverse_weight_range.size() - - edge.fwd_segment_position - 1]; - 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.)); - - line_int_index.add(forward_rate); - line_int_index.add(reverse_rate); - - // Duration values - const auto forward_duration_range = - facade.GetUncompressedForwardDurations(geometry_id); - const auto reverse_duration_range = - facade.GetUncompressedReverseDurations(geometry_id); - const auto forward_duration = forward_duration_range[edge.fwd_segment_position]; - const auto reverse_duration = reverse_duration_range[reverse_duration_range.size() - - edge.fwd_segment_position - 1]; - - line_int_index.add(forward_duration); - line_int_index.add(reverse_duration); - } - // Begin the layer features block { + SpeedLayer speeds_layer{tile}; + // Each feature gets a unique id, starting at 1 unsigned id = 1; for (const auto &edge_index : sorted_edge_indexes) @@ -526,84 +490,10 @@ void encodeVectorTile(const DataFacadeBase &facade, const auto name_id = facade.GetNameIndex(edge.forward_segment_id.id); auto name = facade.GetNameForID(name_id); - line_string_index.add(name); - - const auto encode_tile_line = [&line_layer_writer, - &component_id, - &id, - &max_datasource_id, - &line_int_index]( - const FixedLine &tile_line, - const std::uint32_t speed_kmh_idx, - const std::uint32_t rate_idx, - const std::size_t weight_idx, - const std::size_t duration_idx, - const DatasourceID datasource_idx, - const std::size_t name_idx, - 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_idx, 127u)); // save the speed value, capped at 127 - field.add_element(1); // "is_small" tag key offset - field.add_element( - 128 + (component_id.is_tiny ? 0 : 1)); // is_small feature offset - field.add_element(2); // "datasource" tag key offset - field.add_element(130 + datasource_idx); // datasource value offset - field.add_element(3); // "weight" tag key offset - field.add_element(130 + max_datasource_id + 1 + - weight_idx); // weight value offset - field.add_element(4); // "duration" tag key offset - field.add_element(130 + max_datasource_id + 1 + - duration_idx); // duration value offset - field.add_element(5); // "name" tag key 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); - } - { - - // 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_duration != 0 && edge.forward_segment_id.enabled) { - std::int32_t start_x = 0; - std::int32_t start_y = 0; - // Calculate the speed for this line - // Speeds are looked up in a simple 1:1 table, so the speed value == lookup - // table index std::uint32_t speed_kmh_idx = static_cast(round(length / forward_duration * 10 * 3.6)); @@ -616,15 +506,19 @@ void encodeVectorTile(const DataFacadeBase &facade, auto tile_line = coordinatesToTileLine(a, b, tile_bbox); if (!tile_line.empty()) { - encode_tile_line(tile_line, - speed_kmh_idx, - line_int_index.indexOf(forward_rate), - line_int_index.indexOf(forward_weight), - line_int_index.indexOf(forward_duration), - forward_datasource_idx, - line_string_index.indexOf(name), - start_x, - start_y); + SpeedLayerFeatureBuilder fbuilder{speeds_layer, id}; + fbuilder.add_linestring_from_container(tile_line); + + fbuilder.set_speed(speed_kmh_idx); + fbuilder.set_is_small(component_id.is_tiny); + fbuilder.set_datasource( + facade.GetDatasourceName(forward_datasource_idx).to_string()); + fbuilder.set_weight(forward_weight / 10.0); + fbuilder.set_duration(forward_duration / 10.0); + fbuilder.set_name(name); + fbuilder.set_rate(forward_rate / 10.0); + + fbuilder.commit(); } } @@ -632,12 +526,7 @@ void encodeVectorTile(const DataFacadeBase &facade, // properties if (reverse_duration != 0 && edge.reverse_segment_id.enabled) { - std::int32_t start_x = 0; - std::int32_t start_y = 0; - // Calculate the speed for this line - // Speeds are looked up in a simple 1:1 table, so the speed value == lookup - // table index std::uint32_t speed_kmh_idx = static_cast(round(length / reverse_duration * 10 * 3.6)); @@ -650,232 +539,52 @@ void encodeVectorTile(const DataFacadeBase &facade, auto tile_line = coordinatesToTileLine(b, a, tile_bbox); if (!tile_line.empty()) { - encode_tile_line(tile_line, - speed_kmh_idx, - line_int_index.indexOf(reverse_rate), - line_int_index.indexOf(reverse_weight), - line_int_index.indexOf(reverse_duration), - reverse_datasource_idx, - line_string_index.indexOf(name), - start_x, - start_y); + SpeedLayerFeatureBuilder fbuilder{speeds_layer, id}; + fbuilder.add_linestring_from_container(tile_line); + + fbuilder.set_speed(speed_kmh_idx); + fbuilder.set_is_small(component_id.is_tiny); + fbuilder.set_datasource( + facade.GetDatasourceName(reverse_datasource_idx).to_string()); + fbuilder.set_weight(reverse_weight / 10.0); + fbuilder.set_duration(reverse_duration / 10.0); + fbuilder.set_name(name); + fbuilder.set_rate(reverse_rate / 10.0); + + fbuilder.commit(); } } } } - - // Field id 3 is the "keys" attribute - // We need two "key" fields, these are referred to with 0 and 1 (their array - // indexes) earlier - line_layer_writer.add_string(util::vector_tile::KEY_TAG, "speed"); - line_layer_writer.add_string(util::vector_tile::KEY_TAG, "is_small"); - line_layer_writer.add_string(util::vector_tile::KEY_TAG, "datasource"); - line_layer_writer.add_string(util::vector_tile::KEY_TAG, "weight"); - line_layer_writer.add_string(util::vector_tile::KEY_TAG, "duration"); - line_layer_writer.add_string(util::vector_tile::KEY_TAG, "name"); - line_layer_writer.add_string(util::vector_tile::KEY_TAG, "rate"); - - // Now, we write out the possible speed value arrays and possible is_tiny - // values. Field type 4 is the "values" field. It's a variable type field, - // so requires a two-step write (create the field, then write its value) - for (std::size_t i = 0; i < 128; i++) - { - // Writing field type 4 == variant type - protozero::pbf_writer values_writer(line_layer_writer, - util::vector_tile::VARIANT_TAG); - // Attribute value 5 == uint64 type - values_writer.add_uint64(util::vector_tile::VARIANT_TYPE_UINT64, i); - } - { - protozero::pbf_writer values_writer(line_layer_writer, - util::vector_tile::VARIANT_TAG); - // Attribute value 7 == bool type - values_writer.add_bool(util::vector_tile::VARIANT_TYPE_BOOL, true); - } - { - protozero::pbf_writer values_writer(line_layer_writer, - util::vector_tile::VARIANT_TAG); - // Attribute value 7 == bool type - values_writer.add_bool(util::vector_tile::VARIANT_TYPE_BOOL, false); - } - for (std::size_t i = 0; i <= max_datasource_id; i++) - { - // Writing field type 4 == variant type - 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, - facade.GetDatasourceName(i).to_string()); - } - for (auto value : line_int_index.values()) - { - // Writing field type 4 == variant type - protozero::pbf_writer values_writer(line_layer_writer, - util::vector_tile::VARIANT_TAG); - // Attribute value 2 == float type - // Durations come out of OSRM in integer deciseconds, so we convert them - // to seconds with a simple /10 for display - values_writer.add_double(util::vector_tile::VARIANT_TYPE_DOUBLE, value / 10.); - } - - for (const auto &name : line_string_index.values()) - { - // Writing field type 4 == variant type - 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.data(), name.size()); - } } // Only add the turn layer to the tile if it has some features (we sometimes won't // 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 = 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( - osrm::guidance::internalInstructionTypeToString(t.turn_instruction.type)); - auto turnmodifier_idx = - point_string_index.add(osrm::guidance::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 - // (2.1) - protozero::pbf_writer point_layer_writer(tile_writer, util::vector_tile::LAYER_TAG); - point_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version - point_layer_writer.add_string(util::vector_tile::NAME_TAG, "turns"); // name - point_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG, - util::vector_tile::EXTENT); // extent - - // Begin writing the set of point features + TurnsLayer turns_layer{tile}; + uint64_t id = 0; + for (const auto &turn_data : all_turn_data) { - // Start each features with an ID starting at 1 - int id = 1; - - // Helper function to encode a new point feature on a vector tile. - const auto encode_tile_point = [&](const FixedPoint &tile_point, - const auto &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 - feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id - { - // Write out the 4 properties we want on the feature. These - // refer to indexes in the properties lookup table, which we - // add to the tile after we add all features. - 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.angle_index); - field.add_element(1); // "turn_angle" tag key offset - field.add_element(point_turn_data.turn_index); - field.add_element(2); // "cost" tag key offset - field.add_element(point_int_index.size() + point_turn_data.duration_index); - field.add_element(3); // "weight" tag key offset - 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 - protozero::packed_field_uint32 geometry( - feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG); - encodePoint(tile_point, geometry); - } - }; - - // 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(turn_data.coordinate, tile_bbox); + if (boost::geometry::within(point_t(tile_point.x, tile_point.y), clip_box)) { - const auto tile_point = coordinatesToTilePoint(turndata.coordinate, tile_bbox); - if (!boost::geometry::within(point_t(tile_point.x, tile_point.y), clip_box)) - { - continue; - } - encode_tile_point(tile_point, turndata); - } - } + TurnsLayerFeatureBuilder fbuilder{turns_layer, ++id}; + fbuilder.add_point(tile_point); - // Add the names of the three attributes we added to all the turn penalty - // features previously. The indexes used there refer to these keys. - point_layer_writer.add_string(util::vector_tile::KEY_TAG, "bearing_in"); - 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 : 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 : 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); + fbuilder.set_bearing_in(turn_data.in_angle); + fbuilder.set_turn_angle(turn_data.turn_angle); + fbuilder.set_cost(turn_data.duration / 10.0); + fbuilder.set_weight(turn_data.weight / 10.0); + fbuilder.set_turn(turn_data.turn_instruction); + + fbuilder.commit(); + } } } // OSM Node tile layer { - protozero::pbf_writer point_layer_writer(tile_writer, util::vector_tile::LAYER_TAG); - point_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version - point_layer_writer.add_string(util::vector_tile::NAME_TAG, "osmnodes"); // name - point_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG, - util::vector_tile::EXTENT); // extent - std::vector internal_nodes; internal_nodes.reserve(edges.size() * 2); for (const auto &edge : edges) @@ -887,6 +596,8 @@ void encodeVectorTile(const DataFacadeBase &facade, auto new_end = std::unique(internal_nodes.begin(), internal_nodes.end()); internal_nodes.resize(new_end - internal_nodes.begin()); + vtzero::layer_builder osmnodes_layer{tile, "osmnodes"}; + for (const auto &internal_node : internal_nodes) { const auto coord = facade.GetCoordinateOfNode(internal_node); @@ -895,32 +606,19 @@ void encodeVectorTile(const DataFacadeBase &facade, { continue; } - 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 - const auto osmid = - static_cast(facade.GetOSMNodeIDOfNode(internal_node)); - feature_writer.add_uint64(util::vector_tile::ID_TAG, osmid); // id - // There are no additional properties, just the ID and the geometry - { - // Add the geometry as the last field in this feature - protozero::packed_field_uint32 geometry( - feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG); - encodePoint(tile_point, geometry); - } + + vtzero::point_feature_builder fbuilder{osmnodes_layer}; + fbuilder.set_id( + static_cast(facade.GetOSMNodeIDOfNode(internal_node))); + fbuilder.add_point(tile_point); + fbuilder.commit(); } } + // Internal nodes tile layer { - protozero::pbf_writer line_layer_writer(tile_writer, util::vector_tile::LAYER_TAG); - line_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version - line_layer_writer.add_string(util::vector_tile::NAME_TAG, "internal-nodes"); // name - line_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG, - util::vector_tile::EXTENT); // extent + vtzero::layer_builder internal_nodes_layer{tile, "internal-nodes"}; - unsigned id = 0; for (auto edgeNodeID : segregated_nodes) { auto const geomIndex = facade.GetGeometryIndex(edgeNodeID); @@ -937,46 +635,21 @@ void encodeVectorTile(const DataFacadeBase &facade, points.push_back(facade.GetCoordinateOfNode(nodeID)); } - const auto encode_tile_line = [&line_layer_writer, &id]( - const FixedLine &tile_line, std::int32_t &start_x, std::int32_t &start_y) { - - protozero::pbf_writer feature_writer(line_layer_writer, - util::vector_tile::FEATURE_TAG); - - feature_writer.add_enum(util::vector_tile::GEOMETRY_TAG, - util::vector_tile::GEOMETRY_TYPE_LINE); // geometry type - - feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id - { - - protozero::packed_field_uint32 field( - feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG); - } - { - - // 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); - } - }; - - std::int32_t start_x = 0; - std::int32_t start_y = 0; - auto tile_lines = coordinatesToTileLine(points, tile_bbox); if (!tile_lines.empty()) { - for (auto const &tl : tile_lines) + vtzero::linestring_feature_builder fbuilder{internal_nodes_layer}; + for (auto const &tile_line : tile_lines) { - encode_tile_line(tl, start_x, start_y); + fbuilder.add_linestring_from_container(tile_line); } + fbuilder.commit(); } } } } - // protozero serializes data during object destructors, so once the scope closes, - // our result buffer will have all the tile data encoded into it. + + tile.serialize(pbf_buffer); } } diff --git a/test/nodejs/constants.js b/test/nodejs/constants.js index bd3c454cb55..0726bc91c3d 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': 168571}; +exports.test_tile = {'at': [17059, 11948, 15], 'size': 148750}; // Test files generated by the routing engine; check test/data if (process.env.OSRM_DATA_PATH !== undefined) { diff --git a/unit_tests/library/tile.cpp b/unit_tests/library/tile.cpp index f46993f82e7..16c073d3425 100644 --- a/unit_tests/library/tile.cpp +++ b/unit_tests/library/tile.cpp @@ -14,252 +14,141 @@ #include "util/typedefs.hpp" #include "util/vector_tile.hpp" -#include +#include +#include + +#include #define CHECK_EQUAL_RANGE(R1, R2) \ BOOST_CHECK_EQUAL_COLLECTIONS(R1.begin(), R1.end(), R2.begin(), R2.end()); BOOST_AUTO_TEST_SUITE(tile) -void validate_value(protozero::pbf_reader value) +using variant_type = boost::variant; + +std::string to_string(const protozero::data_view &view) { - using namespace osrm; - while (value.next()) + return std::string{view.data(), view.size()}; +} + +void validate_feature_layer(vtzero::layer layer) +{ + BOOST_CHECK_EQUAL(layer.version(), 2); + BOOST_CHECK_EQUAL(to_string(layer.name()), "speeds"); + BOOST_CHECK_EQUAL(layer.extent(), osrm::util::vector_tile::EXTENT); + BOOST_CHECK_EQUAL(layer.key_table().size(), 7); + BOOST_CHECK(layer.num_features() > 2500); + + while (auto feature = layer.next_feature()) { - switch (value.tag()) - { - case util::vector_tile::VARIANT_TYPE_BOOL: - value.get_bool(); - break; - case util::vector_tile::VARIANT_TYPE_DOUBLE: - value.get_double(); - break; - case util::vector_tile::VARIANT_TYPE_FLOAT: - value.get_float(); - break; - case util::vector_tile::VARIANT_TYPE_STRING: - value.get_string(); - break; - case util::vector_tile::VARIANT_TYPE_UINT64: - value.get_uint64(); - break; - case util::vector_tile::VARIANT_TYPE_SINT64: - value.get_sint64(); - break; - } + BOOST_CHECK(feature.has_id()); + BOOST_CHECK(feature.geometry_type() == vtzero::GeomType::LINESTRING); + BOOST_CHECK(!feature.empty()); + + auto props = vtzero::create_properties_map>(feature); + + BOOST_CHECK(props.find("speed") != props.end()); + BOOST_CHECK(props["speed"].type() == typeid(uint64_t)); + + BOOST_CHECK(props.find("rate") != props.end()); + BOOST_CHECK(props["rate"].type() == typeid(double)); + + BOOST_CHECK(props.find("weight") != props.end()); + BOOST_CHECK(props["weight"].type() == typeid(double)); + + BOOST_CHECK(props.find("duration") != props.end()); + BOOST_CHECK(props["duration"].type() == typeid(double)); + + BOOST_CHECK(props.find("is_small") != props.end()); + BOOST_CHECK(props["is_small"].type() == typeid(bool)); + + BOOST_CHECK(props.find("datasource") != props.end()); + BOOST_CHECK(props["datasource"].type() == typeid(std::string)); + + BOOST_CHECK(props.find("name") != props.end()); + BOOST_CHECK(props["name"].type() == typeid(std::string)); } + + auto number_of_uint_values = + std::count_if(layer.value_table().begin(), layer.value_table().end(), [](auto v) { + return v.type() == vtzero::property_value_type::uint_value; + }); + BOOST_CHECK_EQUAL(number_of_uint_values, 77); } -void validate_feature_layer(protozero::pbf_reader &layer_message) +void validate_turn_layer(vtzero::layer layer) { - using namespace osrm; + BOOST_CHECK_EQUAL(layer.version(), 2); + BOOST_CHECK_EQUAL(to_string(layer.name()), "turns"); + BOOST_CHECK_EQUAL(layer.extent(), osrm::util::vector_tile::EXTENT); + BOOST_CHECK_EQUAL(layer.key_table().size(), 6); + BOOST_CHECK(layer.num_features() > 700); - const auto check_feature = [](protozero::pbf_reader feature_message) { - feature_message.next(); // advance parser to first entry - BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::GEOMETRY_TAG); - BOOST_CHECK_EQUAL(feature_message.get_enum(), util::vector_tile::GEOMETRY_TYPE_LINE); - - feature_message.next(); // advance to next entry - BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::ID_TAG); - feature_message.get_uint64(); // id - - feature_message.next(); // advance to next entry - BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::FEATURE_ATTRIBUTES_TAG); - // properties - auto property_iter_pair = feature_message.get_packed_uint32(); - auto value_begin = property_iter_pair.begin(); - auto value_end = property_iter_pair.end(); - BOOST_CHECK_EQUAL(std::distance(value_begin, value_end), 14); - auto iter = value_begin; - BOOST_CHECK_EQUAL(*iter++, 0); // speed key - BOOST_CHECK_LT(*iter++, 128); // speed value - BOOST_CHECK_EQUAL(*iter++, 1); // component key - // component value - BOOST_CHECK_GE(*iter, 128); - BOOST_CHECK_LE(*iter, 129); - iter++; - BOOST_CHECK_EQUAL(*iter++, 2); // data source key - *iter++; // skip value check, can be valud uint32 - BOOST_CHECK_EQUAL(*iter++, 3); // weight key - BOOST_CHECK_GT(*iter++, 130); // weight value - BOOST_CHECK_EQUAL(*iter++, 4); // duration key - BOOST_CHECK_GT(*iter++, 130); // duration value - // name - BOOST_CHECK_EQUAL(*iter++, 5); - BOOST_CHECK_GT(*iter++, 130); - // rate - BOOST_CHECK_EQUAL(*iter++, 6); - BOOST_CHECK_GT(*iter++, 130); - BOOST_CHECK(iter == value_end); - // geometry - feature_message.next(); - auto geometry_iter_pair = feature_message.get_packed_uint32(); - BOOST_CHECK_GT(std::distance(geometry_iter_pair.begin(), geometry_iter_pair.end()), 1); - }; - - auto number_of_speed_keys = 0u; - auto number_of_speed_values = 0u; - - while (layer_message.next()) + while (auto feature = layer.next_feature()) { - switch (layer_message.tag()) - { - case util::vector_tile::VERSION_TAG: - BOOST_CHECK_EQUAL(layer_message.get_uint32(), 2); - break; - case util::vector_tile::NAME_TAG: - BOOST_CHECK_EQUAL(layer_message.get_string(), "speeds"); - break; - case util::vector_tile::EXTENT_TAG: - BOOST_CHECK_EQUAL(layer_message.get_uint32(), util::vector_tile::EXTENT); - break; - case util::vector_tile::FEATURE_TAG: - check_feature(layer_message.get_message()); - break; - case util::vector_tile::KEY_TAG: - layer_message.get_string(); - number_of_speed_keys++; - break; - case util::vector_tile::VARIANT_TAG: - validate_value(layer_message.get_message()); - number_of_speed_values++; - break; - default: - BOOST_CHECK(false); // invalid tag - break; - } + BOOST_CHECK(feature.has_id()); + BOOST_CHECK(feature.geometry_type() == vtzero::GeomType::POINT); + BOOST_CHECK(!feature.empty()); + + auto props = vtzero::create_properties_map>(feature); + + BOOST_CHECK(props.find("bearing_in") != props.end()); + BOOST_CHECK(props["bearing_in"].type() == typeid(std::int64_t)); + + BOOST_CHECK(props.find("turn_angle") != props.end()); + BOOST_CHECK(props["turn_angle"].type() == typeid(std::int64_t)); + + BOOST_CHECK(props.find("weight") != props.end()); + BOOST_CHECK(props["weight"].type() == typeid(float)); + + BOOST_CHECK(props.find("cost") != props.end()); + BOOST_CHECK(props["cost"].type() == typeid(float)); + + BOOST_CHECK(props.find("type") != props.end()); + BOOST_CHECK(props["type"].type() == typeid(std::string)); + + BOOST_CHECK(props.find("modifier") != props.end()); + BOOST_CHECK(props["modifier"].type() == typeid(std::string)); } - BOOST_CHECK_EQUAL(number_of_speed_keys, 7); - BOOST_CHECK_GT(number_of_speed_values, 128); // speed value resolution + auto number_of_float_values = + std::count_if(layer.value_table().begin(), layer.value_table().end(), [](auto v) { + return v.type() == vtzero::property_value_type::float_value; + }); + + BOOST_CHECK_EQUAL(number_of_float_values, 74); } -void validate_turn_layer(protozero::pbf_reader &layer_message) +void validate_node_layer(vtzero::layer layer) { - using namespace osrm; + BOOST_CHECK_EQUAL(layer.version(), 2); + BOOST_CHECK_EQUAL(to_string(layer.name()), "osmnodes"); + BOOST_CHECK_EQUAL(layer.extent(), osrm::util::vector_tile::EXTENT); + BOOST_CHECK_EQUAL(layer.key_table().size(), 0); + BOOST_CHECK_EQUAL(layer.num_features(), 1791); - const auto check_turn_feature = [](protozero::pbf_reader feature_message) { - feature_message.next(); // advance parser to first entry - BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::GEOMETRY_TAG); - BOOST_CHECK_EQUAL(feature_message.get_enum(), util::vector_tile::GEOMETRY_TYPE_POINT); - - feature_message.next(); // advance to next entry - BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::ID_TAG); - feature_message.get_uint64(); // id - - feature_message.next(); // advance to next entry - 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()), 12); - auto iter = feature_iter_pair.begin(); - BOOST_CHECK_EQUAL(*iter++, 0); // bearing_in key - *iter++; - BOOST_CHECK_EQUAL(*iter++, 1); // turn_angle key - *iter++; - BOOST_CHECK_EQUAL(*iter++, 2); // turn cost (duration) key - *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(); - auto geometry_iter_pair = feature_message.get_packed_uint32(); - BOOST_CHECK_GT(std::distance(geometry_iter_pair.begin(), geometry_iter_pair.end()), 1); - }; - - auto number_of_turn_keys = 0u; - auto number_of_turns_found = 0u; - - while (layer_message.next()) + while (auto feature = layer.next_feature()) { - switch (layer_message.tag()) - { - case util::vector_tile::VERSION_TAG: - BOOST_CHECK_EQUAL(layer_message.get_uint32(), 2); - break; - case util::vector_tile::NAME_TAG: - BOOST_CHECK_EQUAL(layer_message.get_string(), "turns"); - break; - case util::vector_tile::EXTENT_TAG: - BOOST_CHECK_EQUAL(layer_message.get_uint32(), util::vector_tile::EXTENT); - break; - case util::vector_tile::FEATURE_TAG: - check_turn_feature(layer_message.get_message()); - number_of_turns_found++; - break; - case util::vector_tile::KEY_TAG: - layer_message.get_string(); - number_of_turn_keys++; - break; - case util::vector_tile::VARIANT_TAG: - validate_value(layer_message.get_message()); - break; - default: - BOOST_CHECK(false); // invalid tag - break; - } + BOOST_CHECK(feature.has_id()); + BOOST_CHECK(feature.geometry_type() == vtzero::GeomType::POINT); + BOOST_CHECK(feature.empty()); } - - BOOST_CHECK_EQUAL(number_of_turn_keys, 6); - BOOST_CHECK(number_of_turns_found > 700); } -void validate_node_layer(protozero::pbf_reader &layer_message) +void validate_internal_nodes_layer(vtzero::layer layer) { - using namespace osrm; - auto number_of_nodes_found = 0u; - - const auto check_osmnode_feature = [](protozero::pbf_reader feature_message) { - feature_message.next(); // advance parser to first entry - BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::GEOMETRY_TAG); - BOOST_CHECK_EQUAL(feature_message.get_enum(), util::vector_tile::GEOMETRY_TYPE_POINT); - - feature_message.next(); // advance to next entry - BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::ID_TAG); - feature_message.get_uint64(); // id - - feature_message.next(); // advance to next entry - // Note - on this layer, there should be no feature attributes, the next thing - // we get should be the geometry - BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::FEATURE_GEOMETRIES_TAG); - auto geometry_iter_pair = feature_message.get_packed_uint32(); - BOOST_CHECK_GT(std::distance(geometry_iter_pair.begin(), geometry_iter_pair.end()), 1); - }; - - while (layer_message.next()) + BOOST_CHECK_EQUAL(layer.version(), 2); + BOOST_CHECK_EQUAL(to_string(layer.name()), "internal-nodes"); + BOOST_CHECK_EQUAL(layer.extent(), osrm::util::vector_tile::EXTENT); + BOOST_CHECK_EQUAL(layer.key_table().size(), 0); + BOOST_CHECK_EQUAL(layer.num_features(), 24); + + while (auto feature = layer.next_feature()) { - switch (layer_message.tag()) - { - case util::vector_tile::VERSION_TAG: - BOOST_CHECK_EQUAL(layer_message.get_uint32(), 2); - break; - case util::vector_tile::NAME_TAG: - BOOST_CHECK_EQUAL(layer_message.get_string(), "osmnodes"); - break; - case util::vector_tile::EXTENT_TAG: - BOOST_CHECK_EQUAL(layer_message.get_uint32(), util::vector_tile::EXTENT); - break; - case util::vector_tile::FEATURE_TAG: - check_osmnode_feature(layer_message.get_message()); - number_of_nodes_found++; - break; - case util::vector_tile::KEY_TAG: - BOOST_CHECK(false); // There should be no properties on node features - break; - case util::vector_tile::VARIANT_TAG: - BOOST_CHECK(false); // There should be no properties on node features - break; - default: - BOOST_CHECK(false); // invalid tag - break; - } + BOOST_CHECK(!feature.has_id()); + BOOST_CHECK(feature.geometry_type() == vtzero::GeomType::LINESTRING); + BOOST_CHECK(feature.empty()); } - BOOST_CHECK_EQUAL(number_of_nodes_found, 1791); } void validate_tile(const osrm::OSRM &osrm) @@ -275,22 +164,12 @@ void validate_tile(const osrm::OSRM &osrm) BOOST_CHECK(result.size() > 114000); - protozero::pbf_reader tile_message(result); - - tile_message.next(); - BOOST_CHECK_EQUAL(tile_message.tag(), util::vector_tile::LAYER_TAG); // must be a layer - protozero::pbf_reader layer_message = tile_message.get_message(); - validate_feature_layer(layer_message); + vtzero::vector_tile tile{result}; - tile_message.next(); - BOOST_CHECK_EQUAL(tile_message.tag(), util::vector_tile::LAYER_TAG); // must be a layer - layer_message = tile_message.get_message(); - validate_turn_layer(layer_message); - - tile_message.next(); - BOOST_CHECK_EQUAL(tile_message.tag(), util::vector_tile::LAYER_TAG); // must be a layer - layer_message = tile_message.get_message(); - validate_node_layer(layer_message); + validate_feature_layer(tile.next_layer()); + validate_turn_layer(tile.next_layer()); + validate_node_layer(tile.next_layer()); + validate_internal_nodes_layer(tile.next_layer()); } BOOST_AUTO_TEST_CASE(test_tile_ch) @@ -330,147 +209,50 @@ void test_tile_turns(const osrm::OSRM &osrm) BOOST_CHECK_GT(result.size(), 128); - protozero::pbf_reader tile_message(result); - tile_message.next(); - BOOST_CHECK_EQUAL(tile_message.tag(), util::vector_tile::LAYER_TAG); // must be a layer - // Skip the segments layer - tile_message.skip(); - - tile_message.next(); - auto layer_message = tile_message.get_message(); - - std::vector found_bearing_in_indexes; - 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 - BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::GEOMETRY_TAG); - BOOST_CHECK_EQUAL(feature_message.get_enum(), util::vector_tile::GEOMETRY_TYPE_POINT); - - feature_message.next(); // advance to next entry - BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::ID_TAG); - feature_message.get_uint64(); // id - - feature_message.next(); // advance to next entry - 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()), 12); - auto iter = feature_iter_pair.begin(); - BOOST_CHECK_EQUAL(*iter++, 0); // bearing_in key - found_bearing_in_indexes.push_back(*iter++); - BOOST_CHECK_EQUAL(*iter++, 1); // turn_angle key - found_turn_angles_indexes.push_back(*iter++); - BOOST_CHECK_EQUAL(*iter++, 2); // "cost" key (actually duration) - 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(); - auto geometry_iter_pair = feature_message.get_packed_uint32(); - BOOST_CHECK_GT(std::distance(geometry_iter_pair.begin(), geometry_iter_pair.end()), 1); - }; - - std::unordered_map float_vals; - std::unordered_map sint64_vals; - std::unordered_map string_vals; - - int kv_index = 0; - - const auto check_value = [&](protozero::pbf_reader value) { - while (value.next()) - { - switch (value.tag()) - { - case util::vector_tile::VARIANT_TYPE_FLOAT: - float_vals[kv_index] = value.get_float(); - break; - 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); - } - kv_index++; - } - }; - - auto number_of_turn_keys = 0u; - auto number_of_turns_found = 0u; - - while (layer_message.next()) - { - switch (layer_message.tag()) - { - case util::vector_tile::VERSION_TAG: - BOOST_CHECK_EQUAL(layer_message.get_uint32(), 2); - break; - case util::vector_tile::NAME_TAG: - BOOST_CHECK_EQUAL(layer_message.get_string(), "turns"); - break; - case util::vector_tile::EXTENT_TAG: - BOOST_CHECK_EQUAL(layer_message.get_uint32(), util::vector_tile::EXTENT); - break; - case util::vector_tile::FEATURE_TAG: - check_turn_feature(layer_message.get_message()); - number_of_turns_found++; - break; - case util::vector_tile::KEY_TAG: - layer_message.get_string(); - number_of_turn_keys++; - break; - case util::vector_tile::VARIANT_TAG: - check_value(layer_message.get_message()); - break; - default: - BOOST_CHECK(false); // invalid tag - break; - } - } + vtzero::vector_tile tile{result}; + + tile.next_layer(); + auto layer = tile.next_layer(); + BOOST_CHECK_EQUAL(to_string(layer.name()), "turns"); - // Verify that we got the expected turn penalties std::vector actual_time_turn_penalties; - for (const auto &i : found_time_penalties_indexes) + std::vector actual_weight_turn_penalties; + std::vector actual_turn_types; + std::vector actual_turn_modifiers; + std::vector actual_turn_angles; + std::vector actual_turn_bearings; + + while (auto feature = layer.next_feature()) { - BOOST_CHECK(float_vals.count(i) == 1); - actual_time_turn_penalties.push_back(float_vals[i]); + auto props = vtzero::create_properties_map>(feature); + + BOOST_CHECK(props["cost"].type() == typeid(float)); + actual_time_turn_penalties.push_back(boost::get(props["cost"])); + BOOST_CHECK(props["weight"].type() == typeid(float)); + actual_weight_turn_penalties.push_back(boost::get(props["weight"])); + BOOST_CHECK(props["turn_angle"].type() == typeid(std::int64_t)); + actual_turn_angles.push_back(boost::get(props["turn_angle"])); + BOOST_CHECK(props["bearing_in"].type() == typeid(std::int64_t)); + actual_turn_bearings.push_back(boost::get(props["bearing_in"])); + BOOST_CHECK(props["type"].type() == typeid(std::string)); + actual_turn_types.push_back(boost::get(props["type"])); + BOOST_CHECK(props["modifier"].type() == typeid(std::string)); + actual_turn_modifiers.push_back(boost::get(props["modifier"])); } + + // Verify that we got the expected turn penalties std::sort(actual_time_turn_penalties.begin(), actual_time_turn_penalties.end()); const std::vector expected_time_turn_penalties = { 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_time_turn_penalties, expected_time_turn_penalties); // Verify that we got the expected turn penalties - std::vector actual_weight_turn_penalties; - for (const auto &i : found_weight_penalties_indexes) - { - BOOST_CHECK(float_vals.count(i) == 1); - actual_weight_turn_penalties.push_back(float_vals[i]); - } std::sort(actual_weight_turn_penalties.begin(), actual_weight_turn_penalties.end()); const std::vector expected_weight_turn_penalties = { 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)", @@ -493,12 +275,6 @@ void test_tile_turns(const osrm::OSRM &osrm) 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", @@ -521,24 +297,12 @@ void test_tile_turns(const osrm::OSRM &osrm) 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) - { - BOOST_CHECK(sint64_vals.count(i) == 1); - actual_turn_angles.push_back(sint64_vals[i]); - } std::sort(actual_turn_angles.begin(), actual_turn_angles.end()); const std::vector expected_turn_angles = { -122, -120, -117, -65, -57, -30, -28, -3, -2, 2, 3, 28, 30, 57, 65, 117, 120, 122}; CHECK_EQUAL_RANGE(actual_turn_angles, expected_turn_angles); // Verify the expected bearings - std::vector actual_turn_bearings; - for (const auto &i : found_bearing_in_indexes) - { - BOOST_CHECK(sint64_vals.count(i) == 1); - actual_turn_bearings.push_back(sint64_vals[i]); - } std::sort(actual_turn_bearings.begin(), actual_turn_bearings.end()); const std::vector expected_turn_bearings = { 49, 49, 107, 107, 169, 169, 171, 171, 229, 229, 257, 257, 286, 286, 349, 349, 352, 352}; @@ -586,132 +350,18 @@ void test_tile_speeds(const osrm::OSRM &osrm) BOOST_CHECK_GT(result.size(), 128); - protozero::pbf_reader tile_message(result); - tile_message.next(); - BOOST_CHECK_EQUAL(tile_message.tag(), util::vector_tile::LAYER_TAG); // must be a layer - protozero::pbf_reader layer_message = tile_message.get_message(); - - std::vector found_speed_indexes; - std::vector found_component_indexes; - std::vector found_datasource_indexes; - std::vector found_weight_indexes; - std::vector found_duration_indexes; - std::vector found_name_indexes; - std::vector found_rate_indexes; - - const auto check_feature = [&](protozero::pbf_reader feature_message) { - feature_message.next(); // advance parser to first entry - BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::GEOMETRY_TAG); - BOOST_CHECK_EQUAL(feature_message.get_enum(), util::vector_tile::GEOMETRY_TYPE_LINE); - - feature_message.next(); // advance to next entry - BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::ID_TAG); - feature_message.get_uint64(); // id - - feature_message.next(); // advance to next entry - BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::FEATURE_ATTRIBUTES_TAG); - // properties - auto property_iter_pair = feature_message.get_packed_uint32(); - auto value_begin = property_iter_pair.begin(); - auto value_end = property_iter_pair.end(); - BOOST_CHECK_EQUAL(std::distance(value_begin, value_end), 14); - auto iter = value_begin; - BOOST_CHECK_EQUAL(*iter++, 0); // speed key - found_speed_indexes.push_back(*iter++); - BOOST_CHECK_EQUAL(*iter++, 1); // component key - // component value - found_component_indexes.push_back(*iter++); - BOOST_CHECK_EQUAL(*iter++, 2); // data source key - found_datasource_indexes.push_back(*iter++); - BOOST_CHECK_EQUAL(*iter++, 3); // weight key - found_weight_indexes.push_back(*iter++); - BOOST_CHECK_EQUAL(*iter++, 4); // duration key - found_duration_indexes.push_back(*iter++); - // name - BOOST_CHECK_EQUAL(*iter++, 5); - found_name_indexes.push_back(*iter++); - BOOST_CHECK_EQUAL(*iter++, 6); - found_rate_indexes.push_back(*iter++); - BOOST_CHECK(iter == value_end); - // geometry - feature_message.next(); - auto geometry_iter_pair = feature_message.get_packed_uint32(); - BOOST_CHECK_GT(std::distance(geometry_iter_pair.begin(), geometry_iter_pair.end()), 1); - }; - - std::unordered_map string_vals; - std::unordered_map bool_vals; - std::unordered_map uint64_vals; - std::unordered_map double_vals; - - int kv_index = 0; - - const auto check_value = [&](protozero::pbf_reader value) { - while (value.next()) - { - switch (value.tag()) - { - case util::vector_tile::VARIANT_TYPE_BOOL: - bool_vals[kv_index] = value.get_bool(); - break; - case util::vector_tile::VARIANT_TYPE_DOUBLE: - double_vals[kv_index] = value.get_double(); - break; - case util::vector_tile::VARIANT_TYPE_FLOAT: - value.get_float(); - break; - case util::vector_tile::VARIANT_TYPE_STRING: - string_vals[kv_index] = value.get_string(); - break; - case util::vector_tile::VARIANT_TYPE_UINT64: - uint64_vals[kv_index] = value.get_uint64(); - break; - case util::vector_tile::VARIANT_TYPE_SINT64: - value.get_sint64(); - break; - } - kv_index++; - } - }; - - auto number_of_speed_keys = 0u; - auto number_of_speed_values = 0u; - - while (layer_message.next()) - { - switch (layer_message.tag()) - { - case util::vector_tile::VERSION_TAG: - BOOST_CHECK_EQUAL(layer_message.get_uint32(), 2); - break; - case util::vector_tile::NAME_TAG: - BOOST_CHECK_EQUAL(layer_message.get_string(), "speeds"); - break; - case util::vector_tile::EXTENT_TAG: - BOOST_CHECK_EQUAL(layer_message.get_uint32(), util::vector_tile::EXTENT); - break; - case util::vector_tile::FEATURE_TAG: - check_feature(layer_message.get_message()); - break; - case util::vector_tile::KEY_TAG: - layer_message.get_string(); - number_of_speed_keys++; - break; - case util::vector_tile::VARIANT_TAG: - check_value(layer_message.get_message()); - number_of_speed_values++; - break; - default: - BOOST_CHECK(false); // invalid tag - break; - } - } + vtzero::vector_tile tile{result}; + + auto layer = tile.next_layer(); + BOOST_CHECK_EQUAL(to_string(layer.name()), "speeds"); std::vector actual_names; - for (const auto &i : found_name_indexes) + while (auto feature = layer.next_feature()) { - BOOST_CHECK(string_vals.count(i) == 1); - actual_names.push_back(string_vals[i]); + auto props = vtzero::create_properties_map>(feature); + + BOOST_CHECK(props["name"].type() == typeid(std::string)); + actual_names.push_back(boost::get(props["name"])); } std::sort(actual_names.begin(), actual_names.end()); const std::vector expected_names = {"Avenue du Carnier", @@ -780,62 +430,17 @@ void test_tile_nodes(const osrm::OSRM &osrm) BOOST_CHECK_GT(result.size(), 128); - protozero::pbf_reader tile_message(result); - tile_message.next(); - BOOST_CHECK_EQUAL(tile_message.tag(), util::vector_tile::LAYER_TAG); // must be a layer - - // Skip the segments and turns layers - tile_message.skip(); - tile_message.next(); - tile_message.skip(); + vtzero::vector_tile tile{result}; - // Get the osmnodes layer - tile_message.next(); - protozero::pbf_reader layer_message = tile_message.get_message(); + tile.next_layer(); + tile.next_layer(); + auto layer = tile.next_layer(); + BOOST_CHECK_EQUAL(to_string(layer.name()), "osmnodes"); std::vector found_node_ids; - - const auto check_feature = [&](protozero::pbf_reader feature_message) { - feature_message.next(); // advance parser to first entry - BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::GEOMETRY_TAG); - BOOST_CHECK_EQUAL(feature_message.get_enum(), util::vector_tile::GEOMETRY_TYPE_POINT); - - feature_message.next(); // advance to next entry - BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::ID_TAG); - found_node_ids.push_back(feature_message.get_uint64()); // id - - feature_message.next(); // advance to next entry - BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::FEATURE_GEOMETRIES_TAG); - auto geometry_iter_pair = feature_message.get_packed_uint32(); - BOOST_CHECK_GT(std::distance(geometry_iter_pair.begin(), geometry_iter_pair.end()), 1); - }; - - while (layer_message.next()) + while (auto feature = layer.next_feature()) { - switch (layer_message.tag()) - { - case util::vector_tile::VERSION_TAG: - BOOST_CHECK_EQUAL(layer_message.get_uint32(), 2); - break; - case util::vector_tile::NAME_TAG: - BOOST_CHECK_EQUAL(layer_message.get_string(), "osmnodes"); - break; - case util::vector_tile::EXTENT_TAG: - BOOST_CHECK_EQUAL(layer_message.get_uint32(), util::vector_tile::EXTENT); - break; - case util::vector_tile::FEATURE_TAG: - check_feature(layer_message.get_message()); - break; - case util::vector_tile::KEY_TAG: - BOOST_CHECK(false); // There should be no keys - break; - case util::vector_tile::VARIANT_TAG: - BOOST_CHECK(false); // There should be no values - break; - default: - BOOST_CHECK(false); // invalid tag - break; - } + found_node_ids.push_back(feature.id()); } std::sort(found_node_ids.begin(), found_node_ids.end());