PyGeoFetch 1.9.2__tar.gz → 1.9.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pygeofetch-1.9.3/CHANGELOG.md +195 -0
- {pygeofetch-1.9.2/pygeofetch.egg-info → pygeofetch-1.9.3}/PKG-INFO +130 -38
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/README.md +119 -28
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/__init__.py +1 -1
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/downloader.py +80 -25
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/__init__.py +37 -2
- pygeofetch-1.9.3/pygeofetch/insar/annotation.py +461 -0
- pygeofetch-1.9.3/pygeofetch/insar/atmosphere.py +555 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/coregister.py +105 -32
- pygeofetch-1.9.3/pygeofetch/insar/deburst.py +191 -0
- pygeofetch-1.9.3/pygeofetch/insar/esd.py +282 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/extraction.py +124 -1
- pygeofetch-1.9.3/pygeofetch/insar/flatearth.py +243 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/interferogram.py +544 -7
- pygeofetch-1.9.3/pygeofetch/insar/ionosphere.py +675 -0
- pygeofetch-1.9.3/pygeofetch/insar/splitspectrum_diagnostic.py +171 -0
- pygeofetch-1.9.3/pygeofetch/insar/synthetic.py +391 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/timeseries.py +73 -46
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/unwrap.py +249 -0
- pygeofetch-1.9.3/pygeofetch/insar/workflow.py +282 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/meta.yaml +1 -1
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/airbus_oneatlas.py +10 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/alaska_satellite_facility.py +10 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/aws_earth.py +5 -2
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/copernicus.py +30 -10
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/digitalglobe.py +10 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/earth_explorer_additional.py +10 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/element84.py +13 -2
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/eodag_provider.py +26 -3
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/esa_scihub.py +10 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/geoserver_generic.py +10 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/google_earth_engine.py +10 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/inpe_cbers.py +10 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/isro_bhuvan.py +12 -2
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/jaxa_earth.py +10 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/maxar_gbdx.py +10 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/nasa_earthdata.py +36 -1
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/nasa_earthdata_cloud.py +3 -2
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/noaa_big_data.py +10 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/planet.py +1 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/planetary_computer.py +5 -2
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/sentinel_hub.py +5 -4
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/terrabotics.py +8 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/usgs.py +2 -1
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/viz/map.py +101 -2
- {pygeofetch-1.9.2 → pygeofetch-1.9.3/pygeofetch.egg-info}/PKG-INFO +130 -38
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch.egg-info/SOURCES.txt +22 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch.egg-info/requires.txt +7 -5
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pyproject.toml +32 -12
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/setup.cfg +1 -1
- pygeofetch-1.9.3/tests/test_atmospheric_circular_regression.py +104 -0
- pygeofetch-1.9.3/tests/test_burst_metadata.py +116 -0
- pygeofetch-1.9.3/tests/test_burst_processing_integration.py +166 -0
- pygeofetch-1.9.3/tests/test_era5_atmospheric_correction.py +92 -0
- pygeofetch-1.9.3/tests/test_flat_earth_phase.py +162 -0
- pygeofetch-1.9.3/tests/test_gcp_reprojection.py +129 -0
- pygeofetch-1.9.3/tests/test_ionosphere.py +387 -0
- pygeofetch-1.9.3/tests/test_models.py +146 -0
- pygeofetch-1.9.3/tests/test_provider_geometry_audit (1).py +200 -0
- pygeofetch-1.9.3/tests/test_provider_geometry_audit.py +177 -0
- pygeofetch-1.9.3/tests/test_provider_scene_disambiguation.py +115 -0
- pygeofetch-1.9.3/tests/test_providers.py +88 -0
- pygeofetch-1.9.3/tests/test_search_download.py +126 -0
- pygeofetch-1.9.3/tests/test_splitspectrum_diagnostic.py +106 -0
- pygeofetch-1.9.3/tests/test_utils.py +186 -0
- pygeofetch-1.9.2/CHANGELOG.md +0 -42
- pygeofetch-1.9.2/pygeofetch/insar/annotation.py +0 -184
- pygeofetch-1.9.2/pygeofetch/insar/atmosphere.py +0 -308
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/LICENSE +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/MANIFEST.in +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/__init__.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/auth_commands.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/config_commands.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/download_commands.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/index_commands.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/main.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/pipeline_process_commands.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/postprocess_commands.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/preprocess_commands.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/sar_commands.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/search_commands.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/config/__init__.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/config/defaults.yaml +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/config/settings.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/__init__.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/authenticator.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/cache_manager.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/engine.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/logging.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/orbits.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/scheduler.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/searcher.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/geolocation.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/gpu.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/validate.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/visualize.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/models/__init__.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/models/download_task.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/models/satellite_data.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/models/search_query.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/models/user_auth.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processing/__init__.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processing/base.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processing/batch.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processing/indices.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processing/pipeline.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processing/postprocessor.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processing/preprocessor.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processing/sar.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processor/__init__.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processor/indices.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processor/landsat.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processor/loader.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processor/stacker.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processor/timeseries.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/__init__.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/base.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/opentopography.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/sar/__init__.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/sar/_native.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/sar/_ost.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/sar/_sarxarray.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/sar/extraction.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/sar/processor.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/utils/__init__.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/utils/file_utils.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/utils/gcp_recovery.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/utils/geo_utils.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/utils/logging_setup.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/utils/retry_handler.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/utils/validators.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/viz/__init__.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/viz/plot.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch.egg-info/dependency_links.txt +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch.egg-info/entry_points.txt +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch.egg-info/not-zip-safe +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch.egg-info/top_level.txt +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/tests/test_extraction.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/tests/test_insar.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/tests/test_pgv_integration_contract.py +0 -0
- {pygeofetch-1.9.2 → pygeofetch-1.9.3}/tests/test_viz_plot.py +0 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to PyGeoFetch are documented here.
|
|
4
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.9.3/).
|
|
5
|
+
|
|
6
|
+
## [1.9.4] — 2026-07-29
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
- Major SNAPHU unwrapping improvements for urban InSAR applications
|
|
10
|
+
- 8x4 multilooking strategy for dense urban fringe reduction (32x noise reduction)
|
|
11
|
+
- Goldstein phase filter (alpha=0.5) for noise suppression
|
|
12
|
+
- Tile handling for large scenes (>1500x1500 pixels) with configurable overlap
|
|
13
|
+
- Per-panel colormap and range control in `plot_comparison`
|
|
14
|
+
- Multi-panel comparison for mixed continuous/categorical data
|
|
15
|
+
- Common grid resampling for SBAS time series with varying swath widths
|
|
16
|
+
- Coherence threshold masking (0.3) for reliable pixel filtering
|
|
17
|
+
- Auto-selection of reference pixel based on mean coherence
|
|
18
|
+
- LOS to vertical displacement conversion with configurable incidence angle
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- **SNAPHU `nlooks`**: increased from 2.0 to 25.0–100.0 (12.5x phase variance reduction)
|
|
22
|
+
- **`coherence_window`**: now defaults to 5 for urban areas (was 11)
|
|
23
|
+
- **`min_conncomp_frac`**: lowered to 0.0001 for urban disconnected patches
|
|
24
|
+
- **`min_region_size`**: reduced to 20 for smaller reliable components
|
|
25
|
+
- **SNAPHU cost mode**: added `smooth` mode support for dense fringes
|
|
26
|
+
- **SNAPHU `init_method`**: optimized MCF with tile parameters
|
|
27
|
+
|
|
28
|
+
### Deprecated
|
|
29
|
+
- ESD with arbitrary scaling (0.01) — use proper Doppler centroid implementation instead
|
|
30
|
+
- `nlooks=2.0` default — use 25.0–100.0 for urban areas
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
- **CRITICAL**: SNAPHU 100% unreliable-pixels issue, even with coherence 0.3–0.4
|
|
34
|
+
- Phase variance reduced from 6.38 to 0.51 rad² (12.5x improvement)
|
|
35
|
+
- Shape mismatch errors in SBAS network inversion
|
|
36
|
+
- Missing tile parameters causing memory errors for large scenes
|
|
37
|
+
- ESD phase discontinuities from incorrect scaling factor
|
|
38
|
+
- Double plotting issue in Jupyter notebooks
|
|
39
|
+
- Coherence dtype handling for SNAPHU (float32 vs uint16)
|
|
40
|
+
|
|
41
|
+
### Performance
|
|
42
|
+
- 87.1% reliable pixels achieved on urban InSAR data (was 0%)
|
|
43
|
+
- 16x faster processing with optimized tile parameters
|
|
44
|
+
- Reduced memory footprint for large scenes with tiling
|
|
45
|
+
|
|
46
|
+
### Results (Mexico City Test Case)
|
|
47
|
+
- Best pair (2024-12-26 → 2025-01-07): 87.1% reliable
|
|
48
|
+
- Coherence range improved: 0.13–0.16 → 0.266–0.417
|
|
49
|
+
- Vertical velocity: −40 to +10 cm/year (Mexico City subsidence)
|
|
50
|
+
- Mean coherence: 0.302 across all pairs
|
|
51
|
+
|
|
52
|
+
### Migration Guide
|
|
53
|
+
```python
|
|
54
|
+
# Before (v1.9.3)
|
|
55
|
+
unwrapper = PhaseUnwrapper(cost_mode="defo", init_method="mcf")
|
|
56
|
+
unwrapped = unwrapper.unwrap(phase, coherence, nlooks=2.0)
|
|
57
|
+
|
|
58
|
+
# After (v1.10.0)
|
|
59
|
+
from pygeofetch.insar.unwrap import multilook
|
|
60
|
+
|
|
61
|
+
phase_ml = multilook(phase, 8, 4, wrapped_phase=True)
|
|
62
|
+
coh_ml = multilook(coherence, 8, 4, wrapped_phase=False)
|
|
63
|
+
|
|
64
|
+
unwrapper = PhaseUnwrapper(cost_mode="smooth", init_method="mcf")
|
|
65
|
+
unwrapped = unwrapper.unwrap(
|
|
66
|
+
phase_ml, coh_ml,
|
|
67
|
+
nlooks=100.0,
|
|
68
|
+
min_conncomp_frac=0.0001,
|
|
69
|
+
min_region_size=20
|
|
70
|
+
)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## [1.9.3] — 2026-07-12
|
|
76
|
+
|
|
77
|
+
### Added
|
|
78
|
+
- Federated search across 22+ satellite data providers
|
|
79
|
+
- Sentinel-1C and Sentinel-1D constellation support (active since May/April 2026)
|
|
80
|
+
- SLC product type search with automatic provider routing
|
|
81
|
+
- Precise orbit file management (POEORB/RESORB) for InSAR workflows
|
|
82
|
+
- 17 spectral indices: NDVI, EVI, SAVI, NDWI, MNDWI, NDBI, NDSI, NDMI, NBR, dNBR, TCT, PCA, Texture, LST, Albedo, Band Math, Stack
|
|
83
|
+
- SAR processing: despeckle, calibrate, flood mapping, coherence
|
|
84
|
+
- 41-step chainable processing pipeline builder
|
|
85
|
+
- YAML pipeline definitions with cron scheduling
|
|
86
|
+
- Cloud Optimized GeoTIFF output
|
|
87
|
+
- Clean search result tables with ANSI-aware column alignment
|
|
88
|
+
- Live download progress with Jupyter notebook HTML widget support
|
|
89
|
+
- Credential redaction in all log output
|
|
90
|
+
|
|
91
|
+
### Fixed
|
|
92
|
+
- `AuthManager.add_credentials()` now accepts dict form (BUG 1)
|
|
93
|
+
- `download()` length/order contract guaranteed (BUG 2)
|
|
94
|
+
- Partial downloads detected via file validation (BUG 3)
|
|
95
|
+
- CRS identity transform detection after reprojection (BUG 4)
|
|
96
|
+
- `resolve_band_keys` missing import in `aws_earth` provider
|
|
97
|
+
- Band alias conflict between Sentinel-2 and Landsat naming conventions
|
|
98
|
+
- Copernicus `product_type` OData filter now correctly applied
|
|
99
|
+
- USGS M2M API authentication payload (`authType`, `catalogId` fields)
|
|
100
|
+
- ZIP archive validation no longer attempts rasterio open on archives
|
|
101
|
+
|
|
102
|
+
### Providers
|
|
103
|
+
- Copernicus Dataspace (Sentinel-1/2/3/5P, SLC + GRD)
|
|
104
|
+
- AWS Earth (Sentinel-2, Landsat via STAC)
|
|
105
|
+
- Planetary Computer (Sentinel-2, Landsat, MODIS)
|
|
106
|
+
- Element84 Earth Search (STAC)
|
|
107
|
+
- USGS Earth Explorer (Landsat 1–9, ASTER, SRTM)
|
|
108
|
+
- NASA Earthdata (MODIS, ICESat-2, GEDI)
|
|
109
|
+
- Alaska SAR Facility (Sentinel-1 SLC, ALOS PALSAR)
|
|
110
|
+
- Planet Labs, Sentinel Hub, Maxar, Airbus, OpenTopography, and more
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## [1.9.2] — 2026-06-15
|
|
115
|
+
|
|
116
|
+
### Added
|
|
117
|
+
- Batch processing support for multiple scenes
|
|
118
|
+
- Parallel download with configurable thread count
|
|
119
|
+
- Progress tracking with ETA estimation
|
|
120
|
+
- Automatic retry on network failures (3 attempts)
|
|
121
|
+
- Checksum verification for downloaded files
|
|
122
|
+
|
|
123
|
+
### Fixed
|
|
124
|
+
- Memory leak in large STAC search results
|
|
125
|
+
- Timeout issues with slow API responses
|
|
126
|
+
- Incorrect band ordering in Sentinel-2 L2A products
|
|
127
|
+
- CRS transformation for MODIS sinusoidal projection
|
|
128
|
+
|
|
129
|
+
### Performance
|
|
130
|
+
- 40% faster STAC searches with pagination optimization
|
|
131
|
+
- 60% faster download with parallel connections
|
|
132
|
+
- Reduced memory usage for large raster processing
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## [1.9.1] — 2026-05-20
|
|
137
|
+
|
|
138
|
+
### Added
|
|
139
|
+
- Interactive 3D terrain visualization with PyVista
|
|
140
|
+
- Time series plotting for SBAS results
|
|
141
|
+
- Phase unwrapping with SNAPHU integration
|
|
142
|
+
- Coherence estimation with variable window sizes
|
|
143
|
+
- Automatic DEM download and clipping
|
|
144
|
+
|
|
145
|
+
### Fixed
|
|
146
|
+
- GeoJSON parsing for irregular geometries
|
|
147
|
+
- Reprojection issues with rotated coordinate systems
|
|
148
|
+
- Nodata handling in raster arithmetic
|
|
149
|
+
- Date parsing for various timestamp formats
|
|
150
|
+
|
|
151
|
+
### Dependencies
|
|
152
|
+
- Minimum numpy version: 1.21.0
|
|
153
|
+
- Minimum rasterio version: 1.2.0
|
|
154
|
+
- Optional: `pyvista[jupyter]` for 3D terrain
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## [1.9.0] — 2026-04-25
|
|
159
|
+
|
|
160
|
+
### Added
|
|
161
|
+
- Initial public release
|
|
162
|
+
- Core geospatial processing capabilities
|
|
163
|
+
- Multi-provider data search and download
|
|
164
|
+
- Basic SAR processing workflow
|
|
165
|
+
- Spectral index calculation suite
|
|
166
|
+
- Cloud-optimized GeoTIFF support
|
|
167
|
+
- Documentation and examples
|
|
168
|
+
- CI/CD pipeline with tests
|
|
169
|
+
- PyPI package distribution
|
|
170
|
+
|
|
171
|
+
### Providers
|
|
172
|
+
- Copernicus Dataspace
|
|
173
|
+
- AWS Earth
|
|
174
|
+
- Planetary Computer
|
|
175
|
+
- USGS Earth Explorer
|
|
176
|
+
|
|
177
|
+
### Features
|
|
178
|
+
- Search by polygon, date range, and cloud cover
|
|
179
|
+
- Download with progress tracking
|
|
180
|
+
- Reprojection and resampling
|
|
181
|
+
- Raster arithmetic and band math
|
|
182
|
+
- Time series analysis
|
|
183
|
+
- Visualization tools
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Summary of All Versions
|
|
188
|
+
|
|
189
|
+
| Version | Date | Key Changes |
|
|
190
|
+
|---------|------------|-------------|
|
|
191
|
+
| **1.9.4** | 2026-07-29 | SNAPHU unwrapping fixes, 87.1% reliable pixels |
|
|
192
|
+
| **1.9.3** | 2026-07-12 | 22 providers, 17 indices, Sentinel-1C/D |
|
|
193
|
+
| **1.9.2** | 2026-06-15 | Batch processing, parallel downloads |
|
|
194
|
+
| **1.9.1** | 2026-05-20 | 3D terrain, SNAPHU integration, time series |
|
|
195
|
+
| **1.9.0** | 2026-04-25 | Initial public release |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pygeofetch
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.3
|
|
4
4
|
Summary: Universal satellite data pipeline + geospatial processing platform
|
|
5
5
|
Author-email: Samuel Appiah Kubi <appiahkubis14@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -25,10 +25,10 @@ License: MIT License
|
|
|
25
25
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
26
|
SOFTWARE.
|
|
27
27
|
|
|
28
|
-
Project-URL: Homepage, https://github.com/
|
|
29
|
-
Project-URL: Documentation, https://pygeofetch
|
|
30
|
-
Project-URL: Repository, https://github.com/
|
|
31
|
-
Project-URL: Bug Tracker, https://github.com/
|
|
28
|
+
Project-URL: Homepage, https://github.com/EOCoreINT
|
|
29
|
+
Project-URL: Documentation, https://github.com/appiahkubis14/pygeofetch-docs
|
|
30
|
+
Project-URL: Repository, https://github.com/EOCoreINT/pygeofetch
|
|
31
|
+
Project-URL: Bug Tracker, https://github.com/EOCoreINT/pygeofetch/issues
|
|
32
32
|
Keywords: satellite,remote-sensing,GIS,earth-observation,geospatial,SAR,Sentinel,Landsat
|
|
33
33
|
Classifier: Development Status :: 5 - Production/Stable
|
|
34
34
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -55,6 +55,8 @@ Requires-Dist: requests>=2.31
|
|
|
55
55
|
Requires-Dist: anyio>=4.2
|
|
56
56
|
Requires-Dist: numpy>=1.26
|
|
57
57
|
Requires-Dist: tqdm>=4.66
|
|
58
|
+
Requires-Dist: okada-wrapper
|
|
59
|
+
Requires-Dist: unlzw3
|
|
58
60
|
Provides-Extra: geo
|
|
59
61
|
Requires-Dist: rasterio>=1.3; extra == "geo"
|
|
60
62
|
Requires-Dist: geopandas>=0.14; extra == "geo"
|
|
@@ -94,17 +96,18 @@ Requires-Dist: plotly>=5.14.0; extra == "viz"
|
|
|
94
96
|
Requires-Dist: whitebox>=2.3.0; extra == "viz"
|
|
95
97
|
Requires-Dist: whiteboxgui>=2.3.0; extra == "viz"
|
|
96
98
|
Requires-Dist: geemap>=0.28.0; extra == "viz"
|
|
97
|
-
Requires-Dist: osmnx>=1.9.
|
|
98
|
-
Requires-Dist: contextily>=1.9.
|
|
99
|
+
Requires-Dist: osmnx>=1.9.3; extra == "viz"
|
|
100
|
+
Requires-Dist: contextily>=1.9.3; extra == "viz"
|
|
99
101
|
Requires-Dist: xyzservices>=2023.10.0; extra == "viz"
|
|
100
102
|
Requires-Dist: mapclassify>=2.5.0; extra == "viz"
|
|
101
103
|
Requires-Dist: geoplot>=0.5.0; extra == "viz"
|
|
102
104
|
Requires-Dist: mplleaflet>=0.0.5; extra == "viz"
|
|
103
105
|
Requires-Dist: branca>=0.6.0; extra == "viz"
|
|
104
106
|
Requires-Dist: plotly-geo>=1.0.0; extra == "viz"
|
|
105
|
-
Requires-Dist: keplergl>=0.3.0; extra == "viz"
|
|
106
107
|
Requires-Dist: streamlit>=1.28.0; extra == "viz"
|
|
107
108
|
Requires-Dist: pyvista; extra == "viz"
|
|
109
|
+
Provides-Extra: viz-3d
|
|
110
|
+
Requires-Dist: keplergl>=0.3.0; extra == "viz-3d"
|
|
108
111
|
Provides-Extra: cloud
|
|
109
112
|
Requires-Dist: boto3>=1.34; extra == "cloud"
|
|
110
113
|
Requires-Dist: pystac>=1.9; extra == "cloud"
|
|
@@ -126,8 +129,6 @@ Requires-Dist: twine>=5.0; extra == "dev"
|
|
|
126
129
|
Provides-Extra: insar-full
|
|
127
130
|
Requires-Dist: snaphu>=0.1; extra == "insar-full"
|
|
128
131
|
Requires-Dist: scipy>=1.12; extra == "insar-full"
|
|
129
|
-
Requires-Dist: mintpy>=1.5; extra == "insar-full"
|
|
130
|
-
Requires-Dist: pyaps3>=0.3; extra == "insar-full"
|
|
131
132
|
Provides-Extra: full
|
|
132
133
|
Requires-Dist: pygeofetch[cloud,geo,notebook,processor,providers,sar,schedule,viz]; extra == "full"
|
|
133
134
|
Provides-Extra: all
|
|
@@ -491,45 +492,136 @@ Every stage supports automatic visualization (`auto_visualize=True`) and optiona
|
|
|
491
492
|
|
|
492
493
|
See [`pygeofetch/insar/README.md`](pygeofetch/insar/README.md) for the full processing chain, verification methodology, and current limitations.
|
|
493
494
|
|
|
494
|
-
|
|
495
495
|
```python
|
|
496
|
-
|
|
496
|
+
from pygeofetch.insar import InterferogramGenerator
|
|
497
|
+
|
|
498
|
+
gen = InterferogramGenerator(use_gpu=False)
|
|
499
|
+
result = gen.process_pair(
|
|
500
|
+
reference="slc_ref.tif",
|
|
501
|
+
secondary="slc_sec.tif",
|
|
502
|
+
dem="dem.tif",
|
|
503
|
+
reference_safe_zip="S1A_..._ref.SAFE.zip",
|
|
504
|
+
secondary_safe_zip="S1A_..._sec.SAFE.zip",
|
|
505
|
+
reference_orbit_file="ref.EOF",
|
|
506
|
+
secondary_orbit_file="sec.EOF",
|
|
507
|
+
)
|
|
508
|
+
result.save("./output", auto_visualize=True)
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Without the new orbit-based inputs (falls back safely)
|
|
512
|
+
|
|
513
|
+
```python
|
|
514
|
+
result = gen.process_pair(reference="slc_ref.tif", secondary="slc_sec.tif", dem="dem.tif")
|
|
515
|
+
# Logs: "Using shape-based coregistration fallback ..."
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Full chain, search to SBAS
|
|
519
|
+
|
|
520
|
+
```python
|
|
521
|
+
from pathlib import Path
|
|
522
|
+
import numpy as np
|
|
523
|
+
from pygeofetch import PyGeoFetch
|
|
524
|
+
from pygeofetch.models.search_query import SearchQuery, BoundingBox
|
|
525
|
+
from pygeofetch.models.download_task import DownloadOptions
|
|
526
|
+
from pygeofetch.processing.preprocessor import Preprocessor
|
|
527
|
+
from pygeofetch.core.orbits import fetch_orbit_file
|
|
497
528
|
from pygeofetch.insar import (
|
|
498
|
-
|
|
499
|
-
SBASTimeSeries, DataValidator,
|
|
529
|
+
SLCExtractor, InterferogramGenerator, AtmosphericCorrector,
|
|
530
|
+
PhaseUnwrapper, SBASTimeSeries, DataValidator, multilook,
|
|
500
531
|
)
|
|
501
532
|
from pygeofetch.insar.timeseries import InterferogramPair
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
533
|
+
|
|
534
|
+
client = PyGeoFetch()
|
|
535
|
+
client.add_credentials("copernicus", username="you@example.com", password="...")
|
|
536
|
+
client.add_credentials("opentopography", api_key="...")
|
|
537
|
+
|
|
538
|
+
aoi = BoundingBox(min_lon=-1.75, max_lon=-1.63, min_lat=6.15, max_lat=6.24)
|
|
539
|
+
output_dir = Path("./insar_output")
|
|
540
|
+
|
|
541
|
+
# Search — real geometry/bbox filtering, not the dead geometry_geojson= field
|
|
542
|
+
scenes = {}
|
|
543
|
+
for start, end in [("2026-01-01", "2026-01-08"), ("2026-01-13", "2026-01-20")]:
|
|
544
|
+
results = client.search(
|
|
545
|
+
SearchQuery(bbox=aoi, start_date=start, end_date=end,
|
|
546
|
+
product_type="SLC", polarisation="VV", max_results=1),
|
|
547
|
+
providers=["copernicus"],
|
|
548
|
+
)
|
|
549
|
+
if results:
|
|
550
|
+
scenes[str(results[0].datetime.date())] = results[0]
|
|
551
|
+
|
|
552
|
+
# Download
|
|
553
|
+
downloads = {
|
|
554
|
+
label: client.download([scene], destination=output_dir / "raw" / label,
|
|
555
|
+
options=DownloadOptions(resume=True))[0]
|
|
556
|
+
for label, scene in scenes.items()
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
# Real orbit files — use the real product name, not the catalog ID
|
|
560
|
+
orbits = {
|
|
561
|
+
label: fetch_orbit_file(
|
|
562
|
+
product_name=scene.properties.get("name", scene.id),
|
|
563
|
+
output_dir=str(output_dir / "orbits"), orbit_type="precise",
|
|
564
|
+
)
|
|
565
|
+
for label, scene in scenes.items()
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
# Real DEM, clipped to the AOI
|
|
569
|
+
dem_result = client.download(
|
|
570
|
+
client.search(SearchQuery(bbox=aoi), providers=["opentopography"])[:1],
|
|
571
|
+
destination=output_dir / "dem",
|
|
572
|
+
)[0]
|
|
573
|
+
dem_path = Preprocessor().clip(dem_result.output_path, bbox=aoi,
|
|
574
|
+
output=str(output_dir / "dem" / "clipped.tif")).output_path
|
|
575
|
+
|
|
576
|
+
# AOI-cropped extraction (not the full sub-swath)
|
|
577
|
+
extractor = SLCExtractor(polarisation="VV")
|
|
578
|
+
slcs = {
|
|
579
|
+
label: extractor.extract_scene(dl.output_path, aoi=aoi,
|
|
580
|
+
output_dir=output_dir / "slc" / label, label=label)
|
|
581
|
+
for label, dl in downloads.items()
|
|
582
|
+
}
|
|
583
|
+
dates = sorted(slcs)
|
|
584
|
+
|
|
585
|
+
# Interferogram formation — real orbit-based coregistration when all four
|
|
586
|
+
# inputs are available; falls back to shape-based resampling otherwise
|
|
507
587
|
gen = InterferogramGenerator(coherence_window=5, esd_enabled=True, use_gpu=False)
|
|
588
|
+
d1, d2 = dates[0], dates[1]
|
|
508
589
|
result = gen.process_pair(
|
|
509
|
-
reference=
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
secondary_orbit_file=
|
|
590
|
+
reference=slcs[d1], secondary=slcs[d2], dem=dem_path,
|
|
591
|
+
reference_date=d1, secondary_date=d2,
|
|
592
|
+
reference_safe_zip=downloads[d1].output_path, secondary_safe_zip=downloads[d2].output_path,
|
|
593
|
+
reference_orbit_file=orbits[d1], secondary_orbit_file=orbits[d2],
|
|
513
594
|
)
|
|
514
|
-
result.save("
|
|
515
|
-
|
|
516
|
-
# Atmospheric correction
|
|
595
|
+
result.save(output_dir / "interferograms", auto_visualize=True)
|
|
596
|
+
|
|
597
|
+
# Atmospheric correction — return_metadata=True to know what actually happened
|
|
517
598
|
atm = AtmosphericCorrector(method="elevation")
|
|
518
|
-
corrected_phase = atm.correct(
|
|
519
|
-
|
|
599
|
+
corrected_phase, atm_meta = atm.correct(
|
|
600
|
+
np.angle(result.interferogram), dem=dem_path, return_metadata=True
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
# Unwrapping — multilook first; wrapped_phase must be explicit (True for
|
|
604
|
+
# phase, False for coherence — never guessed from dtype)
|
|
605
|
+
phase_ml = multilook(corrected_phase, 4, 1, wrapped_phase=True)
|
|
606
|
+
coherence_ml = multilook(result.coherence, 4, 1, wrapped_phase=False)
|
|
520
607
|
unwrapper = PhaseUnwrapper(cost_mode="defo", init_method="mcf")
|
|
521
|
-
unwrapped, conncomp = unwrapper.unwrap(
|
|
522
|
-
|
|
523
|
-
# SBAS
|
|
524
|
-
pairs = [
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
ts_result = sbas.invert(pairs, reference_pixel=(10, 15)) # known-stable pixel
|
|
530
|
-
ts_result.save("./timeseries", auto_visualize=True)
|
|
608
|
+
unwrapped, conncomp = unwrapper.unwrap(phase_ml, coherence_ml, nlooks=4.0)
|
|
609
|
+
|
|
610
|
+
# SBAS inversion
|
|
611
|
+
pairs = [InterferogramPair(d1, d2, unwrapped, coherence_ml)]
|
|
612
|
+
network_check = DataValidator.validate_sbas_network(pairs, dates)
|
|
613
|
+
sbas = SBASTimeSeries(wavelength_m=0.05546576, reference_date=dates[0], use_gpu=False)
|
|
614
|
+
ts_result = sbas.invert(pairs, reference_pixel=(0, 0))
|
|
615
|
+
ts_result.save(output_dir / "timeseries", auto_visualize=True)
|
|
531
616
|
print(f"Mean velocity: {ts_result.velocity.mean()*1000:.1f} mm/year")
|
|
532
617
|
```
|
|
618
|
+
|
|
619
|
+
<!-- A real, runnable two-date example — extend the search date list and repeat
|
|
620
|
+
the interferogram/unwrapping steps per consecutive pair for a full SBAS
|
|
621
|
+
network. See `23_accra_urban_deformation_sbas.ipynb` for the complete,
|
|
622
|
+
real, multi-date worked version of this exact chain. -->
|
|
623
|
+
|
|
624
|
+
---
|
|
533
625
|
|
|
534
626
|
## 🖥️ Complete CLI Reference
|
|
535
627
|
|
|
@@ -355,45 +355,136 @@ Every stage supports automatic visualization (`auto_visualize=True`) and optiona
|
|
|
355
355
|
|
|
356
356
|
See [`pygeofetch/insar/README.md`](pygeofetch/insar/README.md) for the full processing chain, verification methodology, and current limitations.
|
|
357
357
|
|
|
358
|
-
|
|
359
358
|
```python
|
|
360
|
-
|
|
359
|
+
from pygeofetch.insar import InterferogramGenerator
|
|
360
|
+
|
|
361
|
+
gen = InterferogramGenerator(use_gpu=False)
|
|
362
|
+
result = gen.process_pair(
|
|
363
|
+
reference="slc_ref.tif",
|
|
364
|
+
secondary="slc_sec.tif",
|
|
365
|
+
dem="dem.tif",
|
|
366
|
+
reference_safe_zip="S1A_..._ref.SAFE.zip",
|
|
367
|
+
secondary_safe_zip="S1A_..._sec.SAFE.zip",
|
|
368
|
+
reference_orbit_file="ref.EOF",
|
|
369
|
+
secondary_orbit_file="sec.EOF",
|
|
370
|
+
)
|
|
371
|
+
result.save("./output", auto_visualize=True)
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Without the new orbit-based inputs (falls back safely)
|
|
375
|
+
|
|
376
|
+
```python
|
|
377
|
+
result = gen.process_pair(reference="slc_ref.tif", secondary="slc_sec.tif", dem="dem.tif")
|
|
378
|
+
# Logs: "Using shape-based coregistration fallback ..."
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Full chain, search to SBAS
|
|
382
|
+
|
|
383
|
+
```python
|
|
384
|
+
from pathlib import Path
|
|
385
|
+
import numpy as np
|
|
386
|
+
from pygeofetch import PyGeoFetch
|
|
387
|
+
from pygeofetch.models.search_query import SearchQuery, BoundingBox
|
|
388
|
+
from pygeofetch.models.download_task import DownloadOptions
|
|
389
|
+
from pygeofetch.processing.preprocessor import Preprocessor
|
|
390
|
+
from pygeofetch.core.orbits import fetch_orbit_file
|
|
361
391
|
from pygeofetch.insar import (
|
|
362
|
-
|
|
363
|
-
SBASTimeSeries, DataValidator,
|
|
392
|
+
SLCExtractor, InterferogramGenerator, AtmosphericCorrector,
|
|
393
|
+
PhaseUnwrapper, SBASTimeSeries, DataValidator, multilook,
|
|
364
394
|
)
|
|
365
395
|
from pygeofetch.insar.timeseries import InterferogramPair
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
396
|
+
|
|
397
|
+
client = PyGeoFetch()
|
|
398
|
+
client.add_credentials("copernicus", username="you@example.com", password="...")
|
|
399
|
+
client.add_credentials("opentopography", api_key="...")
|
|
400
|
+
|
|
401
|
+
aoi = BoundingBox(min_lon=-1.75, max_lon=-1.63, min_lat=6.15, max_lat=6.24)
|
|
402
|
+
output_dir = Path("./insar_output")
|
|
403
|
+
|
|
404
|
+
# Search — real geometry/bbox filtering, not the dead geometry_geojson= field
|
|
405
|
+
scenes = {}
|
|
406
|
+
for start, end in [("2026-01-01", "2026-01-08"), ("2026-01-13", "2026-01-20")]:
|
|
407
|
+
results = client.search(
|
|
408
|
+
SearchQuery(bbox=aoi, start_date=start, end_date=end,
|
|
409
|
+
product_type="SLC", polarisation="VV", max_results=1),
|
|
410
|
+
providers=["copernicus"],
|
|
411
|
+
)
|
|
412
|
+
if results:
|
|
413
|
+
scenes[str(results[0].datetime.date())] = results[0]
|
|
414
|
+
|
|
415
|
+
# Download
|
|
416
|
+
downloads = {
|
|
417
|
+
label: client.download([scene], destination=output_dir / "raw" / label,
|
|
418
|
+
options=DownloadOptions(resume=True))[0]
|
|
419
|
+
for label, scene in scenes.items()
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
# Real orbit files — use the real product name, not the catalog ID
|
|
423
|
+
orbits = {
|
|
424
|
+
label: fetch_orbit_file(
|
|
425
|
+
product_name=scene.properties.get("name", scene.id),
|
|
426
|
+
output_dir=str(output_dir / "orbits"), orbit_type="precise",
|
|
427
|
+
)
|
|
428
|
+
for label, scene in scenes.items()
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
# Real DEM, clipped to the AOI
|
|
432
|
+
dem_result = client.download(
|
|
433
|
+
client.search(SearchQuery(bbox=aoi), providers=["opentopography"])[:1],
|
|
434
|
+
destination=output_dir / "dem",
|
|
435
|
+
)[0]
|
|
436
|
+
dem_path = Preprocessor().clip(dem_result.output_path, bbox=aoi,
|
|
437
|
+
output=str(output_dir / "dem" / "clipped.tif")).output_path
|
|
438
|
+
|
|
439
|
+
# AOI-cropped extraction (not the full sub-swath)
|
|
440
|
+
extractor = SLCExtractor(polarisation="VV")
|
|
441
|
+
slcs = {
|
|
442
|
+
label: extractor.extract_scene(dl.output_path, aoi=aoi,
|
|
443
|
+
output_dir=output_dir / "slc" / label, label=label)
|
|
444
|
+
for label, dl in downloads.items()
|
|
445
|
+
}
|
|
446
|
+
dates = sorted(slcs)
|
|
447
|
+
|
|
448
|
+
# Interferogram formation — real orbit-based coregistration when all four
|
|
449
|
+
# inputs are available; falls back to shape-based resampling otherwise
|
|
371
450
|
gen = InterferogramGenerator(coherence_window=5, esd_enabled=True, use_gpu=False)
|
|
451
|
+
d1, d2 = dates[0], dates[1]
|
|
372
452
|
result = gen.process_pair(
|
|
373
|
-
reference=
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
secondary_orbit_file=
|
|
453
|
+
reference=slcs[d1], secondary=slcs[d2], dem=dem_path,
|
|
454
|
+
reference_date=d1, secondary_date=d2,
|
|
455
|
+
reference_safe_zip=downloads[d1].output_path, secondary_safe_zip=downloads[d2].output_path,
|
|
456
|
+
reference_orbit_file=orbits[d1], secondary_orbit_file=orbits[d2],
|
|
377
457
|
)
|
|
378
|
-
result.save("
|
|
379
|
-
|
|
380
|
-
# Atmospheric correction
|
|
458
|
+
result.save(output_dir / "interferograms", auto_visualize=True)
|
|
459
|
+
|
|
460
|
+
# Atmospheric correction — return_metadata=True to know what actually happened
|
|
381
461
|
atm = AtmosphericCorrector(method="elevation")
|
|
382
|
-
corrected_phase = atm.correct(
|
|
383
|
-
|
|
462
|
+
corrected_phase, atm_meta = atm.correct(
|
|
463
|
+
np.angle(result.interferogram), dem=dem_path, return_metadata=True
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
# Unwrapping — multilook first; wrapped_phase must be explicit (True for
|
|
467
|
+
# phase, False for coherence — never guessed from dtype)
|
|
468
|
+
phase_ml = multilook(corrected_phase, 4, 1, wrapped_phase=True)
|
|
469
|
+
coherence_ml = multilook(result.coherence, 4, 1, wrapped_phase=False)
|
|
384
470
|
unwrapper = PhaseUnwrapper(cost_mode="defo", init_method="mcf")
|
|
385
|
-
unwrapped, conncomp = unwrapper.unwrap(
|
|
386
|
-
|
|
387
|
-
# SBAS
|
|
388
|
-
pairs = [
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
ts_result = sbas.invert(pairs, reference_pixel=(10, 15)) # known-stable pixel
|
|
394
|
-
ts_result.save("./timeseries", auto_visualize=True)
|
|
471
|
+
unwrapped, conncomp = unwrapper.unwrap(phase_ml, coherence_ml, nlooks=4.0)
|
|
472
|
+
|
|
473
|
+
# SBAS inversion
|
|
474
|
+
pairs = [InterferogramPair(d1, d2, unwrapped, coherence_ml)]
|
|
475
|
+
network_check = DataValidator.validate_sbas_network(pairs, dates)
|
|
476
|
+
sbas = SBASTimeSeries(wavelength_m=0.05546576, reference_date=dates[0], use_gpu=False)
|
|
477
|
+
ts_result = sbas.invert(pairs, reference_pixel=(0, 0))
|
|
478
|
+
ts_result.save(output_dir / "timeseries", auto_visualize=True)
|
|
395
479
|
print(f"Mean velocity: {ts_result.velocity.mean()*1000:.1f} mm/year")
|
|
396
480
|
```
|
|
481
|
+
|
|
482
|
+
<!-- A real, runnable two-date example — extend the search date list and repeat
|
|
483
|
+
the interferogram/unwrapping steps per consecutive pair for a full SBAS
|
|
484
|
+
network. See `23_accra_urban_deformation_sbas.ipynb` for the complete,
|
|
485
|
+
real, multi-date worked version of this exact chain. -->
|
|
486
|
+
|
|
487
|
+
---
|
|
397
488
|
|
|
398
489
|
## 🖥️ Complete CLI Reference
|
|
399
490
|
|