Skip to content

explorer: dense point overlap saturates to yellow, looks like Smithsonian dots #231

@rdhyee

Description

@rdhyee

Symptom

At high zoom in point mode over a dense cluster (e.g. Pyla-Koutsopetria, Cyprus), the explorer renders yellow and orange dots that visually match the Smithsonian source color in the legend. These dots are not actually Smithsonian samples — they're OpenContext samples whose translucent point primitives accumulate to yellow under WebGL blending when ~500+ stack at the same screen pixel.

Users naturally assume yellow = Smithsonian (per the legend) and the data is wrong. Reality: the data is correct, but the source-color encoding has been silently overloaded by density.

Reproduction

URL: https://isamples.org/explorer.html?search=pottery+Cyprus#v=1&lat=34.9836&lng=33.7064&alt=292&mode=point

Five red dots at moderate density. Two-to-three dots that look yellow or orange. Click one of the yellow dots → popup shows "Object 5506-41 / OpenContext" (not Smithsonian).

Evidence

Within 5 km of this view, the source-of-truth parquet (samples_map_lite) has 23,313 samples, 100% OpenContext, zero Smithsonian. Verified by direct DuckDB query against data.isamples.org. The H3 cluster summaries at res 4/6/8 also show OpenContext-dominant for every cell in the area.

In the live Cesium scene, all 4,391 visible samplePoints have RGBA = (0.863, 0.224, 0.071, 0.90) — exact OpenContext #DC3912. The h3Points collection has show=false. So nothing rendering at this view is anything other than OpenContext red.

Yet WebGL readPixels at the dense bucket centers returns yellow.

Density-vs-pixel-color correlation (live measurement):

samples stacked at this 5px bucket rendered RGB hex appearance
104 (220, 57, 18) #DC3912 exact OpenContext red
141 (223, 58, 18) #DF3A12 red
224 (255, 69, 22) #FF4516 red-orange (R saturates)
384 (255, 119, 37) #FF7725 orange
392 (255, 185, 58) #FFB93A orange-yellow
564 (255, 174, 55) #FFAE37 orange-yellow
647 (255, 255, 82) #FFFF52 pure yellow
754 (255, 233, 74) #FFE94A yellow

R saturates first (only takes ~2 stacks). G keeps accumulating from the 0.22 component until it also clamps near ~600 stacks. B stays low. Result: source-color drift along the red→orange→yellow axis purely as a function of stacking density.

The reproduction URL parks the camera in a 9,555-sample H3 res-8 cell — guaranteed to over-stack.

Mechanism

PointPrimitiveCollection.blendOption = OPAQUE_AND_TRANSLUCENT with disableDepthTestDistance set (issue #185 workaround) means all 4,391 translucent point quads render in submission order at the same depth. Cesium's translucent-point shader appears to accumulate color channel-wise; with pixelSize: 6 scaled to ~48px by scaleByDistance(1e2, 8, 2e5, 3) at alt=292m, the discs heavily overlap and the per-channel saturation drifts the perceived hue.

Why it matters

  1. False source attribution. Users with a quick glance and the legend in hand will read yellow as Smithsonian, especially in Cyprus where there are zero Smithsonian samples. This is a correctness problem dressed as a rendering problem.
  2. Affects every dense site. Pyla-Koutsopetria, Polis, Catalhöyük — anywhere a single H3 res-8 cell holds thousands of samples, you'll see this.
  3. Worse for OpenContext than other sources. SESAR (#3366CC, blue) and GEOME (#109618, green) have lower green/red components and would saturate differently. The exact failure mode is OpenContext → yellow because of where #DC3912 sits in RGB space.

Suggested fixes

(Pick one, or a combination; all are localized to explorer.qmd L2441-2467.)

  • (A) Auto-cluster threshold. When the per-pixel stacking exceeds a threshold (say >50 stacks per pixel anywhere on screen), force-promote back to cluster mode and surface a phase message ("Cluster mode for clarity — zoom further to see individual samples"). Effort: small.
  • (B) Opaque point fill. Drop the .withAlpha(0.9) to .withAlpha(1.0). With fully opaque points, depth-test-disabled overlap will paint the front-most color rather than accumulate — preserves source-color truth at the cost of "I can't see what's behind." Effort: one line.
  • (C) Smaller pixelSize at high zoom. The NearFarScalar(1e2, 8, …) scales to 8× at 100 m altitude, making 6 px → 48 px. Capping the scale at 3-4× would still keep dots visible but reduce overlap area dramatically. Effort: tune one number.
  • (D) Density-aware fragment shader. Custom point appearance that limits how many overlapping discs contribute to a pixel — proper fix, larger effort.

(B) is the cheapest sanity check; (A) is the most user-friendly. (C) is a good compromise.

Out of scope

  • The popup click-through resolves correctly (picks a real OpenContext sample by Z-order). The bug is purely visual.
  • Cluster mode (h3Points) is unaffected — dominant_source is a single discrete value per cell.

Cross-refs

Acceptance

  • Yellow dots at Pyla-Koutsopetria (the reproduction URL) read as OpenContext red, or
  • A clear UI signal explains that the view is too dense for individual rendering, with appropriate fallback (cluster mode or capped-stack warning).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingexplorerInteractive Explorer features

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions