Skip to content

Add progressive globe with H3 aggregation and sample drill-down#50

Merged
rdhyee merged 12 commits intoisamplesorg:mainfrom
rdhyee:main
Feb 13, 2026
Merged

Add progressive globe with H3 aggregation and sample drill-down#50
rdhyee merged 12 commits intoisamplesorg:mainfrom
rdhyee:main

Conversation

@rdhyee
Copy link
Contributor

@rdhyee rdhyee commented Feb 13, 2026

Summary

  • New progressive globe demo (tutorials/progressive_globe.qmd) with 4-tier zoom: H3 res4 → res6 → res8 → individual samples, all powered by pre-computed parquet files on Cloudflare R2 with zero backend
  • Sub-res8 sample drill-down: Zoom below 120km to see individual sample points with two-stage cards (instant metadata from lite parquet, lazy full description from wide parquet)
  • H3 spatial indexing and facet loading optimizations across existing tutorials (explorer, analysis, cesium globe, narrow-vs-wide performance)
  • Navigation: Added progressive globe to sidebar menu

Data architecture

4 parquet files on R2 (all browser-accessible via DuckDB-WASM HTTP range requests):

  • h3_summary_res4.parquet (580 KB) — continental clusters
  • h3_summary_res6.parquet (1.6 MB) — city-level clusters
  • h3_summary_res8.parquet (2.5 MB) — neighborhood clusters
  • samples_map_lite.parquet (60 MB, 6M rows) — individual sample points

Key features

  • Hysteresis mode transitions (120km enter / 180km exit) to prevent flicker
  • Viewport caching with 30% padding to minimize re-queries
  • Stale-request guards for fast navigation
  • Click-to-fly-to drill-down at every level
  • Sample card with source color coding, place name breadcrumbs, and lazy-loaded descriptions

Test plan

  • Visit progressive globe page and verify H3 clusters load at world view
  • Zoom into a region and verify res4 → res6 → res8 transitions
  • Zoom below 120km and verify individual sample points appear
  • Click a sample point and verify card with metadata appears
  • Verify sidebar navigation link works
  • Check existing tutorial pages still render correctly

🤖 Generated with Claude Code

rdhyee and others added 12 commits February 10, 2026 19:59
…zations (#5)

Add H3 spatial indexing, two-tier facet loading, and benchmark optimizations

## Changes
- isamples_explorer.qmd: Two-tier facet loading (2KB summary for instant counts)
- parquet_cesium_isamples_wide.qmd: Zoom-adaptive H3 clustering with LOD
- zenodo_isamples_analysis.qmd: Data-driven H3 regional analysis
- narrow_vs_wide_performance.qmd: Added geospatial and facet benchmarks

## Fixes Applied (Codex review)
- Fixed MODE(n) → MODE(source) for cluster coloring
- Added camera listener cleanup to prevent leaks
- Added NaN guard for cluster label parsing
- Added user-facing warning for facet summary failures

Closes #1, #2, #3, #4
Loads 580KB H3 res4 summary for instant globe render (<1s),
then switches to res6/res8 on zoom with viewport filtering.
Click triggers sample detail query from full 280MB parquet.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DOM elements created in raw HTML aren't available when OJS cells
execute. Move legend, stats bar, and phase indicator into OJS cells
and add null guards on all getElementById calls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clicking an H3 cluster now flies the camera to that location at
an altitude that triggers the next resolution level (res4→res6→res8).
The zoom watcher then automatically loads finer detail.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Side-by-side layout: globe left, live info panel right (always visible)
- Load full H3 files globally (no viewport filtering) — no gaps when panning
- Click shows cluster info + nearby samples in side panel (no camera fly-to)
- Zoom watcher switches resolution automatically: res4 → res6 → res8
- Stats, legend, cluster card, and sample list all in side panel

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The side panel was causing a reactive cycle:
  globeStatus change → sidePanel re-render → layout re-render →
  viewer re-create → phase1 re-run → globeStatus change → loop

Fix: all side panel content is static HTML. Stats, cluster card,
and sample list are updated via getElementById/innerHTML only.
No OJS mutable variables, no reactive cascade.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New 4th zoom tier: below 120km altitude, switches from H3 clusters
  to individual sample points loaded from lite parquet (60MB vs 280MB)
- Two-stage sample card: instant metadata from lite file, lazy-loaded
  description from full wide parquet on click
- Viewport caching with 30% padding for smooth panning
- Stale-request guards for async camera/query flows
- Hysteresis thresholds (120km enter / 180km exit) to prevent flicker
- Separate PointPrimitiveCollection for samples vs clusters
- Cluster click queries now use lite parquet instead of wide (5x faster)

Data files on R2:
- isamples_202601_samples_map_lite.parquet (60MB, 6M rows, 9 columns)
- Still uses H3 summary files for res4/6/8 cluster view

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- loadRes: wrap in try/catch/finally so `loading` flag always resets
  on query failure (was permanent deadlock — finding #2)
- Schema fix: cluster-click query used `n as source` but the lite
  parquet has column named `source` (finding #4)
- Remove unnecessary ORDER BY on H3 loads (finding isamplesorg#8)
- Use .pop() instead of [0] for performance timing entries (finding isamplesorg#11)
- Add rel="noopener noreferrer" to target="_blank" link (finding isamplesorg#7)

Deferred: XSS escaping (trusted data), antimeridian handling,
detail click caching, startup error fallback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…parquet

The samples_map_lite.parquet doesn't have a description column.
Use place_name for nearby sample cards instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Hash-based URL state: lat, lng, alt, heading, pitch, mode, pid
- v=1 schema versioning for future compatibility
- parseNum with Number.isFinite (avoids lat=0 bug from Codex review)
- replaceState for continuous camera movement, pushState for mode
  transitions and sample/cluster selection
- Browser back/forward via hashchange listener with flight animation
- Suppress flag prevents hash write loops during navigation restore
- Deep-link startup: fly to position and restore sample card from pid
- Share View button copies current URL to clipboard with toast
- pid takes precedence over h3 (canonicalized on write)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ds it

v._initialHash was set after the once() closure that references it,
causing undefined.lat TypeError on page load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@rdhyee rdhyee merged commit a24d363 into isamplesorg:main Feb 13, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant