mapbox-gl
Version:
A WebGL interactive maps library
96 lines (69 loc) • 3.53 kB
JavaScript
;
var interpolate = require('../util/interpolate');
var Anchor = require('../symbol/anchor');
var checkMaxAngle = require('./check_max_angle');
module.exports = getAnchors;
function getAnchors(line, spacing, maxAngle, shapedText, shapedIcon, glyphSize, boxScale, overscaling) {
// 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
// on the line.
var angleWindowSize = shapedText ?
3 / 5 * glyphSize * boxScale :
0;
var labelLength = Math.max(
shapedText ? shapedText.right - shapedText.left : 0,
shapedIcon ? shapedIcon.right - shapedIcon.left : 0);
// Is the line continued from outside the tile boundary?
if (line[0].x === 0 || line[0].x === 4096 || line[0].y === 0 || line[0].y === 4096) {
var continuedLine = true;
}
// Is the label long, relative to the spacing?
// If so, adjust the spacing so there is always a minimum space of `spacing / 4` between label edges.
if (spacing - labelLength * boxScale < spacing / 4) {
spacing = labelLength * boxScale + spacing / 4;
}
// Offset the first anchor by:
// Either half the label length plus a fixed extra offset if the line is not continued
// Or half the spacing if the line is continued.
// For non-continued lines, add a bit of fixed extra offset to avoid collisions at T intersections.
var fixedExtraOffset = glyphSize * 2;
var offset = !continuedLine ?
((labelLength / 2 + fixedExtraOffset) * boxScale * overscaling) % spacing :
(spacing / 2 * overscaling) % spacing;
return resample(line, offset, spacing, angleWindowSize, maxAngle, labelLength * boxScale, continuedLine, false);
}
function resample(line, offset, spacing, angleWindowSize, maxAngle, labelLength, continuedLine, placeAtMiddle) {
var distance = 0,
markedDistance = offset - spacing;
var anchors = [];
for (var i = 0; i < line.length - 1; i++) {
var a = line[i],
b = line[i + 1];
var segmentDist = a.dist(b),
angle = b.angleTo(a);
while (markedDistance + spacing < distance + segmentDist) {
markedDistance += spacing;
var t = (markedDistance - distance) / segmentDist,
x = interpolate(a.x, b.x, t),
y = interpolate(a.y, b.y, t);
if (x >= 0 && x < 4096 && y >= 0 && y < 4096) {
x = Math.round(x);
y = Math.round(y);
var anchor = new Anchor(x, y, angle, i);
if (!angleWindowSize || checkMaxAngle(line, anchor, labelLength, angleWindowSize, maxAngle)) {
anchors.push(anchor);
}
}
}
distance += segmentDist;
}
if (!placeAtMiddle && !anchors.length && !continuedLine) {
// The first attempt at finding anchors at which labels can be placed failed.
// Try again, but this time just try placing one anchor at the middle of the line.
// This has the most effect for short lines in overscaled tiles, since the
// initial offset used in overscaled tiles is calculated to align labels with positions in
// parent tiles instead of placing the label as close to the beginning as possible.
anchors = resample(line, distance / 2, spacing, angleWindowSize, maxAngle, labelLength, continuedLine, true);
}
return anchors;
}