curryer.correction.regrid¶
GCP chip regridding algorithms.
This module provides functionality to transform GCP chips from irregular geodetic grids (derived from ECEF coordinates) to regular latitude/longitude grids. The regridding process is mission-agnostic and configurable via RegridConfig.
The main workflow is: 1. Load raw GCP chip with ECEF coordinates (from HDF file) 2. Convert ECEF → geodetic (lon, lat, h) 3. Determine output grid bounds and spacing 4. Interpolate data onto regular grid using bilinear interpolation 5. Return ImageGrid with regular lat/lon coordinates
Attributes¶
Functions¶
|
Compute bounds for regular output grid from irregular input. |
|
Create regular lat/lon grid. |
|
Check if point is inside triangle using barycentric coordinates. |
|
Bilinear interpolation within an irregular quadrilateral. |
|
Find which cell in irregular grid contains the target point. |
|
Regrid data from irregular geodetic grid to regular lat/lon grid. |
|
High-level function: Regrid GCP chip from ECEF to regular lat/lon grid. |
Module Contents¶
- curryer.correction.regrid.logger¶
- curryer.correction.regrid.compute_regular_grid_bounds(lon_irregular: numpy.ndarray, lat_irregular: numpy.ndarray, conservative: bool = True) tuple[float, float, float, float]¶
Compute bounds for regular output grid from irregular input.
- Parameters:
lon_irregular (np.ndarray) – 2D arrays of irregular grid coordinates (degrees).
lat_irregular (np.ndarray) – 2D arrays of irregular grid coordinates (degrees).
conservative (bool, default=True) – If True, shrink bounds to ensure all output points are within input. Conservative bounds avoid extrapolation at edges by taking the maximum of left/bottom edges and minimum of right/top edges.
- Returns:
minlon, maxlon, minlat, maxlat – Bounding box for regular grid (degrees).
- Return type:
float
Notes
Conservative bounds (default) follow MATLAB behavior: - minlon = max(bottom_left_lon, top_left_lon) - maxlon = min(bottom_right_lon, top_right_lon) - minlat = max(bottom_left_lat, bottom_right_lat) - maxlat = min(top_left_lat, top_right_lat)
This ensures the regular grid lies entirely within the irregular grid.
- curryer.correction.regrid.create_regular_grid(bounds: tuple[float, float, float, float], grid_size: tuple[int, int] | None = None, resolution: tuple[float, float] | None = None) tuple[numpy.ndarray, numpy.ndarray]¶
Create regular lat/lon grid.
- Parameters:
bounds (tuple[float, float, float, float]) – (minlon, maxlon, minlat, maxlat) in degrees.
grid_size (tuple[int, int], optional) – (nrows, ncols). If None, derive from resolution.
resolution (tuple[float, float], optional) – (dlat, dlon) in degrees. If None, use grid_size.
- Returns:
lon_regular, lat_regular – 2D arrays of regular grid coordinates (degrees), shape (nrows, ncols).
- Return type:
np.ndarray
Notes
Exactly one of grid_size or resolution must be provided. Grid follows MATLAB convention: - Row index increases going south (latitude decreases) - Column index increases going east (longitude increases)
- curryer.correction.regrid.point_in_triangle(point: numpy.ndarray, triangle: numpy.ndarray) tuple[bool, numpy.ndarray]¶
Check if point is inside triangle using barycentric coordinates.
- Parameters:
point (np.ndarray) – Point [x, y] to test.
triangle (np.ndarray) – Triangle vertices, shape (3, 2): [[x1, y1], [x2, y2], [x3, y3]].
- Returns:
inside (bool) – True if point is inside triangle (barycentric coords all in (0, 1)).
barycentric_coords (np.ndarray) – Barycentric coordinates [w1, w2, w3].
Notes
Uses the cross product method from MATLAB bandval function. Point P is inside triangle ABC if all barycentric weights are in (0, 1).
- curryer.correction.regrid.bilinear_interpolate_quad(point: numpy.ndarray, corners_lon: numpy.ndarray, corners_lat: numpy.ndarray, corner_values: numpy.ndarray) float¶
Bilinear interpolation within an irregular quadrilateral.
- Parameters:
point (np.ndarray) – Target point [lon, lat] (degrees).
corners_lon (np.ndarray) – Coordinates of 4 corners, ordered clockwise from top-left: [top-left, top-right, bottom-right, bottom-left].
corners_lat (np.ndarray) – Coordinates of 4 corners, ordered clockwise from top-left: [top-left, top-right, bottom-right, bottom-left].
corner_values (np.ndarray) – Values at the 4 corners.
- Returns:
interpolated_value – Interpolated value at target point.
- Return type:
float
Notes
Uses matrix inversion method from MATLAB code: Solves [1, lon, lat, lon*lat]^T = M * [w1, w2, w3, w4]^T where M is constructed from corner coordinates.
- curryer.correction.regrid.find_containing_cell(point: numpy.ndarray, lon_grid: numpy.ndarray, lat_grid: numpy.ndarray, start_cell: tuple[int, int] | None = None) tuple[int, int] | None¶
Find which cell in irregular grid contains the target point.
- Parameters:
point (np.ndarray) – Target point [lon, lat] (degrees).
lon_grid (np.ndarray) – 2D arrays of irregular grid coordinates (degrees).
lat_grid (np.ndarray) – 2D arrays of irregular grid coordinates (degrees).
start_cell (tuple[int, int], optional) – Starting cell (i, j) for search (optimization hint).
- Returns:
cell_indices – (i, j) of cell containing point, or None if not found.
- Return type:
tuple[int, int] or None
Notes
Uses barycentric coordinate test to check if point is inside quadrilateral. For each cell, tests two triangles (upper-left and lower-right) that together form the quadrilateral.
Search strategy follows MATLAB optimization: - Start from hint if provided - Check cells near last found cell (spatial locality) - If not found, search all cells
Optimization: Inline triangle test to avoid array allocations.
- curryer.correction.regrid.regrid_irregular_to_regular(data_irregular: numpy.ndarray, lon_irregular: numpy.ndarray, lat_irregular: numpy.ndarray, lon_regular: numpy.ndarray, lat_regular: numpy.ndarray, method: str = 'bilinear', fill_value: float = np.nan, use_spatial_index: bool = True) numpy.ndarray¶
Regrid data from irregular geodetic grid to regular lat/lon grid.
This is the core algorithm: for each point in the regular output grid, find the corresponding quadrilateral cell in the irregular input grid and interpolate the value.
- Parameters:
data_irregular (np.ndarray) – 2D array of values on irregular grid.
lon_irregular (np.ndarray) – 2D arrays of irregular grid coordinates (degrees).
lat_irregular (np.ndarray) – 2D arrays of irregular grid coordinates (degrees).
lon_regular (np.ndarray) – 2D arrays of regular grid coordinates (degrees).
lat_regular (np.ndarray) – 2D arrays of regular grid coordinates (degrees).
method (str, default="bilinear") – Interpolation method: “bilinear” or “nearest”.
fill_value (float, default=np.nan) – Value for output points that fall outside input grid.
use_spatial_index (bool, default=True) – If True, build a spatial index (KD-tree) for faster cell finding. Recommended for large grids (>100×100). Adds ~0.1s overhead.
- Returns:
data_regular – 2D array of interpolated values on regular grid.
- Return type:
np.ndarray
Notes
Algorithm (follows MATLAB Chip_regrid2.m): 1. For each point P in regular grid:
Search for containing quadrilateral in irregular grid
Perform bilinear interpolation using 4 corner values
Optimization: Use spatial locality (start search near last found cell)
Points outside irregular grid are filled with fill_value
Performance: O(n²) worst case, O(n²/k) typical with spatial locality.
Optimizations applied: - Minimize array allocations in inner loop - Extract corner data once per cell - Use scalar operations where possible - Optional spatial index for O(log n) nearest neighbor queries
- curryer.correction.regrid.regrid_gcp_chip(band_data: numpy.ndarray, ecef_coords: tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray], config: curryer.correction.config.RegridConfig, output_file: str | None = None, output_metadata: dict[str, str] | None = None) curryer.correction.grid_types.ImageGrid¶
High-level function: Regrid GCP chip from ECEF to regular lat/lon grid.
This is the main entry point for GCP chip regridding. It handles the complete workflow from ECEF coordinates to a regular geodetic grid.
- Parameters:
band_data (np.ndarray) – 2D array of radiometric values.
ecef_coords (tuple[np.ndarray, np.ndarray, np.ndarray]) – (X, Y, Z) ECEF coordinate arrays (meters), each shape (nrows, ncols).
config (RegridConfig) – Regridding configuration.
output_file (str, optional) – If provided, save the regridded chip to this NetCDF file path. File will be created with CF-compliant metadata.
output_metadata (dict[str, str], optional) – Additional metadata to include in the NetCDF file (only used if output_file is specified). Common keys: ‘source_file’, ‘mission’, ‘sensor’, ‘band’.
- Returns:
regridded_chip (ImageGrid) – Regridded data on regular lat/lon grid.
Workflow
——–
1. Convert ECEF → geodetic (lon, lat, h) using curryer.compute.spatial
2. Compute regular grid bounds (conservative or full extent)
3. Create regular output grid (from resolution or size)
4. Regrid data using bilinear interpolation
5. Return ImageGrid with (data, lat, lon, h)
6. Optionally save to NetCDF file
Examples
Basic usage (return in-memory):
>>> from curryer.correction.image_io import load_gcp_chip_from_hdf >>> from curryer.correction.regrid import regrid_gcp_chip, RegridConfig >>> band, x, y, z = load_gcp_chip_from_hdf("chip.hdf") >>> config = RegridConfig(output_resolution_deg=(0.001, 0.001)) >>> regridded = regrid_gcp_chip(band, (x, y, z), config)
With NetCDF output:
>>> regridded = regrid_gcp_chip( ... band, (x, y, z), config, ... output_file="regridded_chip.nc", ... output_metadata={ ... 'source_file': 'LT08CHP.20140803.p002r071.c01.v001.hdf', ... 'mission': 'CLARREO Pathfinder', ... 'band': 'red', ... } ... )