extract label shaping

This commit is contained in:
Konstantin Käfer
2014-03-12 15:27:20 +01:00
parent df56f9fbce
commit d9589573bc
14 changed files with 107 additions and 17 deletions

View File

@@ -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"
},

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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 }};
};

View File

@@ -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;
};

View File

@@ -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);

View File

@@ -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;

View File

@@ -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";

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;