#include #include #include #include #include #include #include #include namespace mbgl { class VectorTileLayer; using packed_iter_type = protozero::iterator_range; struct VectorTileLayerData { VectorTileLayerData(std::shared_ptr); // Hold a reference to the underlying pbf data that backs the lazily-built // components of the owning VectorTileLayer and VectorTileFeature objects std::shared_ptr data; uint32_t version = 1; uint32_t extent = 4096; std::unordered_map keysMap; std::vector> keys; std::vector values; }; class VectorTileFeature : public GeometryTileFeature { public: VectorTileFeature(protozero::pbf_reader, std::shared_ptr layerData); FeatureType getType() const override { return type; } optional getValue(const std::string&) const override; std::unordered_map getProperties() const override; optional getID() const override; GeometryCollection getGeometries() const override; private: std::shared_ptr layerData; optional 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); std::size_t featureCount() const override { return features.size(); } std::unique_ptr getFeature(std::size_t) const override; std::string getName() const override; private: friend class VectorTileData; friend class VectorTileFeature; std::string name; std::vector features; std::shared_ptr data; }; class VectorTileData : public GeometryTileData { public: VectorTileData(std::shared_ptr data); std::unique_ptr clone() const override { return std::make_unique(*this); } const GeometryTileLayer* getLayer(const std::string&) const override; private: std::shared_ptr data; mutable bool parsed = false; mutable std::unordered_map layers; }; VectorTile::VectorTile(const OverscaledTileID& id_, std::string sourceID_, const TileParameters& parameters, const Tileset& tileset) : GeometryTile(id_, sourceID_, parameters), loader(*this, id_, parameters, tileset) { } void VectorTile::setNecessity(Necessity necessity) { loader.setNecessity(necessity); } void VectorTile::setData(std::shared_ptr data_, optional modified_, optional expires_) { modified = modified_; expires = expires_; GeometryTile::setData(data_ ? std::make_unique(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(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 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(feature_pbf.get_enum()); break; case 4: // geometry geometry_iter = feature_pbf.get_packed_uint32(); break; default: feature_pbf.skip(); break; } } } optional VectorTileFeature::getValue(const std::string& key) const { auto keyIter = layerData->keysMap.find(key); if (keyIter == layerData->keysMap.end()) { return optional(); } auto start_itr = tags_iter.begin(); const auto & end_itr = tags_iter.end(); while (start_itr != end_itr) { auto tag_key = static_cast(*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(*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(); } std::unordered_map VectorTileFeature::getProperties() const { std::unordered_map properties; auto start_itr = tags_iter.begin(); const auto & end_itr = tags_iter.end(); while (start_itr != end_itr) { auto tag_key = static_cast(*start_itr++); if (start_itr == end_itr) { throw std::runtime_error("uneven number of feature tag ids"); } auto tag_val = static_cast(*start_itr++); properties[layerData->keys.at(tag_key)] = layerData->values.at(tag_val); } return properties; } optional 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(*g_itr++); cmd = cmd_length & 0x7; length = cmd_length >> 3; } --length; if (cmd == 1 || cmd == 2) { x += protozero::decode_zigzag32(static_cast(*g_itr++)); y += protozero::decode_zigzag32(static_cast(*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 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 pbfData) : data(std::move(pbfData)) {} VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf, std::shared_ptr pbfData) : data(std::make_shared(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(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 VectorTileLayer::getFeature(std::size_t i) const { return std::make_unique(features.at(i), data); } std::string VectorTileLayer::getName() const { return name; } } // namespace mbgl