Release Notes¶
0.4.0 (2026-04-26) — API cleanup, unit fixes, and footprint improvements¶
This release removes redundant CLI options, fixes unit-conversion bugs in the NGL adapter and the CSV-to-raster preprocessor, and improves the raster-footprint algorithm. No new processing features are introduced.
Removed¶
``FittingConfig.method`` — the fitting algorithm is no longer stored in the configuration dataclass. It is controlled solely by the
--fitting/-fiCLI flag (defaultleast_squares). This removes a redundancy that could silently override the CLI value.``SpatialConfig.hull_method`` and ``–hull_method`` / ``-hm`` CLI flag — the hull algorithm is now selected automatically based on raster shape, with no user choice required.
``–inverse_los`` / ``-iv`` CLI flag — superseded by the more explicit
--los_convention/-lcflag (ENUorNEU).
Fixed¶
``tools/reference.py`` — duplicate
WAVELENGTH_MMconstant (55.465763) replaced with an import ofSENTINEL1_WAVELENGTH_MMfromconfig.py. Ensures a single authoritative source for the Sentinel-1 wavelength.``gnss/ngl.py`` —
ve/vn/vu/se/sn/sucolumns in the station DataFrame were left in m/yr (native NGL MIDAS units) after the LOS projection step. They are now multiplied by 1000 in_normalise()so that the DataFrame conforms to the canonicalSTATION_COLUMNSschema (all velocities in mm/yr). The metadata JSON output now also reports these fields in mm/yr, consistent with the schema comment.``tools/csv2raster.py`` — displacement time-series columns (
DTs) were written in cm instead of radians. The samecm → radconversion (4π / λ × 0.01) applied to the mean velocity band is now also applied to all time-series columns.``tools/geoprocessing.py`` — hard-coded wavelength
55.465763mm replaced withSENTINEL1_WAVELENGTH_MMimported fromconfig.py.
Changed¶
``geo/spatial.py`` — raster-to-polygon footprint function rewritten. Replaces the former
_complex_hull_from_raster/_simple_hull_from_rasterpair with a single algorithm: OR-pooling subsampling to at most 600 × 600 pixels, 5 × 5 binary closing for exterior smoothing, and size-based selective hole filling (interior holes smaller than 5 % of the image area are filled; border-connected regions such as open sea are never touched). Handles both compact and complex (multi-island, coastal) shapes without user intervention.``doc/testing.rst`` — Case 3 station exclusion list updated with a full rationale table. The optimised command produces RMS residuals of 0.23 mm/yr (down from 1.1 mm/yr).
Package version —
src/__init__.pyupdated to0.4.0.
0.3.0 (2026-04-18) — Robustness, weighting, and output quality¶
This release focuses on scientific correctness of the GNSS weighting scheme, robustness of the EPOS API connector against server-side format changes, and a major reduction in pipeline verbosity. The public CLI is backward-compatible.
Added¶
``–plate`` / ``-pl`` CLI argument — overrides the tectonic plate code (
CFG.gnss.plate) at runtime, allowing different ITRF plate-motion corrections without editingconfig.py. Propagated end-to-end throughrunner.py,correction.py,referencing.py,factory.py, andEPOSjson.py.
Changed¶
Fisher-information GNSS station weighting — replaced the previous heuristic linear combination with a statistically principled formulation:
\[w_i \propto \frac{N_i}{\sigma_i^2}\]where \(N_i\) is the number of GNSS observation days and \(\sigma_i\) is the projected LOS velocity uncertainty (mm/yr). Weights are normalised to unit mean. This automatically down-weights stations with short or imprecise time series without requiring manual tuning of per-criterion coefficients. Dead helper functions
_minmax()andnormalize()removed fromtools/geodetic.py.``EPOSjson.py`` — tqdm progress bar — the outer station-download loop is now wrapped with a
tqdmprogress bar displaying live counters (added / cached / skipped). The former per-station"Dictionaries added."INFO log is replaced by a single summary line after the loop:[EPOS] Metadata fetch complete — N new, M cached, K skipped (no data).``EPOSjson.py`` — verbose log demotion — the following messages are demoted from WARNING to DEBUG (they are expected during normal operation and do not require user attention):
“No availability data for station …”
“Unexpected velocity/time-series response for …”
“Empty ‘results’ in velocity response …”
“Empty time series for …”
“Could not build metadata record for …”
Cache log demotion —
tools/caching.py:"Cached '…'"messages demoted from INFO to DEBUG.NumPy RuntimeWarning suppression —
warnings.filterwarnings("ignore", category=RuntimeWarning, module="numpy")added at module level inEPOSjson.pyto suppress expected"Mean of empty slice"and"invalid value encountered in divide"messages during LOS projection and outlier filtering.SSL warning suppression —
urllib3.disable_warnings(InsecureRequestWarning)moved to module level inEPOSjson.pyto suppress per-request SSL warnings when connecting to the EPOS server (self-signed certificate;verify=Falseis intentional).``tools/timeseries.py`` — unsupported time-series format now raises
NotImplementedErrorwith a clear message instead of silently returningNone.
Fixed¶
EPOS API: list-wrapped scalar values — the EPOS REST API began wrapping scalar velocity components (
de/dt,dn/dt,du/dt,sed,snd,sud) in single-element lists. The new_scalar()helper inEPOSjson.pyhandles bothvalueand[value]transparently.EPOS API: JSON-string time-series response — for some stations the time-series endpoint returns a plain JSON string instead of a dict. The
isinstance(ts_payload, dict)guard inEPOSjson.pyprevents the resultingTypeError: string indices must be integers.``correction.py`` —
grid_df[["nbr_days"]] = 0(double brackets causing “Cannot set a DataFrame with multiple columns to the single column nbr_days”) corrected togrid_df["nbr_days"] = 0.``reference.py`` — multiple scientific and structural fixes:
Renamed
delta_days→delta_years(variable represented years, not days).Pinned reference epoch with
idxmin()instead ofiloc[0]to correctly anchor to the globally earliest acquisition.Endpoint velocity replaced by
scipy.stats.linregresson the model trajectory, providing a statistically robust velocity estimate.Antenna offset correction now applied only to
*_datacolumns, not*_modelcolumns.IRLS prior weights added:
w_prior = 1 / sigma²(previously all stations were equally weighted in the referencing fit).Fixed
return pd.DataFrame()→return None, Noneto match the declared return type.Added
close()and__del__methods toInsarReferencerto properly release the openrasteriodataset.
0.2.0 (2025-04-17) — Modular architecture refactoring¶
This release replaces the original monolithic src/tools/ package with a
clean, package-oriented architecture. The public command-line interface is
unchanged.
Added¶
src/config.py: all user-tunable parameters as frozen dataclasses (ProcessingConfig,IOConfig,FittingConfig,GNSSConfig,SpatialConfig). Replacestools/parameters.py.src/schema.py: shared data contracts —STATION_COLUMNS,FitResult,failed_fit,validate_station_df,normalise_station_df,empty_station_df.src/fitting/: modular plane-fitting package.fitting/weighting.py— Huber weights, Tukey weights, MAD scale estimate, prior-weight computation from GNSS precision and coverage.fitting/irls.py— IRLS with Huber/Tukey robust reweighting, k-fold cross-validation, full metric suite (MSE, MAE, R², WRSS, σ²).fitting/ransac.py— RANSAC with weighted OLS re-fit on inliers.fitting/__init__.py— unifiedfit_plane(X, y, method=…)dispatcher.
src/geo/: geodetic and raster geometry package.geo/geodetic.py— WGS-84 LLH ↔ ECEF conversion, CRS/projection selection, Voronoi-based outlier removal.geo/raster.py— windowed raster sampling, LOS projection of NEU velocity vectors, band-interleave rewriting.geo/spatial.py— convex/concave hull computation, Ordinary Kriging fallback grid.
src/gnss/: GNSS data-source adapters following theGNSSSourceabstract base class (gnss/base.py).gnss/epos.py,gnss/ngl.py— provider-specific adapters.gnss/csv.py— user-supplied CSV velocity file reader.gnss/factory.py— runtime source selection and LOS projection.gnss/stations.py— station filtering, weighting, and spatial distribution assessment.
src/insar/: InSAR product ingestion (insar/reader.py) and raster correction (insar/corrector.py).src/ioutil/metadata.py:.metasidecar file writer.src/pipeline/: orchestration layer.pipeline/runner.py— input validation, session model, workflow dispatch.pipeline/correction.py— velocity-field correction workflow.pipeline/referencing.py— time-series referencing workflow.
src/utils/: cross-cutting utilities.utils/cache.py— rewritten LRU on-disk file cache with clean public API (get,set,limit,size).utils/logging_setup.py— structured logging initialisation.utils/validation.py— CLI-argument validators.
src/viz/plots.py: diagnostic figures (scatter plots, station maps, residual histogram, fitted-plane contour, metrics summary).src/rringg.py: rewritten as a thin CLI wrapper (~150 lines). All processing logic moved intopipeline/.--log-leveland--log-fileCLI arguments.tests/conftest.py: shared pytest path fixture.tests/test_schema.py,tests/test_fitting.py,tests/test_geo_geodetic.py,tests/test_utils.py: 99 unit tests covering the new packages (all passing).
Changed¶
src/rringg.pyreduced from ~1 050 lines to ~270 lines.All
tools.*imports in new modules migrated to thematic package equivalents (geo.*,gnss.*,fitting.*, etc.).utils/cache.pyre-implemented from scratch; retains the same LRU behaviour as the originaltools/caching.pybut with improved error reporting.Python 3.9 compatibility restored:
from __future__ import annotationsadded to all modules usingX | Yunion syntax.Documentation fully updated to reflect new architecture.
Fixed¶
Typo
velocity_raser_path→velocity_raster_paththroughout.Attribute name mismatches between
SpatialConfigandpipeline/correction.py(x_offset→grid_x_offset_m, etc.).Wrong call signature:
station_weighting(df)replaced bycompute_prior_weights(df).normalize(non-existent ingeo.geodetic) replaced bynormalise_weightsfromfitting.weighting.RANSAC
cross_validatekeyword argument incompatibility with the installed scikit-learn version.
0.0.1 (2024-01-13) — Initial release¶
First public release of RRINGG.