diff --git a/include/llmr/renderer/fill_bucket.hpp b/include/llmr/renderer/fill_bucket.hpp index 2d1f61d6a..95d53ce75 100644 --- a/include/llmr/renderer/fill_bucket.hpp +++ b/include/llmr/renderer/fill_bucket.hpp @@ -41,7 +41,7 @@ public: FillBucket(FillVertexBuffer& vertexBuffer, TriangleElementsBuffer& triangleElementsBuffer, LineElementsBuffer& lineElementsBuffer, - const BucketDescription& bucket_desc); + const BucketFillDescription& properties); ~FillBucket(); virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id); @@ -55,7 +55,7 @@ public: void drawVertices(OutlineShader& shader); public: - const BucketGeometryDescription geom_desc; + const BucketFillDescription &properties; private: TESSalloc *allocator; diff --git a/include/llmr/renderer/icon_bucket.hpp b/include/llmr/renderer/icon_bucket.hpp index 0878622ec..5d653a15f 100644 --- a/include/llmr/renderer/icon_bucket.hpp +++ b/include/llmr/renderer/icon_bucket.hpp @@ -26,7 +26,7 @@ class VectorTileFeature; class IconBucket : public Bucket { public: IconBucket(IconVertexBuffer& vertexBuffer, - const BucketDescription& bucket_desc); + const BucketIconDescription& properties); virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id); virtual bool hasData() const; @@ -37,7 +37,7 @@ public: void drawIcons(DotShader& shader); public: - const BucketGeometryDescription geometry; + const BucketIconDescription &properties; private: diff --git a/include/llmr/renderer/line_bucket.hpp b/include/llmr/renderer/line_bucket.hpp index e28c85b7a..d7253ace9 100644 --- a/include/llmr/renderer/line_bucket.hpp +++ b/include/llmr/renderer/line_bucket.hpp @@ -25,7 +25,7 @@ public: LineBucket(LineVertexBuffer& vertexBuffer, TriangleElementsBuffer& triangleElementsBuffer, PointElementsBuffer& pointElementsBuffer, - const BucketDescription& bucket_desc); + const BucketLineDescription& properties); virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id); virtual bool hasData() const; @@ -39,7 +39,7 @@ public: void drawPoints(LinejoinShader& shader); public: - const BucketGeometryDescription geometry; + const BucketLineDescription &properties; private: diff --git a/include/llmr/renderer/text_bucket.hpp b/include/llmr/renderer/text_bucket.hpp index 36c50ae7f..566ed459e 100644 --- a/include/llmr/renderer/text_bucket.hpp +++ b/include/llmr/renderer/text_bucket.hpp @@ -28,7 +28,7 @@ public: TextBucket( TextVertexBuffer &vertexBuffer, TriangleElementsBuffer &triangleElementsBuffer, - const BucketDescription &bucket_desc, Placement &placement); + const BucketTextDescription &properties, Placement &placement); virtual void render(Painter &painter, const std::string &layer_name, const Tile::ID &id); virtual bool hasData() const; @@ -43,7 +43,7 @@ public: void drawGlyphs(TextShader &shader); public: - const BucketGeometryDescription &geom_desc; + const BucketTextDescription &properties; private: TextVertexBuffer& vertexBuffer; diff --git a/include/llmr/style/bucket_description.hpp b/include/llmr/style/bucket_description.hpp index f77ff5170..a2386e436 100644 --- a/include/llmr/style/bucket_description.hpp +++ b/include/llmr/style/bucket_description.hpp @@ -82,31 +82,50 @@ inline float verticalAlignmentType(const std::string& alignment) { else return 0.5; }; +class BucketFillDescription { +public: +}; -class BucketGeometryDescription { +class BucketLineDescription { public: CapType cap = CapType::None; JoinType join = JoinType::None; - std::string font; - std::string field; - std::string icon; - float size = 0.0f; float miter_limit = 2.0f; float round_limit = 1.0f; - TextPathType path = TextPathType::Horizontal; - float alignment = 0.5; - float vertical_alignment = 0.5; - float line_height = 1.2 * 24; - float max_width = 15.0f * 24; - float letter_spacing = 0; - vec2 translate {0, 0}; - float padding = 2.0f; - float textMinDistance = 250.0f; - float rotate = 0.0f; // what is this? - float maxAngleDelta = M_PI; - bool alwaysVisible = false; }; +class BucketIconDescription { +public: + uint16_t size = 16; + vec2 translate {0, 0}; + std::string icon; +}; + +class BucketTextDescription { +public: + std::string field; + TextPathType path = TextPathType::Horizontal; + std::string font; + float max_size = 16.0f; + float max_width = 15.0f * 24; + float line_height = 1.2f * 24; + float letter_spacing = 0.0f; + float alignment = 0.5f; + float vertical_alignment = 0.5; + vec2 translate {0, 0}; + float max_angle_delta = M_PI; + float min_distance = 250.0f; + float rotate = 0.0f; // what is this? + float padding = 2.0f; + bool always_visible = false; +}; + +class BucketRasterDescription { +public: +}; + +typedef util::variant BucketRenderDescription; class BucketDescription { public: @@ -120,7 +139,7 @@ public: PropertyFilterExpression filter = std::true_type(); // Specifies how the geometry for this bucket should be created - BucketGeometryDescription geometry; + BucketRenderDescription render; }; std::ostream& operator<<(std::ostream&, const BucketDescription& bucket); diff --git a/include/llmr/text/placement.hpp b/include/llmr/text/placement.hpp index fb3ddaa66..ec7462aa9 100644 --- a/include/llmr/text/placement.hpp +++ b/include/llmr/text/placement.hpp @@ -10,14 +10,14 @@ namespace llmr { class TextBucket; -class BucketGeometryDescription; +class BucketTextDescription; class Placement { public: Placement(int8_t zoom); void addFeature(TextBucket &bucket, const std::vector &line, - const BucketGeometryDescription &info, + const BucketTextDescription &info, const GlyphPositions &face, const Shaping &shaping); diff --git a/src/map/tile_parser.cpp b/src/map/tile_parser.cpp index 21d2bf1bf..1c1268c80 100644 --- a/src/map/tile_parser.cpp +++ b/src/map/tile_parser.cpp @@ -166,24 +166,38 @@ void TileParser::addBucketFeatures(Bucket &bucket, const VectorTileLayer &layer, std::unique_ptr TileParser::createFillBucket(const VectorTileLayer &layer, const BucketDescription &bucket_desc) { + if (!bucket_desc.render.valid() || !bucket_desc.render.is()) { + return nullptr; + } + std::unique_ptr bucket = std::make_unique( - tile.fillVertexBuffer, tile.triangleElementsBuffer, tile.lineElementsBuffer, bucket_desc); + tile.fillVertexBuffer, tile.triangleElementsBuffer, tile.lineElementsBuffer, + bucket_desc.render.get()); addBucketFeatures(bucket, layer, bucket_desc); return obsolete() ? nullptr : std::move(bucket); } std::unique_ptr TileParser::createLineBucket(const VectorTileLayer &layer, const BucketDescription &bucket_desc) { + if (!bucket_desc.render.valid() || !bucket_desc.render.is()) { + return nullptr; + } + std::unique_ptr bucket = std::make_unique( - tile.lineVertexBuffer, tile.triangleElementsBuffer, tile.pointElementsBuffer, bucket_desc); + tile.lineVertexBuffer, tile.triangleElementsBuffer, tile.pointElementsBuffer, + bucket_desc.render.get()); 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); + if (!bucket_desc.render.valid() || !bucket_desc.render.is()) { + return nullptr; + } + + std::unique_ptr bucket = std::make_unique( + tile.iconVertexBuffer, bucket_desc.render.get()); addBucketFeatures(bucket, layer, bucket_desc, *spriteAtlas); return obsolete() ? nullptr : std::move(bucket); } @@ -192,8 +206,15 @@ typedef std::pair GlyphRange; std::unique_ptr TileParser::createTextBucket(const VectorTileLayer &layer, const BucketDescription &bucket_desc) { + + if (!bucket_desc.render.valid() || !bucket_desc.render.is()) { + return nullptr; + } + + const BucketTextDescription &properties = bucket_desc.render.get(); + std::unique_ptr bucket = std::make_unique( - tile.textVertexBuffer, tile.triangleElementsBuffer, bucket_desc, placement); + tile.textVertexBuffer, tile.triangleElementsBuffer, properties, placement); util::utf8_to_utf32 ucs4conv; @@ -210,7 +231,7 @@ std::unique_ptr TileParser::createTextBucket(const VectorTileLayer &laye VectorTileFeature feature{feature_pbf, layer}; const std::u32string string = ucs4conv.convert( - util::replaceTokens(bucket_desc.geometry.field, feature.properties)); + util::replaceTokens(properties.field, feature.properties)); // Loop through all characters of this text and collect unique codepoints. for (uint32_t chr : string) { @@ -220,23 +241,23 @@ std::unique_ptr TileParser::createTextBucket(const VectorTileLayer &laye labels.emplace_back(string, feature.geometry); } - glyphStore->waitForGlyphRanges(bucket_desc.geometry.font, ranges); + glyphStore->waitForGlyphRanges(properties.font, ranges); } // Create a copy! - const FontStack &fontStack = glyphStore->getFontStack(bucket_desc.geometry.font); + const FontStack &fontStack = glyphStore->getFontStack(properties.font); GlyphPositions face; // Shape and place all labels. for (const std::pair &label : labels) { // Shape labels. - const Shaping shaping = fontStack.getShaping(label.first, bucket_desc.geometry.max_width, - bucket_desc.geometry.line_height, bucket_desc.geometry.alignment, - bucket_desc.geometry.vertical_alignment, bucket_desc.geometry.letter_spacing); + const Shaping shaping = fontStack.getShaping(label.first, properties.max_width, + properties.line_height, properties.alignment, + properties.vertical_alignment, properties.letter_spacing); // Place labels. - addGlyph(tile.id.to_uint64(), bucket_desc.geometry.font, label.first, fontStack, *glyphAtlas, + addGlyph(tile.id.to_uint64(), properties.font, label.first, fontStack, *glyphAtlas, face); bucket->addFeature(label.second, face, shaping); diff --git a/src/renderer/fill_bucket.cpp b/src/renderer/fill_bucket.cpp index 702156659..d7ba048a5 100644 --- a/src/renderer/fill_bucket.cpp +++ b/src/renderer/fill_bucket.cpp @@ -30,30 +30,26 @@ void FillBucket::free(void *, void *ptr) { ::free(ptr); } -FillBucket::FillBucket(FillVertexBuffer& vertexBuffer, - TriangleElementsBuffer& triangleElementsBuffer, - LineElementsBuffer& lineElementsBuffer, - const BucketDescription& bucket_desc) - : geom_desc(bucket_desc.geometry), - allocator(new TESSalloc { - &alloc, - &realloc, - &free, - nullptr, // userData - 64, // meshEdgeBucketSize - 64, // meshVertexBucketSize - 32, // meshFaceBucketSize - 64, // dictNodeBucketSize - 8, // regionBucketSize - 128, // extraVertices allocated for the priority queue. -}), -tesselator(tessNewTess(allocator)), -vertexBuffer(vertexBuffer), -triangleElementsBuffer(triangleElementsBuffer), -lineElementsBuffer(lineElementsBuffer), -vertex_start(vertexBuffer.index()), -triangle_elements_start(triangleElementsBuffer.index()), -line_elements_start(lineElementsBuffer.index()) { +FillBucket::FillBucket(FillVertexBuffer &vertexBuffer, + TriangleElementsBuffer &triangleElementsBuffer, + LineElementsBuffer &lineElementsBuffer, + const BucketFillDescription &properties) + : properties(properties), + allocator(new TESSalloc{&alloc, &realloc, &free, nullptr, // userData + 64, // meshEdgeBucketSize + 64, // meshVertexBucketSize + 32, // meshFaceBucketSize + 64, // dictNodeBucketSize + 8, // regionBucketSize + 128, // extraVertices allocated for the priority queue. + }), + tesselator(tessNewTess(allocator)), + vertexBuffer(vertexBuffer), + triangleElementsBuffer(triangleElementsBuffer), + lineElementsBuffer(lineElementsBuffer), + vertex_start(vertexBuffer.index()), + triangle_elements_start(triangleElementsBuffer.index()), + line_elements_start(lineElementsBuffer.index()) { assert(tesselator); } diff --git a/src/renderer/icon_bucket.cpp b/src/renderer/icon_bucket.cpp index fd43f6951..b6c324d2a 100644 --- a/src/renderer/icon_bucket.cpp +++ b/src/renderer/icon_bucket.cpp @@ -19,8 +19,8 @@ struct geometry_too_long_exception : std::exception {}; using namespace llmr; IconBucket::IconBucket(IconVertexBuffer& vertexBuffer, - const BucketDescription& bucket_desc) - : geometry(bucket_desc.geometry), + const BucketIconDescription& properties) + : properties(properties), vertexBuffer(vertexBuffer), vertex_start(vertexBuffer.index()) { } @@ -28,15 +28,15 @@ IconBucket::IconBucket(IconVertexBuffer& vertexBuffer, void IconBucket::addFeature(const VectorTileFeature &feature, SpriteAtlas &sprite_atlas) { std::string field; - if (geometry.field.size()) { - field = util::replaceTokens(geometry.field, feature.properties); + if (properties.icon.size()) { + field = util::replaceTokens(properties.icon, feature.properties); } if (!field.size()) { field = ""; } - const Rect rect = sprite_atlas.getIcon(geometry.size, field); + const Rect rect = sprite_atlas.getIcon(properties.size, field); const uint16_t tx = rect.x + rect.w / 2; const uint16_t ty = rect.y + rect.h / 2; diff --git a/src/renderer/line_bucket.cpp b/src/renderer/line_bucket.cpp index 58dde3f50..d6d3dcf77 100644 --- a/src/renderer/line_bucket.cpp +++ b/src/renderer/line_bucket.cpp @@ -20,8 +20,8 @@ using namespace llmr; LineBucket::LineBucket(LineVertexBuffer& vertexBuffer, TriangleElementsBuffer& triangleElementsBuffer, PointElementsBuffer& pointElementsBuffer, - const BucketDescription& bucket_desc) - : geometry(bucket_desc.geometry), + const BucketLineDescription& properties) + : properties(properties), vertexBuffer(vertexBuffer), triangleElementsBuffer(triangleElementsBuffer), pointElementsBuffer(pointElementsBuffer), @@ -60,9 +60,6 @@ struct TriangleElement { typedef uint16_t PointElement; void LineBucket::addGeometry(const std::vector& vertices) { - const JoinType join = geometry.join; - const CapType cap = geometry.cap; - const float miterLimit = geometry.miter_limit; // TODO: use roundLimit // const float roundLimit = geometry.round_limit; @@ -80,8 +77,8 @@ void LineBucket::addGeometry(const std::vector& vertices) { return; } - CapType beginCap = cap; - CapType endCap = closed ? CapType::Butt : cap; + CapType beginCap = properties.cap; + CapType endCap = closed ? CapType::Butt : properties.cap; JoinType currentJoin = JoinType::None; @@ -111,7 +108,7 @@ void LineBucket::addGeometry(const std::vector& vertices) { if (currentVertex) prevVertex = currentVertex; currentVertex = vertices[i]; - currentJoin = join; + currentJoin = properties.join; if (prevVertex) distance += util::dist(currentVertex, prevVertex); @@ -164,7 +161,7 @@ void LineBucket::addGeometry(const std::vector& vertices) { // Switch to miter joins if the angle is very low. if (currentJoin != JoinType::Miter) { - if (std::fabs(joinAngularity) < 0.5 && roundness < miterLimit) { + if (std::fabs(joinAngularity) < 0.5 && roundness < properties.miter_limit) { currentJoin = JoinType::Miter; } } @@ -213,14 +210,14 @@ void LineBucket::addGeometry(const std::vector& vertices) { // The two normals are almost parallel. joinNormal.x = -nextNormal.y; joinNormal.y = nextNormal.x; - } else if (roundness > miterLimit) { + } else if (roundness > properties.miter_limit) { // If the miter grows too large, flip the direction to make a // bevel join. joinNormal.x = (prevNormal.x - nextNormal.x) / joinAngularity; joinNormal.y = (prevNormal.y - nextNormal.y) / joinAngularity; } - if (roundness > miterLimit) { + if (roundness > properties.miter_limit) { flip = -flip; } diff --git a/src/renderer/painter_icon.cpp b/src/renderer/painter_icon.cpp index 8ddf493d0..8c07349b2 100644 --- a/src/renderer/painter_icon.cpp +++ b/src/renderer/painter_icon.cpp @@ -41,7 +41,7 @@ void Painter::renderIcon(IconBucket& bucket, const std::string& layer_name, cons spriteAtlas.bind(map.getState().isChanging()); - const float iconSize = bucket.geometry.size * map.getState().getPixelRatio(); + const float iconSize = bucket.properties.size * map.getState().getPixelRatio(); iconShader->setSize(iconSize); #ifndef GL_ES_VERSION_2_0 glPointSize(iconSize); diff --git a/src/renderer/painter_text.cpp b/src/renderer/painter_text.cpp index 5307a12ac..baebe2669 100644 --- a/src/renderer/painter_text.cpp +++ b/src/renderer/painter_text.cpp @@ -20,7 +20,7 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons mat4 exMatrix; matrix::copy(exMatrix, projMatrix); - if (bucket.geom_desc.path == TextPathType::Curve) { + if (bucket.properties.path == TextPathType::Curve) { matrix::rotate_z(exMatrix, exMatrix, map.getState().getAngle()); } @@ -30,7 +30,7 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons } // If layerStyle.size > bucket.info.fontSize then labels may collide - float fontSize = std::fmin(properties.size, bucket.geom_desc.size); + float fontSize = std::fmin(properties.size, bucket.properties.max_size); matrix::scale(exMatrix, exMatrix, fontSize / 24.0f, fontSize / 24.0f, 1.0f); const mat4 &vtxMatrix = translatedMatrix(properties.translate, id, properties.translateAnchor); @@ -48,10 +48,10 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons float angle = std::round((map.getState().getAngle() + rotate) / M_PI * 128); // adjust min/max zooms for variable font sies - float zoomAdjust = log(fontSize / bucket.geom_desc.size) / log(2); + float zoomAdjust = log(fontSize / bucket.properties.max_size) / log(2); textShader->setAngle((int32_t)(angle + 256) % 256); - textShader->setFlip(bucket.geom_desc.path == TextPathType::Curve ? 1 : 0); + textShader->setFlip(bucket.properties.path == TextPathType::Curve ? 1 : 0); textShader->setZoom((map.getState().getNormalizedZoom() - zoomAdjust) * 10); // current zoom level // Label fading diff --git a/src/renderer/text_bucket.cpp b/src/renderer/text_bucket.cpp index da6de3817..bd18f0576 100644 --- a/src/renderer/text_bucket.cpp +++ b/src/renderer/text_bucket.cpp @@ -22,8 +22,8 @@ using namespace llmr; TextBucket::TextBucket( TextVertexBuffer &vertexBuffer, TriangleElementsBuffer &triangleElementsBuffer, - const BucketDescription &bucket_desc, Placement &placement) - : geom_desc(bucket_desc.geometry), + const BucketTextDescription &properties, Placement &placement) + : properties(properties), vertexBuffer(vertexBuffer), triangleElementsBuffer(triangleElementsBuffer), placement(placement), @@ -113,14 +113,14 @@ void TextBucket::addFeature(const pbf &geom_pbf, while ((cmd = geometry.next(x, y)) != Geometry::end) { if (cmd == Geometry::move_to) { if (!line.empty()) { - placement.addFeature(*this, line, geom_desc, face, shaping); + placement.addFeature(*this, line, properties, face, shaping); line.clear(); } } line.emplace_back(x, y); } if (line.size()) { - placement.addFeature(*this, line, geom_desc, face, shaping); + placement.addFeature(*this, line, properties, face, shaping); } } diff --git a/src/style/bucket_description.cpp b/src/style/bucket_description.cpp index b154c3a17..24d086f64 100644 --- a/src/style/bucket_description.cpp +++ b/src/style/bucket_description.cpp @@ -8,10 +8,6 @@ std::ostream& llmr::operator<<(std::ostream& os, const BucketDescription& bucket os << " - type: " << (uint32_t)bucket.type << std::endl; os << " - source_name: " << bucket.source_name << std::endl; os << " - source_layer: " << bucket.source_layer << std::endl; - os << " - cap: " << (uint32_t)bucket.geometry.cap << std::endl; - os << " - join: " << (uint32_t)bucket.geometry.join << std::endl; - os << " - font: " << bucket.geometry.font << std::endl; - os << " - size: " << bucket.geometry.size << std::endl; return os; } diff --git a/src/style/style_parser.cpp b/src/style/style_parser.cpp index b686c0ec5..edce8d2b6 100644 --- a/src/style/style_parser.cpp +++ b/src/style/style_parser.cpp @@ -73,134 +73,40 @@ PropertyFilterExpression StyleParser::parseFilterOrExpression(JSVal value) { BucketDescription StyleParser::parseBucket(JSVal value) { BucketDescription bucket; - rapidjson::Value::ConstMemberIterator itr = value.MemberBegin(); - for (; itr != value.MemberEnd(); ++itr) { - const std::string name(itr->name.GetString(), itr->name.GetStringLength()); - JSVal value = itr->value; - - if (name == "type") { - if (value.IsString()) { - bucket.type = bucketType({ value.GetString(), value.GetStringLength() }); - } else { - throw Style::exception("bucket type must be a string"); - } - } else if (name == "feature_type") { - if (value.IsString()) { - bucket.feature_type = bucketType({ value.GetString(), value.GetStringLength() }); - } else { - throw Style::exception("feature type must be a string"); - } - } else if (name == "source") { - if (value.IsString()) { - bucket.source_name = { value.GetString(), value.GetStringLength() }; - } else { - throw Style::exception("source name must be a string"); - } - } else if (name == "layer") { - if (value.IsString()) { - bucket.source_layer = { value.GetString(), value.GetStringLength() }; - } else { - throw Style::exception("layer name must be a string"); - } - } else if (name == "cap") { - if (value.IsString()) { - bucket.geometry.cap = capType({ value.GetString(), value.GetStringLength() }); - } else { - throw Style::exception("cap type must be a string"); - } - } else if (name == "join") { - if (value.IsString()) { - bucket.geometry.join = joinType({ value.GetString(), value.GetStringLength() }); - } else { - throw Style::exception("join type must be a string"); - } - } else if (name == "font") { - if (value.IsString()) { - bucket.geometry.font = { value.GetString(), value.GetStringLength() }; - } else { - throw Style::exception("font stack must be a string"); - } - } else if (name == "fontSize" || name == "size") { - if (value.IsNumber()) { - bucket.geometry.size = value.GetDouble(); - } else { - throw Style::exception("font size must be a number"); - } - } else if (name == "text_field" || name == "icon") { - if (value.IsString()) { - bucket.geometry.field = { value.GetString(), value.GetStringLength() }; - } else { - throw Style::exception("text field must be a string"); - } - } else if (name == "path") { - if (value.IsString()) { - bucket.geometry.path = textPathType({ value.GetString(), value.GetStringLength() }); - } else { - throw Style::exception("curve must be a string"); - } - } else if (name == "alignment") { - if (value.IsString()) { - bucket.geometry.alignment = alignmentType({ value.GetString(), value.GetStringLength() }); - } else { - throw Style::exception("alignment must be a string"); - } - } else if (name == "translate") { - if (value.IsArray()) { - bucket.geometry.translate.x = value[(rapidjson::SizeType)0].GetDouble() * 24; - bucket.geometry.translate.y = value[(rapidjson::SizeType)1].GetDouble() * -24; - } else { - throw Style::exception("translate must be a string"); - } - } else if (name == "verticalAlignment") { - if (value.IsString()) { - bucket.geometry.vertical_alignment = verticalAlignmentType({ value.GetString(), value.GetStringLength() }); - } else { - throw Style::exception("verticalAlignment must be a string"); - } - } else if (name == "lineHeight") { - if (value.IsNumber()) { - bucket.geometry.line_height = value.GetDouble() * 24; - } else { - throw Style::exception("line height must be a number"); - } - } else if (name == "maxWidth") { - if (value.IsNumber()) { - bucket.geometry.max_width = value.GetDouble() * 24; - } else { - throw Style::exception("max width must be a number"); - } - } else if (name == "letterSpacing") { - if (value.IsNumber()) { - bucket.geometry.letter_spacing = value.GetDouble() * 24; - } else { - throw Style::exception("letter spacing must be a number"); - } - } else if (name == "miterLimit") { - if (value.IsNumber()) { - bucket.geometry.miter_limit = value.GetDouble(); - } else { - throw Style::exception("miter limit must be a number"); - } - } else if (name == "roundLimit") { - if (value.IsNumber()) { - bucket.geometry.round_limit = value.GetDouble(); - } else { - throw Style::exception("round limit must be a number"); - } - } else if (name == "textMinDistance") { - if (value.IsNumber()) { - bucket.geometry.textMinDistance = value.GetDouble(); - } else { - throw Style::exception("text min distance must be a number"); - } - } else if (name == "maxAngleDelta") { - if (value.IsNumber()) { - bucket.geometry.maxAngleDelta = value.GetDouble(); - } else { - throw Style::exception("max angle delta must be a number"); - } + if (value.HasMember("type")) { + JSVal type = value["type"]; + if (type.IsString()) { + bucket.type = bucketType({ type.GetString(), type.GetStringLength() }); + } else { + throw Style::exception("bucket type must be a string"); } + } + if (value.HasMember("feature_type")) { + JSVal feature_type = value["feature_type"]; + if (feature_type.IsString()) { + bucket.feature_type = bucketType({ feature_type.GetString(), feature_type.GetStringLength() }); + } else { + throw Style::exception("feature type must be a string"); + } + } + + if (value.HasMember("source")) { + JSVal source = value["source"]; + if (source.IsString()) { + bucket.source_name = { source.GetString(), source.GetStringLength() }; + } else { + throw Style::exception("source name must be a string"); + } + } + + if (value.HasMember("layer")) { + JSVal source_layer = value["layer"]; + if (source_layer.IsString()) { + bucket.source_layer = { source_layer.GetString(), source_layer.GetStringLength() }; + } else { + throw Style::exception("layer name must be a string"); + } } if (value.HasMember("filter")) { @@ -214,6 +120,206 @@ BucketDescription StyleParser::parseBucket(JSVal value) { bucket.feature_type = bucket.type; } + switch (bucket.type) { + case BucketType::Fill: { + bucket.render = BucketFillDescription{}; + } break; + + case BucketType::Line: { + bucket.render = BucketLineDescription{}; + BucketLineDescription &render = bucket.render.get(); + + if (value.HasMember("cap")) { + JSVal property = value["cap"]; + if (property.IsString()) { + render.cap = capType({property.GetString(), property.GetStringLength()}); + } else { + throw Style::exception("cap type must be a string"); + } + } + if (value.HasMember("join")) { + JSVal property = value["join"]; + if (property.IsString()) { + render.join = joinType({property.GetString(), property.GetStringLength()}); + } else { + throw Style::exception("join type must be a string"); + } + } + if (value.HasMember("miterLimit")) { + JSVal property = value["miterLimit"]; + if (property.IsNumber()) { + render.miter_limit = property.GetDouble(); + } else { + throw Style::exception("miter limit must be a number"); + } + } + if (value.HasMember("roundLimit")) { + JSVal property = value["roundLimit"]; + if (property.IsNumber()) { + render.round_limit = property.GetDouble(); + } else { + throw Style::exception("round limit must be a number"); + } + } + } break; + + case BucketType::Icon: { + bucket.render = BucketIconDescription{}; + BucketIconDescription &render = bucket.render.get(); + + if (value.HasMember("size")) { + JSVal property = value["size"]; + if (property.IsNumber()) { + render.size = property.GetDouble(); + } else { + throw Style::exception("font size must be a number"); + } + } + + if (value.HasMember("icon")) { + JSVal property = value["icon"]; + if (property.IsString()) { + render.icon = {property.GetString(), property.GetStringLength()}; + } else { + throw Style::exception("text field must be a string"); + } + } + + if (value.HasMember("translate")) { + JSVal property = value["translate"]; + if (property.IsArray()) { + render.translate.x = property[(rapidjson::SizeType)0].GetDouble() * 24; + render.translate.y = property[(rapidjson::SizeType)1].GetDouble() * -24; + } else { + throw Style::exception("translate must be a string"); + } + } + } break; + + case BucketType::Text: { + bucket.render = BucketTextDescription{}; + BucketTextDescription &render = bucket.render.get(); + + if (value.HasMember("text_field")) { + JSVal property = value["text_field"]; + if (property.IsString()) { + render.field = {property.GetString(), property.GetStringLength()}; + } else { + throw Style::exception("text field must be a string"); + } + } + + if (value.HasMember("path")) { + JSVal property = value["path"]; + if (property.IsString()) { + render.path = textPathType({property.GetString(), property.GetStringLength()}); + } else { + throw Style::exception("curve must be a string"); + } + } + + if (value.HasMember("font")) { + JSVal property = value["font"]; + if (property.IsString()) { + render.font = {property.GetString(), property.GetStringLength()}; + } else { + throw Style::exception("font stack must be a string"); + } + } + + if (value.HasMember("fontSize")) { + JSVal property = value["fontSize"]; + if (property.IsNumber()) { + render.max_size = property.GetDouble(); + } else { + throw Style::exception("font size must be a number"); + } + } + + if (value.HasMember("maxWidth")) { + JSVal property = value["maxWidth"]; + if (property.IsNumber()) { + render.max_width = property.GetDouble() * 24; + } else { + throw Style::exception("max width must be a number"); + } + } + + if (value.HasMember("lineHeight")) { + JSVal property = value["lineHeight"]; + if (property.IsNumber()) { + render.line_height = property.GetDouble() * 24; + } else { + throw Style::exception("line height must be a number"); + } + } + + if (value.HasMember("letterSpacing")) { + JSVal property = value["letterSpacing"]; + if (property.IsNumber()) { + render.letter_spacing = property.GetDouble() * 24; + } else { + throw Style::exception("letter spacing must be a number"); + } + } + + if (value.HasMember("alignment")) { + JSVal property = value["alignment"]; + if (property.IsString()) { + render.alignment = + alignmentType({property.GetString(), property.GetStringLength()}); + } else { + throw Style::exception("alignment must be a string"); + } + } + + if (value.HasMember("verticalAlignment")) { + JSVal property = value["verticalAlignment"]; + if (property.IsString()) { + render.vertical_alignment = + verticalAlignmentType({property.GetString(), property.GetStringLength()}); + } else { + throw Style::exception("verticalAlignment must be a string"); + } + } + + if (value.HasMember("translate")) { + JSVal property = value["translate"]; + if (property.IsArray()) { + render.translate.x = property[(rapidjson::SizeType)0].GetDouble() * 24; + render.translate.y = property[(rapidjson::SizeType)1].GetDouble() * -24; + } else { + throw Style::exception("translate must be a string"); + } + } + + if (value.HasMember("maxAngleDelta")) { + JSVal property = value["maxAngleDelta"]; + if (property.IsNumber()) { + render.max_angle_delta = property.GetDouble(); + } else { + throw Style::exception("max angle delta must be a number"); + } + } + + if (value.HasMember("textMinDistance")) { + JSVal property = value["textMinDistance"]; + if (property.IsNumber()) { + render.min_distance = property.GetDouble(); + } else { + throw Style::exception("text min distance must be a number"); + } + } + } break; + + case BucketType::Raster: { + bucket.render = BucketRasterDescription{}; + } break; + + default: + break; + } + return bucket; } diff --git a/src/text/placement.cpp b/src/text/placement.cpp index b15e32496..112f1d79c 100644 --- a/src/text/placement.cpp +++ b/src/text/placement.cpp @@ -244,19 +244,11 @@ void getGlyphs(PlacedGlyphs &glyphs, GlyphBoxes &boxes, } } -void Placement::addFeature(TextBucket& bucket, - const std::vector &line, - const BucketGeometryDescription &info, - const GlyphPositions &face, +void Placement::addFeature(TextBucket &bucket, const std::vector &line, + const BucketTextDescription &info, const GlyphPositions &face, const Shaping &shaping) { - const bool horizontal = info.path == TextPathType::Horizontal; - const float padding = info.padding; - const float maxAngleDelta = info.maxAngleDelta; - const float textMinDistance = info.textMinDistance; - const float rotate = info.rotate; - const float fontScale = - (tileExtent / util::tileSize) / (glyphSize / info.size); + const float fontScale = (tileExtent / util::tileSize) / (glyphSize / info.max_size); Anchors anchors; @@ -268,7 +260,7 @@ void Placement::addFeature(TextBucket& bucket, } else { // Line labels - anchors = interpolate(line, textMinDistance, minScale); + anchors = interpolate(line, info.min_distance, minScale); // Sort anchors by segment so that we can start placement with // the anchors that can be shown at the lowest zoom levels. @@ -279,11 +271,10 @@ void Placement::addFeature(TextBucket& bucket, PlacedGlyphs glyphs; GlyphBoxes boxes; - getGlyphs(glyphs, boxes, anchor, info.translate, shaping, face, fontScale, horizontal, - line, maxAngleDelta, rotate); - PlacementProperty place = - collision.place(boxes, anchor, anchor.scale, maxPlacementScale, - padding, horizontal, info.alwaysVisible); + getGlyphs(glyphs, boxes, anchor, info.translate, shaping, face, fontScale, horizontal, line, + info.max_angle_delta, info.rotate); + PlacementProperty place = collision.place(boxes, anchor, anchor.scale, maxPlacementScale, + info.padding, horizontal, info.always_visible); if (place) { bucket.addGlyphs(glyphs, place.zoom, place.rotationRange, zoom - zOffset); }