// deepzoom.mjs — procedural detail tiles from 10 km/px down to street level, globe and flat, seams and poles const wait = `const a = window.mapViewer; for (let i = 0; i < 60; i++) { await new Promise(r => setTimeout(r, 500)); const t = a.view.tiles; if (i > 2 && t.inflight === 0 && t.queue.length === 0) break; } const t = a.view.tiles; return { z: t.z, active: t.active, ready: [...t.tiles.values()].filter(e => e.state === 'ready').length, errors: [...t.tiles.values()].filter(e => e.state === 'error').length, badge: !document.getElementById('detail-badge').hidden, coast: a.view.lines.objects.has('coast'), rivers: a.view.lines.objects.has('rivers'), scale: document.getElementById('scale-text').textContent };`; const at = (lat, lon, alt) => `window.mapViewer.view.setState({ lat: ${lat}, lon: ${lon}, alt: ${alt} });`; const detail = `const a = window.mapViewer, gl = a.renderer.getContext(); a.view.render(); const n = 64, p = new Uint8Array(4 * n * n); gl.readPixels((gl.drawingBufferWidth - n) >> 1, (gl.drawingBufferHeight - n) >> 1, n, n, gl.RGBA, gl.UNSIGNED_BYTE, p); let s = 0, s2 = 0; for (let i = 0; i < p.length; i += 4) { const v = p[i] + p[i + 1] + p[i + 2]; s += v; s2 += v * v; } const m = s / (n * n); return Math.sqrt(s2 / (n * n) - m * m);`; export const steps = [ { name: '20 km view: tiles on, badge shown, coast lines hidden', wait: 4000, js: at(41, 3.5, 0.003) + wait, expect: v => v.active && v.z >= 10 && v.ready >= 4 && v.errors === 0 && v.badge && !v.coast && !v.rivers }, { name: 'the deep view has fine detail', js: detail, expect: v => v > 3 }, { name: 'on a data layer the tiles draw no rivers, so the overlay stays', expect: v => v.data && !v.relief, js: `const a = window.mapViewer; await a.setLayer('biomes'); a.dirty = true; await new Promise(r => setTimeout(r, 400)); const data = a.view.lines.objects.has('rivers'); await a.setLayer('relief'); a.dirty = true; await new Promise(r => setTimeout(r, 400)); return { data, relief: a.view.lines.objects.has('rivers') };` }, { name: 'street-level limit (20 m above sea level with 3D off)', js: `const a = window.mapViewer; a.setTerrain(0); a.zoomBy(1e-6); return a.view.getState().alt * 12742000;`, expect: v => Math.abs(v - 20) < 1e-3 }, { name: 'max-zoom tiles load', js: wait, expect: v => v.active && v.z === 16 && v.ready >= 4 && v.errors === 0 && / m · /.test(v.scale) }, { name: 'antimeridian', js: at(10, 179.999, 0.004) + wait, expect: v => v.active && v.ready >= 4 && v.errors === 0 }, { name: 'north pole', js: at(89.95, 0, 0.004) + wait, expect: v => v.active && v.ready >= 1 && v.errors === 0 }, { name: 'switching away frees the globe tiles', expect: v => v.size === 0 && v.queue === 0, js: `const b = window.mapViewer; b.view.setState({ lat: 41, lon: 3.5, alt: 0.003 }); await new Promise(r => setTimeout(r, 1500)); const g = b.views.globe.tiles; b.setView('equal_earth'); await new Promise(r => setTimeout(r, 1000)); return { size: g.tiles.size, queue: g.queue.length, inflight: g.inflight };` }, { name: 'flat deep zoom', js: `const b = window.mapViewer; b.setView('equal_earth'); b.view.setState({ lat: 41, lon: 3.5, zoom: 2000 });` + wait, expect: v => v.active && v.z >= 9 && v.ready >= 4 && v.errors === 0 }, { name: 'zoom out: tiles off, coast back', js: `const b = window.mapViewer; b.setView('globe'); b.home();` + wait, expect: v => !v.active && v.coast && !v.badge }, ];