separate bucket render properties and change naming
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<float> 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<float> 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<float> 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<BucketFillDescription, BucketLineDescription, BucketIconDescription,
|
||||
BucketTextDescription, BucketRasterDescription> 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);
|
||||
|
||||
@@ -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<Coordinate> &line,
|
||||
const BucketGeometryDescription &info,
|
||||
const BucketTextDescription &info,
|
||||
const GlyphPositions &face,
|
||||
const Shaping &shaping);
|
||||
|
||||
|
||||
@@ -166,24 +166,38 @@ void TileParser::addBucketFeatures(Bucket &bucket, const VectorTileLayer &layer,
|
||||
|
||||
std::unique_ptr<Bucket> TileParser::createFillBucket(const VectorTileLayer &layer,
|
||||
const BucketDescription &bucket_desc) {
|
||||
if (!bucket_desc.render.valid() || !bucket_desc.render.is<BucketFillDescription>()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<FillBucket> bucket = std::make_unique<FillBucket>(
|
||||
tile.fillVertexBuffer, tile.triangleElementsBuffer, tile.lineElementsBuffer, bucket_desc);
|
||||
tile.fillVertexBuffer, tile.triangleElementsBuffer, tile.lineElementsBuffer,
|
||||
bucket_desc.render.get<BucketFillDescription>());
|
||||
addBucketFeatures(bucket, layer, bucket_desc);
|
||||
return obsolete() ? nullptr : std::move(bucket);
|
||||
}
|
||||
|
||||
std::unique_ptr<Bucket> TileParser::createLineBucket(const VectorTileLayer &layer,
|
||||
const BucketDescription &bucket_desc) {
|
||||
if (!bucket_desc.render.valid() || !bucket_desc.render.is<BucketLineDescription>()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<LineBucket> bucket = std::make_unique<LineBucket>(
|
||||
tile.lineVertexBuffer, tile.triangleElementsBuffer, tile.pointElementsBuffer, bucket_desc);
|
||||
tile.lineVertexBuffer, tile.triangleElementsBuffer, tile.pointElementsBuffer,
|
||||
bucket_desc.render.get<BucketLineDescription>());
|
||||
addBucketFeatures(bucket, layer, bucket_desc);
|
||||
return obsolete() ? nullptr : std::move(bucket);
|
||||
}
|
||||
|
||||
std::unique_ptr<Bucket> TileParser::createIconBucket(const VectorTileLayer &layer,
|
||||
const BucketDescription &bucket_desc) {
|
||||
std::unique_ptr<IconBucket> bucket =
|
||||
std::make_unique<IconBucket>(tile.iconVertexBuffer, bucket_desc);
|
||||
if (!bucket_desc.render.valid() || !bucket_desc.render.is<BucketIconDescription>()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<IconBucket> bucket = std::make_unique<IconBucket>(
|
||||
tile.iconVertexBuffer, bucket_desc.render.get<BucketIconDescription>());
|
||||
addBucketFeatures(bucket, layer, bucket_desc, *spriteAtlas);
|
||||
return obsolete() ? nullptr : std::move(bucket);
|
||||
}
|
||||
@@ -192,8 +206,15 @@ typedef std::pair<uint16_t, uint16_t> GlyphRange;
|
||||
|
||||
std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer &layer,
|
||||
const BucketDescription &bucket_desc) {
|
||||
|
||||
if (!bucket_desc.render.valid() || !bucket_desc.render.is<BucketTextDescription>()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const BucketTextDescription &properties = bucket_desc.render.get<BucketTextDescription>();
|
||||
|
||||
std::unique_ptr<TextBucket> bucket = std::make_unique<TextBucket>(
|
||||
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<Bucket> 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<Bucket> 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<std::u32string, pbf> &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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = "<circle>";
|
||||
}
|
||||
|
||||
const Rect<uint16_t> rect = sprite_atlas.getIcon(geometry.size, field);
|
||||
const Rect<uint16_t> 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;
|
||||
|
||||
|
||||
@@ -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<Coordinate>& 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<Coordinate>& 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<Coordinate>& vertices) {
|
||||
if (currentVertex) prevVertex = currentVertex;
|
||||
|
||||
currentVertex = vertices[i];
|
||||
currentJoin = join;
|
||||
currentJoin = properties.join;
|
||||
|
||||
if (prevVertex) distance += util::dist<double>(currentVertex, prevVertex);
|
||||
|
||||
@@ -164,7 +161,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& 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<Coordinate>& 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<BucketLineDescription>();
|
||||
|
||||
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<BucketIconDescription>();
|
||||
|
||||
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<BucketTextDescription>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -244,19 +244,11 @@ void getGlyphs(PlacedGlyphs &glyphs, GlyphBoxes &boxes,
|
||||
}
|
||||
}
|
||||
|
||||
void Placement::addFeature(TextBucket& bucket,
|
||||
const std::vector<Coordinate> &line,
|
||||
const BucketGeometryDescription &info,
|
||||
const GlyphPositions &face,
|
||||
void Placement::addFeature(TextBucket &bucket, const std::vector<Coordinate> &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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user