Skip to content

[Optimization 3/4] H3 hexagon LOD layer in parquet_cesium_isamples_wide.qmd #3

@rdhyee

Description

@rdhyee

Priority: 4

Context

The Cesium 3D globe currently renders 6.7M+ individual PointPrimitives. This works but loads everything at once. H3 enables zoom-adaptive level-of-detail (LOD) rendering.

Data Files on R2

File URL Size
Wide + H3 https://pub-a18234d962364c22a50c787b7ca09fa5.r2.dev/isamples_202601_wide_h3.parquet 292 MB
Facet cross-tab https://pub-a18234d962364c22a50c787b7ca09fa5.r2.dev/isamples_202601_facet_cross.parquet 1 KB

H3 columns: h3_res4 (BIGINT), h3_res6 (BIGINT), h3_res8 (BIGINT). Pre-computed as integers — no H3 extension needed for grouping/filtering.

File to Modify

tutorials/parquet_cesium_isamples_wide.qmd

Current Behavior

Renders all points at once using Cesium PointPrimitiveCollection:

  • Loads data in chunks (25K points per batch)
  • Color-coded by source (SESAR=blue, OpenContext=green, GEOME=orange, Smithsonian=purple)
  • Works but initial load is heavy

Desired Changes

1. Switch to H3 file

Update parquet URL:

const wide_url = "https://pub-a18234d962364c22a50c787b7ca09fa5.r2.dev/isamples_202601_wide_h3.parquet";

2. Add zoom-adaptive LOD

Query different H3 resolutions based on camera height:

// Determine H3 resolution from zoom level
function getH3ResForHeight(height) {
    if (height > 5000000) return 4;   // Continental view
    if (height > 500000) return 6;    // Regional view
    return 8;                          // Local view
}

// On camera move, re-query with appropriate resolution
viewer.camera.changed.addEventListener(async () => {
    const height = viewer.camera.positionCartographic.height;
    const h3Res = getH3ResForHeight(height);

    const clusters = await db.query(`
        SELECT
            h3_res${h3Res} as hex_id,
            COUNT(*) as n,
            AVG(latitude) as lat,
            AVG(longitude) as lon,
            MODE(n) as dominant_source
        FROM read_parquet('${wide_url}')
        WHERE otype = 'MaterialSampleRecord' AND h3_res${h3Res} IS NOT NULL
        GROUP BY h3_res${h3Res}
    `);

    // Render as sized/colored points
    // Point radius proportional to log(count)
    // Color by dominant source
});

3. Keep original point rendering as toggle

Add a UI toggle: "Clustered view" (H3 LOD, default) vs "All points" (existing behavior). This lets users see both approaches.

4. Show cluster count in tooltip

When hovering over an H3 cluster point, show: count, dominant source, H3 cell ID.

Important Notes

  • The h3_res4/6/8 columns are pre-computed BIGINT values — you do NOT need the H3 DuckDB extension for filtering/grouping
  • You only need the H3 extension if you want to convert cell IDs back to boundaries (for rendering hex outlines)
  • For point-based cluster rendering, just use AVG(lat), AVG(lon) per cell — no extension needed
  • DuckDB-WASM may not support the H3 community extension — design for graceful fallback

Acceptance Criteria

  • Zoom-adaptive clustering using H3 resolution
  • Point size reflects cluster count
  • Color reflects dominant source
  • Toggle between clustered and full-point views
  • Smoother initial load (clustered view loads faster)
  • Tooltip shows cluster details

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions