extract label shaping
This commit is contained in:
@@ -222,6 +222,7 @@ module.exports = {
|
||||
"opacity": [ "linear", 15, 0, 1.0, 0, 0.75 ]
|
||||
},
|
||||
"country_label": {
|
||||
"type": "text",
|
||||
"stroke": [ 1, 1, 1, 0.7 ],
|
||||
"color": "text"
|
||||
},
|
||||
|
||||
@@ -90,7 +90,6 @@ public:
|
||||
std::shared_ptr<TriangleElementsBuffer> triangleElementsBuffer;
|
||||
std::shared_ptr<LineElementsBuffer> lineElementsBuffer;
|
||||
std::shared_ptr<PointElementsBuffer> pointElementsBuffer;
|
||||
std::shared_ptr<TextElementsBuffer> textElementsBuffer;
|
||||
|
||||
// Holds the buckets of this tile.
|
||||
// They contain the location offsets in the buffers stored above
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <llmr/geometry/vao.hpp>
|
||||
#include <llmr/geometry/elements_buffer.hpp>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace llmr {
|
||||
@@ -14,6 +15,9 @@ class Style;
|
||||
class TextVertexBuffer;
|
||||
class TriangleElementsBuffer;
|
||||
class TextShader;
|
||||
class VectorTileFeature;
|
||||
class VectorTileFace;
|
||||
class VectorTilePlacement;
|
||||
struct Coordinate;
|
||||
struct pbf;
|
||||
|
||||
@@ -26,7 +30,13 @@ public:
|
||||
|
||||
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
|
||||
|
||||
void addGeometry(pbf& data);
|
||||
void addFeature(const VectorTileFeature& feature,
|
||||
const std::vector<const VectorTileFace *>& faces,
|
||||
const std::map<Value, std::vector<VectorTilePlacement>>& shaping);
|
||||
|
||||
void addLabel(const std::vector<Coordinate>& line,
|
||||
const std::vector<const VectorTileFace *>& faces,
|
||||
const std::vector<VectorTilePlacement>& placements);
|
||||
|
||||
bool empty() const;
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ inline BucketType bucketType(const std::string& type) {
|
||||
if (type == "fill") return BucketType::Fill;
|
||||
else if (type == "line") return BucketType::Line;
|
||||
else if (type == "point") return BucketType::Point;
|
||||
else if (type == "text") return BucketType::Text;
|
||||
else return BucketType::None;
|
||||
}
|
||||
|
||||
@@ -58,6 +59,7 @@ public:
|
||||
CapType cap = CapType::None;
|
||||
JoinType join = JoinType::None;
|
||||
std::string font;
|
||||
std::string text_field;
|
||||
float font_size = 0.0f;
|
||||
float miter_limit = 2.0f;
|
||||
float round_limit = 1.0f;
|
||||
@@ -65,6 +67,7 @@ public:
|
||||
|
||||
class BucketDescription {
|
||||
public:
|
||||
BucketType feature_type = BucketType::None;
|
||||
BucketType type = BucketType::None;
|
||||
|
||||
// Specify what data to pull into this bucket
|
||||
|
||||
@@ -14,6 +14,7 @@ public:
|
||||
std::map<std::string, FillClass> fill;
|
||||
std::map<std::string, LineClass> line;
|
||||
std::map<std::string, PointClass> point;
|
||||
std::map<std::string, TextClass> text;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -112,6 +112,14 @@ struct FillProperties {
|
||||
std::string image;
|
||||
};
|
||||
|
||||
struct TextClass {
|
||||
Color color = {{ 1, 1, 1, 1 }};
|
||||
};
|
||||
|
||||
struct TextProperties {
|
||||
Color color = {{ 1, 1, 1, 1 }};
|
||||
};
|
||||
|
||||
struct BackgroundClass {
|
||||
Color color = {{ 1, 1, 1, 1 }};
|
||||
};
|
||||
|
||||
@@ -56,6 +56,7 @@ public:
|
||||
std::map<std::string, FillProperties> fills;
|
||||
std::map<std::string, LineProperties> lines;
|
||||
std::map<std::string, PointProperties> points;
|
||||
std::map<std::string, TextProperties> texts;
|
||||
} computed;
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ private:
|
||||
FillClass parseFillClass(JSVal value);
|
||||
LineClass parseLineClass(JSVal value);
|
||||
PointClass parsePointClass(JSVal value);
|
||||
TextClass parseTextClass(JSVal value);
|
||||
BackgroundClass parseBackgroundClass(JSVal value);
|
||||
|
||||
bool parseBoolean(JSVal value);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <llmr/geometry/fill_buffer.hpp>
|
||||
#include <llmr/geometry/line_buffer.hpp>
|
||||
#include <llmr/geometry/point_buffer.hpp>
|
||||
#include <llmr/geometry/text_buffer.hpp>
|
||||
#include <llmr/geometry/elements_buffer.hpp>
|
||||
#include <llmr/renderer/fill_bucket.hpp>
|
||||
#include <llmr/renderer/line_bucket.hpp>
|
||||
@@ -24,6 +25,7 @@ TileData::TileData(Tile::ID id, const Style& style)
|
||||
fillVertexBuffer(std::make_shared<FillVertexBuffer>()),
|
||||
lineVertexBuffer(std::make_shared<LineVertexBuffer>()),
|
||||
pointVertexBuffer(std::make_shared<PointVertexBuffer>()),
|
||||
textVertexBuffer(std::make_shared<TextVertexBuffer>()),
|
||||
triangleElementsBuffer(std::make_shared<TriangleElementsBuffer>()),
|
||||
lineElementsBuffer(std::make_shared<LineElementsBuffer>()),
|
||||
pointElementsBuffer(std::make_shared<PointElementsBuffer>()),
|
||||
@@ -157,7 +159,7 @@ std::shared_ptr<Bucket> TileData::createBucket(const VectorTile& tile, const Buc
|
||||
} else if (bucket_desc.type == BucketType::Text) {
|
||||
return createTextBucket(tile, layer, bucket_desc);
|
||||
} else {
|
||||
// TODO: create other bucket types.
|
||||
throw std::runtime_error("unknown bucket type");
|
||||
}
|
||||
} else {
|
||||
// The layer specified in the bucket does not exist. Do nothing.
|
||||
@@ -206,7 +208,7 @@ std::shared_ptr<Bucket> TileData::createTextBucket(const VectorTile& tile, const
|
||||
}
|
||||
|
||||
// TODO: currently hardcoded to use the first font stack.
|
||||
const std::map<Value, std::vector<VectorTilePlacement>>& stack = layer.shaping.begin()->second;
|
||||
const std::map<Value, std::vector<VectorTilePlacement>>& shaping = layer.shaping.begin()->second;
|
||||
|
||||
std::vector<const VectorTileFace *> faces;
|
||||
for (const std::string& face : layer.faces) {
|
||||
@@ -221,11 +223,9 @@ std::shared_ptr<Bucket> TileData::createTextBucket(const VectorTile& tile, const
|
||||
std::shared_ptr<TextBucket> bucket = std::make_shared<TextBucket>(textVertexBuffer, triangleElementsBuffer, bucket_desc);
|
||||
|
||||
FilteredVectorTileLayer filtered_layer(layer, bucket_desc);
|
||||
for (pbf feature_pbf : filtered_layer) {
|
||||
for (const pbf& feature_pbf : filtered_layer) {
|
||||
if (state == State::obsolete) return nullptr;
|
||||
|
||||
const VectorTileFeature feature(feature_pbf, layer);
|
||||
std::cerr << feature;
|
||||
bucket->addFeature({ feature_pbf, layer }, faces, shaping);
|
||||
}
|
||||
|
||||
return bucket;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <llmr/platform/platform.hpp>
|
||||
|
||||
const char *llmr::kTileURL = "http://a.gl-api-us-east-1.tilestream.net/v3/mapbox.mapbox-streets-v4/%d/%d/%d.gl.pbf";
|
||||
const char *llmr::kSpriteURL = "https://dl.dropboxusercontent.com/u/575564/sprite";
|
||||
const char *llmr::kTileURL = "http://localhost:3333/v3/mapbox.mapbox-streets-v4/%d/%d/%d.gl.pbf";
|
||||
const char *llmr::kSpriteURL = "http://localhost:3333/images/sprite";
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
#include <llmr/util/math.hpp>
|
||||
#include <llmr/platform/gl.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
|
||||
|
||||
#include <cassert>
|
||||
|
||||
struct geometry_too_long_exception : std::exception {};
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
TextBucket::TextBucket(const std::shared_ptr<TextVertexBuffer>& vertexBuffer,
|
||||
@@ -26,12 +26,55 @@ TextBucket::TextBucket(const std::shared_ptr<TextVertexBuffer>& vertexBuffer,
|
||||
vertexBuffer(vertexBuffer),
|
||||
triangleElementsBuffer(triangleElementsBuffer),
|
||||
vertex_start(vertexBuffer->index()),
|
||||
triangle_elements_start(triangleElementsBuffer->index())
|
||||
{
|
||||
triangle_elements_start(triangleElementsBuffer->index()) {
|
||||
}
|
||||
|
||||
void TextBucket::addGeometry(pbf& geom) {
|
||||
// noop
|
||||
void TextBucket::addFeature(const VectorTileFeature& feature,
|
||||
const std::vector<const VectorTileFace *>& faces,
|
||||
const std::map<Value, std::vector<VectorTilePlacement>>& shaping) {
|
||||
auto it_prop = feature.properties.find(geometry.text_field);
|
||||
if (it_prop == feature.properties.end()) {
|
||||
// feature does not have the correct property
|
||||
return;
|
||||
}
|
||||
const Value& value = it_prop->second;
|
||||
|
||||
|
||||
auto it_shaping = shaping.find(value);
|
||||
if (it_shaping == shaping.end()) {
|
||||
// we lack shaping information for this label
|
||||
return;
|
||||
}
|
||||
const std::vector<VectorTilePlacement>& placements = it_shaping->second;
|
||||
|
||||
// std::cerr << "we have shaping for " << value << std::endl;
|
||||
|
||||
// Decode all lines.
|
||||
std::vector<Coordinate> line;
|
||||
Geometry::command cmd;
|
||||
|
||||
Coordinate coord;
|
||||
pbf geom = feature.geometry;
|
||||
Geometry geometry(geom);
|
||||
int32_t x, y;
|
||||
while ((cmd = geometry.next(x, y)) != Geometry::end) {
|
||||
if (cmd == Geometry::move_to) {
|
||||
if (!line.empty()) {
|
||||
addLabel(line, faces, placements);
|
||||
line.clear();
|
||||
}
|
||||
}
|
||||
line.emplace_back(x, y);
|
||||
}
|
||||
if (line.size()) {
|
||||
addLabel(line, faces, placements);
|
||||
}
|
||||
}
|
||||
|
||||
void TextBucket::addLabel(const std::vector<Coordinate>& line,
|
||||
const std::vector<const VectorTileFace *>& faces,
|
||||
const std::vector<VectorTilePlacement>& placements) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void TextBucket::render(Painter& painter, const std::string& layer_name, const Tile::ID& id) {
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
const unsigned char resources::style[] = R"JSON({"buckets":{"water":{"source":"streets","layer":"water","type":"fill"},"road_large":{"source":"streets","layer":"road","field":"class","value":["motorway","main"],"type":"line","cap":"round","join":"round"},"road_regular":{"source":"streets","layer":"road","field":"class","value":"street","type":"line","cap":"round","join":"round"},"road_limited":{"source":"streets","layer":"road","field":"class","value":"street_limited","type":"line","cap":"round","join":"round"},"park":{"source":"streets","layer":"landuse","field":"class","value":"park","type":"fill"},"wood":{"source":"streets","layer":"landuse","field":"class","value":"wood","type":"fill"},"school":{"source":"streets","layer":"landuse","field":"class","value":"school","type":"fill"},"cemetery":{"source":"streets","layer":"landuse","field":"class","value":"cemetery","type":"fill"},"industrial":{"source":"streets","layer":"landuse","field":"class","value":"industrial","type":"fill"},"building":{"source":"streets","layer":"building","type":"fill"},"alcohol_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Alcohol"],"type":"point"},"cafe_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Cafe"],"type":"point"},"embassy_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Embassy"],"type":"point"},"park_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Park"],"type":"point"},"restaurant_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Restaurant"],"type":"point"}},"sprite":"img/maki-sprite","constants":{"park":"#c8df9f"},"structure":[{"name":"park","bucket":"park"},{"name":"wood","bucket":"wood"},{"name":"water","bucket":"water"},{"name":"building","bucket":"building"},{"name":"road_limited","bucket":"road_limited"},{"name":"road_regular","bucket":"road_regular"},{"name":"road_large","bucket":"road_large"},{"name":"alcohol_poi","bucket":"alcohol_poi"},{"name":"cafe_poi","bucket":"cafe_poi"},{"name":"embassy_poi","bucket":"embassy_poi"},{"name":"park_poi","bucket":"park_poi"},{"name":"restaurant_poi","bucket":"restaurant_poi"}],"classes":[{"name":"default","layers":{"background":{"type":"background","color":"#FFFFFF"},"park":{"type":"fill","color":"park","antialias":true,"image":"park"},"wood":{"type":"fill","color":"#33AA66","antialias":true,"opacity":0.1},"water":{"type":"fill","color":"#bae0e9","stroke":"#73b6e6","antialias":true,"image":"water_etsy"},"building":{"type":"fill","color":"#000000","antialias":true,"opacity":["linear",13,0,0.1,0,0.1]},"road_limited":{"type":"line","color":"#BBBBBB","width":["stops",{"z":0,"val":1},{"z":30,"val":1}]},"road_regular":{"type":"line","color":"#999999","width":["stops",{"z":0,"val":0.5},{"z":13,"val":0.5},{"z":16,"val":2},{"z":20,"val":32},{"z":30,"val":32}]},"road_large":{"type":"line","color":"#666666","width":["stops",{"z":0,"val":0.5},{"z":11,"val":0.5},{"z":13,"val":1},{"z":16,"val":4},{"z":20,"val":64},{"z":30,"val":64}]},"alcohol_poi":{"type":"point","color":"#cccccc","size":18,"image":"alcohol-shop","opacity":["linear",15,0,1,0,0.75]},"cafe_poi":{"type":"point","color":"#cccccc","size":18,"image":"cafe","opacity":["linear",15,0,1,0,0.75]},"embassy_poi":{"type":"point","color":"#cccccc","size":18,"image":"embassy","opacity":["linear",15,0,1,0,0.75]},"park_poi":{"type":"point","color":"#cccccc","size":18,"image":"park","opacity":["linear",15,0,1,0,0.75]},"restaurant_poi":{"type":"point","color":"#cccccc","size":18,"image":"restaurant","opacity":["linear",15,0,1,0,0.75]}}}]})JSON";
|
||||
const unsigned char resources::style[] = R"JSON({"buckets":{"water":{"source":"streets","layer":"water","type":"fill"},"road_large":{"source":"streets","layer":"road","field":"class","value":["motorway","main"],"type":"line","cap":"round","join":"round"},"road_regular":{"source":"streets","layer":"road","field":"class","value":"street","type":"line","cap":"round","join":"round"},"road_limited":{"source":"streets","layer":"road","field":"class","value":"street_limited","type":"line","cap":"round","join":"round"},"park":{"source":"streets","layer":"landuse","field":"class","value":"park","type":"fill"},"wood":{"source":"streets","layer":"landuse","field":"class","value":"wood","type":"fill"},"school":{"source":"streets","layer":"landuse","field":"class","value":"school","type":"fill"},"cemetery":{"source":"streets","layer":"landuse","field":"class","value":"cemetery","type":"fill"},"industrial":{"source":"streets","layer":"landuse","field":"class","value":"industrial","type":"fill"},"building":{"source":"streets","layer":"building","type":"fill"},"alcohol_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Alcohol"],"type":"point"},"cafe_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Cafe"],"type":"point"},"embassy_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Embassy"],"type":"point"},"park_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Park"],"type":"point"},"restaurant_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Restaurant"],"type":"point"},"country_label":{"source":"streets","type":"text","layer":"country_label","feature_type":"point","padding":10,"text_field":"name","path":"horizontal","font":"Open Sans, Jomolhari, Siyam Rupali, Alef, Arial Unicode MS","fontSize":16}},"sprite":"img/maki-sprite","constants":{"park":"#c8df9f","text":"#000000"},"structure":[{"name":"park","bucket":"park"},{"name":"wood","bucket":"wood"},{"name":"water","bucket":"water"},{"name":"building","bucket":"building"},{"name":"road_limited","bucket":"road_limited"},{"name":"road_regular","bucket":"road_regular"},{"name":"road_large","bucket":"road_large"},{"name":"alcohol_poi","bucket":"alcohol_poi"},{"name":"cafe_poi","bucket":"cafe_poi"},{"name":"embassy_poi","bucket":"embassy_poi"},{"name":"park_poi","bucket":"park_poi"},{"name":"restaurant_poi","bucket":"restaurant_poi"},{"name":"country_label","bucket":"country_label"}],"classes":[{"name":"default","layers":{"background":{"type":"background","color":"#FFFFFF"},"park":{"type":"fill","color":"park","antialias":true,"image":"park"},"wood":{"type":"fill","color":"#33AA66","antialias":true,"opacity":0.1},"water":{"type":"fill","color":"#bae0e9","stroke":"#73b6e6","antialias":true,"image":"water_etsy"},"building":{"type":"fill","color":"#000000","antialias":true,"opacity":["linear",13,0,0.1,0,0.1]},"road_limited":{"type":"line","color":"#BBBBBB","width":["stops",{"z":0,"val":1},{"z":30,"val":1}]},"road_regular":{"type":"line","color":"#999999","width":["stops",{"z":0,"val":0.5},{"z":13,"val":0.5},{"z":16,"val":2},{"z":20,"val":32},{"z":30,"val":32}]},"road_large":{"type":"line","color":"#666666","width":["stops",{"z":0,"val":0.5},{"z":11,"val":0.5},{"z":13,"val":1},{"z":16,"val":4},{"z":20,"val":64},{"z":30,"val":64}]},"alcohol_poi":{"type":"point","color":"#cccccc","size":18,"image":"alcohol-shop","opacity":["linear",15,0,1,0,0.75]},"cafe_poi":{"type":"point","color":"#cccccc","size":18,"image":"cafe","opacity":["linear",15,0,1,0,0.75]},"embassy_poi":{"type":"point","color":"#cccccc","size":18,"image":"embassy","opacity":["linear",15,0,1,0,0.75]},"park_poi":{"type":"point","color":"#cccccc","size":18,"image":"park","opacity":["linear",15,0,1,0,0.75]},"restaurant_poi":{"type":"point","color":"#cccccc","size":18,"image":"restaurant","opacity":["linear",15,0,1,0,0.75]},"country_label":{"type":"text","stroke":[1,1,1,0.7],"color":"text"}}}]})JSON";
|
||||
const unsigned long resources::style_size = sizeof(resources::style);
|
||||
|
||||
@@ -76,6 +76,16 @@ void Style::cascade(float z) {
|
||||
point.image = layer.image;
|
||||
}
|
||||
|
||||
for (const auto& text_pair : sheetClass.text) {
|
||||
const std::string& layer_name = text_pair.first;
|
||||
const llmr::TextClass& layer = text_pair.second;
|
||||
|
||||
// TODO: This should be restricted to point styles that have actual
|
||||
// values so as to not override with default values.
|
||||
llmr::TextProperties& text = computed.texts[layer_name];
|
||||
text.color = layer.color;
|
||||
}
|
||||
|
||||
// Cascade background
|
||||
computed.background.color = sheetClass.background.color;
|
||||
}
|
||||
|
||||
@@ -233,6 +233,8 @@ void StyleParser::parseClass(const std::string& name, JSVal value, ClassDescript
|
||||
class_desc.line.insert({ name, std::forward<LineClass>(parseLineClass(value)) });
|
||||
} else if (type_name == "point") {
|
||||
class_desc.point.insert({ name, std::forward<PointClass>(parsePointClass(value)) });
|
||||
} else if (type_name == "text") {
|
||||
class_desc.text.insert({ name, std::forward<TextClass>(parseTextClass(value)) });
|
||||
} else if (type_name == "background") {
|
||||
class_desc.background = parseBackgroundClass(value);
|
||||
} else {
|
||||
@@ -471,6 +473,17 @@ PointClass StyleParser::parsePointClass(JSVal value) {
|
||||
return klass;
|
||||
}
|
||||
|
||||
|
||||
TextClass StyleParser::parseTextClass(JSVal value) {
|
||||
TextClass klass;
|
||||
|
||||
if (value.HasMember("color")) {
|
||||
klass.color = parseColor(value["color"]);
|
||||
}
|
||||
|
||||
return klass;
|
||||
}
|
||||
|
||||
BackgroundClass StyleParser::parseBackgroundClass(JSVal value) {
|
||||
BackgroundClass klass;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user