Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
46b35f9
Add GEBCO, IBCSO, and IBCAO bathymetry dataset modules
Apr 22, 2026
5205eff
Add tests for IBCSO, GEBCO, IBCAO metadata interfaces and coverage va…
aklocker42 Apr 22, 2026
d84862d
Add IBCSO, GEBCO, IBCAO to documentation dataset tables
aklocker42 Apr 22, 2026
b81e86e
Reorganize dataset table by category and fix link text for new bathym…
aklocker42 Apr 22, 2026
f503418
Address reviewer comments: validate_dataset_coverage fallback, datase…
aklocker42 Apr 22, 2026
89d665e
Add ArchGDAL to project dependencies
aklocker42 Apr 22, 2026
477f334
Make ArchGDAL a weak dependency via extension
aklocker42 Apr 22, 2026
6e725d5
Update ext/NumericalEarthArchGDALExt.jl
simone-silvestri Apr 22, 2026
849d1b6
Update src/DataWrangling/IBCAO/IBCAO.jl
simone-silvestri Apr 22, 2026
e027bd0
Update ext/NumericalEarthArchGDALExt.jl
simone-silvestri Apr 22, 2026
d3577fb
Fix typo: reproject_ibcao_to_netcdf declaration had spurious underscore
aklocker42 Apr 22, 2026
0ed9637
Merge branch 'main' into add-gebco-ibcso-ibcao
simone-silvestri Apr 22, 2026
df672c1
Fix test: import metadata_filename, use height_above_water=0 for land…
aklocker42 Apr 23, 2026
a07f884
Merge branch 'main' into add-gebco-ibcso-ibcao
simone-silvestri Apr 27, 2026
b7a5193
a generic AbstractBathymetryDataset
simone-silvestri Apr 27, 2026
8878842
Merge branch 'main' into add-gebco-ibcso-ibcao
simone-silvestri Apr 27, 2026
96f0377
Merge branch 'main' into add-gebco-ibcso-ibcao
simone-silvestri Apr 27, 2026
1ae2485
Merge branch 'main' into add-gebco-ibcso-ibcao
simone-silvestri Apr 29, 2026
befeb27
Merge branch 'main' into add-gebco-ibcso-ibcao
simone-silvestri May 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Thermodynamics = "b60c26fb-14c3-4610-9d3e-2d17fe7ff00c"
ZipFile = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea"

[weakdeps]
ArchGDAL = "c9ce4bd3-c3d5-55b8-8973-c0e20141b8c3"
Breeze = "660aa2fb-d4c8-4359-a52c-9c057bc511da"
CDSAPI = "8a7b9de3-9c00-473e-88b4-7eccd7ef2fea"
CondaPkg = "992eb4ea-22a4-4c89-a5bb-47a3300528ab"
Expand All @@ -44,6 +45,7 @@ WorldOceanAtlasTools = "04f20302-f1b9-11e8-29d9-7d841cb0a64a"
XESMF = "2e0b0046-e7a1-486f-88de-807ee8ffabe5"

[extensions]
NumericalEarthArchGDALExt = "ArchGDAL"
NumericalEarthBreezeExt = "Breeze"
NumericalEarthCDSAPIExt = "CDSAPI"
NumericalEarthCopernicusMarineExt = "CopernicusMarine"
Expand All @@ -54,6 +56,8 @@ NumericalEarthWOAExt = "WorldOceanAtlasTools"

[compat]
Adapt = "4"
ArchGDAL = "0.10"

Breeze = "0.4"
CDSAPI = "2.2.1"
CFTime = "0.1, 0.2"
Expand Down
6 changes: 6 additions & 0 deletions docs/src/Metadata/metadata_overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,18 @@ NumericalEarth currently ships connectors for the following data products:

| Dataset | Supported Variables | Documentation Link |
|--------------------|-----------------------------------------------------------|-----------------------------------------------------------------------------------------------------|
| **Bathymetry** | | |
| `ETOPO2022` | [Supported variables](@ref dataset-etopo2022-vars) | [NOAA ETOPO 2022 overview](https://www.ncei.noaa.gov/products/etopo-global-relief-model) |
| `GEBCO2024` | [Supported variables](@ref dataset-gebco2024-vars) | [GEBCO 2024 overview](https://www.gebco.net/data_and_products/gridded_bathymetry_data/) |
| `IBCSOv2` | [Supported variables](@ref dataset-ibcsov2-vars) | [IBCSO overview](https://ibcso.org/ibcso-2024-annual-release/) |
| `IBCAOv5` | [Supported variables](@ref dataset-ibcaov5-vars) | [IBCAO overview](https://www.gebco.net/data_and_products/gridded_bathymetry_data/arctic_ocean/) |
| **Ocean reanalysis** | | |
| `ECCO2Monthly` | [Supported variables](@ref dataset-ecco2monthly-vars) | [ECCO2 documentation](https://ecco.jpl.nasa.gov/products/all/) |
| `ECCO2Daily` | [Supported variables](@ref dataset-ecco2daily-vars) | [ECCO2 documentation](https://ecco.jpl.nasa.gov/products/all/) |
| `ECCO4Monthly` | [Supported variables](@ref dataset-ecco4monthly-vars) | [ECCO V4r4 product guide](https://ecco-group.org/products-ECCO-V4r4.htm) |
| `EN4Monthly` | [Supported variables](@ref dataset-en4monthly-vars) | [Met Office EN4 overview](https://www.metoffice.gov.uk/hadobs/en4/) |
| `GLORYSDaily` | [Supported variables](@ref dataset-glorysdaily-vars) | [Copernicus GLORYS product page](https://data.marine.copernicus.eu/product/GLOBAL_MULTIYEAR_PHY_001_030/description) |
| `GLORYSMonthly` | [Supported variables](@ref dataset-glorysmonthly-vars) | [Copernicus GLORYS product page](https://data.marine.copernicus.eu/product/GLOBAL_MULTIYEAR_PHY_001_030/description) |
| **Atmospheric forcing** | | |
| `RepeatYearJRA55` | [Supported variables](@ref dataset-repeatyearjra55-vars) | [JRA-55 Reanalysis](https://www.data.jma.go.jp/jra/html/JRA-55/index_en.html) |
| `MultiYearJRA55` | [Supported variables](@ref dataset-multiyearjra55-vars) | [JRA-55 Reanalysis](https://www.data.jma.go.jp/jra/html/JRA-55/index_en.html) |
18 changes: 18 additions & 0 deletions docs/src/Metadata/supported_variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ NumericalEarth currently ships connectors for the following data products:

| Dataset | Supported Variables | Documentation Link |
|--------------------|-----------------------------------------------------------|-----------------------------------------------------------------------------------------------------|
| **Bathymetry** | | |
| `ETOPO2022` | [Supported variables](@ref dataset-etopo2022-vars) | [NOAA ETOPO 2022 overview](https://www.ncei.noaa.gov/products/etopo-global-relief-model) |
| `GEBCO2024` | [Supported variables](@ref dataset-gebco2024-vars) | [GEBCO 2024 overview](https://www.gebco.net/data_and_products/gridded_bathymetry_data/) |
| `IBCSOv2` | [Supported variables](@ref dataset-ibcsov2-vars) | [IBCSO overview](https://ibcso.org/ibcso-2024-annual-release/) |
| `IBCAOv5` | [Supported variables](@ref dataset-ibcaov5-vars) | [IBCAO overview](https://www.gebco.net/data_and_products/gridded_bathymetry_data/arctic_ocean/) |
| **Ocean reanalysis** | | |
| `ECCO2Monthly` | [Supported variables](@ref dataset-ecco2monthly-vars) | [ECCO2 documentation](https://ecco.jpl.nasa.gov/products/all/) |
| `ECCO2Daily` | [Supported variables](@ref dataset-ecco2daily-vars) | [ECCO2 documentation](https://ecco.jpl.nasa.gov/products/all/) |
| `ECCO4Monthly` | [Supported variables](@ref dataset-ecco4monthly-vars) | [ECCO V4r4 product guide](https://ecco-group.org/products-ECCO-V4r4.htm) |
| `EN4Monthly` | [Supported variables](@ref dataset-en4monthly-vars) | [Met Office EN4 overview](https://www.metoffice.gov.uk/hadobs/en4/) |
| `GLORYSDaily` | [Supported variables](@ref dataset-glorysdaily-vars) | [Copernicus GLORYS product page](https://data.marine.copernicus.eu/product/GLOBAL_MULTIYEAR_PHY_001_030/description) |
| `GLORYSMonthly` | [Supported variables](@ref dataset-glorysmonthly-vars) | [Copernicus GLORYS product page](https://data.marine.copernicus.eu/product/GLOBAL_MULTIYEAR_PHY_001_030/description) |
| **Atmospheric forcing** | | |
| `RepeatYearJRA55` | [Supported variables](@ref dataset-repeatyearjra55-vars) | [JRA-55 Reanalysis](https://www.data.jma.go.jp/jra/html/JRA-55/index_en.html) |
| `MultiYearJRA55` | [Supported variables](@ref dataset-multiyearjra55-vars) | [JRA-55 Reanalysis](https://www.data.jma.go.jp/jra/html/JRA-55/index_en.html) |

Expand Down Expand Up @@ -114,3 +120,15 @@ NumericalEarth currently ships connectors for the following data products:
- `:snow_freshwater_flux` - Precipitation flux from snow/ice (kg m⁻² s⁻¹).
- `:river_freshwater_flux` - River discharge flux (kg m⁻² s⁻¹).
- `:iceberg_freshwater_flux` - Iceberg calving flux (kg m⁻² s⁻¹).

## [Supported variables for IBCSOv2](@id dataset-ibcsov2-vars)

- `:bottom_height` - Southern Ocean bathymetry at 500 m resolution, south of 50°S (m).

## [Supported variables for GEBCO2024](@id dataset-gebco2024-vars)

- `:bottom_height` - Global bathymetry and topography at 15 arc-second resolution (m).

## [Supported variables for IBCAOv5](@id dataset-ibcaov5-vars)

- `:bottom_height` - Arctic Ocean bathymetry at 100 m resolution, north of 64°N, including Greenland ice sheet surface elevation (m).
50 changes: 50 additions & 0 deletions ext/NumericalEarthArchGDALExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
module NumericalEarthArchGDALExt

using NumericalEarth
using ArchGDAL
using NCDatasets

import NumericalEarth.DataWrangling.IBCAO: reproject_ibcao_to_netcdf

function reproject_ibcao_to_netcdf(tiff_path, nc_path)
ArchGDAL.read(tiff_path) do src
# Warp from EPSG:3996 (Polar Stereographic) to EPSG:4326 (WGS84)
# at 0.01° resolution, clipping to 64–90°N
ArchGDAL.gdalwarp([src],
["-t_srs", "EPSG:4326",
"-te", "-180", "64", "180", "90", # xmin ymin xmax ymax
"-tr", "0.01", "0.01", # target resolution (degrees)
"-r", "bilinear", # resampling method
"-ot", "Float32"]) do warped

# ArchGDAL returns data as (Nx, Ny) with y from north to south (GDAL convention)
data = Float32.(ArchGDAL.read(warped, 1))
data = data[:, end:-1:1] # flip y: j=1 → 64°N (south), j=Ny → 90°N (north)

Nx, Ny = size(data) # expected: (36000, 2600)

NCDataset(nc_path, "c") do ds
defDim(ds, "lon", Nx)
defDim(ds, "lat", Ny)

lon_var = defVar(ds, "lon", Float64, ("lon",);
attrib = ["units" => "degrees_east",
"long_name" => "longitude"])
lat_var = defVar(ds, "lat", Float64, ("lat",);
attrib = ["units" => "degrees_north",
"long_name" => "latitude"])
z_var = defVar(ds, "z", Float32, ("lon", "lat");
attrib = ["long_name" => "elevation",
"units" => "m"])

lon_var[:] = range(-180 + 0.005, 180 - 0.005; length=Nx)
lat_var[:] = range(64 + 0.005, 90 - 0.005; length=Ny)
z_var[:, :] = data
end
end
end

return nothing
end

end # module NumericalEarthArchGDALExt
3 changes: 2 additions & 1 deletion src/Bathymetry/Bathymetry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ using NCDatasets
using Printf
using Scratch

using ..DataWrangling: Metadatum, native_grid, metadata_path, download_dataset
using ..DataWrangling: Metadatum, native_grid, metadata_path, download_dataset,
dataset_variable_name, validate_dataset_coverage
using ..DataWrangling.ETOPO: ETOPO2022

include("regrid_bathymetry.jl")
Expand Down
4 changes: 3 additions & 1 deletion src/Bathymetry/regrid_bathymetry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ function regrid_bathymetry(target_grid, metadata;
major_basins = 1,
cache = true)

validate_dataset_coverage(target_grid, metadata)

config = BathymetryRegridding(target_grid, metadata;
height_above_water, minimum_depth,
interpolation_passes, major_basins)
Expand Down Expand Up @@ -240,7 +242,7 @@ function _regrid_bathymetry(target_grid, metadata;
filepath = metadata_path(metadata)
dataset = Dataset(filepath, "r")

z_data = convert(Array{FT}, dataset["z"][:, :])
z_data = convert(Array{FT}, dataset[dataset_variable_name(metadata)][:, :])
close(dataset)

if !isnothing(height_above_water)
Expand Down
30 changes: 30 additions & 0 deletions src/DataWrangling/DataWrangling.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ restoring, or validation.
module DataWrangling

export Metadata, Metadatum, DatewiseFilename, ECCOMetadatum, EN4Metadatum, all_dates, first_date, last_date
export validate_dataset_coverage, metadata_filename
export BoundingBox, Column, Linear, Nearest
export WOAClimatology, WOAAnnual, WOAMonthly
export metadata_time_step, metadata_epoch
Expand Down Expand Up @@ -198,6 +199,29 @@ function binary_data_size end

default_mask_value(dataset) = NaN

"""
AbstractStaticDataset

Supertype for datasets without a time dimension. Provides default no-op implementations for the date-related interface
(`all_dates`, `first_date`, `last_date`).
"""
abstract type AbstractStaticDataset end

all_dates(::AbstractStaticDataset, args...) = nothing
first_date(::AbstractStaticDataset, args...) = nothing
last_date(::AbstractStaticDataset, args...) = nothing

"""
AbstractStaticBathymetry <: AbstractStaticDataset

Supertype for static, two-dimensional bathymetry datasets (e.g. ETOPO, GEBCO, IBCSO, IBCAO).
Adds defaults for the degenerate vertical axis and a variable-agnostic `Base.size`.
"""
abstract type AbstractStaticBathymetry <: AbstractStaticDataset end

z_interfaces(::AbstractStaticBathymetry) = (0, 1)
Base.size(dataset::AbstractStaticBathymetry, variable) = size(dataset)

# Fundamentals
include("metadata.jl")
include("metadata_field.jl")
Expand Down Expand Up @@ -231,6 +255,9 @@ include("ORCA/ORCA.jl")
include("WOA/WOA.jl")
include("JRA55/JRA55.jl")
include("OSPapa/OSPapa.jl")
include("IBCSO/IBCSO.jl")
include("GEBCO/GEBCO.jl")
include("IBCAO/IBCAO.jl")

using .ETOPO
using .ECCO
Expand All @@ -241,6 +268,9 @@ using .ORCA
using .WOA
using .JRA55
using .OSPapa
using .IBCSO
using .GEBCO
using .IBCAO

# Fallback: if no download extension is loaded, check that all files already exist
function download_dataset(metadata::Metadata)
Expand Down
14 changes: 2 additions & 12 deletions src/DataWrangling/ETOPO/ETOPO.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,15 @@ using Oceananigans
using Oceananigans.DistributedComputations: @root
using Scratch

using ..DataWrangling: download_progress, Metadatum, metadata_path
using ..DataWrangling: download_progress, Metadatum, metadata_path, AbstractStaticBathymetry

import NumericalEarth.DataWrangling:
metadata_filename,
default_download_directory,
all_dates,
first_date,
last_date,
dataset_variable_name,
download_dataset,
longitude_interfaces,
latitude_interfaces,
z_interfaces,
reversed_vertical_axis

download_ETOPO_cache::String = ""
Expand All @@ -31,18 +27,13 @@ ETOPO_bathymetry_variable_names = Dict(
:bottom_height => "z",
)

struct ETOPO2022 end
struct ETOPO2022 <: AbstractStaticBathymetry end

default_download_directory(::ETOPO2022) = download_ETOPO_cache
reversed_vertical_axis(::ETOPO2022) = true
longitude_interfaces(::ETOPO2022) = (-180, 180)
latitude_interfaces(::ETOPO2022) = (-90, 90)
Base.size(::ETOPO2022) = (21600, 10800, 1)
Base.size(dataset::ETOPO2022, variable) = size(dataset)

all_dates(::ETOPO2022, args...) = nothing
first_date(::ETOPO2022, args...) = nothing
last_date(::ETOPO2022, args...) = nothing

const ETOPOMetadatum = Metadatum{<:ETOPO2022}

Expand All @@ -51,7 +42,6 @@ dataset_variable_name(data::ETOPOMetadatum) = ETOPO_bathymetry_variable_names[da
const ETOPO_url = "https://www.dropbox.com/scl/fi/6pwalcuuzgtpanysn4h6f/" *
"ETOPO_2022_v1_60s_N90W180_surface.nc?rlkey=2t7890ruyk4nd5t5eov5768lt&st=yfxsy1lu&dl=0"

z_interfaces(::ETOPOMetadatum) = (0, 1)
metadata_url(::ETOPOMetadatum) = ETOPO_url
metadata_filename(::ETOPO2022, name, date, region) = "ETOPO_2022_v1_60s_N90W180_surface.nc"

Expand Down
125 changes: 125 additions & 0 deletions src/DataWrangling/GEBCO/GEBCO.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
module GEBCO

export GEBCO2024

using Downloads
using ZipFile
using Oceananigans
using Oceananigans.DistributedComputations: @root
using Scratch

using ..DataWrangling: download_progress, Metadatum, metadata_path, AbstractStaticBathymetry

import NumericalEarth.DataWrangling:
metadata_filename,
default_download_directory,
dataset_variable_name,
download_dataset,
longitude_interfaces,
latitude_interfaces,
reversed_vertical_axis

download_GEBCO_cache::String = ""
function __init__()
global download_GEBCO_cache = @get_scratch!("GEBCO")
end

GEBCO_bathymetry_variable_names = Dict(
:bottom_height => "elevation", # Variable name in NetCDF
)

"""
GEBCO2024

General Bathymetric Chart of the Oceans 2024 release.
Global bathymetry and topography at 15 arc-second resolution.

The GEBCO_2024 Grid is a global terrain model for ocean and land,
providing elevation data on a 15 arc-second interval grid.

Reference: GEBCO Compilation Group (2024) GEBCO 2024 Grid
Data source: https://www.gebco.net/data_and_products/gridded_bathymetry_data/
"""
struct GEBCO2024 <: AbstractStaticBathymetry end

default_download_directory(::GEBCO2024) = download_GEBCO_cache
reversed_vertical_axis(::GEBCO2024) = false

# GEBCO covers the entire globe
longitude_interfaces(::GEBCO2024) = (-180, 180)
latitude_interfaces(::GEBCO2024) = (-90, 90)

# Grid size for 15 arc-second resolution
# 360° / (15/3600)° = 86400 points in longitude
# 180° / (15/3600)° = 43200 points in latitude
Base.size(::GEBCO2024) = (86400, 43200, 1)

const GEBCOMetadatum = Metadatum{<:GEBCO2024}

dataset_variable_name(data::GEBCOMetadatum) = GEBCO_bathymetry_variable_names[data.name]

# GEBCO 2024 download URL from BODC
# Note: This is a large file (~8 GB zipped, ~22 GB unzipped)
const GEBCO_zip_url = "https://www.bodc.ac.uk/data/open_download/gebco/gebco_2024/zip/"

# The expected NetCDF filename inside the ZIP
const GEBCO_nc_filename = "GEBCO_2024.nc"
metadata_filename(::GEBCO2024, name, date, bounding_box) = GEBCO_nc_filename

function download_dataset(metadatum::GEBCOMetadatum)
filepath = metadata_path(metadatum)
download_dir = metadatum.dir

@root if !isfile(filepath)
@info "Downloading GEBCO data: $(metadatum.name) to $download_dir..."
@info "Note: GEBCO is a large dataset (~8 GB download, ~22 GB uncompressed). This may take a while."

# Download the ZIP file
zip_path = joinpath(download_dir, "GEBCO_2024.zip")

try
@info "Downloading from BODC..."
Downloads.download(GEBCO_zip_url, zip_path; progress=download_progress)

# Extract the NetCDF file from the ZIP using ZipFile.jl
@info "Extracting NetCDF from ZIP archive..."
zf = ZipFile.Reader(zip_path)
extracted = false
for f in zf.files
if endswith(f.name, GEBCO_nc_filename)
open(filepath, "w") do io
write(io, read(f))
end
extracted = true
break
end
end
close(zf)

if !extracted
error("Could not find $GEBCO_nc_filename in ZIP archive")
end

if isfile(filepath)
@info "GEBCO data extracted successfully"
else
error("Failed to extract GEBCO NetCDF file")
end

# Clean up ZIP file to save space
rm(zip_path; force=true)

catch e
@warn "Failed to download GEBCO: $e"

# Clean up any partial download
rm(zip_path; force=true)

rethrow(e)
end
end

return filepath
end

end # module
Loading
Loading