diff options
Diffstat (limited to 'viewer/js/overlay_geom.js')
| -rw-r--r-- | viewer/js/overlay_geom.js | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/viewer/js/overlay_geom.js b/viewer/js/overlay_geom.js new file mode 100644 index 0000000..b43609e --- /dev/null +++ b/viewer/js/overlay_geom.js @@ -0,0 +1,106 @@ +// overlay_geom.js — vector overlays → line-segment vertex arrays for the globe and flat views +import { R_KM, toVec, interpolate, project, EE_XMAX, PC_XMAX } from './geo.js'; + +const onSeam = (a, b) => Math.abs(a[0]) === 180 && Math.abs(b[0]) === 180; + +export function linesFromGeoJSON(fc, keep = () => true) { + const out = []; + const add = coords => { // split at segments that run along the antimeridian cut + let cur = []; + coords.forEach((c, i) => { + if (i > 0 && onSeam(coords[i - 1], c)) { + if (cur.length > 1) out.push(cur); + cur = []; + } + cur.push({ lat: c[1], lon: c[0] }); + }); + if (cur.length > 1) out.push(cur); + }; + for (const f of fc.features || []) { + const g = f.geometry; + if (!g || !keep(f.properties || {})) continue; + if (g.type === 'LineString') add(g.coordinates); + else if (g.type === 'MultiLineString' || g.type === 'Polygon') g.coordinates.forEach(add); + else if (g.type === 'MultiPolygon') g.coordinates.forEach(poly => poly.forEach(add)); + } + return out; +} + +export function densify(lines, stepDeg = 1) { + return lines.map(line => { + if (line.length < 2) return [...line]; + const out = []; + for (let i = 0; i + 1 < line.length; i++) out.push(...interpolate(line[i], line[i + 1], stepDeg).slice(i === 0 ? 0 : 1)); + return out; + }); +} + +export function globeSegments(lines, radius = 1.002, stepDeg = 1) { + const pos = []; + for (const line of lines) { + for (let i = 0; i + 1 < line.length; i++) { + const pts = interpolate(line[i], line[i + 1], stepDeg); + for (let k = 0; k + 1 < pts.length; k++) { + const a = toVec(pts[k].lat, pts[k].lon), b = toVec(pts[k + 1].lat, pts[k + 1].lon); + pos.push(a[0] * radius, a[1] * radius, a[2] * radius, b[0] * radius, b[1] * radius, b[2] * radius); + } + } + } + return new Float32Array(pos); +} + +// Lines laid on the 3D ground near the camera. Segments with an end inside the cap (cosCap around n, the eye's +// direction) are cut into pieces ≈ 1/100 of their distance from the eye; each vertex sits on heightM (the drawn +// ground, m), then is pulled 1% of the way toward the eye: the same pixel, a nearer depth, so the line wins over +// the ground it lies on but hills in front still hide it. Positions are relative to origin. +const PULL = 0.01; +export function drapedSegments(lines, { eye, n, cosCap, origin, heightM }) { + const pos = [], kmPerDeg = (R_KM * Math.PI) / 180; + const inCap = p => { const v = toVec(p.lat, p.lon); return v[0] * n[0] + v[1] * n[1] + v[2] * n[2] >= cosCap; }; + const distKm = p => { const v = toVec(p.lat, p.lon); return Math.hypot(v[0] - eye[0], v[1] - eye[1], v[2] - eye[2]) * R_KM; }; + const put = p => { + const v = toVec(p.lat, p.lon), r = 1 + (Math.max(0, heightM(p.lat, p.lon)) + 1) / (R_KM * 1000); + for (let i = 0; i < 3; i++) pos.push(v[i] * r + (eye[i] - v[i] * r) * PULL - origin[i]); + }; + for (const line of lines) { + for (let i = 0; i + 1 < line.length; i++) { + const a = line[i], b = line[i + 1]; + if (!inCap(a) && !inCap(b)) continue; + const pts = interpolate(a, b, Math.max(0.01, Math.min(distKm(a), distKm(b)) / 100) / kmPerDeg); + for (let k = 0; k + 1 < pts.length; k++) { + put(pts[k]); + put(pts[k + 1]); + } + } + } + return new Float32Array(pos); +} + +export function flatSegments(lines, view, z = 0.001) { + const xmax = view === 'equal_earth' ? EE_XMAX : PC_XMAX; + const pos = []; + for (const line of lines) { + let prev = null; + for (const p of line) { + const q = project(view, p.lat, p.lon); + if (prev && Math.abs(q.x - prev.x) < xmax) pos.push(prev.x, prev.y, z, q.x, q.y, z); + prev = q; + } + } + return new Float32Array(pos); +} + +export function graticuleLines(step = 30) { + const lines = []; + for (let lon = -180; lon <= 180; lon += step) { + const l = []; + for (let lat = -90; lat <= 90; lat += 2) l.push({ lat, lon }); + lines.push(l); + } + for (let lat = -90 + step; lat < 90; lat += step) { + const l = []; + for (let lon = -180; lon <= 180; lon += 2) l.push({ lat, lon }); + lines.push(l); + } + return lines; +} |
