Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sort overlapping symbols in the y direction #1184

Merged
merged 1 commit into from
Apr 30, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 39 additions & 5 deletions js/data/symbol_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,9 @@ SymbolBucket.prototype.addFeature = function(lines, shapedText, shapedIcon) {
iconPadding = layout['icon-padding'] * collision.tilePixelRatio,
textMaxAngle = layout['text-max-angle'] / 180 * Math.PI,
textAlongLine = layout['text-rotation-alignment'] === 'map' && layout['symbol-placement'] === 'line',
iconAlongLine = layout['icon-rotation-alignment'] === 'map' && layout['symbol-placement'] === 'line';
iconAlongLine = layout['icon-rotation-alignment'] === 'map' && layout['symbol-placement'] === 'line',
mayOverlap = layout['text-allow-overlap'] || layout['icon-allow-overlap'] ||
layout['text-ignore-placement'] || layout['icon-ignore-placement'];

if (layout['symbol-placement'] === 'line') {
lines = clipLine(lines, 0, 0, 4096, 4096);
Expand All @@ -164,7 +166,18 @@ SymbolBucket.prototype.addFeature = function(lines, shapedText, shapedIcon) {

if (avoidEdges && !inside) continue;

this.symbolInstances.push(new SymbolInstance(anchor, line, shapedText, shapedIcon, layout, inside,
// Normally symbol layers are drawn across tile boundaries. Only symbols
// with their anchors within the tile boundaries are added to the buffers
// to prevent symbols from being drawn twice.
//
// Symbols in layers with overlap are sorted in the y direction so that
// symbols lower on the canvas are drawn on top of symbols near the top.
// To preserve this order across tile boundaries these symbols can't
// be drawn across tile boundaries. Instead they need to be included in
// the buffers for both tiles and clipped to tile boundaries at draw time.
var addToBuffers = inside || mayOverlap;

this.symbolInstances.push(new SymbolInstance(anchor, line, shapedText, shapedIcon, layout, addToBuffers,
textBoxScale, textPadding, textAlongLine,
iconBoxScale, iconPadding, iconAlongLine));
}
Expand All @@ -191,6 +204,25 @@ SymbolBucket.prototype.placeFeatures = function(buffers, collisionDebug) {
var textAlongLine = layout['text-rotation-alignment'] === 'map' && layout['symbol-placement'] === 'line';
var iconAlongLine = layout['icon-rotation-alignment'] === 'map' && layout['symbol-placement'] === 'line';

var mayOverlap = layout['text-allow-overlap'] || layout['icon-allow-overlap'] ||
layout['text-ignore-placement'] || layout['icon-ignore-placement'];

// Sort symbols by their y position on the canvas so that they lower symbols
// are drawn on top of higher symbols.
// Don't sort symbols that won't overlap because it isn't necessary and
// because it causes more labels to pop in and out when rotating.
if (mayOverlap) {
var angle = this.collision.angle;
var sin = Math.sin(angle),
cos = Math.cos(angle);

this.symbolInstances.sort(function(a, b) {
var aRotated = sin * a.x + cos * a.y;
var bRotated = sin * b.x + cos * b.y;
return bRotated - aRotated;
});
}

for (var p = 0; p < this.symbolInstances.length; p++) {
var symbolInstance = this.symbolInstances[p];
var hasText = symbolInstance.hasText;
Expand Down Expand Up @@ -395,20 +427,22 @@ SymbolBucket.prototype.addToDebugBuffers = function() {
}
};

function SymbolInstance(anchor, line, shapedText, shapedIcon, layout, inside,
function SymbolInstance(anchor, line, shapedText, shapedIcon, layout, addToBuffers,
textBoxScale, textPadding, textAlongLine,
iconBoxScale, iconPadding, iconAlongLine) {

this.x = anchor.x;
this.y = anchor.y;
this.hasText = !!shapedText;
this.hasIcon = !!shapedIcon;

if (this.hasText) {
this.glyphQuads = inside ? getGlyphQuads(anchor, shapedText, textBoxScale, line, layout, textAlongLine) : [];
this.glyphQuads = addToBuffers ? getGlyphQuads(anchor, shapedText, textBoxScale, line, layout, textAlongLine) : [];
this.textCollisionFeature = new CollisionFeature(line, anchor, shapedText, textBoxScale, textPadding, textAlongLine);
}

if (this.hasIcon) {
this.iconQuads = inside ? getIconQuads(anchor, shapedIcon, iconBoxScale, line, layout, iconAlongLine) : [];
this.iconQuads = addToBuffers ? getIconQuads(anchor, shapedIcon, iconBoxScale, line, layout, iconAlongLine) : [];
this.iconCollisionFeature = new CollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconAlongLine);
}
}
19 changes: 17 additions & 2 deletions js/render/draw_symbol.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,20 @@ function drawSymbols(painter, layer, posMatrix, tile) {
var elementGroups = tile.elementGroups[layer.ref || layer.id];
if (!elementGroups) return;

var drawAcrossEdges = !(layer.layout['text-allow-overlap'] || layer.layout['icon-allow-overlap'] ||
layer.layout['text-ignore-placement'] || layer.layout['icon-ignore-placement']);

var gl = painter.gl;
gl.disable(gl.STENCIL_TEST);

if (drawAcrossEdges) {
// Disable the stencil test so that labels aren't clipped to tile boundaries.
//
// Layers with features that may be drawn overlapping aren't clipped. These
// layers are sorted in the y direction, and to draw the correct ordering near
// tile edges the icons are included in both tiles and clipped when drawing.
gl.disable(gl.STENCIL_TEST);
}

if (elementGroups.text.groups.length) {
drawSymbol(painter, layer, posMatrix, tile, elementGroups.text, 'text', true);
}
Expand All @@ -23,7 +35,10 @@ function drawSymbols(painter, layer, posMatrix, tile) {
}

drawCollisionDebug(painter, layer, posMatrix, tile);
gl.enable(gl.STENCIL_TEST);

if (drawAcrossEdges) {
gl.enable(gl.STENCIL_TEST);
}
}

var defaultSizes = {
Expand Down