Merge branch 'master' into label-styles-line-break
This commit is contained in:
38
bin/style.js
38
bin/style.js
@@ -266,7 +266,7 @@ module.exports = {
|
||||
"field": "index",
|
||||
"value": 10,
|
||||
"path": "curve",
|
||||
"text_field": "ele",
|
||||
"text_field": "{{ele}}m",
|
||||
"font": "Open Sans Regular, Arial Unicode MS Regular",
|
||||
"fontSize": 10,
|
||||
"feature_type": "line",
|
||||
@@ -647,7 +647,7 @@ module.exports = {
|
||||
"country_label": {
|
||||
"source": "outdoors",
|
||||
"layer": "country_label",
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "horizontal",
|
||||
"font": "Open Sans Bold, Arial Unicode MS Bold",
|
||||
"fontSize": 24,
|
||||
@@ -664,7 +664,7 @@ module.exports = {
|
||||
"layer": "marine_label",
|
||||
"feature_type": "line",
|
||||
"type": "text",
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "curve",
|
||||
"font": "Open Sans Italic, Arial Unicode MS Regular",
|
||||
"fontSize": 16
|
||||
@@ -674,7 +674,7 @@ module.exports = {
|
||||
"layer": "marine_label",
|
||||
"feature_type": "point",
|
||||
"type": "text",
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "horizontal",
|
||||
"field": "labelrank",
|
||||
"value": 1,
|
||||
@@ -686,7 +686,7 @@ module.exports = {
|
||||
"layer": "marine_label",
|
||||
"feature_type": "point",
|
||||
"type": "text",
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "horizontal",
|
||||
"field": "labelrank",
|
||||
"value": 2,
|
||||
@@ -698,7 +698,7 @@ module.exports = {
|
||||
"layer": "marine_label",
|
||||
"feature_type": "point",
|
||||
"type": "text",
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "horizontal",
|
||||
"field": "labelrank",
|
||||
"value": [3,4,5,6],
|
||||
@@ -708,7 +708,7 @@ module.exports = {
|
||||
"state_label": {
|
||||
"source": "outdoors",
|
||||
"layer": "state_label",
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "horizontal",
|
||||
"font": "Open Sans Regular, Arial Unicode MS Regular",
|
||||
"fontSize": 16,
|
||||
@@ -721,7 +721,7 @@ module.exports = {
|
||||
"layer": "place_label",
|
||||
"field": "type",
|
||||
"value": "city",
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "horizontal",
|
||||
"font": "Open Sans Semibold, Arial Unicode MS Bold",
|
||||
"fontSize": 20,
|
||||
@@ -733,7 +733,7 @@ module.exports = {
|
||||
"layer": "place_label",
|
||||
"field": "type",
|
||||
"value": "town",
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "horizontal",
|
||||
"font": "Open Sans Semibold, Arial Unicode MS Bold",
|
||||
"fontSize": 24,
|
||||
@@ -745,7 +745,7 @@ module.exports = {
|
||||
"layer": "place_label",
|
||||
"field": "type",
|
||||
"value": "village",
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "horizontal",
|
||||
"font": "Open Sans Semibold, Arial Unicode MS Bold",
|
||||
"fontSize": 22,
|
||||
@@ -761,7 +761,7 @@ module.exports = {
|
||||
"suburb",
|
||||
"neighbourhood"
|
||||
],
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "horizontal",
|
||||
"font": "Open Sans Semibold, Arial Unicode MS Bold",
|
||||
"fontSize": 18,
|
||||
@@ -774,7 +774,7 @@ module.exports = {
|
||||
"layer": "road_label",
|
||||
"field": "class",
|
||||
"value": ["motorway","main"],
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "curve",
|
||||
"padding": 2,
|
||||
"font": "Open Sans Regular, Arial Unicode MS Regular",
|
||||
@@ -788,7 +788,7 @@ module.exports = {
|
||||
"layer": "road_label",
|
||||
"field": "class",
|
||||
"value": ["street","street_limited"],
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "curve",
|
||||
"padding": 2,
|
||||
"font": "Open Sans Regular, Arial Unicode MS Regular",
|
||||
@@ -802,7 +802,7 @@ module.exports = {
|
||||
"layer": "road_label",
|
||||
"field": "class",
|
||||
"value": ["service","driveway","path"],
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "curve",
|
||||
"padding": 2,
|
||||
"font": "Open Sans Regular, Arial Unicode MS Regular",
|
||||
@@ -814,7 +814,7 @@ module.exports = {
|
||||
"water_label": {
|
||||
"source": "outdoors",
|
||||
"layer": "water_label",
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "horizontal",
|
||||
"font": "Open Sans Semibold Italic, Arial Unicode MS Bold",
|
||||
"fontSize": 12,
|
||||
@@ -824,7 +824,7 @@ module.exports = {
|
||||
"waterway_label": {
|
||||
"source": "outdoors",
|
||||
"layer": "waterway_label",
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "curve",
|
||||
"font": "Open Sans Semibold Italic, Arial Unicode MS Bold",
|
||||
"fontSize": 12,
|
||||
@@ -876,7 +876,7 @@ module.exports = {
|
||||
1,
|
||||
2
|
||||
],
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "horizontal",
|
||||
"padding": 2,
|
||||
"maxWidth": 8,
|
||||
@@ -892,7 +892,7 @@ module.exports = {
|
||||
"layer": "poi_label",
|
||||
"field": "scalerank",
|
||||
"value": 3,
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "horizontal",
|
||||
"padding": 2,
|
||||
"maxWidth": 8,
|
||||
@@ -908,7 +908,7 @@ module.exports = {
|
||||
"layer": "poi_label",
|
||||
"field": "scalerank",
|
||||
"value": 4,
|
||||
"text_field": "name",
|
||||
"text_field": "{{name}}",
|
||||
"path": "horizontal",
|
||||
"padding": 2,
|
||||
"maxWidth": 8,
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
'cflags_cc': ['-std=c++11', '-Wall', '-Wextra'],
|
||||
'configurations': {
|
||||
'Debug': {
|
||||
'cflags_cc': [ '-g', '-O1', '-fno-omit-frame-pointer','-fwrapv', '-fstack-protector-all', '-fno-common' ],
|
||||
'cflags_cc': [ '-g', '-O0', '-fno-omit-frame-pointer','-fwrapv', '-fstack-protector-all', '-fno-common' ],
|
||||
'defines': [ 'DEBUG' ],
|
||||
'xcode_settings': {
|
||||
'GCC_OPTIMIZATION_LEVEL': '1',
|
||||
'GCC_OPTIMIZATION_LEVEL': '0',
|
||||
'GCC_GENERATE_DEBUGGING_SYMBOLS': 'YES',
|
||||
'DEAD_CODE_STRIPPING': 'NO',
|
||||
'GCC_INLINES_ARE_PRIVATE_EXTERN': 'NO',
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
const float &alignment,
|
||||
const float &verticalAlignment,
|
||||
const float &letterSpacing) const;
|
||||
Shaping lineWrap(Shaping &shaping,
|
||||
void lineWrap(Shaping &shaping,
|
||||
const float &lineHeight,
|
||||
const float &maxWidth,
|
||||
const float &alignment,
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <llmr/util/std.hpp>
|
||||
#include <llmr/util/utf.hpp>
|
||||
|
||||
#include <regex>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
TileParser::TileParser(const std::string &data, VectorTileData &tile,
|
||||
@@ -182,6 +184,11 @@ std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer &laye
|
||||
tile.textVertexBuffer, tile.triangleElementsBuffer, bucket_desc, placement);
|
||||
|
||||
util::utf8_to_utf32 ucs4conv;
|
||||
std::regex token_regex("\\{\\{(\\w+)\\}\\}");
|
||||
const auto tokens_end = std::sregex_token_iterator();
|
||||
|
||||
|
||||
std::vector<std::pair<std::u32string, pbf>> labels;
|
||||
|
||||
// Determine and load glyph ranges
|
||||
{
|
||||
@@ -193,23 +200,41 @@ std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer &laye
|
||||
return nullptr;
|
||||
VectorTileFeature feature{feature_pbf, layer};
|
||||
|
||||
auto it_prop = feature.properties.find(bucket_desc.geometry.field);
|
||||
if (it_prop == feature.properties.end()) {
|
||||
// feature does not have the correct property
|
||||
if (debug::labelTextMissingWarning) {
|
||||
fprintf(stderr,
|
||||
"[WARNING] feature doesn't have property '%s' required for labelling\n",
|
||||
bucket_desc.geometry.field.c_str());
|
||||
const std::string &field = bucket_desc.geometry.field;
|
||||
std::string source_string;
|
||||
source_string.reserve(field.size());
|
||||
|
||||
bool token = false;
|
||||
for (auto token_it = std::sregex_token_iterator(field.begin(), field.end(), token_regex, {-1, 1}); token_it != tokens_end; ++token_it, token = !token) {
|
||||
if (!token_it->matched) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token) {
|
||||
auto it_prop = feature.properties.find(token_it->str());
|
||||
if (it_prop == feature.properties.end()) {
|
||||
// feature does not have the correct property
|
||||
if (debug::labelTextMissingWarning) {
|
||||
fprintf(stderr,
|
||||
"[WARNING] feature doesn't have property '%s' required for labelling\n",
|
||||
token_it->str().c_str());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
source_string += toString(it_prop->second);
|
||||
} else {
|
||||
source_string += token_it->str();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::u32string string = ucs4conv.convert(toString(it_prop->second));
|
||||
const std::u32string string = ucs4conv.convert(source_string);
|
||||
|
||||
// Loop through all characters of this text and collect unique codepoints.
|
||||
for (uint32_t chr : string) {
|
||||
ranges.insert(getGlyphRange(chr));
|
||||
}
|
||||
|
||||
labels.emplace_back(string, feature.geometry);
|
||||
}
|
||||
|
||||
glyphStore->waitForGlyphRanges(bucket_desc.geometry.font, ranges);
|
||||
@@ -220,38 +245,18 @@ std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer &laye
|
||||
GlyphPositions face;
|
||||
|
||||
// Shape and place all labels.
|
||||
{
|
||||
FilteredVectorTileLayer filtered_layer(layer, bucket_desc);
|
||||
for (const pbf &feature_pbf : filtered_layer) {
|
||||
if (obsolete())
|
||||
return nullptr;
|
||||
VectorTileFeature feature{feature_pbf, layer};
|
||||
for (const std::pair<std::u32string, pbf> &label : labels) {
|
||||
|
||||
auto it_prop = feature.properties.find(bucket_desc.geometry.field);
|
||||
if (it_prop == feature.properties.end()) {
|
||||
// feature does not have the correct property
|
||||
if (debug::labelTextMissingWarning) {
|
||||
fprintf(stderr,
|
||||
"[WARNING] feature doesn't have property '%s' required for labelling\n",
|
||||
bucket_desc.geometry.field.c_str());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 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 std::string source_string = toString(it_prop->second);
|
||||
const std::u32string string = ucs4conv.convert(source_string);
|
||||
// Place labels.
|
||||
addGlyph(tile.id.to_uint64(), bucket_desc.geometry.font, label.first, fontStack, *glyphAtlas,
|
||||
face);
|
||||
|
||||
// Shape labels.
|
||||
const Shaping shaping = fontStack.getShaping(string, bucket_desc.geometry.max_width,
|
||||
bucket_desc.geometry.line_height, bucket_desc.geometry.alignment,
|
||||
bucket_desc.geometry.vertical_alignment, bucket_desc.geometry.letter_spacing);
|
||||
|
||||
// Place labels.
|
||||
addGlyph(tile.id.to_uint64(), bucket_desc.geometry.font, string, fontStack, *glyphAtlas,
|
||||
face);
|
||||
|
||||
bucket->addFeature(feature.geometry, face, shaping);
|
||||
}
|
||||
bucket->addFeature(label.second, face, shaping);
|
||||
}
|
||||
|
||||
return std::move(bucket);
|
||||
|
||||
@@ -38,8 +38,7 @@ const Shaping FontStack::getShaping(const std::u32string &string, const float &m
|
||||
Shaping shaping;
|
||||
// Loop through all characters of this label and shape.
|
||||
for (uint32_t chr : string) {
|
||||
GlyphPlacement glyph = GlyphPlacement(0, chr, x, 0);
|
||||
shaping.push_back(glyph);
|
||||
shaping.emplace_back(0, chr, x, 0);
|
||||
i++;
|
||||
x += metrics.find(chr)->second.advance + letterSpacing;
|
||||
}
|
||||
@@ -58,18 +57,24 @@ void alignVertically(Shaping &shaping, const uint32_t &lines, const float &lineH
|
||||
|
||||
void alignHorizontally(Shaping &shaping, const std::map<uint32_t, GlyphMetrics> &metrics,
|
||||
const uint32_t &start, const uint32_t &end, const float &alignment) {
|
||||
|
||||
uint32_t lastAdvance = metrics.find(shaping[end].glyph)->second.advance;
|
||||
int32_t lineIndent = (shaping[end].x + lastAdvance) * alignment;
|
||||
|
||||
for (uint32_t j = start; j <= end; j++) {
|
||||
shaping[j].x -= lineIndent;
|
||||
auto & shape = shaping.at(end);
|
||||
auto metric = metrics.find(shape.glyph);
|
||||
if (metric != metrics.end()) {
|
||||
uint32_t lastAdvance = metric->second.advance;
|
||||
int32_t lineIndent = (shape.x + lastAdvance) * alignment;
|
||||
for (uint32_t j = start; j <= end; j++) {
|
||||
shaping[j].x -= lineIndent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Shaping FontStack::lineWrap(Shaping &shaping, const float &lineHeight, const float &maxWidth,
|
||||
void FontStack::lineWrap(Shaping &shaping, const float &lineHeight, const float &maxWidth,
|
||||
const float &alignment, const float &verticalAlignment) const {
|
||||
|
||||
std::size_t num_shapes = shaping.size();
|
||||
if (!num_shapes) {
|
||||
return;
|
||||
}
|
||||
uint32_t lastSafeBreak = 0;
|
||||
uint32_t lengthBeforeCurrentLine = 0;
|
||||
uint32_t lineStartIndex = 0;
|
||||
@@ -77,11 +82,10 @@ Shaping FontStack::lineWrap(Shaping &shaping, const float &lineHeight, const flo
|
||||
|
||||
for (uint32_t i = 0; i < shaping.size(); i++) {
|
||||
GlyphPlacement &shape = shaping[i];
|
||||
|
||||
shape.x -= lengthBeforeCurrentLine;
|
||||
shape.y += lineHeight * line;
|
||||
|
||||
if (shape.x > maxWidth && lastSafeBreak != 0) {
|
||||
if (shape.x > maxWidth && lastSafeBreak > 0) {
|
||||
|
||||
uint32_t lineLength = shaping[lastSafeBreak + 1].x;
|
||||
|
||||
@@ -103,7 +107,6 @@ Shaping FontStack::lineWrap(Shaping &shaping, const float &lineHeight, const flo
|
||||
lastSafeBreak = i;
|
||||
}
|
||||
}
|
||||
|
||||
alignHorizontally(shaping, metrics, lineStartIndex, shaping.size() - 1, alignment);
|
||||
alignVertically(shaping, line + 1, lineHeight, verticalAlignment);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user