Skip to content

Latest commit

 

History

299 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SARdine

SAR Data INspection and Exploration

Browser-native visualization and export for NISAR HDF5 and Cloud Optimized GeoTIFFs

CI Version Live demo License: GPL v3 WebGL2 No Server

SARdine viewer — NISAR GCOV dual-pol RGB composite over the Pacaya-Samiria floodplain

▲ Click the screenshot and your browser starts streaming a full NISAR frame at native 20 m resolution — a ~1 GB Cloud Optimized GeoTIFF pair — rendered as a dual-pol RGB composite (HH / HV / HH÷HV) over the Ucayali floodplain in Pacaya-Samiria, Peru. No login, no server, no download: HTTP Range reads fetch only the tiles you look at (a few MB), so it opens in seconds — then pan and zoom anywhere in the 240×270 km frame down to full detail. To stream the same region live from the NASA archive as HDF5 (CMR granule search + chunked range reads, free Earthdata token required), use this link.

This project is under active development. Some features are experimental or incomplete. Bug reports welcome via GitHub Issues.

The hosted build runs entirely in your browser. Three ways to load data:

What you do What you need
Drag-and-drop Drop a .tif, NISAR .h5, NITF/SICD, or .geojson onto the page A local file
Public URL Paste a public COG URL into "Direct URL" — Capella Open Data, Umbra Open Data, Sentinel-2 cogs, etc. A CORS-friendly URL
Earthdata streaming Open the Earthdata Login panel, paste a token from urs.earthdata.nasa.gov/profileGenerate Token, and stream NISAR / Sentinel-1 / OPERA directly from NASA DAACs An EDL token (free)

About your Earthdata token (read this if you're security-conscious)

NASA DAACs don't send CORS headers, so the hosted build relays data requests through a small Cloudflare Worker (sardine-edl-proxy/, ~200 lines) that adds CORS and forwards your token to NASA. Straight answers about that:

  • Your token stays in your browser's localStorage and is sent only to the Worker and NASA servers. It travels in a request header (not the URL). The Worker hard-allowlists NASA hostnames, drops the auth header on any redirect leaving the allowlist, and doesn't log or store tokens — but SARdine is a research project, and you shouldn't have to take our word for it:
  • Run your own relay in ~2 minutes. The Worker source is right here in the repo. cd sardine-edl-proxy && npx wrangler deploy puts an identical copy on your free Cloudflare account; paste your *.workers.dev URL into SARdine's Earthdata panel (Proxy URL field) and your token never touches ours.
  • The stakes are low by design. EDL tokens are read-only data-access credentials for (mostly public) NASA data. They expire, and you can revoke them anytime. Worst case is someone downloads free satellite data under your account.
  • Running locally? npm run dev uses a local Vite proxy — no Worker involved at all.

What It Is

SARdine is a browser-native exploration and visualization GUI for NISAR HDF5 data. Drop in a file, explore the data, and export a figure or a georeferenced dataset. Everything runs in the browser; there is nothing to install.

It reads NISAR L2 GCOV HDF5 (.h5) directly using a custom JavaScript HDF5 reader (h5chunk). Rendering runs on the GPU via WebGL2 shaders: dB scaling, colormaps, contrast stretching, and RGB composites stay interactive while panning and zooming. GCOV/GUNW files are 2–8 GB but only viewport-intersecting chunks are ever read.


What You Can Do

Explore full-resolution SAR data Drop in a large NISAR file and pan/zoom through it at full resolution. Switch polarizations (HH, HV, VV), adjust contrast with the live histogram, and apply stretch modes (linear, sqrt, gamma, sigmoid).

Make publication-ready figures Enable the dual-pol RGB composite to map polarization channels to color. Export a georeferenced figure PNG with scale bar, corner coordinates, and colorbar. Export scatter plots and histograms as publication SVGs.

Classify and map land cover Draw an ROI, open the 2D feature space scatter (e.g. HH dB vs HV dB), define class regions by drawing rectangles, and see the classification overlay on the map in real time. Filter by incidence angle range (NISAR HDF5 only).

Subset and export data Draw an ROI and export a subregion as a raw Float32 GeoTIFF (linear power, with CRS and tiepoints) or a rendered RGBA GeoTIFF. Works for both single-pol and RGB composites.

Time series and change detection Load multiple dates for ROI time series plotting, or compose a multi-date RGB composite for change detection visualization.

Stream from S3 Paste a presigned URL and stream directly from a bucket — same workflow, no download required. Chunk decompression runs in a Web Worker pool, and a persistent IndexedDB cache speeds up repeat visits to the same scene.

Share exactly what you see Copy Link captures the data source, render settings, and active ROI as a URL. A link with just a bbox resolves its own granule via NASA CMR (newest, best-coverage, full-frame first) and loads only the region — coordinates outlive granule IDs across reprocessing campaigns. See docs/DEEP_LINKS.md.

Save your markup, keep your provenance Annotations, ROIs, transects, and classifier regions save/load as GeoJSON with a versioned schema (observer, method, timestamps, per-ROI statistics). Every GeoTIFF export writes a {name}.tif.json sidecar carrying the full product identification, render state, and derived_from lineage.


Quick Start

SARdine requires Node.js (v18 or later) and npm.

git clone https://github.com/nicksteiner/sardine.git
cd sardine
npm install
npm run dev

Open http://localhost:5173 in Chrome, Edge, or Firefox (WebGL2 required).

Installing Node.js

macOS: brew install node or download from https://nodejs.org/

Windows: Download the installer from https://nodejs.org/ (LTS recommended).

Linux (Debian/Ubuntu):

curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs

Loading Data

Pick a mode from the File Type dropdown in the left panel.

Local HDF5 Files

  1. File TypeNISAR GCOV HDF5 (Local File)
  2. Choose File → pick your .h5
  3. Pick Frequency (frequencyA = high res, frequencyB = low res)
  4. Pick Polarization (HHHH, HVHV, VVVV, etc.)
  5. Optionally switch to RGB Composite and pick a preset
  6. Load Dataset

Nothing is uploaded. Chunks are read directly from disk via the browser File API.

Presigned URLs (S3 / HTTPS)

  1. File TypeRemote Bucket / S3
  2. Paste the presigned URL into Direct URL
  3. Load from URL — metadata arrives via Range requests (~8 MB)
  4. Pick frequency and polarization → Load Remote Dataset

File type is detected from the path: .h5/.hdf5/.he5 → NISAR, .tif/.tiff → COG.

Deep Links

Every load is addressable. Examples (full parameter reference in docs/DEEP_LINKS.md):

?url=<granule .h5/.tif URL>&min=-16&max=2&db=1&cmap=viridis     # explicit source + render state
?url=<granule>&bbox=-77.48,38.90,-77.26,39.01                   # + region: view, ROI, and chunk
                                                                #   fetching scoped to the bbox
?bbox=-74.26,40.49,-73.70,40.92&db=1                            # region-first: resolves the best
                                                                #   NISAR granule via NASA CMR
?bbox=…&t=2025-12-01/2025-12-31                                 # same place, bounded in time

Links carrying a region auto-load (the fetch is bounded); plain granule links stay click-to-load. A verified end-to-end walkthrough against live ASF DAAC data is in docs/DEMO.md.

COG URLs

  1. File TypeCloud Optimized GeoTIFF (URL)
  2. Paste URL (presigned or public)
  3. Load COG

Tutorials

Tutorial 1 — RGB Composite

RGB mode assigns polarization channels to color, which makes land cover patterns easier to see. The most common case is a dual-pol file (HH + HV), which gives you two independent channels — SARdine fills the third using a ratio.

You need: A NISAR GCOV file with at least two polarizations (e.g. HHHH and HVHV).

  1. Load your file (File TypeNISAR GCOV HDF5 (Local File), click Choose File)
  2. In the Mode selector, switch from Single Polarization to RGB Composite
  3. Set Preset to dual-pol-h (HH+HV) or dual-pol-v (VV+VH) depending on your file
  4. Click Load Dataset — SARdine loads both bands and renders the composite
  5. Press R to auto-stretch contrast across all channels
  6. Open the histogram (H) — you'll see three overlapping distributions, one per channel
  7. Drag per-channel contrast handles to balance the composite if one color dominates
  8. Press Ctrl+S to export a figure PNG with the RGB triangle colorbar

Tips:

  • Water appears dark in all channels — usually a deep blue-black
  • Vegetation is bright in cross-pol (HV) — shows as green
  • Urban double-bounce is bright in co-pol (HH) — shows as red/orange
  • Switch Stretch to sqrt or gamma to bring out low-backscatter features

Tutorial 2 — Land Cover Classification

The feature space classifier lets you define land cover classes by drawing regions on a 2D scatter plot of two polarimetric channels. Classification updates on the map in real time.

You need: A NISAR GCOV file with at least two polarizations (e.g. HHHH and HVHV).

  1. Load a single-pol dataset first (e.g. HHHH) so the basemap is visible
  2. Draw an ROI — click and drag on the map to define your analysis region. Keep it focused on your area of interest; large ROIs are slower to scatter.
  3. Press C to open the Feature Space panel
  4. Set the X axis to HH dB and Y axis to HV dB using the axis dropdowns
  5. The scatter plot populates with one point per pixel in your ROI
  6. Click + Add Class and give it a name (e.g. "Water")
  7. Draw a rectangle on the scatter plot by clicking and dragging — pixels within that dB range light up on the map in the class color
  8. Repeat for additional classes (e.g. "Vegetation", "Urban")
  9. To filter by incidence angle (NISAR only): expand Incidence Angle and set a min/max range
  10. Export options:
    • SVG button → publication-quality scatter plot with class regions
    • Map button → classification raster as a georeferenced PNG
    • GeoTIFF → rendered classification as an RGBA GeoTIFF

Tips:

  • Zoom in on the scatter plot to draw precise class boundaries
  • Water typically clusters at very low HH and HV (bottom-left corner)
  • Urban double-bounce is high HH, low HV (right side, low on Y axis)
  • Vegetation is high HV relative to HH (upper-left to center)

Tutorial 3 — Time Series: ROI Plot and Multi-Date RGB Mosaic

SARdine supports two time series workflows: plotting backscatter over time for a fixed ROI, and compositing multiple dates into a single RGB image for change detection.

You need: Two or more NISAR GCOV files from the same track, different acquisition dates.

ROI Time Series Plot

  1. Load your first date as usual (Load Dataset)
  2. Draw an ROI over your target area (e.g. a field, wetland, or urban block)
  3. In the Time Series panel, click Add Date and load a second .h5 file
  4. Repeat for all dates you want to include
  5. SARdine extracts the mean backscatter (dB) within the ROI for each date and plots it as a time series
  6. Click Export SVG to export the time series chart as a vector figure

Tips:

  • Use a small, homogeneous ROI for cleaner signal — a 1 km² patch of a single land cover type
  • Multi-look averaging is applied before extraction, matching on-screen rendering
  • Add multiple ROIs to compare two land cover types on the same chart

Multi-Date RGB Mosaic (Change Detection)

This workflow assigns each of three acquisition dates to a color channel (R, G, B). Pixels that appear white have not changed; colored pixels changed between dates. Magenta or cyan patches often indicate flooding or vegetation change.

  1. In Mode, select RGB CompositePreset: Multi-Date
  2. In the Red channel slot, load your earliest date
  3. In the Green channel slot, load your middle date
  4. In the Blue channel slot, load your latest date
  5. Click Load Dataset — SARdine reads the same polarization from all three files and composites them
  6. Press R to auto-stretch each channel independently
  7. Zoom into areas of interest — colored patches indicate change, white/grey indicates stable backscatter
  8. Export via Ctrl+S for a figure PNG, or use GeoTIFFRGB Composite for a 3-band georeferenced export

Tips:

  • Use the same polarization (e.g. HHHH) across all dates for a fair comparison
  • Flood detection: flooded areas go dark in SAR — they appear as a single-channel dropout (pure R, G, or B color)
  • For subtle change, try adjusting stretch to sigmoid to compress the dynamic range

Controls

Control Description
Colormap Grayscale, viridis, inferno, plasma, phase, sardine, flood, diverging, polarimetric
Contrast Min/max dB range — drag sliders or use Auto for percentile-based stretch
Stretch Linear, sqrt, gamma, sigmoid transfer function
Multi-look Data reduction (box-filter averaging in linear power)
Histogram Floating viewport histogram — auto-updates on pan/zoom, SVG export
Classifier 2D/1D feature space scatter with class region drawing and incidence angle filter
Overture Overlay boundaries, roads, or places from Overture Maps

Keyboard Shortcuts

Key Action
H Toggle histogram overlay
C Toggle feature space classifier
F Fit view to data bounds
R Reset contrast to auto
G Toggle coordinate grid
M Toggle overview map
Ctrl+S Save figure (PNG)

Classification Workflow

  1. Draw an ROI on the map (click and drag)
  2. Press C to open the Feature Space scatter plot
  3. Click + Add Class to define a land cover class
  4. Draw a rectangle on the scatter plot to assign pixels in that dB range
  5. Pixels within the class region are colored on the map in real time
  6. Export: SVG for the scatter plot, Map for the classification raster

Export

GeoTIFF

  • Raw Float32 — linear power values with CRS and tiepoints
  • Rendered RGBA — what you see on screen (dB, colormap, contrast) as a 4-band GeoTIFF
  • RGB Composite — 3-band GeoTIFF when in composite mode

Draw an ROI to export a subregion, or export the full extent.

Figure PNG

Canvas capture with overlays: scale bar, corner coordinates, colorbar (or RGB triangle for composites), and classification overlay.

Publication SVG

Vector graphics:

  • Scatter plot — density heatmap with class regions, open L-axes, outward ticks, Helvetica
  • Histogram — filled distribution with contrast limit markers and legend
  • Classification map — embedded raster with vector legend and pixel counts

How It Works

File/URL → h5chunk → Chunks → GPU Texture → GLSL Shader → Screen
                                                ↓
                                   dB scale → stretch → colormap → contrast

h5chunk is a pure JavaScript HDF5 chunk reader. It parses the HDF5 superblock, object headers, and B-tree to build a chunk index, then fetches only the chunks intersecting the current viewport via File.slice() (local) or HTTP Range (remote). Chunks are decompressed (deflate + shuffle) into Float32Arrays and uploaded directly as WebGL2 textures. The fragment shader handles the rest.

GCOV and GUNW products are supported. GUNW (interferometric phase and coherence) is developmental.

Tech Stack

Dependency Role
React 18 UI framework
deck.gl 8.9 WebGL tile/bitmap rendering
geotiff.js COG loading via HTTP Range
h5chunk (built-in) Cloud-optimized HDF5 streaming (pure JS)
h5wasm HDF5 attribute/metadata parsing (WASM)
MapLibre GL Basemap rendering
Vite Dev server and build tool

Server Mode (JupyterHub / NISAR ODS)

cd ~/sardine
npm install --legacy-peer-deps && npm run build
node server/launch.cjs --data-dir /home/jovyan

Access via JupyterLab proxy: https://<hub-host>/user/<username>/proxy/8050/


Development

npm install          # Install dependencies
npm run dev          # Dev server at localhost:5173
npm run build        # Production build → dist/
npm test             # Full suite: structural + pipeline regressions + unit tests
npm run test:unit    # Behavioral unit tests (test/unit/*.test.mjs, auto-discovered)
npm run test:quick   # Fast smoke tests
npm run benchmark    # GPU vs CPU performance comparison

Known Issues

Issue Status
Georeferencing errors on coarse overviews Low priority
Slow loading RGB frequency A Low priority — frequency A has no HDF5 pyramids; zoomed-in views are fast, full-frame overviews are not. Use a bbox deep link or frequency B for browse.

License

AGPL-3.0. Previously MIT (through 3/5/26). Commercial licensing available — contact nick.steiner@gmail.com.


h5wasm · geotiff.js · deck.gl · NISAR cloud-optimization: NSIDC + JPL

CCNY Earth & Atmospheric Sciences

About

No description, website, or topics provided.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages