diff options
-rw-r--r-- | benchmark/parse/vector_tile.benchmark.cpp | 28 | ||||
-rw-r--r-- | cmake/benchmark-files.cmake | 1 | ||||
-rw-r--r-- | cmake/benchmark.cmake | 1 | ||||
-rw-r--r-- | cmake/core-files.cmake | 2 | ||||
-rw-r--r-- | src/mbgl/tile/vector_tile.cpp | 300 | ||||
-rw-r--r-- | src/mbgl/tile/vector_tile_data.cpp | 247 | ||||
-rw-r--r-- | src/mbgl/tile/vector_tile_data.hpp | 85 |
7 files changed, 366 insertions, 298 deletions
diff --git a/benchmark/parse/vector_tile.benchmark.cpp b/benchmark/parse/vector_tile.benchmark.cpp new file mode 100644 index 0000000000..24623dbda7 --- /dev/null +++ b/benchmark/parse/vector_tile.benchmark.cpp @@ -0,0 +1,28 @@ +#include <benchmark/benchmark.h> + +#include <mbgl/tile/vector_tile_data.hpp> +#include <mbgl/util/io.hpp> + +using namespace mbgl; + +static void Parse_VectorTile(benchmark::State& state) { + auto data = std::make_shared<std::string>(util::read_file("test/fixtures/api/assets/streets/10-163-395.vector.pbf")); + + while (state.KeepRunning()) { + std::size_t length = 0; + VectorTileData tile(data); + for (const auto& name : tile.layerNames()) { + if (auto layer = tile.getLayer(name)) { + const std::size_t count = layer->featureCount(); + for (std::size_t i = 0; i < count; i++) { + if (auto feature = layer->getFeature(i)) { + length += feature->getGeometries().size(); + length += feature->getProperties().size(); + } + } + } + } + } +} + +BENCHMARK(Parse_VectorTile); diff --git a/cmake/benchmark-files.cmake b/cmake/benchmark-files.cmake index 0306340fe0..3736df11f6 100644 --- a/cmake/benchmark-files.cmake +++ b/cmake/benchmark-files.cmake @@ -9,6 +9,7 @@ set(MBGL_BENCHMARK_FILES # parse benchmark/parse/filter.benchmark.cpp + benchmark/parse/vector_tile.benchmark.cpp # src benchmark/src/main.cpp diff --git a/cmake/benchmark.cmake b/cmake/benchmark.cmake index c298d8ee28..174d0bc19c 100644 --- a/cmake/benchmark.cmake +++ b/cmake/benchmark.cmake @@ -19,6 +19,7 @@ target_link_libraries(mbgl-benchmark target_add_mason_package(mbgl-benchmark PRIVATE benchmark) target_add_mason_package(mbgl-benchmark PRIVATE rapidjson) +target_add_mason_package(mbgl-benchmark PRIVATE protozero) mbgl_platform_benchmark() diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index cdbd5c8094..172b2854b3 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -513,6 +513,8 @@ set(MBGL_CORE_FILES src/mbgl/tile/tile_observer.hpp src/mbgl/tile/vector_tile.cpp src/mbgl/tile/vector_tile.hpp + src/mbgl/tile/vector_tile_data.cpp + src/mbgl/tile/vector_tile_data.hpp # util include/mbgl/util/any.hpp diff --git a/src/mbgl/tile/vector_tile.cpp b/src/mbgl/tile/vector_tile.cpp index f6f8de72d1..0e13b78a7d 100644 --- a/src/mbgl/tile/vector_tile.cpp +++ b/src/mbgl/tile/vector_tile.cpp @@ -1,85 +1,12 @@ #include <mbgl/tile/vector_tile.hpp> +#include <mbgl/tile/vector_tile_data.hpp> #include <mbgl/tile/tile_loader_impl.hpp> -#include <mbgl/tile/geometry_tile_data.hpp> #include <mbgl/renderer/tile_parameters.hpp> -#include <protozero/pbf_reader.hpp> - -#include <unordered_map> -#include <functional> -#include <utility> +#include <memory> namespace mbgl { -class VectorTileLayer; - -using packed_iter_type = protozero::iterator_range<protozero::pbf_reader::const_uint32_iterator>; - -struct VectorTileLayerData { - VectorTileLayerData(std::shared_ptr<const std::string>); - - // Hold a reference to the underlying pbf data that backs the lazily-built - // components of the owning VectorTileLayer and VectorTileFeature objects - std::shared_ptr<const std::string> data; - - uint32_t version = 1; - uint32_t extent = 4096; - std::unordered_map<std::string, uint32_t> keysMap; - std::vector<std::reference_wrapper<const std::string>> keys; - std::vector<Value> values; -}; - -class VectorTileFeature : public GeometryTileFeature { -public: - VectorTileFeature(protozero::pbf_reader, std::shared_ptr<VectorTileLayerData> layerData); - - FeatureType getType() const override { return type; } - optional<Value> getValue(const std::string&) const override; - std::unordered_map<std::string,Value> getProperties() const override; - optional<FeatureIdentifier> getID() const override; - GeometryCollection getGeometries() const override; - -private: - std::shared_ptr<VectorTileLayerData> layerData; - optional<FeatureIdentifier> id; - FeatureType type = FeatureType::Unknown; - packed_iter_type tags_iter; - packed_iter_type geometry_iter; -}; - -class VectorTileLayer : public GeometryTileLayer { -public: - VectorTileLayer(protozero::pbf_reader, std::shared_ptr<const std::string>); - - std::size_t featureCount() const override { return features.size(); } - std::unique_ptr<GeometryTileFeature> getFeature(std::size_t) const override; - std::string getName() const override; - -private: - friend class VectorTileData; - friend class VectorTileFeature; - - std::string name; - std::vector<protozero::pbf_reader> features; - std::shared_ptr<VectorTileLayerData> data; -}; - -class VectorTileData : public GeometryTileData { -public: - VectorTileData(std::shared_ptr<const std::string> data); - - std::unique_ptr<GeometryTileData> clone() const override { - return std::make_unique<VectorTileData>(*this); - } - - const GeometryTileLayer* getLayer(const std::string&) const override; - -private: - std::shared_ptr<const std::string> data; - mutable bool parsed = false; - mutable std::unordered_map<std::string, VectorTileLayer> layers; -}; - VectorTile::VectorTile(const OverscaledTileID& id_, std::string sourceID_, const TileParameters& parameters, @@ -101,227 +28,4 @@ void VectorTile::setData(std::shared_ptr<const std::string> data_, GeometryTile::setData(data_ ? std::make_unique<VectorTileData>(data_) : nullptr); } -Value parseValue(protozero::pbf_reader data) { - Value value; - while (data.next()) { - switch (data.tag()) { - case 1: // string_value - value = data.get_string(); - break; - case 2: // float_value - value = static_cast<double>(data.get_float()); - break; - case 3: // double_value - value = data.get_double(); - break; - case 4: // int_value - value = data.get_int64(); - break; - case 5: // uint_value - value = data.get_uint64(); - break; - case 6: // sint_value - value = data.get_sint64(); - break; - case 7: // bool_value - value = data.get_bool(); - break; - default: - data.skip(); - break; - } - } - return value; -} - -VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, std::shared_ptr<VectorTileLayerData> layerData_) - : layerData(std::move(layerData_)) { - while (feature_pbf.next()) { - switch (feature_pbf.tag()) { - case 1: // id - id = { feature_pbf.get_uint64() }; - break; - case 2: // tags - tags_iter = feature_pbf.get_packed_uint32(); - break; - case 3: // type - type = static_cast<FeatureType>(feature_pbf.get_enum()); - break; - case 4: // geometry - geometry_iter = feature_pbf.get_packed_uint32(); - break; - default: - feature_pbf.skip(); - break; - } - } -} - -optional<Value> VectorTileFeature::getValue(const std::string& key) const { - auto keyIter = layerData->keysMap.find(key); - if (keyIter == layerData->keysMap.end()) { - return optional<Value>(); - } - - auto start_itr = tags_iter.begin(); - const auto & end_itr = tags_iter.end(); - while (start_itr != end_itr) { - auto tag_key = static_cast<uint32_t>(*start_itr++); - - if (layerData->keysMap.size() <= tag_key) { - throw std::runtime_error("feature referenced out of range key"); - } - - if (start_itr == end_itr) { - throw std::runtime_error("uneven number of feature tag ids"); - } - - auto tag_val = static_cast<uint32_t>(*start_itr++);; - if (layerData->values.size() <= tag_val) { - throw std::runtime_error("feature referenced out of range value"); - } - - if (tag_key == keyIter->second) { - return layerData->values[tag_val]; - } - } - - return optional<Value>(); -} - -std::unordered_map<std::string,Value> VectorTileFeature::getProperties() const { - std::unordered_map<std::string,Value> properties; - auto start_itr = tags_iter.begin(); - const auto & end_itr = tags_iter.end(); - while (start_itr != end_itr) { - auto tag_key = static_cast<uint32_t>(*start_itr++); - if (start_itr == end_itr) { - throw std::runtime_error("uneven number of feature tag ids"); - } - auto tag_val = static_cast<uint32_t>(*start_itr++); - properties[layerData->keys.at(tag_key)] = layerData->values.at(tag_val); - } - return properties; -} - -optional<FeatureIdentifier> VectorTileFeature::getID() const { - return id; -} - -GeometryCollection VectorTileFeature::getGeometries() const { - uint8_t cmd = 1; - uint32_t length = 0; - int32_t x = 0; - int32_t y = 0; - const float scale = float(util::EXTENT) / layerData->extent; - - GeometryCollection lines; - - lines.emplace_back(); - GeometryCoordinates* line = &lines.back(); - - auto g_itr = geometry_iter.begin(); - while (g_itr != geometry_iter.end()) { - if (length == 0) { - auto cmd_length = static_cast<uint32_t>(*g_itr++); - cmd = cmd_length & 0x7; - length = cmd_length >> 3; - } - - --length; - - if (cmd == 1 || cmd == 2) { - x += protozero::decode_zigzag32(static_cast<uint32_t>(*g_itr++)); - y += protozero::decode_zigzag32(static_cast<uint32_t>(*g_itr++)); - - if (cmd == 1 && !line->empty()) { // moveTo - lines.emplace_back(); - line = &lines.back(); - } - - line->emplace_back(::round(x * scale), ::round(y * scale)); - - } else if (cmd == 7) { // closePolygon - if (!line->empty()) { - line->push_back((*line)[0]); - } - - } else { - throw std::runtime_error("unknown command"); - } - } - - if (layerData->version >= 2 || type != FeatureType::Polygon) { - return lines; - } - - return fixupPolygons(lines); -} - -VectorTileData::VectorTileData(std::shared_ptr<const std::string> data_) - : data(std::move(data_)) { -} - -const GeometryTileLayer* VectorTileData::getLayer(const std::string& name) const { - if (!parsed) { - parsed = true; - protozero::pbf_reader tile_pbf(*data); - while (tile_pbf.next(3)) { - VectorTileLayer layer(tile_pbf.get_message(), data); - layers.emplace(layer.name, std::move(layer)); - } - } - - auto it = layers.find(name); - if (it != layers.end()) { - return &it->second; - } - return nullptr; -} - -VectorTileLayerData::VectorTileLayerData(std::shared_ptr<const std::string> pbfData) : - data(std::move(pbfData)) -{} - -VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf, std::shared_ptr<const std::string> pbfData) - : data(std::make_shared<VectorTileLayerData>(std::move(pbfData))) -{ - while (layer_pbf.next()) { - switch (layer_pbf.tag()) { - case 1: // name - name = layer_pbf.get_string(); - break; - case 2: // feature - features.push_back(layer_pbf.get_message()); - break; - case 3: // keys - { - auto iter = data->keysMap.emplace(layer_pbf.get_string(), data->keysMap.size()); - data->keys.emplace_back(std::reference_wrapper<const std::string>(iter.first->first)); - } - break; - case 4: // values - data->values.emplace_back(parseValue(layer_pbf.get_message())); - break; - case 5: // extent - data->extent = layer_pbf.get_uint32(); - break; - case 15: // version - data->version = layer_pbf.get_uint32(); - break; - default: - layer_pbf.skip(); - break; - } - } -} - -std::unique_ptr<GeometryTileFeature> VectorTileLayer::getFeature(std::size_t i) const { - return std::make_unique<VectorTileFeature>(features.at(i), data); -} - -std::string VectorTileLayer::getName() const { - return name; -} - } // namespace mbgl diff --git a/src/mbgl/tile/vector_tile_data.cpp b/src/mbgl/tile/vector_tile_data.cpp new file mode 100644 index 0000000000..5eb199f482 --- /dev/null +++ b/src/mbgl/tile/vector_tile_data.cpp @@ -0,0 +1,247 @@ +#include <mbgl/tile/vector_tile_data.hpp> +#include <mbgl/util/constants.hpp> + +namespace mbgl { + +Value parseValue(protozero::pbf_reader data) { + Value value; + while (data.next()) { + switch (data.tag()) { + case 1: // string_value + value = data.get_string(); + break; + case 2: // float_value + value = static_cast<double>(data.get_float()); + break; + case 3: // double_value + value = data.get_double(); + break; + case 4: // int_value + value = data.get_int64(); + break; + case 5: // uint_value + value = data.get_uint64(); + break; + case 6: // sint_value + value = data.get_sint64(); + break; + case 7: // bool_value + value = data.get_bool(); + break; + default: + data.skip(); + break; + } + } + return value; +} + +VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, std::shared_ptr<VectorTileLayerData> layerData_) + : layerData(std::move(layerData_)) { + while (feature_pbf.next()) { + switch (feature_pbf.tag()) { + case 1: // id + id = { feature_pbf.get_uint64() }; + break; + case 2: // tags + tags_iter = feature_pbf.get_packed_uint32(); + break; + case 3: // type + type = static_cast<FeatureType>(feature_pbf.get_enum()); + break; + case 4: // geometry + geometry_iter = feature_pbf.get_packed_uint32(); + break; + default: + feature_pbf.skip(); + break; + } + } +} + +optional<Value> VectorTileFeature::getValue(const std::string& key) const { + auto keyIter = layerData->keysMap.find(key); + if (keyIter == layerData->keysMap.end()) { + return optional<Value>(); + } + + auto start_itr = tags_iter.begin(); + const auto & end_itr = tags_iter.end(); + while (start_itr != end_itr) { + auto tag_key = static_cast<uint32_t>(*start_itr++); + + if (layerData->keysMap.size() <= tag_key) { + throw std::runtime_error("feature referenced out of range key"); + } + + if (start_itr == end_itr) { + throw std::runtime_error("uneven number of feature tag ids"); + } + + auto tag_val = static_cast<uint32_t>(*start_itr++);; + if (layerData->values.size() <= tag_val) { + throw std::runtime_error("feature referenced out of range value"); + } + + if (tag_key == keyIter->second) { + return layerData->values[tag_val]; + } + } + + return optional<Value>(); +} + +std::unordered_map<std::string,Value> VectorTileFeature::getProperties() const { + std::unordered_map<std::string,Value> properties; + auto start_itr = tags_iter.begin(); + const auto & end_itr = tags_iter.end(); + while (start_itr != end_itr) { + auto tag_key = static_cast<uint32_t>(*start_itr++); + if (start_itr == end_itr) { + throw std::runtime_error("uneven number of feature tag ids"); + } + auto tag_val = static_cast<uint32_t>(*start_itr++); + properties[layerData->keys.at(tag_key)] = layerData->values.at(tag_val); + } + return properties; +} + +optional<FeatureIdentifier> VectorTileFeature::getID() const { + return id; +} + +GeometryCollection VectorTileFeature::getGeometries() const { + uint8_t cmd = 1; + uint32_t length = 0; + int32_t x = 0; + int32_t y = 0; + const float scale = float(util::EXTENT) / layerData->extent; + + GeometryCollection lines; + + lines.emplace_back(); + GeometryCoordinates* line = &lines.back(); + + auto g_itr = geometry_iter.begin(); + while (g_itr != geometry_iter.end()) { + if (length == 0) { + auto cmd_length = static_cast<uint32_t>(*g_itr++); + cmd = cmd_length & 0x7; + length = cmd_length >> 3; + } + + --length; + + if (cmd == 1 || cmd == 2) { + x += protozero::decode_zigzag32(static_cast<uint32_t>(*g_itr++)); + y += protozero::decode_zigzag32(static_cast<uint32_t>(*g_itr++)); + + if (cmd == 1 && !line->empty()) { // moveTo + lines.emplace_back(); + line = &lines.back(); + } + + line->emplace_back(::round(x * scale), ::round(y * scale)); + + } else if (cmd == 7) { // closePolygon + if (!line->empty()) { + line->push_back((*line)[0]); + } + + } else { + throw std::runtime_error("unknown command"); + } + } + + if (layerData->version >= 2 || type != FeatureType::Polygon) { + return lines; + } + + return fixupPolygons(lines); +} + +VectorTileData::VectorTileData(std::shared_ptr<const std::string> data_) + : data(std::move(data_)) { +} + +const GeometryTileLayer* VectorTileData::getLayer(const std::string& name) const { + if (!parsed) { + parse(); + } + + auto it = layers.find(name); + if (it != layers.end()) { + return &it->second; + } + return nullptr; +} + +std::vector<std::string> VectorTileData::layerNames() const { + if (!parsed) { + parse(); + } + + std::vector<std::string> names; + names.reserve(layers.size()); + for (auto const& layer : layers) { + names.emplace_back(layer.first); + } + return names; +} + +void VectorTileData::parse() const { + parsed = true; + layers.clear(); + protozero::pbf_reader tile_pbf(*data); + while (tile_pbf.next(3)) { + VectorTileLayer layer(tile_pbf.get_message(), data); + layers.emplace(layer.name, std::move(layer)); + } +} + +VectorTileLayerData::VectorTileLayerData(std::shared_ptr<const std::string> pbfData) : + data(std::move(pbfData)) +{} + +VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf, std::shared_ptr<const std::string> pbfData) + : data(std::make_shared<VectorTileLayerData>(std::move(pbfData))) +{ + while (layer_pbf.next()) { + switch (layer_pbf.tag()) { + case 1: // name + name = layer_pbf.get_string(); + break; + case 2: // feature + features.push_back(layer_pbf.get_message()); + break; + case 3: // keys + { + auto iter = data->keysMap.emplace(layer_pbf.get_string(), data->keysMap.size()); + data->keys.emplace_back(std::reference_wrapper<const std::string>(iter.first->first)); + } + break; + case 4: // values + data->values.emplace_back(parseValue(layer_pbf.get_message())); + break; + case 5: // extent + data->extent = layer_pbf.get_uint32(); + break; + case 15: // version + data->version = layer_pbf.get_uint32(); + break; + default: + layer_pbf.skip(); + break; + } + } +} + +std::unique_ptr<GeometryTileFeature> VectorTileLayer::getFeature(std::size_t i) const { + return std::make_unique<VectorTileFeature>(features.at(i), data); +} + +std::string VectorTileLayer::getName() const { + return name; +} + +} // namespace mbgl diff --git a/src/mbgl/tile/vector_tile_data.hpp b/src/mbgl/tile/vector_tile_data.hpp new file mode 100644 index 0000000000..57b42ecd19 --- /dev/null +++ b/src/mbgl/tile/vector_tile_data.hpp @@ -0,0 +1,85 @@ +#include <mbgl/tile/geometry_tile_data.hpp> + +#include <protozero/pbf_reader.hpp> + +#include <unordered_map> +#include <functional> +#include <utility> + +namespace mbgl { + +class VectorTileLayer; + +using packed_iter_type = protozero::iterator_range<protozero::pbf_reader::const_uint32_iterator>; + +struct VectorTileLayerData { + VectorTileLayerData(std::shared_ptr<const std::string>); + + // Hold a reference to the underlying pbf data that backs the lazily-built + // components of the owning VectorTileLayer and VectorTileFeature objects + std::shared_ptr<const std::string> data; + + uint32_t version = 1; + uint32_t extent = 4096; + std::unordered_map<std::string, uint32_t> keysMap; + std::vector<std::reference_wrapper<const std::string>> keys; + std::vector<Value> values; +}; + +class VectorTileFeature : public GeometryTileFeature { +public: + VectorTileFeature(protozero::pbf_reader, std::shared_ptr<VectorTileLayerData> layerData); + + FeatureType getType() const override { return type; } + optional<Value> getValue(const std::string&) const override; + std::unordered_map<std::string,Value> getProperties() const override; + optional<FeatureIdentifier> getID() const override; + GeometryCollection getGeometries() const override; + +private: + std::shared_ptr<VectorTileLayerData> layerData; + optional<FeatureIdentifier> id; + FeatureType type = FeatureType::Unknown; + packed_iter_type tags_iter; + packed_iter_type geometry_iter; +}; + +class VectorTileLayer : public GeometryTileLayer { +public: + VectorTileLayer(protozero::pbf_reader, std::shared_ptr<const std::string>); + + std::size_t featureCount() const override { return features.size(); } + std::unique_ptr<GeometryTileFeature> getFeature(std::size_t) const override; + std::string getName() const override; + +private: + friend class VectorTileData; + friend class VectorTileFeature; + + std::string name; + std::vector<protozero::pbf_reader> features; + std::shared_ptr<VectorTileLayerData> data; +}; + +class VectorTileData : public GeometryTileData { +public: + VectorTileData(std::shared_ptr<const std::string> data); + + std::unique_ptr<GeometryTileData> clone() const override { + return std::make_unique<VectorTileData>(*this); + } + + const GeometryTileLayer* getLayer(const std::string&) const override; + + std::vector<std::string> layerNames() const; + +private: + void parse() const; + +private: + std::shared_ptr<const std::string> data; + mutable bool parsed = false; + mutable std::unordered_map<std::string, VectorTileLayer> layers; +}; + +} // namespace mbgl |