UNPKG

protomaps-leaflet

Version:

Vector tile rendering and labeling for [Leaflet](https://github.com/Leaflet/Leaflet).

149 lines (134 loc) 3.52 kB
import Point from "@mapbox/point-geometry"; export interface LabelableSegment { length: number; beginIndex: number; beginDistance: number; endIndex: number; endDistance: number; } // code from https://github.com/naturalatlas/linelabel (Apache2) const linelabel = ( pts: Point[], maxAngleDelta: number, targetLen: number, ): LabelableSegment[] => { const chunks = []; let a: Point; let b: Point; let c: Point; 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((pts[1].x - pts[0].x) ** 2 + (pts[1].y - pts[0].y) ** 2); return [ { length: d, beginIndex: 0, beginDistance: 0, endIndex: 2, endDistance: d, }, ]; } abmag = Math.sqrt((pts[1].x - pts[0].x) ** 2 + (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 interface LabelCandidate { start: Point; end: Point; } export function simpleLabel( mls: Point[][], minimum: number, repeatDistance: number, cellSize: number, ): LabelCandidate[] { 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: Point, b: Point, length: number, spacing: number) { // determine function of line const dx = b.x - a.x; const dy = b.y - a.y; const dist = Math.sqrt((b.x - a.x) ** 2 + (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; }