[core] Fix symbol rendering for multipoints
Ports https://github.com/mapbox/mapbox-gl-js/pull/3763 and https://github.com/mapbox/mapbox-gl-js/pull/3806.
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
"lodash": "^4.16.4",
|
||||
"mapbox-gl": "mapbox/mapbox-gl-js#ab836206d415ca3a74257a3066d11a54ab2838cb",
|
||||
"mapbox-gl-style-spec": "mapbox/mapbox-gl-style-spec#49e8b407bdbbe6f7c92dbcb56d3d51f425fc2653",
|
||||
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#bbe948a30c3879b77a220eaa1336f61d46db5953",
|
||||
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#e1ada02a706fd124fc3441fd3a2b3cda67960ff5",
|
||||
"mkdirp": "^0.5.1",
|
||||
"node-cmake": "^1.2.1",
|
||||
"pixelmatch": "^4.0.2",
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace mbgl {
|
||||
|
||||
class SymbolFeature {
|
||||
public:
|
||||
FeatureType type;
|
||||
GeometryCollection geometry;
|
||||
optional<std::u16string> text;
|
||||
optional<std::string> icon;
|
||||
|
||||
@@ -103,16 +103,8 @@ SymbolLayout::SymbolLayout(std::string bucketName_,
|
||||
}
|
||||
|
||||
if (ft.text || ft.icon) {
|
||||
auto &multiline = ft.geometry;
|
||||
|
||||
GeometryCollection geometryCollection = feature->getGeometries();
|
||||
for (auto& line : geometryCollection) {
|
||||
multiline.emplace_back();
|
||||
for (auto& point : line) {
|
||||
multiline.back().emplace_back(point.x, point.y);
|
||||
}
|
||||
}
|
||||
|
||||
ft.type = feature->getType();
|
||||
ft.geometry = feature->getGeometries();
|
||||
features.push_back(std::move(ft));
|
||||
}
|
||||
}
|
||||
@@ -229,17 +221,17 @@ void SymbolLayout::prepare(uintptr_t tileUID,
|
||||
|
||||
// if either shapedText or icon position is present, add the feature
|
||||
if (shapedText || shapedIcon) {
|
||||
addFeature(feature.geometry, shapedText, shapedIcon, face, feature.index);
|
||||
addFeature(feature, shapedText, shapedIcon, face);
|
||||
}
|
||||
}
|
||||
|
||||
features.clear();
|
||||
}
|
||||
|
||||
|
||||
void SymbolLayout::addFeature(const GeometryCollection &lines,
|
||||
const Shaping &shapedText, const PositionedIcon &shapedIcon, const GlyphPositions &face, const size_t index) {
|
||||
|
||||
void SymbolLayout::addFeature(const SymbolFeature& feature,
|
||||
const Shaping& shapedText,
|
||||
const PositionedIcon& shapedIcon,
|
||||
const GlyphPositions& face) {
|
||||
const float minScale = 0.5f;
|
||||
const float glyphSize = 24.0f;
|
||||
|
||||
@@ -258,55 +250,75 @@ void SymbolLayout::addFeature(const GeometryCollection &lines,
|
||||
const SymbolPlacementType iconPlacement = layout.get<IconRotationAlignment>() != AlignmentType::Map
|
||||
? SymbolPlacementType::Point
|
||||
: layout.get<SymbolPlacement>();
|
||||
const bool isLine = layout.get<SymbolPlacement>() == SymbolPlacementType::Line;
|
||||
const float textRepeatDistance = symbolSpacing / 2;
|
||||
IndexedSubfeature indexedFeature = {feature.index, sourceLayerName, bucketName, symbolInstances.size()};
|
||||
|
||||
auto& clippedLines = isLine ?
|
||||
util::clipLines(lines, 0, 0, util::EXTENT, util::EXTENT) :
|
||||
lines;
|
||||
auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) {
|
||||
// https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers
|
||||
// +-------------------+ Symbols with anchors located on tile edges
|
||||
// |(0,0) || are duplicated on neighbor tiles.
|
||||
// | ||
|
||||
// | || In continuous mode, to avoid overdraw we
|
||||
// | || skip symbols located on the extent edges.
|
||||
// | Tile || In still mode, we include the features in
|
||||
// | || the buffers for both tiles and clip them
|
||||
// | || at draw time.
|
||||
// | ||
|
||||
// +-------------------| In this scenario, the inner bounding box
|
||||
// +-------------------+ is called 'withinPlus0', and the outer
|
||||
// (extent,extent) is called 'inside'.
|
||||
const bool withinPlus0 = anchor.point.x >= 0 && anchor.point.x < util::EXTENT && anchor.point.y >= 0 && anchor.point.y < util::EXTENT;
|
||||
const bool inside = withinPlus0 || anchor.point.x == util::EXTENT || anchor.point.y == util::EXTENT;
|
||||
|
||||
IndexedSubfeature indexedFeature = {index, sourceLayerName, bucketName, symbolInstances.size()};
|
||||
if (avoidEdges && !inside) return;
|
||||
|
||||
for (const auto& line : clippedLines) {
|
||||
if (line.empty()) continue;
|
||||
const bool addToBuffers = mode == MapMode::Still || withinPlus0;
|
||||
|
||||
// Calculate the anchor points around which you want to place labels
|
||||
Anchors anchors = isLine ?
|
||||
getAnchors(line, symbolSpacing, textMaxAngle, shapedText.left, shapedText.right, shapedIcon.left, shapedIcon.right, glyphSize, textMaxBoxScale, overscaling) :
|
||||
Anchors({ Anchor(float(line[0].x), float(line[0].y), 0, minScale) });
|
||||
symbolInstances.emplace_back(anchor, line, shapedText, shapedIcon, layout, addToBuffers, symbolInstances.size(),
|
||||
textBoxScale, textPadding, textPlacement,
|
||||
iconBoxScale, iconPadding, iconPlacement,
|
||||
face, indexedFeature);
|
||||
};
|
||||
|
||||
// For each potential label, create the placement features used to check for collisions, and the quads use for rendering.
|
||||
for (Anchor &anchor : anchors) {
|
||||
if (shapedText && isLine) {
|
||||
if (anchorIsTooClose(shapedText.text, textRepeatDistance, anchor)) {
|
||||
continue;
|
||||
if (layout.get<SymbolPlacement>() == SymbolPlacementType::Line) {
|
||||
auto clippedLines = util::clipLines(feature.geometry, 0, 0, util::EXTENT, util::EXTENT);
|
||||
for (const auto& line : clippedLines) {
|
||||
Anchors anchors = getAnchors(line,
|
||||
symbolSpacing,
|
||||
textMaxAngle,
|
||||
shapedText.left,
|
||||
shapedText.right,
|
||||
shapedIcon.left,
|
||||
shapedIcon.right,
|
||||
glyphSize,
|
||||
textMaxBoxScale,
|
||||
overscaling);
|
||||
|
||||
for (auto& anchor : anchors) {
|
||||
if (!shapedText || !anchorIsTooClose(shapedText.text, textRepeatDistance, anchor)) {
|
||||
addSymbolInstance(line, anchor);
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers
|
||||
// +-------------------+ Symbols with anchors located on tile edges
|
||||
// |(0,0) || are duplicated on neighbor tiles.
|
||||
// | ||
|
||||
// | || In continuous mode, to avoid overdraw we
|
||||
// | || skip symbols located on the extent edges.
|
||||
// | Tile || In still mode, we include the features in
|
||||
// | || the buffers for both tiles and clip them
|
||||
// | || at draw time.
|
||||
// | ||
|
||||
// +-------------------| In this scenario, the inner bounding box
|
||||
// +-------------------+ is called 'withinPlus0', and the outer
|
||||
// (extent,extent) is called 'inside'.
|
||||
const bool withinPlus0 = anchor.point.x >= 0 && anchor.point.x < util::EXTENT && anchor.point.y >= 0 && anchor.point.y < util::EXTENT;
|
||||
const bool inside = withinPlus0 || anchor.point.x == util::EXTENT || anchor.point.y == util::EXTENT;
|
||||
|
||||
if (avoidEdges && !inside) continue;
|
||||
|
||||
const bool addToBuffers = mode == MapMode::Still || withinPlus0;
|
||||
|
||||
symbolInstances.emplace_back(anchor, line, shapedText, shapedIcon, layout, addToBuffers, symbolInstances.size(),
|
||||
textBoxScale, textPadding, textPlacement,
|
||||
iconBoxScale, iconPadding, iconPlacement,
|
||||
face, indexedFeature);
|
||||
}
|
||||
} else if (feature.type == FeatureType::Polygon) {
|
||||
// TODO: pole of inaccessibility
|
||||
for (const auto& ring : feature.geometry) {
|
||||
for (const auto& point : ring) {
|
||||
Anchor anchor(point.x, point.y, 0, minScale);
|
||||
addSymbolInstance(ring, anchor);
|
||||
}
|
||||
}
|
||||
} else if (feature.type == FeatureType::LineString) {
|
||||
for (const auto& line : feature.geometry) {
|
||||
Anchor anchor(line[0].x, line[0].y, 0, minScale);
|
||||
addSymbolInstance(line, anchor);
|
||||
}
|
||||
} else if (feature.type == FeatureType::Point) {
|
||||
for (const auto& points : feature.geometry) {
|
||||
for (const auto& point : points) {
|
||||
Anchor anchor(point.x, point.y, 0, minScale);
|
||||
addSymbolInstance({point}, anchor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,11 +59,10 @@ public:
|
||||
const std::string sourceLayerName;
|
||||
|
||||
private:
|
||||
void addFeature(const GeometryCollection&,
|
||||
void addFeature(const SymbolFeature&,
|
||||
const Shaping& shapedText,
|
||||
const PositionedIcon& shapedIcon,
|
||||
const GlyphPositions& face,
|
||||
const size_t index);
|
||||
const GlyphPositions& face);
|
||||
|
||||
bool anchorIsTooClose(const std::u16string& text, const float repeatDistance, const Anchor&);
|
||||
std::map<std::u16string, std::vector<Anchor>> compareText;
|
||||
|
||||
@@ -72,6 +72,7 @@ Anchors getAnchors(const GeometryCoordinates &line, float spacing,
|
||||
const float maxAngle, const float textLeft, const float textRight,
|
||||
const float iconLeft, const float iconRight,
|
||||
const float glyphSize, const float boxScale, const float overscaling) {
|
||||
if (line.empty()) return {};
|
||||
|
||||
// Resample a line to get anchor points for labels and check that each
|
||||
// potential label passes text-max-angle check and has enough froom to fit
|
||||
|
||||
@@ -6,24 +6,26 @@
|
||||
const std::u16string aaa = u"a";
|
||||
const std::u16string bbb = u"b";
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
TEST(MergeLines, SameText) {
|
||||
// merges lines with the same text
|
||||
std::vector<mbgl::SymbolFeature> input1 = {
|
||||
{ {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, 0 },
|
||||
{ {{{4, 0}, {5, 0}, {6, 0}}}, bbb, {}, 0 },
|
||||
{ {{{8, 0}, {9, 0}}}, aaa, {}, 0 },
|
||||
{ {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, 0 },
|
||||
{ {{{6, 0}, {7, 0}, {8, 0}}}, aaa, {}, 0 },
|
||||
{ {{{5, 0}, {6, 0}}}, aaa, {}, 0 }
|
||||
{ FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, 0 },
|
||||
{ FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, bbb, {}, 0 },
|
||||
{ FeatureType::LineString, {{{8, 0}, {9, 0}}}, aaa, {}, 0 },
|
||||
{ FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, 0 },
|
||||
{ FeatureType::LineString, {{{6, 0}, {7, 0}, {8, 0}}}, aaa, {}, 0 },
|
||||
{ FeatureType::LineString, {{{5, 0}, {6, 0}}}, aaa, {}, 0 }
|
||||
};
|
||||
|
||||
const std::vector<mbgl::SymbolFeature> expected1 = {
|
||||
{ {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}}}, aaa, {}, 0 },
|
||||
{ {{{4, 0}, {5, 0}, {6, 0}}}, bbb, {}, 0 },
|
||||
{ {{{5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}}}, aaa, {}, 0 },
|
||||
{ {{}}, aaa, {}, 0 },
|
||||
{ {{}}, aaa, {}, 0 },
|
||||
{ {{}}, aaa, {}, 0 }
|
||||
{ FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}}}, aaa, {}, 0 },
|
||||
{ FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, bbb, {}, 0 },
|
||||
{ FeatureType::LineString, {{{5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}}}, aaa, {}, 0 },
|
||||
{ FeatureType::LineString, {{}}, aaa, {}, 0 },
|
||||
{ FeatureType::LineString, {{}}, aaa, {}, 0 },
|
||||
{ FeatureType::LineString, {{}}, aaa, {}, 0 }
|
||||
};
|
||||
|
||||
mbgl::util::mergeLines(input1);
|
||||
@@ -36,15 +38,15 @@ TEST(MergeLines, SameText) {
|
||||
TEST(MergeLines, BothEnds) {
|
||||
// mergeLines handles merge from both ends
|
||||
std::vector<mbgl::SymbolFeature> input2 = {
|
||||
{ {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, 0 },
|
||||
{ {{{4, 0}, {5, 0}, {6, 0}}}, aaa, {}, 0 },
|
||||
{ {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, 0 }
|
||||
{ FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, 0 },
|
||||
{ FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, aaa, {}, 0 },
|
||||
{ FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, 0 }
|
||||
};
|
||||
|
||||
const std::vector<mbgl::SymbolFeature> expected2 = {
|
||||
{ {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}}}, aaa, {}, 0 },
|
||||
{ {{}}, aaa, {}, 0 },
|
||||
{ {{}}, aaa, {}, 0 }
|
||||
{ FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}}}, aaa, {}, 0 },
|
||||
{ FeatureType::LineString, {{}}, aaa, {}, 0 },
|
||||
{ FeatureType::LineString, {{}}, aaa, {}, 0 }
|
||||
};
|
||||
|
||||
mbgl::util::mergeLines(input2);
|
||||
@@ -57,15 +59,15 @@ TEST(MergeLines, BothEnds) {
|
||||
TEST(MergeLines, CircularLines) {
|
||||
// mergeLines handles circular lines
|
||||
std::vector<mbgl::SymbolFeature> input3 = {
|
||||
{ {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, 0 },
|
||||
{ {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, 0 },
|
||||
{ {{{4, 0}, {0, 0}}}, aaa, {}, 0 }
|
||||
{ FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, 0 },
|
||||
{ FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, 0 },
|
||||
{ FeatureType::LineString, {{{4, 0}, {0, 0}}}, aaa, {}, 0 }
|
||||
};
|
||||
|
||||
const std::vector<mbgl::SymbolFeature> expected3 = {
|
||||
{ {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 0}}}, aaa, {}, 0 },
|
||||
{ {{}}, aaa, {}, 0 },
|
||||
{ {{}}, aaa, {}, 0 }
|
||||
{ FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 0}}}, aaa, {}, 0 },
|
||||
{ FeatureType::LineString, {{}}, aaa, {}, 0 },
|
||||
{ FeatureType::LineString, {{}}, aaa, {}, 0 }
|
||||
};
|
||||
|
||||
mbgl::util::mergeLines(input3);
|
||||
|
||||
Reference in New Issue
Block a user