Changelog¶
All notable changes to this project are documented here. The format follows Keep a Changelog and the project uses Semantic Versioning.
[Unreleased]¶
[0.7.0] - 2026-11-04¶
Added¶
egms_client.analysis(extratables):fit(frame, start=, end=)is the model the archive fits to each series (velocity, acceleration, seasonal amplitude, rmse). It gives backmean_velocity,accelerationandrmse_tswithin rounding, on any window, with the day of the year the seasonal cycle peaks.trend_changegives the date a series changes velocity, the velocities before and after, anddelta_bic.unwrapping_jumpsfinds steps of half the Sentinel-1 wavelength (27.7 mm). About 3 seconds each for a whole L3 tile.
[0.6.0] - 2026-11-04¶
Added¶
egms mcp, an MCP server (extramcp, on the MCP Python SDK 2.x) with four tools:releases,find_files(files and sizes over a box, nothing downloaded),velocity_atandarea_summary(coverage, mean, percentiles, share below / above a stable band, the 5 fastest cells). Answers carry their release and files;area_summarydownloads at most--max-tilesL3 tiles per box; the key is read from the server's environment only.mcp_server.create_server()builds it in Python.EgmsArchive.velocity_at(lon, lat, component="U", release=None): the L3 velocity of the 100 m cell at a point in degrees, as aPointVelocitythat also carries its release, tile and file (Nonevelocity at sea or without data). Only that tile is downloaded, once. Pure helpers:tile_for_point(x, y),mosaic.lonlat_to_xy,mosaic.value_at.egms_client.sync.Archive: the client withoutasync, its own event loop in a background thread, so it also works inside Jupyter.- A live check: Pozzuoli rises by more than 50 mm/year, the sea has no velocity.
- Documentation site (MkDocs Material + mkdocstrings): the README as home
page, the notebooks, an API reference generated from the docstrings, the
changelog. Built with
--stricton every pull request, published to GitHub Pages frommain(make docsbuilds it locally). - README links are absolute, so they work on GitHub, PyPI and the site.
- A roadmap page: foundations, time-series analysis and AI features.
llms.txtandllms-full.txton the site, built from the sources by a MkDocs hook (hooks/llms.py): the index for AI assistants, and the whole documentation as one text file with the API reference rendered from the docstrings.- An agent skill,
skills/egms-client/SKILL.md, also served by the site: how to use the library and the rules the archive enforces. A test checks that every import, method and constant it names exists.
[0.5.0] - 2026-11-03¶
Added¶
mosaic.to_xarray(m): a mosaic as anxarray.DataArrayony/xpixel centres in EPSG:3035, with the CRS CF-style in aspatial_refcoordinate (read by rioxarray, GDAL, pyproj).tables.to_xarray(frame): the series of a CSV as aDataArrayover(pid, time), every attribute (mean_velocity,easting, ...) a coordinate alongpid. New extraxarray.Hit.footprint(the outline the archive returns,(lon, lat)corners) andHit.covers(lon, lat): a burst is a tilted parallelogram, so its bbox claims points it does not contain.notebooks/: Campi Flegrei, a landslide in Ancona, Bologna sinking, run on real data, each with its figure indocs/images/.egms_client.decompose:grid_losaverages L2 points (mean velocity and line-of-sight vector, coherent points only) on a grid in EPSG:3035, anddecomposeturns one ascending and one descending grid into vertical and east-westMosaics. Over Ancona it matches L3 with correlation 0.93 (vertical) and 0.79 (east-west), median difference 0.6 and 0.2 mm/year.
[0.4.0] - 2026-11-03¶
Added¶
egms mosaic --bbox W,S,E,N --component U|E: the L3 tiles a box in degrees touches, downloaded once, pasted and written as a Cloud-Optimized GeoTIFF in EPSG:3035 (extraraster).egms_client.mosaic.to_cog(float32, NaN nodata, deflate, overviews) andlonlat_to_laea(a box in degrees to EPSG:3035 metres, edges densified).- Download progress:
fetch(..., on_plan=, on_progress=);egms downloadandegms mosaicshow one updating line on a terminal, a line per file in a log. tests/liveand a weeklyliveworkflow: the archive behaviour the library relies on (lists, the five known refusals, a 5° box, an L3 tile, a small L2B download) checked against the real archive, with an issue opened on failure. Needs the repository secretEGMS_SERVICE_KEY.- README: a real map (vertical speed around Naples, 2020-2025).
Fixed¶
- The archive allows 2 concurrent downloads per account; a third is
answered HTTP 429. Every download of an
EgmsArchive(fetch,fetch_tile, and the caller's own parallel calls) now goes through one gate ofMAX_DOWNLOADS = 2;concurrencydefaults to 2 (it was 4, which failed on the third file). A 429 waits forRetry-After, or at least 30 s. - A download interrupted by an error or a cancellation no longer leaves a
.partfile.
[0.3.0] - 2026-11-03¶
Added¶
egms_client.tables(extratables: pandas, pyarrow, pyproj):read_csvreads any CSV of the archive (L2A, L2B, L3) with float32 dates, optionally without them or with fewer attributes;dates,attributes,series_long;to_geoparquetandcsv_to_geoparquetwrite GeoParquet 1.1 with WKB points in EPSG:3035 fromeasting/northing. The CSV is streamed in blocks: a 250 MB L3 CSV converts in ~7 s with ~0.5 GB of memory, to ~60 MB.fetch(..., keep=["parquet"]): the CSV of each ZIP converted to GeoParquet, the CSV removed unless also kept.egms convert: CSVs already on disk to GeoParquet;egms download --keep parquet.
Fixed¶
- Concurrent requests share one token request instead of each asking for one; a busy token endpoint (429/502/503/504, network errors) is retried. Its 500 (a bad assertion) is still not.
- L3 ZIPs also hold an XML:
keep="xml"is documented for every level.
[0.2.0] - 2026-11-03¶
Added¶
- All the archive's levels: L2A (
BASIC) and L2B (CALIBRATED) bursts besides Ortho L3.Querydescribes a search (levels, release,bboxin degrees,tile,relative_orbit,burst_cycle,swath,direction,product_type);query_bodybuilds it, padding the orbit to the three digits the archive requires. EgmsArchive.find: the files a query matches, latest sub-release of each; a box larger than the archive accepts (5° a side) is split bysplit_bboxand the results merged.EgmsArchive.fetch: downloads streamed to disk (.part, then renamed), in parallel with a limit, retried on network errors and 429/502/503/504, a search searched again when its id expires, files already on disk skipped,keepchoosing the ZIP or itstiff/csv/xml, andmax_bytesrefusing a download before its first byte.EgmsArchive.levels,.bursts;Hitcarries level, path, bbox and the burst fields.ServiceKey.from_env(EGMS_SERVICE_KEYorEGMS_SERVICE_KEY_FILE) andServiceKey.from_file;EgmsArchive()with no arguments opens its own HTTP client and reads the key from the environment.default_directory:EGMS_DATA_DIR, else the user cache.egmscommand line:releases,levels,search,download.
Changed¶
fetch_tilestreams the ZIP to disk instead of holding it in memory, and itsdirectoryis optional.
[0.1.1] - 2026-11-03¶
Fixed¶
EgmsArchive.fetch_tilerefuses a hit whose file name does not belong to the requested tile or contains/(EgmsError), instead of saving it where the cache never finds it again.- The cached sub-release is chosen by number (
_10over_9), aslatest_hitalready did.
[0.1.0] - 2026-11-03¶
Added¶
ServiceKey,assertion,token_request,parse_token: bearer tokens from a CLMS service key (RS256 JWT bearer grant).search_body/parse_search/latest_hit,parse_releases,download_url/check_download/extract_tiff: Ortho L3 tiles, with sea tiles asNoneand refusals served as HTTP 200 asEgmsError.EgmsArchive: async client over an injectedhttpx.AsyncClient, with token renewal and an on-disk cache of tiles and absent tiles.egms_client.mosaic(extraraster): tiles pasted on one grid, CRS compared by parameters (IGNF:ETRS89LAEAtiles accepted).- Extracted from Limen (
src/limen/integrations/egms/at 9b977bb) and relicensed under Apache-2.0 by its sole author. New: file names come from the search hit, so sub-releases other than_1work.