mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
102 lines
3.2 KiB
JavaScript
102 lines
3.2 KiB
JavaScript
import {
|
|
OVERPASS_SIMPLIFY_MIN_BYTES,
|
|
OVERPASS_SIMPLIFY_MIN_POINTS,
|
|
OVERPASS_SIMPLIFY_TOLERANCE_DEG,
|
|
} from './constants.js';
|
|
|
|
/** Iterative Douglas-Peucker on [{lat,lon},...] (planar-degree approx — fine at
|
|
* the ~44 m tolerance used here). Endpoints always kept. */
|
|
function douglasPeucker(points, toleranceDeg) {
|
|
const n = points.length;
|
|
if (n <= 2) return points;
|
|
const keep = new Uint8Array(n);
|
|
keep[0] = 1;
|
|
keep[n - 1] = 1;
|
|
const stack = [[0, n - 1]];
|
|
while (stack.length) {
|
|
const [a, b] = stack.pop();
|
|
if (b - a < 2) continue;
|
|
const ax = points[a].lon;
|
|
const ay = points[a].lat;
|
|
const vx = points[b].lon - ax;
|
|
const vy = points[b].lat - ay;
|
|
const c2 = vx * vx + vy * vy;
|
|
let worst = -1;
|
|
let worstDist = toleranceDeg;
|
|
for (let i = a + 1; i < b; i++) {
|
|
const wx = points[i].lon - ax;
|
|
const wy = points[i].lat - ay;
|
|
let d;
|
|
if (c2 === 0) {
|
|
d = Math.hypot(wx, wy);
|
|
} else {
|
|
const t = Math.max(0, Math.min(1, (vx * wx + vy * wy) / c2));
|
|
d = Math.hypot(wx - t * vx, wy - t * vy);
|
|
}
|
|
if (d > worstDist) {
|
|
worstDist = d;
|
|
worst = i;
|
|
}
|
|
}
|
|
if (worst >= 0) {
|
|
keep[worst] = 1;
|
|
stack.push([a, worst], [worst, b]);
|
|
}
|
|
}
|
|
const out = [];
|
|
for (let i = 0; i < n; i++) if (keep[i]) out.push(points[i]);
|
|
return out;
|
|
}
|
|
|
|
/** Simplify one element's geometry array in place if it is big enough. */
|
|
function simplifyElementGeometry(el, minPoints, toleranceDeg) {
|
|
if (Array.isArray(el?.geometry) && el.geometry.length >= minPoints) {
|
|
el.geometry = douglasPeucker(el.geometry, toleranceDeg);
|
|
}
|
|
if (Array.isArray(el?.members)) {
|
|
for (const member of el.members) {
|
|
if (
|
|
Array.isArray(member?.geometry) &&
|
|
member.geometry.length >= minPoints
|
|
) {
|
|
member.geometry = douglasPeucker(member.geometry, toleranceDeg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Server-side geometry simplification for large Overpass `out geom` payloads.
|
|
* Region/state boundary pivots return multi-MB coastline rings whose fidelity
|
|
* nothing downstream needs (the client re-simplifies for draw); decimating them
|
|
* HERE shrinks the disk cache, the wire, and client parse time — and is what
|
|
* makes the raised read cap safe. Anything unparseable or below the thresholds
|
|
* passes through byte-identical. Exported for tests (opts override thresholds).
|
|
*
|
|
* @param {string} bodyText - Raw upstream JSON body.
|
|
* @returns {string} Possibly-simplified JSON body.
|
|
*/
|
|
function simplifyOverpassPayloadBody(bodyText, opts = {}) {
|
|
const minBytes = opts.minBytes ?? OVERPASS_SIMPLIFY_MIN_BYTES;
|
|
const minPoints = opts.minPoints ?? OVERPASS_SIMPLIFY_MIN_POINTS;
|
|
const toleranceDeg = opts.toleranceDeg ?? OVERPASS_SIMPLIFY_TOLERANCE_DEG;
|
|
if (typeof bodyText !== 'string' || bodyText.length < minBytes)
|
|
return bodyText;
|
|
let data;
|
|
try {
|
|
data = JSON.parse(bodyText);
|
|
} catch {
|
|
return bodyText;
|
|
}
|
|
if (!Array.isArray(data?.elements)) return bodyText;
|
|
for (const el of data.elements)
|
|
simplifyElementGeometry(el, minPoints, toleranceDeg);
|
|
try {
|
|
return JSON.stringify(data);
|
|
} catch {
|
|
return bodyText;
|
|
}
|
|
}
|
|
|
|
export { simplifyOverpassPayloadBody };
|