Add ASTER GED v3 surface-emissivity dataset#415
Conversation
Standalone static ASTERGED submodule (HDF5 lat/lon, not MODIS sinusoidal)
implementing broadband longwave surface emissivity from ASTER GED v3.
- ASTERGEDv3{R} <: AbstractStaticDataset with resolution (:AG100/:AG1km)
and tunable 5-band broadband_coefficients (Ogawa 2003, normalized to a
convex combination).
- Pure, unit-tested core: decode_mean (0.001 scale), decode_sdev (0.0001
scale), broadband_emissivity/uncertainty, band-first broadband maps,
water masking (documented LWmap coding parameter).
- CopernicusDEM-style regional windowing: BoundingBox required via
validate_dataset_coverage, region+resolution-encoded metadata_filename,
is_three_dimensional=false, default_inpainting=nothing,
location=(Center,Center,Center), missing_value=-9999.
- HDF5 read + Earthdata download gated behind fallback errors (HDF5.jl is
not a dependency and must not be added to root Project.toml; credentials
required). Pure logic works and is tested without them.
- Registered in DataWrangling.jl and NumericalEarth.jl.
- Synthetic-tile tests in test/test_asterged.jl (56 assertions pass).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Implement the ASTER GED v3 (AG100) HDF5 read + Earthdata/CMR download in the ArchGDAL extension, replacing the erroring stubs, so `Field(Metadatum(:emissivity; dataset=ASTERGEDv3(), region), grid)` returns real broadband emissivity. ext/NumericalEarthArchGDALExt.jl: - `earthdata_cmr_granules` — CMR granules.json query for AG100.v003 tiles intersecting a BoundingBox, de-duplicated by granule. - `earthdata_download` — authenticated fetch via `netrc_downloader` against urs.earthdata.nasa.gov (EARTHDATA_USERNAME / EARTHDATA_PASSWORD). - `asterged_tiles_to_netcdf` — read `/Emissivity/Mean`, `/Emissivity/SDev` and `/Land_Water_Map/LWmap` via GDAL's HDF5 driver (`HDF5:"file"://path`), build lon/lat from `/Geolocation/*` (no reprojection — plain WGS84 lat/lon), clip + mosaic to the bbox, write a regional NetCDF of raw digital numbers. src ASTERGED module: - `retrieve_data` now reads that NetCDF and feeds the existing pure decode_mean / broadband_emissivity_map / mask_water core. - `Downloads.download` dispatches to the extension; generic module fallbacks error clearly when ArchGDAL is not loaded, and the extension methods are strictly more specific so they add rather than overwrite (Julia 1.12 precompile-time method overwriting is now a hard error). - Add lon/lat name traits, CMR helpers, flip `reversed_latitude_axis` to false (NetCDF written south-to-north). Fix `ASTERGED_WATER_CODE` from 2 to 1: verified on real tile AG100.v003.37.-112 that `/Land_Water_Map/LWmap` uses the 0=land / 1=water coding (851 water cells trace the Colorado River). Update the water-masking test accordingly. Verified live end-to-end: emissivity in [0.909, 0.979], all in [0.7, 1.0]; uncertainty path and 56/56 unit tests also pass. See STATUS report. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…iation - Replace the misattributed, intercept-dropped Cheng et al. (2013) slopes with Ogawa & Schmugge (2004) coefficients: natively convex (sum exactly 1) over the 8.0-13.5 um window. The dropped intercept biased broadband emissivity ~0.02 low over low-emissivity deserts. - Fill water cells with dataset.water_emissivity (default 0.985) instead of NaN, and inpaint retrieval gaps (NearestNeighborInpainting(Inf) default, cached) so no NaN reaches downstream flux kernels. - Reduced (Center, Center, Nothing) location: the emissivity Field is stateindex-able at any k and slots directly into SurfaceRadiationProperties(albedo, emissivity = field). Export ASTERGEDv3 from NumericalEarth. - Decode digital numbers in Float32 (matches eltype(Metadata), halves the host-to-device copy), retry tile downloads 3x with backoff, and guard unbounded regions on the download path. - Remove two stale explicit imports (would fail the ExplicitImports QA test). - Add an architecture-parameterized synthetic-NetCDF test exercising the full retrieve/decode/fill/inpaint/Field pipeline and the radiation wiring on CPU and GPU without credentials. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Concretize the struct (broadband_coefficients::Vector{Float64},
water_emissivity::Float64) so it is no longer a UnionAll. It now appears in
supported_datasets(), matching sibling datasets ETOPO2022/SoilGrids2/WOA.
The fields are CPU-only load-time config, never in a kernel, so concrete
field types cost nothing.
- Set the default water_emissivity to 0.97, matching NumericalEarth's ocean
surface emissivity, so a coupled domain has one consistent water value.
- Test that ASTERGEDv3 is in supported_datasets().
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add the ogawa2004mapping bib entry (DOI verified via Crossref) and cite it inline in the ASTERGEDv3 docstring via [Author (year)](@cite key), per the project citation convention; add the DOI to the coefficient constant's comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Why did you "save for plotting" to NetCDF? is there some issue plotting the |
| """ | ||
| function broadband_emissivity_map(decoded_bands, coefficients) | ||
| _, Nx, Ny = size(decoded_bands) | ||
| result = Array{Float32}(undef, Nx, Ny) |
There was a problem hiding this comment.
should this be done on GPU? Or at least permitted?
|
It might make sense to add a section in the docs tutorial for land data + computations that are done here. It can be a future PR though. |
|
Another note to self is that I am wondering how this will all work in a distributed context, eg if we run 100m resolution globally, we want to do the calculations in here independently on every node. It's ok to put optimizations in a future PR but we should think about it a bit. |
The 0.97 water-surface emissivity was hardcoded at every ocean_surface default and in the ASTERGEDv3 water fill. Follow the default_stefan_boltzmann_constant pattern: one exported constant in Radiations, referenced everywhere, so the values cannot drift apart. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rewrite broadband_emissivity_map / broadband_uncertainty_map as broadcast + band-dimension reductions so they run on whatever architecture holds the decoded arrays (results are bitwise identical to the previous CPU loop). Use the shared default_water_emissivity, note that :AG100/:AG1km mirror NASA's product short names, and note in the extension that the tile .h5 is the download unit (Mean, SDev and LWmap are subdatasets of one file). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
no there's nothing wrong with it. I was just stitching together two vibe-coded scripts because it'll take a while to rerun. but a script like using NumericalEarth
using NumericalEarth.DataWrangling: BoundingBox, Metadatum
using Oceananigans
using ArchGDAL # activates the HDF5 tile read/download extension
using CairoMakie
dataset = ASTERGEDv3()
#####
##### 1. Multi-tile static emissivity map — US Southwest (Grand Canyon / Colorado Plateau)
#####
southwest = BoundingBox(longitude = (-112.8, -111.2), latitude = (35.2, 36.8))
southwest_grid = LatitudeLongitudeGrid(CPU(), Float32;
size = (320, 320),
longitude = (-112.8, -111.2),
latitude = (35.2, 36.8),
topology = (Bounded, Bounded, Flat))
emissivity = Field(Metadatum(:emissivity; dataset, region = southwest), southwest_grid)
uncertainty = Field(Metadatum(:emissivity_uncertainty; dataset, region = southwest), southwest_grid)
#####
##### 2. Coverage-hole probe — Congo basin (persistent cloud)
#####
congo = BoundingBox(longitude = (18, 20), latitude = (-1, 1))
congo_grid = LatitudeLongitudeGrid(CPU(), Float32;
size = (200, 200),
longitude = (18, 20),
latitude = (-1, 1),
topology = (Bounded, Bounded, Flat))
congo_emissivity = Field(Metadatum(:emissivity; dataset, region = congo), congo_grid)
#####
##### Plot the Fields directly — Oceananigans' Makie extension supplies the coordinates
#####
fig = Figure(size = (1650, 520))
Label(fig[0, 1:6], "ASTER GED v3 AG100 static broadband emissivity"; fontsize = 19, font = :bold)
ax1 = Axis(fig[1, 1]; title = "Emissivity — US Southwest (4-tile mosaic)",
xlabel = "longitude (°)", ylabel = "latitude (°)", aspect = 1)
hm1 = heatmap!(ax1, emissivity; colormap = :viridis, colorrange = (0.90, 0.98))
Colorbar(fig[1, 2], hm1; label = "broadband ε")
ax2 = Axis(fig[1, 3]; title = "Uncertainty (SDev path)",
xlabel = "longitude (°)", ylabel = "latitude (°)", aspect = 1)
hm2 = heatmap!(ax2, uncertainty; colormap = :viridis, colorrange = (0, 0.02))
Colorbar(fig[1, 4], hm2; label = "broadband σ(ε)")
ax3 = Axis(fig[1, 5]; title = "Congo basin — coverage holes inpainted, water filled",
xlabel = "longitude (°)", ylabel = "latitude (°)", aspect = 1)
hm3 = heatmap!(ax3, congo_emissivity; colormap = :viridis, colorrange = (0.90, 0.98))
Colorbar(fig[1, 6], hm3; label = "broadband ε")
save("asterged_emissivity_static_map.png", fig)would work fine. |
The struct's user-facing resolution no longer follows NASA's AG100/AG1KM product short names; those stay internal to CMR queries and tile downloads. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Suggestions by Fable 5:
|
More informative than :highresolution/:lowresolution: the names carry both the qualitative ranking and the physical resolution (100 m / 1 km). Underscore per the codebase's snake_case symbol convention. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Refactor Earthdata credentials retrieval to use get() for safer handling of environment variables.
- Removed unused water emissivity handling and associated constants. - Updated dataset resolution defaults and descriptions for clarity. - Simplified broadband emissivity and uncertainty calculations. - Refactored tile placement logic to handle NaN values correctly. - Enhanced test coverage for broadband synthesis and tile placement. - Adjusted metadata handling to ensure variable independence in filenames. - Improved documentation and comments throughout the codebase.
… queries, improve longitude folding in tile placement, and add axis spacing function to prevent divide-by-zero errors.
…val, gap filling, and land/water map handling.
…g land/water map, update related documentation, and add tests for region-gated inpainting functionality.

Adds
ASTERGEDv3— the ASTER Global Emissivity Dataset v3 (NASA LP DAAC): a static 2000–2008 clear-sky-mean land-surface emissivity climatology on a WGS84 lat/lon grid at 1 km (:low_1km, default) or 100 m (:high_100m), as aDataWranglingsubmodule.Usage
What it does
EARTHDATA_USERNAME/EARTHDATA_PASSWORD; tiles cached for reuse), reads them through GDAL's HDF5 driver, and mosaics + clips to theBoundingBox.Float32(mean ×0.001, SDev ×0.0001). The decoded broadband field is cached as a regional NetCDF.(Center, Center, Nothing)Field — safelystateindex-able at anyk, so it plugs directly intoSurfaceRadiationProperties.:emissivity_uncertaintypropagates the per-band SDev to a broadband σ(ε).ASTERGEDv3is exported fromNumericalEarth;:high_100m/:low_1kmmap internally to NASA'sAG100/AG1KMshort names.Gap filling — surface-aware inpainting
ASTER GED retrieves an emissivity almost everywhere, including over water (open-ocean broadband ε ≈ 0.98 — a real value, not a fill). Genuine gaps appear only where a clear-sky retrieval was never obtained (persistent cloud in the humid tropics, snow screened as cloud) and decode to
NaN. Building aFieldfills them withNearestNeighborInpainting(Inf), so the field is finite everywhere.The fill is surface-aware: the tiles' land/water map is read and written alongside the emissivity, so a land gap is filled only from land neighbors and a water gap only from water — a gap never borrows across the coastline (an isolated gap with no reachable same-class donor falls back to nearest-valid so the field stays finite).
This is generic, opt-in
DataWranglingmachinery:inpaint_mask!/Fieldtake an optional per-cellregionslabel field, supplied per dataset via theinpainting_regionshook (ASTER GED returns its land/water partition). With noregions(the default) the inpainting is byte-for-byte unchanged, so ocean datasets (ECCO/EN4/WOA) that deliberately extend values across the coastline are unaffected.Example — 100 m broadband emissivity
Clear-sky desert (Grand Canyon, gap-free) vs. humid tropics (Congo, cloud gaps inpainted). Plots the
Fields directly — no NetCDF round-trip:Notes / follow-ups
@examplepage: follow-up.🤖 Generated with Claude Code