protomaps-leaflet
Version:
Vector tile rendering and labeling for [Leaflet](https://github.com/Leaflet/Leaflet).
106 lines (105 loc) • 3.51 kB
JavaScript
import Point from "@mapbox/point-geometry";
// code from https://github.com/naturalatlas/linelabel (Apache2)
const linelabel = (pts, maxAngleDelta, targetLen) => {
const chunks = [];
let a;
let b;
let c;
let i = 0;
let n = 0;
let d = 0;
let abmag = 0;
let bcmag = 0;
let abx = 0;
let aby = 0;
let bcx = 0;
let bcy = 0;
let dt = 0;
let iStart = 0;
let dStart = 0;
if (pts.length < 2)
return [];
if (pts.length === 2) {
d = Math.sqrt(Math.pow((pts[1].x - pts[0].x), 2) + Math.pow((pts[1].y - pts[0].y), 2));
return [
{
length: d,
beginIndex: 0,
beginDistance: 0,
endIndex: 2,
endDistance: d,
},
];
}
abmag = Math.sqrt(Math.pow((pts[1].x - pts[0].x), 2) + Math.pow((pts[1].y - pts[0].y), 2));
for (i = 1, n = pts.length - 1; i < n; i++) {
a = pts[i - 1];
b = pts[i];
c = pts[i + 1];
abx = b.x - a.x;
aby = b.y - a.y;
bcx = c.x - b.x;
bcy = c.y - b.y;
bcmag = Math.sqrt(bcx * bcx + bcy * bcy);
d += abmag;
dt = Math.acos((abx * bcx + aby * bcy) / (abmag * bcmag));
if (dt > maxAngleDelta || d - dStart > targetLen) {
chunks.push({
length: d - dStart,
beginDistance: dStart,
beginIndex: iStart,
endIndex: i + 1,
endDistance: d,
});
iStart = i;
dStart = d;
}
abmag = bcmag;
}
if (i - iStart > 0) {
chunks.push({
length: d - dStart + bcmag,
beginIndex: iStart,
beginDistance: dStart,
endIndex: i + 1,
endDistance: d + bcmag,
});
}
return chunks;
};
export function simpleLabel(mls, minimum, repeatDistance, cellSize) {
const candidates = [];
for (const ls of mls) {
const segments = linelabel(ls, Math.PI / 45, minimum); // 4 degrees, close to a straight line
for (const segment of segments) {
if (segment.length >= minimum + cellSize) {
const start = new Point(ls[segment.beginIndex].x, ls[segment.beginIndex].y);
const end = ls[segment.endIndex - 1];
const normalized = new Point((end.x - start.x) / segment.length, (end.y - start.y) / segment.length);
// offset from the start by cellSize to allow streets that meet at right angles
// to both be labeled.
for (let i = cellSize; i < segment.length - minimum; i += repeatDistance) {
candidates.push({
start: start.add(normalized.mult(i)),
end: start.add(normalized.mult(i + minimum)),
});
}
}
}
}
return candidates;
}
export function lineCells(a, b, length, spacing) {
// determine function of line
const dx = b.x - a.x;
const dy = b.y - a.y;
const dist = Math.sqrt(Math.pow((b.x - a.x), 2) + Math.pow((b.y - a.y), 2));
const retval = [];
// starting from the anchor, generate square cells,
// guaranteeing to cover the endpoint
for (let i = 0; i < length + spacing; i += 2 * spacing) {
const factor = (i * 1) / dist;
retval.push({ x: a.x + factor * dx, y: a.y + factor * dy });
}
return retval;
}