From e4858b6a0270f6deb22ad2f97be658e9c510673c Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Thu, 8 Jan 2015 23:23:35 -0500 Subject: [PATCH] merge adjacent roads with same text for labelling js: https://github.com/mapbox/mapbox-gl-js/pull/683 --- src/mbgl/renderer/symbol_bucket.cpp | 62 +++++++++------- src/mbgl/renderer/symbol_bucket.hpp | 1 - src/mbgl/util/merge_lines.hpp | 110 ++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 26 deletions(-) create mode 100644 src/mbgl/util/merge_lines.hpp diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index 0eadacec5..b0cfbf974 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace mbgl { @@ -145,7 +146,37 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress const FontStack &fontStack = glyphStore.getFontStack(properties.text.font); - for (const SymbolFeature &feature : features) { + std::vector>> geometries; + + for (unsigned int k = 0; k < features.size(); k++) { + const SymbolFeature &feature = features[k]; + + geometries.emplace_back(); + std::vector> &multiline = geometries.back(); + + // Decode all lines. + Geometry::command cmd; + bool first = true; + pbf geom(feature.geometry); + Geometry geometry(geom); + int32_t x, y; + while ((cmd = geometry.next(x, y)) != Geometry::end) { + if (first || cmd == Geometry::move_to) { + multiline.emplace_back(); + first = false; + } + multiline.back().emplace_back(x, y); + } + } + + if (properties.placement == PlacementType::Line) { + util::mergeLines(features, geometries); + } + + for (unsigned int k = 0; k < geometries.size(); k++) { + if (!geometries[k].size()) continue; + const SymbolFeature &feature = features[k]; + Shaping shaping; Rect image; GlyphPositions face; @@ -181,32 +212,13 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress // if either shaping or icon position is present, add the feature if (shaping.size() || image) { - addFeature(feature.geometry, shaping, face, image); - } - } -} - -void SymbolBucket::addFeature(const pbf &geom_pbf, const Shaping &shaping, - const GlyphPositions &face, const Rect &image) { - // Decode all lines. - std::vector line; - Geometry::command cmd; - - Coordinate coord; - pbf geom(geom_pbf); - Geometry geometry(geom); - int32_t x, y; - while ((cmd = geometry.next(x, y)) != Geometry::end) { - if (cmd == Geometry::move_to) { - if (!line.empty()) { - addFeature(line, shaping, face, image); - line.clear(); + std::vector> &multiline = geometries[k]; + for (const std::vector &line : multiline) { + if (line.size()) { + addFeature(line, shaping, face, image); + } } } - line.emplace_back(x, y); - } - if (line.size()) { - addFeature(line, shaping, face, image); } } diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp index dd596b1a0..f90ff8999 100644 --- a/src/mbgl/renderer/symbol_bucket.hpp +++ b/src/mbgl/renderer/symbol_bucket.hpp @@ -77,7 +77,6 @@ private: std::vector processFeatures(const VectorTileLayer &layer, const FilterExpression &filter, GlyphStore &glyphStore, const Sprite &sprite); - void addFeature(const pbf &geom_pbf, const Shaping &shaping, const GlyphPositions &face, const Rect &image); void addFeature(const std::vector &line, const Shaping &shaping, const GlyphPositions &face, const Rect &image); diff --git a/src/mbgl/util/merge_lines.hpp b/src/mbgl/util/merge_lines.hpp new file mode 100644 index 000000000..433e0b276 --- /dev/null +++ b/src/mbgl/util/merge_lines.hpp @@ -0,0 +1,110 @@ +#ifndef MBGL_UTIL_MERGELINES +#define MBGL_UTIL_MERGELINES + +#include +#include +#include +#include +#include + + +#include +namespace mbgl { +namespace util { + +unsigned int mergeFromRight( + std::vector>> &geometries, + std::map &rightIndex, + std::map::iterator left, + std::string &rightKey, + std::vector> &geom) { + + unsigned int index = left->second; + rightIndex.erase(left); + rightIndex[rightKey] = index; + geometries[index][0].pop_back(); + geometries[index][0].insert(geometries[index][0].end(), geom[0].begin(), geom[0].end()); + geom[0].clear(); + return index; +} + +unsigned int mergeFromLeft( + std::vector>> &geometries, + std::map &leftIndex, + std::string &leftKey, + std::map::iterator right, + std::vector> &geom) { + + unsigned int index = right->second; + leftIndex.erase(right); + leftIndex[leftKey] = index; + geom[0].pop_back(); + geom[0].insert(geom[0].end(), geometries[index][0].begin(), geometries[index][0].end()); + geometries[index][0].clear(); + std::swap(geometries[index][0], geom[0]); + return index; +} + +std::string getKey(const std::u32string &text, const std::vector> &geom, bool onRight) { + const Coordinate &coord = onRight ? geom[0].back() : geom[0].front(); + std::ostringstream key; + for (const char32_t &c : text) { + key << (char)c; + } + key << ":" << coord.x << ":" << coord.y; + return key.str(); +} + + +void mergeLines(const std::vector &features, std::vector>> &geometries) { + + std::map leftIndex; + std::map rightIndex; + + if (features.size()) getKey(features[0].label, geometries[0], false); + + for (unsigned int k = 0; k < features.size(); k++) { + const SymbolFeature &feature = features[k]; + std::vector> &geometry = geometries[k]; + + if (!feature.label.length()) { + continue; + } + + std::string leftKey = getKey(feature.label, geometry, false); + std::string rightKey = getKey(feature.label, geometry, true); + + auto left = rightIndex.find(leftKey); + auto right = leftIndex.find(rightKey); + + if ((left != rightIndex.end()) && (right != leftIndex.end()) && (left->second != right->second)) { + // found lines with the same text adjacent to both ends of the current line, merge all three + unsigned int j = mergeFromLeft(geometries, leftIndex, leftKey, right, geometry); + unsigned int i = mergeFromRight(geometries, rightIndex, left, rightKey, geometries[j]); + + leftIndex.erase(leftIndex.find(leftKey)); + rightIndex.erase(rightIndex.find(rightKey)); + rightIndex[getKey(feature.label, geometries[i], true)] = i; + + } else if (left != rightIndex.end()) { + // found mergeable line adjacent to the start of the current line, merge + mergeFromRight(geometries, rightIndex, left, rightKey, geometry); + + } else if (right != leftIndex.end()) { + // found mergeable line adjacent to the end of the current line, merge + mergeFromLeft(geometries, leftIndex, leftKey, right, geometry); + + } else { + // no adjacent lines, add as a new item + leftIndex[leftKey] = k; + rightIndex[rightKey] = k; + } + + } + +} + +} // end namespace util +} // end namespace mbgl + +#endif