[core] switch to vector-tile library for decoding Mapbox vector tiles
This commit is contained in:
@@ -58,6 +58,7 @@ mason_use(polylabel VERSION 1.0.2 HEADER_ONLY)
|
||||
mason_use(wagyu VERSION 0.4.1 HEADER_ONLY)
|
||||
mason_use(shelf-pack VERSION 2.1.0 HEADER_ONLY)
|
||||
mason_use(cheap-ruler VERSION 2.5.1 HEADER_ONLY)
|
||||
mason_use(vector-tile VERSION 1.0.0-rc6 HEADER_ONLY)
|
||||
|
||||
add_definitions(-DRAPIDJSON_HAS_STDSTRING=1)
|
||||
|
||||
|
||||
@@ -20,6 +20,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)
|
||||
target_add_mason_package(mbgl-benchmark PRIVATE vector-tile)
|
||||
|
||||
mbgl_platform_benchmark()
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ target_add_mason_package(mbgl-core PRIVATE protozero)
|
||||
target_add_mason_package(mbgl-core PRIVATE polylabel)
|
||||
target_add_mason_package(mbgl-core PRIVATE wagyu)
|
||||
target_add_mason_package(mbgl-core PRIVATE shelf-pack)
|
||||
target_add_mason_package(mbgl-core PRIVATE vector-tile)
|
||||
|
||||
mbgl_platform_core()
|
||||
|
||||
|
||||
@@ -3,16 +3,13 @@
|
||||
#include <mbgl/tile/tile_loader_impl.hpp>
|
||||
#include <mbgl/renderer/tile_parameters.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
VectorTile::VectorTile(const OverscaledTileID& id_,
|
||||
std::string sourceID_,
|
||||
const TileParameters& parameters,
|
||||
const Tileset& tileset)
|
||||
: GeometryTile(id_, sourceID_, parameters),
|
||||
loader(*this, id_, parameters, tileset) {
|
||||
: GeometryTile(id_, sourceID_, parameters), loader(*this, id_, parameters, tileset) {
|
||||
}
|
||||
|
||||
void VectorTile::setNecessity(Necessity necessity) {
|
||||
|
||||
@@ -3,170 +3,78 @@
|
||||
|
||||
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(const mapbox::vector_tile::layer& layer,
|
||||
const protozero::data_view& view)
|
||||
: feature(view, layer) {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
FeatureType VectorTileFeature::getType() const {
|
||||
switch (feature.getType()) {
|
||||
case mapbox::vector_tile::GeomType::POINT:
|
||||
return FeatureType::Point;
|
||||
case mapbox::vector_tile::GeomType::LINESTRING:
|
||||
return FeatureType::LineString;
|
||||
case mapbox::vector_tile::GeomType::POLYGON:
|
||||
return FeatureType::Polygon;
|
||||
default:
|
||||
return FeatureType::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
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>();
|
||||
return feature.getValue(key);
|
||||
}
|
||||
|
||||
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;
|
||||
std::unordered_map<std::string, Value> VectorTileFeature::getProperties() const {
|
||||
return feature.getProperties();
|
||||
}
|
||||
|
||||
optional<FeatureIdentifier> VectorTileFeature::getID() const {
|
||||
return id;
|
||||
return feature.getID();
|
||||
}
|
||||
|
||||
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) {
|
||||
const float scale = float(util::EXTENT) / feature.getExtent();
|
||||
auto lines = feature.getGeometries<GeometryCollection>(scale);
|
||||
if (feature.getVersion() >= 2 || feature.getType() != mapbox::vector_tile::GeomType::POLYGON) {
|
||||
return lines;
|
||||
} else {
|
||||
return fixupPolygons(lines);
|
||||
}
|
||||
|
||||
return fixupPolygons(lines);
|
||||
}
|
||||
|
||||
VectorTileData::VectorTileData(std::shared_ptr<const std::string> data_)
|
||||
: data(std::move(data_)) {
|
||||
VectorTileLayer::VectorTileLayer(std::shared_ptr<const std::string> data_,
|
||||
const protozero::data_view& view)
|
||||
: data(std::move(data_)), layer(view) {
|
||||
}
|
||||
|
||||
std::size_t VectorTileLayer::featureCount() const {
|
||||
return layer.featureCount();
|
||||
}
|
||||
|
||||
std::unique_ptr<GeometryTileFeature> VectorTileLayer::getFeature(std::size_t i) const {
|
||||
return std::make_unique<VectorTileFeature>(layer, layer.getFeature(i));
|
||||
}
|
||||
|
||||
std::string VectorTileLayer::getName() const {
|
||||
return layer.getName();
|
||||
}
|
||||
|
||||
VectorTileData::VectorTileData(std::shared_ptr<const std::string> data_) : data(std::move(data_)) {
|
||||
}
|
||||
|
||||
std::unique_ptr<GeometryTileData> VectorTileData::clone() const {
|
||||
return std::make_unique<VectorTileData>(data);
|
||||
}
|
||||
|
||||
const GeometryTileLayer* VectorTileData::getLayer(const std::string& name) const {
|
||||
if (!parsed) {
|
||||
parse();
|
||||
// We're parsing this lazily so that we can construct VectorTileData objects on the main
|
||||
// thread without incurring the overhead of parsing immediately.
|
||||
for (const auto& pair : mapbox::vector_tile::buffer(*data).getLayers()) {
|
||||
layers.emplace(pair.first, VectorTileLayer{ data, pair.second });
|
||||
}
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
auto it = layers.find(name);
|
||||
@@ -177,71 +85,7 @@ const GeometryTileLayer* VectorTileData::getLayer(const std::string& name) const
|
||||
}
|
||||
|
||||
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;
|
||||
return mapbox::vector_tile::buffer(*data).layerNames();
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <mbgl/tile/geometry_tile_data.hpp>
|
||||
|
||||
#include <mapbox/vector_tile.hpp>
|
||||
#include <protozero/pbf_reader.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
@@ -8,74 +9,42 @@
|
||||
|
||||
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);
|
||||
VectorTileFeature(const mapbox::vector_tile::layer&, const protozero::data_view&);
|
||||
|
||||
FeatureType getType() const override { return type; }
|
||||
optional<Value> getValue(const std::string&) const override;
|
||||
std::unordered_map<std::string,Value> getProperties() const override;
|
||||
FeatureType getType() const override;
|
||||
optional<Value> getValue(const std::string& key) 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;
|
||||
mapbox::vector_tile::feature feature;
|
||||
};
|
||||
|
||||
class VectorTileLayer : public GeometryTileLayer {
|
||||
public:
|
||||
VectorTileLayer(protozero::pbf_reader, std::shared_ptr<const std::string>);
|
||||
VectorTileLayer(std::shared_ptr<const std::string> data, const protozero::data_view&);
|
||||
|
||||
std::size_t featureCount() const override { return features.size(); }
|
||||
std::unique_ptr<GeometryTileFeature> getFeature(std::size_t) const override;
|
||||
std::size_t featureCount() const override;
|
||||
std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) 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;
|
||||
std::shared_ptr<const std::string> data;
|
||||
mapbox::vector_tile::layer layer;
|
||||
};
|
||||
|
||||
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::unique_ptr<GeometryTileData> clone() const override;
|
||||
const GeometryTileLayer* getLayer(const std::string& name) const override;
|
||||
|
||||
std::vector<std::string> layerNames() const;
|
||||
|
||||
private:
|
||||
void parse() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<const std::string> data;
|
||||
mutable bool parsed = false;
|
||||
|
||||
Reference in New Issue
Block a user