#include #include #include #include #include #include #include #include #include #include #include #include #include using namespace llmr; TileParser::TileParser(const std::string& data, VectorTileData& tile, const Style& style, GlyphAtlas& glyphAtlas, GlyphStore &glyphStore, SpriteAtlas &spriteAtlas) : vector_data(pbf((const uint8_t *)data.data(), data.size())), tile(tile), style(style), glyphAtlas(glyphAtlas), glyphStore(glyphStore), spriteAtlas(spriteAtlas), placement(tile.id.z) { parseGlyphs(); parseStyleLayers(style.layers); } bool TileParser::obsolete() const { return tile.state == TileData::State::obsolete; } void TileParser::parseGlyphs() { for (const std::pair pair : vector_data.faces) { const std::string &name = pair.first; const VectorTileFace &face = pair.second; GlyphPositions &glyphs = faces[name]; for (const VectorTileGlyph &glyph : face.glyphs) { const Rect rect = glyphAtlas.addGlyph(tile.id.to_uint64(), name, glyph); glyphs.emplace(glyph.id, Glyph{rect, glyph.metrics}); } } } 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 (layer_desc.bucket_name == "background") { // background is a special, fake bucket continue; } else if (bucket_it != style.buckets.end()) { // Only create the new bucket if we have an actual specification // for it. std::unique_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] = std::move(bucket); } } else { // There is no proper specification for this bucket, even though // it is referenced in the stylesheet. if (debug::tileParseWarnings) { fprintf(stderr, "Stylesheet specifies bucket %s, but it is not defined\n", layer_desc.bucket_name.c_str()); } } } } } } std::unique_ptr TileParser::createBucket(const BucketDescription& bucket_desc) { auto layer_it = vector_data.layers.find(bucket_desc.source_layer); if (layer_it != vector_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::Icon) { return createIconBucket(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. if (debug::tileParseWarnings) { fprintf(stderr, "[WARNING] layer '%s' does not exist in tile %d/%d/%d of source '%s'\n", bucket_desc.source_layer.c_str(), tile.id.z, tile.id.x, tile.id.y, bucket_desc.source_name.c_str()); } } 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); } else if (debug::tileParseWarnings) { fprintf(stderr, "[WARNING] geometry is empty\n"); } } } } template void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc, Args&& ...args) { FilteredVectorTileLayer filtered_layer(layer, bucket_desc); for (const pbf& feature_pbf : filtered_layer) { if (obsolete()) return; bucket->addFeature({ feature_pbf, layer }, std::forward(args)...); } } std::unique_ptr TileParser::createFillBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc) { std::unique_ptr bucket = std::make_unique( tile.fillVertexBuffer, tile.triangleElementsBuffer, tile.lineElementsBuffer, bucket_desc); addBucketFeatures(bucket, layer, bucket_desc); return obsolete() ? nullptr : std::move(bucket); } std::unique_ptr TileParser::createLineBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc) { std::unique_ptr bucket = std::make_unique( tile.lineVertexBuffer, tile.triangleElementsBuffer, tile.pointElementsBuffer, bucket_desc); addBucketFeatures(bucket, layer, bucket_desc); return obsolete() ? nullptr : std::move(bucket); } std::unique_ptr TileParser::createIconBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc) { std::unique_ptr bucket = std::make_unique( tile.iconVertexBuffer, bucket_desc); addBucketFeatures(bucket, layer, bucket_desc, spriteAtlas); return obsolete() ? nullptr : std::move(bucket); } typedef std::pair GlyphRange; std::unique_ptr TileParser::createTextBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc) { std::unique_ptr bucket = std::make_unique( tile.textVertexBuffer, tile.triangleElementsBuffer, bucket_desc, placement); // Determine and load glyph ranges { std::set ranges; FilteredVectorTileLayer filtered_layer(layer, bucket_desc); for (const pbf& feature_pbf : filtered_layer) { if (obsolete()) return nullptr; VectorTileFeature feature { feature_pbf, layer }; auto it_prop = feature.properties.find(bucket_desc.geometry.field); if (it_prop == feature.properties.end()) { // feature does not have the correct property if (debug::labelTextMissingWarning) { fprintf(stderr, "[WARNING] feature doesn't have property '%s' required for labelling\n", bucket_desc.geometry.field.c_str()); } continue; } const std::string string = toString(it_prop->second); // Loop through all characters of this text and collect unique codepoints. for (uint32_t chr : string) { ranges.insert(getGlyphRange(chr)); } } glyphStore.waitForGlyphRanges(bucket_desc.geometry.font, ranges); } // Create a copy! const FontStack &fontStack = glyphStore.getFontStack(bucket_desc.geometry.font); // Shape and place all labels. { FilteredVectorTileLayer filtered_layer(layer, bucket_desc); for (const pbf& feature_pbf : filtered_layer) { if (obsolete()) return nullptr; VectorTileFeature feature { feature_pbf, layer }; auto it_prop = feature.properties.find(bucket_desc.geometry.field); if (it_prop == feature.properties.end()) { // feature does not have the correct property if (debug::labelTextMissingWarning) { fprintf(stderr, "[WARNING] feature doesn't have property '%s' required for labelling\n", bucket_desc.geometry.field.c_str()); } continue; } uint32_t i = 0; uint32_t x = 0; const std::string string = toString(it_prop->second); std::map shaping; std::map metrics = fontStack.getMetrics(); // fprintf(stderr, "%s\n", string.c_str()); // Loop through all characters of this label and shape. for (uint32_t chr : string) { // Can we reuse GlyphPlacement here? First arg is a faces index ... GlyphPlacement shaped = GlyphPlacement(0, chr, x, 0); // No idea how to properly put together a shaping std::map ... shaping.emplace(i, shaped); i++; x += metrics[chr].advance; } // TODO: Shape label // TODO: Place label // Can faces here be a std::map of fontstacks? // It looks like nearly the same interface through the rest // of the stack. std::unique_ptr bucket = std::make_unique( tile.textVertexBuffer, tile.triangleElementsBuffer, bucket_desc, placement); addBucketFeatures(bucket, layer, bucket_desc, faces, shaping); } } return std::move(bucket); }