Carsten Bjerre Ludwigsen · Danish Coastal Authorities · cabjl@kyst.dk
Built with the help of Claude Sonnet 5 by Anthropic.
Data/attribution: SWOT data are NASA/JPL-Caltech, CNES, CSA, UKSA. Basemap tiles are © OpenStreetMap contributors. The DVR90 vertical datum grid is from SDFE (Denmark).
A single HTML page that builds a water-level time series at a lat/lon from SWOT L2_HR_PIXC — the raw pixel cloud (~5–25 m spacing), not the coarser river/lake products. Built for coastal points with complex shorelines that reach/node-averaged products don't resolve well.
Open index.html in a browser, or host it anywhere static. Nothing to build.
- Pick a point, a search radius, and a date range.
- The page queries NASA's CMR API for
SWOT_L2_HR_PIXC_Dgranules intersecting your point (no login needed for this step). - Each granule is downloaded and parsed entirely in-browser via h5wasm — no server, no Python.
- Pixels within your search radius, in the reliable water classes
(
water_near_land,open_water,dark_water, and the low-coherence variants), feed the height for that pass. - One point per overpass is plotted and added to the results table.
- Clicking a row (or using Prev/Next) opens that pass's full pixel cloud on its own map.
Each person needs their own free NASA Earthdata token — nothing is shared between users. The page has a "First time here?" panel with the same steps:
- Create an account at https://urs.earthdata.nasa.gov → Profile → Generate Token.
- Paste it into the NASA Earthdata bearer token field. Approve the PO.DAAC application under your profile's Applications tab if downloads 401/403 despite a valid token.
- If direct downloads get CORS-blocked, use a proxy: paste a shared
institutional URL into Proxy base URL, or deploy your own
(
cloudflare-worker.json a free Cloudflare Workers account, orproxy.jslocally withnode proxy.js).
Both cloudflare-worker.js and proxy.js hold no credentials by default —
each browser sends its own token, and the proxy just forwards it and fixes
CORS. It only restricts where it will relay to (NASA/Earthdata/PROJ hosts),
not who can use it. Optional EDL_TOKEN (shared fallback token) and
ACCESS_KEY (shared password) secrets exist for institutions that want the
older shared-credential model, but most deployments should leave both unset.
- Height raw — median (not mean) pixel height above WGS84, uncorrected.
- Height corrected (EGM2008) — median of
height − geoid − solid_earth_tide − load_tide_fes − pole_tide, i.e. relative to the geoid with tide effects removed. - Height corrected (DVR90) — same tide corrections, but referenced to Denmark's national vertical datum instead of EGM2008, via SDFE's official ETRS89-to-DVR90 grid (fetched from PROJ's public CDN). Toggle between the two on the chart and the pixel-cloud map. Note: SWOT's height is on WGS84 while the DVR90 grid is defined for ETRS89 (a small, slowly-growing divergence between the two frames), and DVR90 itself was realized around 1990, so a slow secular drift from current sea level is possible.
- Crossover calibration (
height_cor_xover) is not applied separately — per NASA/JPL documentation it's already folded into PIXC'sheightvariable (unlike the LR_SSH ocean product), so adding it again would double-count it. - Pixels / Mean dist — how many pixels fed the median, and their average distance from your point (a genuine mean, unlike the height values).
- Every classified pixel across the whole tile is collected, but only what's in your current viewport gets drawn — pan/zoom re-filters live.
- The classification checkboxes are a display filter only; the search/stats always use the fixed reliable-water class set regardless.
- Color scale defaults to a fixed -1 to 1 m window for comparability across passes; "Auto range" snaps to the actual data range on demand.
- Prev/Next pass buttons switch passes while keeping your current pan/zoom/color range — clicking a fresh table row re-fits the view instead.
- Time series CSV / chart PNG — below the chart.
- Export view (.dat) — whatever's currently drawn on the map, fixed-width
columns. In QGIS: Add Delimited Text Layer, delimiter type "Regular
expression" with pattern
\s+, discard 1 header line, X=lon, Y=lat, CRS EPSG:4326. Includes both EGM2008 and DVR90 heights as separate columns. - Export view (.dat all tiles) — same, but one
.datper pass across every result, bundled as a.zip. A download link appears next to the button once ready (not auto-triggered, since long processing can cause the browser to silently block an automatic download). - Export view (.png) / (.png all tiles) — map snapshot(s) with a timestamp, granule ID, reference datum, and colorbar burned in. All-tiles version also zips.
- Browser support: Files Folder and PNG export need Chrome or Edge.
- File size: PIXC granules are tens to hundreds of MB each; a long time series means many downloads, and whole-tile pixel collection means memory/CPU scale with how much of the tile is water.
- Not the same as Hydrocron, which only covers river reaches/lakes, not open coast.
- If PIXC's variable paths ever change: the
VARSobject andgetAttr()/attrTriple()helpers near the top of the<script type="module">block are the place to adjust. A commented-outDEBUG_DUMPinsideprocessGranule()can inspect a real granule's structure in the console.