API Reference

Module

SatelliteGriddingModule
SatelliteGridding

A Julia package for gridding satellite Level-2 data onto regular rectangular grids with footprint-aware oversampling.

Satellite instruments have irregularly shaped footprints (quadrilaterals defined by 4 corner coordinates) that can overlap multiple output grid cells. Rather than computing exact geometric intersections, this package subdivides each footprint into n×n sub-pixels and uses "point-in-box" binning. The mean per grid cell is computed incrementally using Welford's online algorithm for numerical stability.

Main functions

  • grid_l2: Grid Level-2 data with footprint oversampling
  • grid_center: Grid data using center coordinates only (e.g., MODIS)
  • load_config: Load a TOML/JSON data source configuration

Example

using SatelliteGridding

config = load_config("tropomi_sif.toml")
grid_spec = GridSpec(lat_min=-90f0, lat_max=90f0, lon_min=-180f0, lon_max=180f0,
                     dlat=1f0, dlon=1f0)
time_spec = TimeSpec(DateTime("2019-01-01"), DateTime("2019-12-31"),
                     Dates.Day(8), 1.0f0)
grid_l2(config, grid_spec, time_spec; outfile="output.nc")
source

Main Functions

SatelliteGridding.gridFunction
grid(config, grid_spec, time_spec; method=SubpixelGridding(), kwargs...)
grid(config, grid_spec, time_spec, method::AbstractGriddingMethod; kwargs...)

Dispatch-based library entry point for gridding. The CLI still exposes explicit l2 and center commands, while library callers can choose a gridding method with a first-class type such as SubpixelGridding, CircularFootprintGridding, or CenterPointGridding.

source
SatelliteGridding.grid_l2Function
grid_l2(config, grid_spec, time_spec; kwargs...)

Grid Level-2 satellite data with footprint-aware oversampling.

Reads input NetCDF files as specified by config, applies quality filters, subdivides each satellite footprint into sub-pixels for proper overlap handling, and accumulates gridded averages. Output is written to a NetCDF4 file. Quadrilateral footprints are used by default; pass footprint_method=CircularFootprintGridding(...) for circular products such as GOSAT. Circular footprints can be read from lat_bnd/lon_bnd or from center lat/lon plus radius.

Arguments

  • config::DataSourceConfig: Data source configuration (from TOML/JSON)
  • grid_spec::GridSpec: Output grid geometry
  • time_spec::TimeSpec: Temporal binning parameters

Keyword Arguments

  • n_oversample::Union{Nothing,Int}=nothing: Sub-pixel factor. nothing = auto-compute from footprint/grid ratio.
  • footprint_method::AbstractGriddingMethod=SubpixelGridding(n_oversample): Footprint geometry method.
  • compute_std::Bool=false: Also compute per-cell standard deviation
  • outfile::String="gridded_output.nc": Output file path
  • backend: KernelAbstractions backend (default: nothing for sequential Welford path). Use CPU(), CUDABackend(), MetalBackend(), or another compatible KA backend.
  • keep_going::Bool=false: Continue after per-file errors. By default, the first file error stops the run.
source
SatelliteGridding.grid_centerFunction
grid_center(config, grid_spec, time_spec; kwargs...)

Grid data using center coordinates only (no footprint bounds), suitable for MODIS-style data where each pixel maps to exactly one grid cell.

Keyword Arguments

  • geo_table::Union{Nothing,String}=nothing: Path to a legacy monolithic geolocation lookup table (NetCDF)
  • geo_cache::Union{Nothing,String}=nothing: Directory for generated per-tile MODIS geolocation cache
  • geo_provider::Union{Symbol,String}=:auto: :auto, :variables, :lut, or :modis
  • veg_indices::Bool=false: Compute vegetation indices (EVI, NDVI, NIRv, NDWI)
  • compute_std::Bool=false: Compute per-cell standard deviation
  • outfile::String="gridded_output.nc": Output file path
  • keep_going::Bool=false: Continue after per-file errors. By default, the first file error stops the run.
source
SatelliteGridding.load_configFunction
load_config(config_path::String) -> DataSourceConfig

Load a configuration file (TOML or JSON) and return a DataSourceConfig.

The file must contain a [grid] section plus filePattern and folder. Footprint-aware L2 gridding usually uses [basic] entries for lat_bnd and lon_bnd. Circular footprints can alternatively use center lat/lon plus a radius variable or scalar [circle] radius. Center-coordinate gridding can use [basic] lat and lon, a geolocation lookup table, or generated MODIS sinusoidal geolocation. Filters are defined in an optional [filter] section using intuitive string expressions:

Example TOML

filePattern = "S5P_PAL__L2B_SIF____YYYYMMDD*.nc"
folder = "/path/to/data/"

[basic]
lat = "PRODUCT/latitude"
lon = "PRODUCT/longitude"
lat_bnd = "PRODUCT/SUPPORT_DATA/GEOLOCATIONS/latitude_bounds"
lon_bnd = "PRODUCT/SUPPORT_DATA/GEOLOCATIONS/longitude_bounds"

[grid]
sif_743 = "PRODUCT/SIF_743"
sif_735 = "PRODUCT/SIF_735"

[filter]
"PRODUCT/SUPPORT_DATA/GEOLOCATIONS/solar_zenith_angle" = "< 80"
"PRODUCT/qa_value" = "> 0.5"
"PRODUCT/methane" = "1600 < x < 2200"
"PRODUCT/mode" = "== 1"

The legacy JSON format with separate filter_gt/filter_lt/filter_eq sections is still supported for backward compatibility.

source

Backends

SatelliteGridding.resolve_backendFunction
resolve_backend(name) -> Union{Nothing, KernelAbstractions.Backend}

Resolve a user-facing backend name to a KernelAbstractions backend object. The optional GPU packages are weak dependencies and are loaded only when the corresponding backend is requested.

source

Types

SatelliteGridding.GridSpecType
GridSpec{T<:AbstractFloat}

Specification of the output grid geometry.

Fields

  • lat_min, lat_max: Latitude bounds (degrees)
  • lon_min, lon_max: Longitude bounds (degrees)
  • dlat, dlon: Grid cell size (degrees)
  • lat, lon: Vectors of cell center coordinates
source
SatelliteGridding.TimeSpecType
TimeSpec

Specification of the temporal gridding parameters.

Fields

  • start_date, stop_date: Date range for processing
  • time_step: Temporal bin size (Dates.Day or Dates.Month)
  • oversample_temporal: Multiplier for the actual averaging window (>1 gives moving-average-like behavior)
source
SatelliteGridding.DataSourceConfigType
DataSourceConfig

Configuration loaded from a TOML/JSON file that defines a satellite data source.

Fields

  • basic: Maps internal keys ("lat", "lon", "lat_bnd", "lon_bnd", "radius") to variable paths in the NetCDF files
  • grid_vars: Ordered mapping of output variable names to input variable paths (all will be gridded)
  • filters: Quality filter rules parsed from [filter] section
  • file_pattern: Glob pattern with YYYY/MM/DD/DOY placeholders for finding input files
  • folder: Root folder for input data (may also contain YYYY/MM/DD placeholders)
  • options: Optional processing settings from [options], [center], or [modis]
source
SatelliteGridding.FilterRuleType
FilterRule

A single filter condition for a NetCDF variable.

Fields

  • variable: Path to the NetCDF variable (may include group separators /)
  • op: Operation — :lt, :gt, :eq, :between
  • lo: Lower bound (threshold for :gt/:lt/:eq, low end for :between)
  • hi: Upper bound (only used for :between)

Config syntax (TOML [filter] section)

[filter]
"solar_zenith_angle" = "< 80"         # less than
"qa_value" = "> 0.5"                  # greater than
"mode" = "== 1"                       # equality
"methane" = "1600 < x < 2200"         # range (between)
source
SatelliteGridding.SubpixelGriddingType
SubpixelGridding(; n_oversample=nothing)

Footprint-aware gridding by sampling an n × n grid of subpoints inside each quadrilateral footprint. nothing keeps the existing automatic oversampling heuristic.

source
SatelliteGridding.CircularFootprintGriddingType
CircularFootprintGridding(; n_oversample=nothing)

Footprint-aware gridding for circular or near-circular footprints described by a center coordinate and four bounding coordinates. The implementation samples an n × n grid over the footprint bounding box and keeps points inside the inferred circle or ellipse. nothing keeps the existing automatic oversampling heuristic.

source
SatelliteGridding.ExactIntersectionGriddingType
ExactIntersectionGridding()

Reserved method for exact footprint/grid-cell intersection gridding. This is not implemented yet, but the type keeps the public API open for an exact geometry backend without changing callers.

source

Oversampling

SatelliteGridding.sort_corners_ccwFunction
sort_corners_ccw(lat_corners, lon_corners) -> (sorted_lat, sorted_lon)

Sort 4 footprint corners into counter-clockwise order by computing the angle from the centroid to each corner. This ensures edges 1→2 and 4→3 are always opposite edges, which is required by compute_subpixels!.

Different satellite products store corners in different orders (e.g., TROPOMI uses CCW order while OCO-2 uses a different convention). This function normalizes them.

source
SatelliteGridding.sort_corners_ccw_ka!Function
sort_corners_ccw_ka!(backend, lat_mat, lon_mat)

KA kernel that sorts footprint corners into CCW order for all soundings in parallel. Uses a 5-comparator sorting network (optimal for 4 elements, no allocations).

  • lat_mat, lon_mat: N×4 matrices of corner coordinates
source
SatelliteGridding.compute_subpixels!Function
compute_subpixels!(points, vert_lat, vert_lon, n, lats_0, lons_0, lats_1, lons_1)

Fill the n×n×2 array points with sub-pixel positions inside the quadrilateral defined by 4 corner coordinates (vert_lat[1:4], vert_lon[1:4]).

The algorithm:

  1. Subdivide edge 1→2 into n baseline points
  2. Subdivide edge 4→3 into n baseline points
  3. For each pair of corresponding baseline points, subdivide the connecting line into n interior points

This produces sub-pixels that fill the footprint quadrilateral. Temporary buffers lats_0, lons_0, lats_1, lons_1 (each length n) must be provided.

source
SatelliteGridding.floor_indices!Function
floor_indices!(ix, iy, points)

Convert the floating-point sub-pixel positions in points to integer grid cell indices via floor. Results stored in ix and iy (each length ).

source
SatelliteGridding.compute_n_oversampleFunction
compute_n_oversample(footprint_extent, cell_size; n_min=2, n_max=20)

Estimate an appropriate oversampling factor n based on the ratio of footprint size to grid cell size. Aims for ~3 sub-pixels per grid cell width.

Returns an integer in [n_min, n_max].

source
SatelliteGridding.compute_footprint_indices_ka!Function
compute_footprint_indices_ka!(backend, ix_out, iy_out, skip_flag,
                              lat_corners, lon_corners, n)

KA kernel that computes n×n sub-pixel grid indices for each footprint. Also sets skip_flag[fp] = true if the footprint's longitude extent >= n (too wide to oversample meaningfully).

For footprints where all 4 corners fall in the same cell, the kernel stores the single cell index in position 1 and marks all other positions with ix=-1 (sentinel). The accumulation kernel handles these fast-path footprints by checking skip_flag.

  • ix_out: (n_fp, n²) Int32 — lat grid cell indices
  • iy_out: (n_fp, n²) Int32 — lon grid cell indices
  • skip_flag: (n_fp,) Int32 — 0=oversample, 1=fast-path (single cell), 2=skip (too wide)
  • lat_corners, lon_corners: (n_fp, 4) — fractional grid indices of corners
  • n: oversampling factor
source
SatelliteGridding.compute_circular_footprint_indices_ka!Function
compute_circular_footprint_indices_ka!(backend, ix_out, iy_out, inside_count,
                                       skip_flag, center_lat, center_lon,
                                       lat_corners, lon_corners, n)

KA kernel that computes sampled grid-cell indices for circular or near-circular footprints. The four coordinates define the footprint bounding box or edge points around the center coordinate. Points outside the inferred circle/ellipse are marked with -1; inside_count[fp] stores the number of valid samples used for normalizing the scatter weight.

source

Geometry

SatelliteGridding.exact_footprint_weightsFunction
exact_footprint_weights(lat_corners, lon_corners; n_lon, n_lat)

Compute exact planar overlap weights between one quadrilateral footprint and regular grid cells in fractional grid-index coordinates. The returned matrix has shape (n_lon, n_lat) and sums to one when the footprint is fully inside the domain.

source
SatelliteGridding.circle_footprint_weightsFunction
circle_footprint_weights(center_lat, center_lon, lat_corners, lon_corners;
                         n_lon, n_lat, n_oversample)

Compute sampled per-cell weights for one circular or near-circular footprint in fractional grid-index coordinates. The four coordinates define the footprint bounding box or edge points. In grid-index space the sampled shape may be an ellipse when the output grid has unequal latitude and longitude resolutions.

source
circle_footprint_weights(center_lat, center_lon, radius_lat, radius_lon;
                         n_lon, n_lat, n_oversample)

Convenience overload for a circular or elliptical footprint specified directly by center coordinate and radius in fractional grid-index units.

source
SatelliteGridding.footprint_weightsFunction
footprint_weights(method, lat_corners, lon_corners; n_lon, n_lat)

Return per-cell footprint weights for a single footprint using the requested geometry method. This is a lightweight geometry-level API used by tests and by future exact-intersection gridders.

source

Accumulation

SatelliteGridding.accumulate_footprint!Function
accumulate_footprint!(grid_data, grid_std, grid_weights, compute_std,
                      lat_idx, lon_idx, values, n_pixels, n_vars, n_oversample,
                      grid_spec, points_buf, ix_buf, iy_buf,
                      lats0_buf, lons0_buf, lats1_buf, lons1_buf)

Accumulate a batch of satellite observations into the output grid using footprint-aware oversampling and Welford's online averaging algorithm.

For each pixel:

  • Fast path: If all 4 corner coordinates fall into the same grid cell, the full observation value is assigned to that cell (weight=1).
  • Oversample path: If the footprint spans multiple cells, it is subdivided into n_oversample × n_oversample sub-pixels. Each sub-pixel contributes weight 1/n² to whichever grid cell it falls in.

The Welford algorithm maintains a running mean and sum-of-squared-deviations (M2), enabling numerically stable computation of mean and variance in a single pass.

Arguments

  • grid_data: Output array (n_lon, n_lat, n_vars) — running mean
  • grid_std: Output array (n_lon, n_lat, n_vars) — running M2 (sum of squared deviations)
  • grid_weights: Output array (n_lon, n_lat) — accumulated weights
  • compute_std: Whether to track variance
  • lat_idx, lon_idx: Pre-computed fractional grid indices for corners, size (n_pixels, 4)
  • values: Input data, size (n_pixels, n_vars)
  • n_pixels: Number of observations in this batch
  • n_vars: Number of variables being gridded
  • n_oversample: Sub-pixel subdivision factor (n)
  • Remaining arguments are pre-allocated work buffers
source
SatelliteGridding.accumulate_circular_footprint!Function
accumulate_circular_footprint!(grid_data, grid_std, grid_weights, compute_std,
                               center_lat_idx, center_lon_idx,
                               lat_idx, lon_idx, values,
                               n_pixels, n_vars, n_oversample)

Accumulate observations with circular or near-circular footprints. The center coordinate gives the footprint center and the four coordinates define its bounding box or edge points. Sampling is done in fractional grid-index space.

source
SatelliteGridding.accumulate_batch!Function
accumulate_batch!(backend, grid_sum, grid_weights,
                  lat_corners, lon_corners, values, n;
                  compute_std=false, grid_std=nothing, grid_mean=nothing)

Accumulate observations into the grid using the KA kernel pipeline. This function works identically on CPU and GPU backends.

The pipeline:

  1. Sort corners into CCW order
  2. Compute n×n subpixel grid indices per footprint
  3. Scatter-accumulate weighted sums into grid cells (using atomics)

After processing all files, call finalize_mean! to convert sums to means.

For compute_std=true, a two-pass approach is used:

  1. First call accumulate_batch! without std to accumulate sums
  2. Call finalize_mean! to get the mean
  3. Call accumulate_std_pass! with the computed mean to accumulate variance

Arguments

  • backend: KernelAbstractions backend (CPU(), CUDABackend(), MetalBackend(), etc.)
  • grid_sum: (nlon, nlat, n_vars) — weighted sum accumulator
  • grid_weights: (nlon, nlat) — weight accumulator
  • lat_corners, lon_corners: (n_fp, 4) — fractional grid indices of corners
  • values: (nfp, nvars) — observation values
  • n: oversampling factor
source
SatelliteGridding.accumulate_circular_batch!Function
accumulate_circular_batch!(backend, grid_sum, grid_weights,
                           center_lat, center_lon,
                           lat_corners, lon_corners, values, n, n_vars)

Accumulate circular or near-circular footprints using the KA pipeline. This is the sum-based backend counterpart to accumulate_circular_footprint! and works with KA CPU, CUDA, Metal, and other compatible backends.

source
SatelliteGridding.accumulate_center!Function
accumulate_center!(grid_data, grid_weights, lat, lon, values, grid_spec)

Accumulate observations using center coordinates only (no footprint bounds). Used for MODIS-style gridding where each pixel maps to exactly one grid cell. Simple sum accumulation — divide by grid_weights afterward to get the mean.

source
SatelliteGridding.scatter_accumulate!Function
scatter_accumulate!(grid_sum, grid_weights, ix, iy, skip_flag, values, n, n_vars)

Sequential scatter-accumulate of weighted values into grid cells. Uses precomputed grid indices from compute_footprint_indices_ka!.

For each footprint:

  • skip_flag == 1 (fast path): add weight=1, value to single cell
  • skip_flag == 0 (oversample): add weight=1/n² to each subpixel's cell
  • skip_flag == 2 (skip): do nothing

This function is sequential (no atomics needed) and works with any array type. For GPU backends, use the @kernel version with @atomic instead.

  • grid_sum: (nlon, nlat, n_vars) — weighted sum accumulator
  • grid_weights: (nlon, nlat) — weight accumulator
  • ix, iy: (n_fp, n²) — precomputed grid indices
  • skip_flag: (n_fp,) — 0=oversample, 1=fast-path, 2=skip
  • values: (nfp, nvars) — observation values
source
SatelliteGridding.scatter_accumulate_ka!Function
scatter_accumulate_ka!(backend, grid_sum, grid_weights,
                       ix, iy, skip_flag, values, n, n_vars)

KA kernel version of scatter-accumulate using @atomic for thread safety. Required for GPU backends where multiple threads write to the same grid cells. For CPU backends, use scatter_accumulate! (sequential) instead.

source
SatelliteGridding.scatter_accumulate_circular!Function
scatter_accumulate_circular!(grid_sum, grid_weights, ix, iy, inside_count,
                             skip_flag, values, n, n_vars)

Sequential scatter-accumulate for circular footprints. Oversampled footprints use per-footprint weights 1 / inside_count[fp] because samples outside the circle/ellipse are masked out.

source
SatelliteGridding.finalize_mean!Function
finalize_mean!(grid_data, grid_weights)

Convert weighted-sum accumulator to mean: mean = sum / weight. Cells with near-zero weight are left as zero.

source

I/O

SatelliteGridding.find_filesFunction
find_files(pattern::String, folder::String, date::DateTime) -> Vector{String}

Find input files matching the given pattern for a specific date.

Resolves YYYY, YY, MM, DD, and DOY placeholders in both pattern and folder, then globs for matching files. Returns only non-empty files.

source
SatelliteGridding.read_nc_variableFunction
read_nc_variable(dataset, path::String; bounds=false)

Read a variable from a NetCDF dataset, handling nested groups via / separators.

If bounds=true, reshape the result so that footprint vertices are in the last dimension, returning an N×4 matrix (N soundings, 4 corners).

source
SatelliteGridding.read_variable_from_fileFunction
read_variable_from_file(path, variable; bounds=false)

Read a variable from a NetCDF-like file. Falls back to the system libnetcdf for flat HDF4/HDF-EOS variables when the Julia NetCDF build cannot open them.

source
SatelliteGridding.read_system_netcdf_variableFunction
read_system_netcdf_variable(path, varname)

Read a flat variable through the system libnetcdf. This is primarily a fallback for HDF4/HDF-EOS2 files when the Julia NetCDF_jll build lacks HDF4 support.

source
SatelliteGridding.create_output_datasetFunction
create_output_dataset(outfile, grid_spec, time_spec, grid_vars, compute_std) -> Dataset, Dict

Create the output NetCDF4 file with dimensions, coordinate variables, and data variable definitions. Returns the dataset and a Dict mapping variable names to NCDatasets variables.

source

Geolocation

SatelliteGridding.modis_sinusoidal_latlonFunction
modis_sinusoidal_latlon(h, v, line, sample; pixels=2400)

Compute the latitude/longitude at a MODLAND sinusoidal pixel center. h and v are zero-based tile indices; line and sample are one-based pixel indices within the tile.

source
SatelliteGridding.write_modis_tile_geolocationFunction
write_modis_tile_geolocation(path, h, v; pixels=2400, overwrite=false)

Generate and write one MODIS sinusoidal tile geolocation file. The output is a small per-tile NetCDF file with latitude(y, x) and longitude(y, x).

source

Filters

SatelliteGridding.apply_filtersFunction
apply_filters(dataset, config::DataSourceConfig, lat_bounds, lon_bounds,
              grid_spec::GridSpec) -> Vector{Int}

Apply spatial bounding box and quality filters, returning indices of pixels that pass all criteria.

The spatial filter requires all 4 corners to be within the grid bounds and the footprint to not cross the dateline (lon span < 50°). Quality filters from config.filters are applied as additional criteria.

source

Vegetation Indices

SatelliteGridding.compute_eviFunction
compute_evi(red, nir, blue; G=2.5f0, C1=6.0f0, C2=7.5f0, L=10000.0f0)

Compute the Enhanced Vegetation Index (EVI).

EVI = G × (NIR - RED) / (NIR + C1×RED - C2×BLUE + L)
source

CLI

SatelliteGridding.parse_l2_argsFunction
parse_l2_args(args=ARGS) -> Dict

Parse command-line arguments for L2 gridding. Matches the interface of the original gridL2_Dates.jl script.

source