From 99cef810acf70fae90ba4ab7f318de402ecefd4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Wed, 12 Mar 2014 17:47:40 +0100 Subject: [PATCH] move tile parsing to its own object --- include/llmr/map/tile_data.hpp | 18 +--- include/llmr/map/tile_parser.hpp | 36 +++++++ src/map/tile_data.cpp | 162 +------------------------------ src/map/tile_parser.cpp | 162 +++++++++++++++++++++++++++++++ 4 files changed, 204 insertions(+), 174 deletions(-) create mode 100644 include/llmr/map/tile_parser.hpp create mode 100644 src/map/tile_parser.cpp diff --git a/include/llmr/map/tile_data.hpp b/include/llmr/map/tile_data.hpp index 26387eb11..c136f334c 100644 --- a/include/llmr/map/tile_data.hpp +++ b/include/llmr/map/tile_data.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -24,9 +25,6 @@ class Style; class Bucket; class LayerDescription; class BucketDescription; -class VectorTile; -class VectorTileLayer; -class VectorTileFace; class FillVertexBuffer; class LineVertexBuffer; class PointVertexBuffer; @@ -59,26 +57,12 @@ public: TileData(Tile::ID id, const Style& style, GlyphAtlas& glyphAtlas); ~TileData(); - // Start loading the tile. void request(); - - // Other functions bool parse(); - void parseStyleLayers(const VectorTile& tile, const std::vector& layers); - std::shared_ptr createBucket(const VectorTile& tile, const BucketDescription& bucket_desc); - template void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc); - std::shared_ptr createFillBucket(const VectorTile& tile, const VectorTileLayer& layer, const BucketDescription& bucket_desc); - std::shared_ptr createLineBucket(const VectorTile& tile, const VectorTileLayer& layer, const BucketDescription& bucket_desc); - std::shared_ptr createPointBucket(const VectorTile& tile, const VectorTileLayer& layer, const BucketDescription& bucket_desc); - std::shared_ptr createTextBucket(const VectorTile& tile, const VectorTileLayer& layer, const BucketDescription& bucket_desc); - void cancel(); const std::string toString() const; -private: - void loadGlyphs(const std::map& faces); - public: const Tile::ID id; std::atomic state; diff --git a/include/llmr/map/tile_parser.hpp b/include/llmr/map/tile_parser.hpp new file mode 100644 index 000000000..dd3b11337 --- /dev/null +++ b/include/llmr/map/tile_parser.hpp @@ -0,0 +1,36 @@ +#ifndef LLMR_MAP_TILE_PARSER +#define LLMR_MAP_TILE_PARSER + +#include + +namespace llmr { + +typedef std::map> GlyphPositions; +typedef std::map FaceGlyphPositions; + +class TileParser { +public: + TileParser(const std::string& data, TileData& tile, const Style& style, GlyphAtlas& glyphAtlas); + +private: + bool obsolete() const; + void parseGlyphs(); + void parseStyleLayers(const std::vector& layers); + std::shared_ptr createBucket(const BucketDescription& bucket_desc); + std::shared_ptr createFillBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc); + std::shared_ptr createLineBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc); + std::shared_ptr createPointBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc); + std::shared_ptr createTextBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc); + template void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc); + +private: + const VectorTile data; + TileData& tile; + const Style& style; + GlyphAtlas& glyphAtlas; + FaceGlyphPositions rects; +}; + +} + +#endif diff --git a/src/map/tile_data.cpp b/src/map/tile_data.cpp index 4f835db2d..6ad1b7d5d 100644 --- a/src/map/tile_data.cpp +++ b/src/map/tile_data.cpp @@ -1,22 +1,13 @@ #include +#include -#include -#include #include #include #include #include #include -#include -#include -#include -#include -#include #include -#include -#include - using namespace llmr; TileData::TileData(Tile::ID id, const Style& style, GlyphAtlas& glyphAtlas) @@ -86,16 +77,10 @@ bool TileData::parse() { } try { - double parse_time_start = platform::time(); - VectorTile tile(pbf((const uint8_t *)data.data(), data.size())); - loadGlyphs(tile.faces); - parseStyleLayers(tile, style.layers); - // double parse_time = (platform::time() - parse_time_start) * 1000.0; - if (state == State::obsolete) { - // fprintf(stderr, "[%p] parsing [%d/%d/%d] cancelled after %8.3fms\n", this, id.z, id.x, id.y, parse_time); - } else { - // fprintf(stderr, "[%p] parsing [%d/%d/%d] took %8.3fms\n", this, id.z, id.x, id.y, parse_time); - } + // Parsing creates state that is encapsulated in TileParser. While parsing, + // the TileParser object writes results into this objects. All other state + // is going to be discarded afterwards. + TileParser parser(data, *this, style, glyphAtlas); } catch (const std::exception& ex) { fprintf(stderr, "[%p] exception [%d/%d/%d]... failed: %s\n", this, id.z, id.x, id.y, ex.what()); cancel(); @@ -110,140 +95,3 @@ bool TileData::parse() { return true; } - - -void TileData::loadGlyphs(const std::map& faces) { - std::map>> rects; - for (const std::pair pair : faces) { - const std::string& name = pair.first; - const VectorTileFace& face = pair.second; - - std::map>& rect = rects[name]; - - for (const VectorTileGlyph& glyph : face.glyphs) { - rect.emplace({ glyph.id, glyphAtlas.addGlyph((uint64_t)id, name, glyph) }); - } - } -} - -void TileData::parseStyleLayers(const VectorTile& tile, const std::vector& layers) { - for (const LayerDescription& layer_desc : layers) { - // Cancel early when parsing. - if (state == State::obsolete) { - return; - } - - if (layer_desc.child_layer.size()) { - // This is a layer group. - // TODO: create framebuffer - parseStyleLayers(tile, layer_desc.child_layer); - // TODO: render framebuffer on previous framebuffer - } else { - // This is a singular layer. Check if this bucket already exists. If not, - // parse this bucket. - auto bucket_it = buckets.find(layer_desc.bucket_name); - if (bucket_it == buckets.end()) { - auto bucket_it = style.buckets.find(layer_desc.bucket_name); - if (bucket_it != style.buckets.end()) { - // Only create the new bucket if we have an actual specification - // for it. - std::shared_ptr bucket = createBucket(tile, bucket_it->second); - if (bucket) { - // Bucket creation might fail because the data tile may not - // contain any data that falls into this bucket. - buckets[layer_desc.bucket_name] = bucket; - } - } else { - // There is no proper specification for this bucket, even though - // it is referenced in the stylesheet. - fprintf(stderr, "Stylesheet specifies bucket %s, but it is not defined\n", layer_desc.bucket_name.c_str()); - } - } - } - } -} - -std::shared_ptr TileData::createBucket(const VectorTile& tile, const BucketDescription& bucket_desc) { - auto layer_it = tile.layers.find(bucket_desc.source_layer); - if (layer_it != tile.layers.end()) { - const VectorTileLayer& layer = layer_it->second; - if (bucket_desc.type == BucketType::Fill) { - return createFillBucket(tile, layer, bucket_desc); - } else if (bucket_desc.type == BucketType::Line) { - return createLineBucket(tile, layer, bucket_desc); - } else if (bucket_desc.type == BucketType::Point) { - return createPointBucket(tile, layer, bucket_desc); - } else if (bucket_desc.type == BucketType::Text) { - return createTextBucket(tile, layer, bucket_desc); - } else { - throw std::runtime_error("unknown bucket type"); - } - } else { - // The layer specified in the bucket does not exist. Do nothing. - } - - return nullptr; -} - -template -void TileData::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc) { - FilteredVectorTileLayer filtered_layer(layer, bucket_desc); - for (pbf feature : filtered_layer) { - if (state == State::obsolete) return; - - while (feature.next(4)) { // geometry - pbf geometry_pbf = feature.message(); - if (geometry_pbf) { - bucket->addGeometry(geometry_pbf); - } - } - } -} - -std::shared_ptr TileData::createFillBucket(const VectorTile& tile, const VectorTileLayer& layer, const BucketDescription& bucket_desc) { - std::shared_ptr bucket = std::make_shared(fillVertexBuffer, triangleElementsBuffer, lineElementsBuffer, bucket_desc); - addBucketFeatures(bucket, layer, bucket_desc); - return state == State::obsolete ? nullptr : bucket; -} - -std::shared_ptr TileData::createLineBucket(const VectorTile& tile, const VectorTileLayer& layer, const BucketDescription& bucket_desc) { - std::shared_ptr bucket = std::make_shared(lineVertexBuffer, triangleElementsBuffer, pointElementsBuffer, bucket_desc); - addBucketFeatures(bucket, layer, bucket_desc); - return state == State::obsolete ? nullptr : bucket; -} - -std::shared_ptr TileData::createPointBucket(const VectorTile& tile, const VectorTileLayer& layer, const BucketDescription& bucket_desc) { - std::shared_ptr bucket = std::make_shared(pointVertexBuffer, bucket_desc); - addBucketFeatures(bucket, layer, bucket_desc); - return state == State::obsolete ? nullptr : bucket; -} - -std::shared_ptr TileData::createTextBucket(const VectorTile& tile, const VectorTileLayer& layer, const BucketDescription& bucket_desc) { - // Determine the correct text stack. - if (!layer.shaping.size()) { - return nullptr; - } - - // TODO: currently hardcoded to use the first font stack. - const std::map>& shaping = layer.shaping.begin()->second; - - std::vector faces; - for (const std::string& face : layer.faces) { - auto it = tile.faces.find(face); - if (it == tile.faces.end()) { - // This layer references an unknown face. - return nullptr; - } - faces.push_back(&it->second); - } - - std::shared_ptr bucket = std::make_shared(textVertexBuffer, triangleElementsBuffer, bucket_desc); - - FilteredVectorTileLayer filtered_layer(layer, bucket_desc); - for (const pbf& feature_pbf : filtered_layer) { - if (state == State::obsolete) return nullptr; - bucket->addFeature({ feature_pbf, layer }, faces, shaping); - } - - return bucket; -} diff --git a/src/map/tile_parser.cpp b/src/map/tile_parser.cpp new file mode 100644 index 000000000..366d8f772 --- /dev/null +++ b/src/map/tile_parser.cpp @@ -0,0 +1,162 @@ +#include + +#include +#include +#include +#include +#include + +using namespace llmr; + + +TileParser::TileParser(const std::string& data, TileData& tile, const Style& style, GlyphAtlas& glyphAtlas) + : data(pbf((const uint8_t *)data.data(), data.size())), + tile(tile), + style(style), + glyphAtlas(glyphAtlas) { + parseGlyphs(); + parseStyleLayers(style.layers); +} + +bool TileParser::obsolete() const { + return tile.state == TileData::State::obsolete; +} + +void TileParser::parseGlyphs() { + for (const std::pair pair : data.faces) { + const std::string& name = pair.first; + const VectorTileFace& face = pair.second; + + GlyphPositions& rect = rects[name]; + + for (const VectorTileGlyph& glyph : face.glyphs) { + rect.emplace(glyph.id, glyphAtlas.addGlyph((uint64_t)tile.id, name, glyph)); + } + } +} + +void TileParser::parseStyleLayers(const std::vector& layers) { + for (const LayerDescription& layer_desc : layers) { + // Cancel early when parsing. + if (obsolete()) { + return; + } + + if (layer_desc.child_layer.size()) { + // This is a layer group. + // TODO: create framebuffer + parseStyleLayers(layer_desc.child_layer); + // TODO: render framebuffer on previous framebuffer + } else { + // This is a singular layer. Check if this bucket already exists. If not, + // parse this bucket. + auto bucket_it = tile.buckets.find(layer_desc.bucket_name); + if (bucket_it == tile.buckets.end()) { + auto bucket_it = style.buckets.find(layer_desc.bucket_name); + if (bucket_it != style.buckets.end()) { + // Only create the new bucket if we have an actual specification + // for it. + std::shared_ptr bucket = createBucket(bucket_it->second); + if (bucket) { + // Bucket creation might fail because the data tile may not + // contain any data that falls into this bucket. + tile.buckets[layer_desc.bucket_name] = bucket; + } + } else { + // There is no proper specification for this bucket, even though + // it is referenced in the stylesheet. + fprintf(stderr, "Stylesheet specifies bucket %s, but it is not defined\n", layer_desc.bucket_name.c_str()); + } + } + } + } +} + +std::shared_ptr TileParser::createBucket(const BucketDescription& bucket_desc) { + auto layer_it = data.layers.find(bucket_desc.source_layer); + if (layer_it != data.layers.end()) { + const VectorTileLayer& layer = layer_it->second; + if (bucket_desc.type == BucketType::Fill) { + return createFillBucket(layer, bucket_desc); + } else if (bucket_desc.type == BucketType::Line) { + return createLineBucket(layer, bucket_desc); + } else if (bucket_desc.type == BucketType::Point) { + return createPointBucket(layer, bucket_desc); + } else if (bucket_desc.type == BucketType::Text) { + return createTextBucket(layer, bucket_desc); + } else { + throw std::runtime_error("unknown bucket type"); + } + } else { + // The layer specified in the bucket does not exist. Do nothing. + } + + return nullptr; +} + +template +void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc) { + FilteredVectorTileLayer filtered_layer(layer, bucket_desc); + for (pbf feature : filtered_layer) { + if (obsolete()) return; + + while (feature.next(4)) { // geometry + pbf geometry_pbf = feature.message(); + if (geometry_pbf) { + bucket->addGeometry(geometry_pbf); + } + } + } +} + +std::shared_ptr TileParser::createFillBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc) { + std::shared_ptr bucket = std::make_shared( + tile.fillVertexBuffer, tile.triangleElementsBuffer, tile.lineElementsBuffer, bucket_desc); + addBucketFeatures(bucket, layer, bucket_desc); + return obsolete() ? nullptr : bucket; +} + +std::shared_ptr TileParser::createLineBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc) { + std::shared_ptr bucket = std::make_shared( + tile.lineVertexBuffer, tile.triangleElementsBuffer, tile.pointElementsBuffer, bucket_desc); + addBucketFeatures(bucket, layer, bucket_desc); + return obsolete() ? nullptr : bucket; +} + +std::shared_ptr TileParser::createPointBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc) { + std::shared_ptr bucket = std::make_shared( + tile.pointVertexBuffer, bucket_desc); + addBucketFeatures(bucket, layer, bucket_desc); + return obsolete() ? nullptr : bucket; +} + +std::shared_ptr TileParser::createTextBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc) { + // Determine the correct text stack. + if (!layer.shaping.size()) { + return nullptr; + } + + // TODO: currently hardcoded to use the first font stack. + const std::map>& shaping = layer.shaping.begin()->second; + + std::vector faces; + for (const std::string& face : layer.faces) { + auto it = data.faces.find(face); + if (it == data.faces.end()) { + // This layer references an unknown face. + return nullptr; + } + faces.push_back(&it->second); + } + + std::shared_ptr bucket = std::make_shared( + tile.textVertexBuffer, tile.triangleElementsBuffer, bucket_desc); + + FilteredVectorTileLayer filtered_layer(layer, bucket_desc); + for (const pbf& feature_pbf : filtered_layer) { + if (obsolete()) return nullptr; + bucket->addFeature({ feature_pbf, layer }, faces, shaping); + } + + return bucket; +}