pygeofetch 2.0.0__tar.gz → 2.0.2__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-2.0.0 → pygeofetch-2.0.2}/CHANGELOG.md +6 -6
- {pygeofetch-2.0.0/pygeofetch.egg-info → pygeofetch-2.0.2}/PKG-INFO +2 -2
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/__init__.py +2 -2
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/cli/main.py +2 -2
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/__init__.py +1 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/coregister.py +140 -52
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/deburst.py +38 -56
- pygeofetch-2.0.2/pygeofetch/insar/esd.py +361 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/interferogram.py +665 -674
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/meta.yaml +1 -1
- {pygeofetch-2.0.0 → pygeofetch-2.0.2/pygeofetch.egg-info}/PKG-INFO +2 -2
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch.egg-info/requires.txt +1 -1
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pyproject.toml +2 -2
- pygeofetch-2.0.0/pygeofetch/insar/esd.py +0 -333
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/LICENSE +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/MANIFEST.in +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/README.md +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/cli/__init__.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/cli/auth_commands.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/cli/config_commands.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/cli/download_commands.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/cli/index_commands.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/cli/pipeline_process_commands.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/cli/postprocess_commands.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/cli/preprocess_commands.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/cli/sar_commands.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/cli/search_commands.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/config/__init__.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/config/defaults.yaml +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/config/settings.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/core/__init__.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/core/authenticator.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/core/cache_manager.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/core/downloader.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/core/engine.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/core/logging.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/core/orbits.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/core/scheduler.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/core/searcher.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/annotation.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/atmosphere.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/extraction.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/flatearth.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/geolocation.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/gpu.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/ionosphere.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/splitspectrum_diagnostic.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/stack_selection.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/synthetic.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/timeseries.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/unwrap.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/validate.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/visualize.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/insar/workflow.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/models/__init__.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/models/download_task.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/models/satellite_data.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/models/search_query.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/models/user_auth.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/processing/__init__.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/processing/base.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/processing/batch.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/processing/indices.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/processing/pipeline.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/processing/postprocessor.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/processing/preprocessor.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/processing/sar.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/processor/__init__.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/processor/indices.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/processor/landsat.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/processor/loader.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/processor/stacker.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/processor/timeseries.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/__init__.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/airbus_oneatlas.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/alaska_satellite_facility.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/aws_earth.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/base.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/copernicus.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/digitalglobe.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/earth_explorer_additional.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/element84.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/eodag_provider.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/esa_scihub.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/geoserver_generic.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/google_earth_engine.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/inpe_cbers.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/isro_bhuvan.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/jaxa_earth.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/maxar_gbdx.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/nasa_earthdata.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/nasa_earthdata_cloud.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/noaa_big_data.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/opentopography.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/planet.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/planetary_computer.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/sentinel_hub.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/terrabotics.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/providers/usgs.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/sar/__init__.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/sar/_native.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/sar/_ost.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/sar/_sarxarray.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/sar/extraction.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/sar/processor.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/utils/__init__.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/utils/file_utils.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/utils/gcp_recovery.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/utils/geo_utils.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/utils/logging_setup.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/utils/retry_handler.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/utils/validators.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/viz/__init__.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/viz/map.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch/viz/plot.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch.egg-info/SOURCES.txt +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch.egg-info/dependency_links.txt +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch.egg-info/entry_points.txt +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch.egg-info/not-zip-safe +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/pygeofetch.egg-info/top_level.txt +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/setup.cfg +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_atmospheric_circular_regression (1).py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_atmospheric_circular_regression.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_build_sbas_network.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_burst_metadata.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_burst_processing_integration.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_coregister_integration.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_coregister_upgrades.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_era5_atmospheric_correction.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_extraction.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_flat_earth_phase.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_gcp_reprojection.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_insar.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_ionosphere.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_models.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_pgv_integration_contract.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_provider_geometry_audit (1).py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_provider_geometry_audit.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_provider_scene_disambiguation.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_providers.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_raster_collocation_integration.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_search_download.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_splitspectrum_diagnostic.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_utils.py +0 -0
- {pygeofetch-2.0.0 → pygeofetch-2.0.2}/tests/test_viz_plot.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
All notable changes to PyGeoFetch are documented here.
|
|
4
|
-
Format follows [Keep a Changelog](https://keepachangelog.com/en/2.0.
|
|
4
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/2.0.2/).
|
|
5
5
|
|
|
6
|
-
## [2.0.
|
|
6
|
+
## [2.0.2] — 2026-07-29
|
|
7
7
|
|
|
8
8
|
### Added
|
|
9
9
|
- Major SNAPHU unwrapping improvements for urban InSAR applications
|
|
@@ -51,7 +51,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/2.0.0/).
|
|
|
51
51
|
|
|
52
52
|
### Migration Guide
|
|
53
53
|
```python
|
|
54
|
-
# Before (v2.0.
|
|
54
|
+
# Before (v2.0.2)
|
|
55
55
|
unwrapper = PhaseUnwrapper(cost_mode="defo", init_method="mcf")
|
|
56
56
|
unwrapped = unwrapper.unwrap(phase, coherence, nlooks=2.0)
|
|
57
57
|
|
|
@@ -72,7 +72,7 @@ unwrapped = unwrapper.unwrap(
|
|
|
72
72
|
|
|
73
73
|
---
|
|
74
74
|
|
|
75
|
-
## [2.0.
|
|
75
|
+
## [2.0.2] — 2026-07-12
|
|
76
76
|
|
|
77
77
|
### Added
|
|
78
78
|
- Federated search across 22+ satellite data providers
|
|
@@ -188,8 +188,8 @@ unwrapped = unwrapper.unwrap(
|
|
|
188
188
|
|
|
189
189
|
| Version | Date | Key Changes |
|
|
190
190
|
|---------|------------|-------------|
|
|
191
|
-
| **2.0.
|
|
192
|
-
| **2.0.
|
|
191
|
+
| **2.0.2** | 2026-07-29 | SNAPHU unwrapping fixes, 87.1% reliable pixels |
|
|
192
|
+
| **2.0.2** | 2026-07-12 | 22 providers, 17 indices, Sentinel-1C/D |
|
|
193
193
|
| **1.9.2** | 2026-06-15 | Batch processing, parallel downloads |
|
|
194
194
|
| **1.9.1** | 2026-05-20 | 3D terrain, SNAPHU integration, time series |
|
|
195
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: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
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
|
|
@@ -85,7 +85,7 @@ Requires-Dist: localtileserver>=1.0.0; extra == "viz"
|
|
|
85
85
|
Requires-Dist: ipysheet>=0.6.0; extra == "viz"
|
|
86
86
|
Requires-Dist: ipyleaflet>=0.17.0; extra == "viz"
|
|
87
87
|
Requires-Dist: ipywidgets>=8.0.0; extra == "viz"
|
|
88
|
-
Requires-Dist: ipyevents>=2.0.
|
|
88
|
+
Requires-Dist: ipyevents>=2.0.2; extra == "viz"
|
|
89
89
|
Requires-Dist: ipyfilechooser>=0.6.0; extra == "viz"
|
|
90
90
|
Requires-Dist: sidecar>=0.6.0; extra == "viz"
|
|
91
91
|
Requires-Dist: bokeh>=3.0.0; extra == "viz"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
PyGeoFetch v2.0.
|
|
2
|
+
PyGeoFetch v2.0.2 — Universal Satellite Data Pipeline.
|
|
3
3
|
|
|
4
4
|
Unified access to 22+ satellite data providers with a consistent
|
|
5
5
|
Python API and CLI. Supports authentication management, federated search,
|
|
@@ -30,7 +30,7 @@ CLI::
|
|
|
30
30
|
pygeofetch doctor
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
|
-
__version__ = "2.0.
|
|
33
|
+
__version__ = "2.0.2"
|
|
34
34
|
__author__ = "PyGeoFetch Contributors"
|
|
35
35
|
__license__ = "MIT"
|
|
36
36
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
PyGeoFetch CLI v2.0.
|
|
2
|
+
PyGeoFetch CLI v2.0.2 main entry point.
|
|
3
3
|
|
|
4
4
|
Full command reference:
|
|
5
5
|
pygeofetch auth — credential management
|
|
@@ -71,7 +71,7 @@ def cli(
|
|
|
71
71
|
) -> None:
|
|
72
72
|
"""
|
|
73
73
|
\b
|
|
74
|
-
PyGeoFetch v2.0.
|
|
74
|
+
PyGeoFetch v2.0.2 — Universal Satellite Data Pipeline
|
|
75
75
|
===========================================================
|
|
76
76
|
Unified access to 22+ satellite data providers.
|
|
77
77
|
|
|
@@ -665,23 +665,67 @@ def _sample_complex_imagette(data, center_row: float, center_col: float, size: i
|
|
|
665
665
|
return (real + 1j * imag).astype(np.complex64)
|
|
666
666
|
|
|
667
667
|
|
|
668
|
-
def _imagette_coherence(ref_imagette, sec_imagette) -> float:
|
|
668
|
+
# def _imagette_coherence(ref_imagette, sec_imagette) -> float:
|
|
669
|
+
# """
|
|
670
|
+
# Single scalar complex coherence between two same-shape imagettes —
|
|
671
|
+
# SNAP CrossCorrelationOp's "Method 1" (no internal sliding window;
|
|
672
|
+
# the whole imagette is treated as one coherence estimation window):
|
|
673
|
+
# https://step.esa.int/main/wp-content/help/versions/13.0.0/snap-toolboxes/eu.esa.microwavetbx.sar.op.insar.ui/operators/CrossCorrelationOp.html
|
|
674
|
+
# """
|
|
675
|
+
# import numpy as np
|
|
676
|
+
|
|
677
|
+
# num = abs(np.mean(ref_imagette * np.conj(sec_imagette)))
|
|
678
|
+
# denom = np.sqrt(
|
|
679
|
+
# np.mean(np.abs(ref_imagette) ** 2) * np.mean(np.abs(sec_imagette) ** 2)
|
|
680
|
+
# )
|
|
681
|
+
# if denom < 1e-12:
|
|
682
|
+
# return 0.0
|
|
683
|
+
# return float(np.clip(num / denom, 0.0, 1.0))
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
# def _imagette_coherence(ref_imagette, sec_imagette) -> float:
|
|
687
|
+
|
|
688
|
+
# import numpy as np
|
|
689
|
+
|
|
690
|
+
# num = abs(np.mean(ref_imagette * np.conj(sec_imagette)))
|
|
691
|
+
# denom = np.sqrt(np.mean(np.abs(ref_imagette) ** 2) * np.mean(np.abs(sec_imagette) ** 2))
|
|
692
|
+
# if denom < 1e-12:
|
|
693
|
+
# return 0.0
|
|
694
|
+
# return float(np.clip(num / denom, 0.0, 1.0))
|
|
695
|
+
|
|
696
|
+
def _imagette_coherence(ref_imagette, sec_imagette, window_size: int = 5) -> float:
|
|
669
697
|
"""
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
698
|
+
Calculate coherence using windowed averaging (standard ESD approach).
|
|
699
|
+
|
|
700
|
+
Args:
|
|
701
|
+
ref_imagette: Reference imagette (complex array)
|
|
702
|
+
sec_imagette: Secondary imagette (complex array)
|
|
703
|
+
window_size: Window size for coherence calculation (default: 5)
|
|
704
|
+
|
|
705
|
+
Returns:
|
|
706
|
+
Average coherence value
|
|
674
707
|
"""
|
|
675
708
|
import numpy as np
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
709
|
+
from scipy.ndimage import uniform_filter
|
|
710
|
+
|
|
711
|
+
# Calculate complex cross-correlation
|
|
712
|
+
cross_corr = ref_imagette * np.conj(sec_imagette)
|
|
713
|
+
|
|
714
|
+
# Calculate windowed averages
|
|
715
|
+
num_real = uniform_filter(cross_corr.real, size=window_size)
|
|
716
|
+
num_imag = uniform_filter(cross_corr.imag, size=window_size)
|
|
717
|
+
num = np.sqrt(num_real**2 + num_imag**2)
|
|
718
|
+
|
|
719
|
+
denom_ref = uniform_filter(np.abs(ref_imagette)**2, size=window_size)
|
|
720
|
+
denom_sec = uniform_filter(np.abs(sec_imagette)**2, size=window_size)
|
|
721
|
+
denom = np.sqrt(denom_ref * denom_sec)
|
|
722
|
+
|
|
723
|
+
# Calculate coherence
|
|
724
|
+
coherence = np.where(denom > 1e-12, num / denom, 0.0)
|
|
725
|
+
coherence = np.clip(coherence, 0.0, 1.0)
|
|
726
|
+
|
|
727
|
+
# Return average coherence
|
|
728
|
+
return float(np.mean(coherence))
|
|
685
729
|
|
|
686
730
|
def refine_offsets_by_coherence(
|
|
687
731
|
ref_complex,
|
|
@@ -1124,11 +1168,11 @@ def resample_with_offset_field(
|
|
|
1124
1168
|
import numpy as np
|
|
1125
1169
|
from scipy.ndimage import map_coordinates
|
|
1126
1170
|
|
|
1127
|
-
h, w = data.shape
|
|
1128
|
-
out_real = np.empty((h, w), dtype=np.float32)
|
|
1129
|
-
out_imag = np.empty((h, w), dtype=np.float32)
|
|
1130
|
-
data_real = data.real
|
|
1131
|
-
data_imag = data.imag
|
|
1171
|
+
# h, w = data.shape
|
|
1172
|
+
# out_real = np.empty((h, w), dtype=np.float32)
|
|
1173
|
+
# out_imag = np.empty((h, w), dtype=np.float32)
|
|
1174
|
+
# data_real = data.real
|
|
1175
|
+
# data_imag = data.imag
|
|
1132
1176
|
|
|
1133
1177
|
# Process in row chunks rather than building ~10 full-resolution
|
|
1134
1178
|
# float64 intermediate arrays (row_idx, col_idx, ref_global_row/col,
|
|
@@ -1142,41 +1186,85 @@ def resample_with_offset_field(
|
|
|
1142
1186
|
# more than sufficient precision for sub-pixel offset resampling and
|
|
1143
1187
|
# halves the footprint on its own; chunking bounds peak memory to
|
|
1144
1188
|
# one row-block regardless of total crop size.
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1189
|
+
|
|
1190
|
+
|
|
1191
|
+
h, w = data.shape
|
|
1192
|
+
out_real = np.empty((h, w), dtype=np.float32)
|
|
1193
|
+
out_imag = np.empty((h, w), dtype=np.float32)
|
|
1194
|
+
data_real = data.real
|
|
1195
|
+
data_imag = data.imag
|
|
1196
|
+
|
|
1197
|
+
# Process in 2D tiles for cache locality and strict memory bounding
|
|
1198
|
+
tile_h, tile_w = 1024, 1024
|
|
1199
|
+
|
|
1200
|
+
for r_start in range(0, h, tile_h):
|
|
1201
|
+
r_end = min(r_start + tile_h, h)
|
|
1202
|
+
for c_start in range(0, w, tile_w):
|
|
1203
|
+
c_end = min(c_start + tile_w, w)
|
|
1204
|
+
|
|
1205
|
+
# Use ogrid to create broadcastable 1D arrays instead of full 2D mgrid matrices
|
|
1206
|
+
row_idx = np.arange(r_start, r_end, dtype=np.float32)[:, None]
|
|
1207
|
+
col_idx = np.arange(c_start, c_end, dtype=np.float32)[None, :]
|
|
1208
|
+
|
|
1209
|
+
ref_global_row = row_idx + ref_row_offset
|
|
1210
|
+
ref_global_col = col_idx + ref_col_offset
|
|
1211
|
+
|
|
1212
|
+
# Evaluate polynomial (broadcasts automatically, minimal memory)
|
|
1213
|
+
offset_row = row_offset_fn(ref_global_row, ref_global_col)
|
|
1214
|
+
offset_col = col_offset_fn(ref_global_row, ref_global_col)
|
|
1215
|
+
|
|
1216
|
+
sample_rows = ref_global_row + offset_row - sec_row_offset
|
|
1217
|
+
sample_cols = ref_global_col + offset_col - sec_col_offset
|
|
1218
|
+
|
|
1219
|
+
# Broadcast to 2D for map_coordinates
|
|
1220
|
+
sample_rows_2d = np.broadcast_to(sample_rows, (r_end - r_start, c_end - c_start))
|
|
1221
|
+
sample_cols_2d = np.broadcast_to(sample_cols, (r_end - r_start, c_end - c_start))
|
|
1222
|
+
|
|
1223
|
+
out_real[r_start:r_end, c_start:c_end] = map_coordinates(
|
|
1224
|
+
data_real, [sample_rows_2d, sample_cols_2d], order=1, mode="constant", cval=0.0
|
|
1225
|
+
)
|
|
1226
|
+
out_imag[r_start:r_end, c_start:c_end] = map_coordinates(
|
|
1227
|
+
data_imag, [sample_rows_2d, sample_cols_2d], order=1, mode="constant", cval=0.0
|
|
1228
|
+
)
|
|
1229
|
+
|
|
1230
|
+
del sample_rows_2d, sample_cols_2d, offset_row, offset_col
|
|
1178
1231
|
|
|
1179
1232
|
return (out_real + 1j * out_imag).astype(np.complex64)
|
|
1233
|
+
# chunk_rows = max(1, min(h, 2000))
|
|
1234
|
+
# for row_start in range(0, h, chunk_rows):
|
|
1235
|
+
# row_end = min(row_start + chunk_rows, h)
|
|
1236
|
+
# row_idx, col_idx = np.mgrid[row_start:row_end, 0:w].astype(np.float32)
|
|
1237
|
+
|
|
1238
|
+
# # row_idx/col_idx are the reference grid's local (0-based)
|
|
1239
|
+
# # coordinates. Convert to full-scene coordinates to correctly
|
|
1240
|
+
# # evaluate the fitted offset functions, which were built on real
|
|
1241
|
+
# # annotation-derived full-scene coordinates, not any particular
|
|
1242
|
+
# # crop's local ones.
|
|
1243
|
+
# ref_global_row = row_idx + ref_row_offset
|
|
1244
|
+
# ref_global_col = col_idx + ref_col_offset
|
|
1245
|
+
|
|
1246
|
+
# offset_row = row_offset_fn(ref_global_row, ref_global_col)
|
|
1247
|
+
# offset_col = col_offset_fn(ref_global_row, ref_global_col)
|
|
1248
|
+
|
|
1249
|
+
# # Where this pixel's secondary counterpart is, in full-scene coordinates
|
|
1250
|
+
# sec_global_row = ref_global_row + offset_row
|
|
1251
|
+
# sec_global_col = ref_global_col + offset_col
|
|
1252
|
+
|
|
1253
|
+
# # Convert to the SECONDARY array's own local coordinates for
|
|
1254
|
+
# # actually indexing into `data` -- the secondary crop can have a
|
|
1255
|
+
# # different offset than the reference crop, so this is not the
|
|
1256
|
+
# # same subtraction as ref_row_offset/ref_col_offset above.
|
|
1257
|
+
# sample_rows = sec_global_row - sec_row_offset
|
|
1258
|
+
# sample_cols = sec_global_col - sec_col_offset
|
|
1259
|
+
|
|
1260
|
+
# out_real[row_start:row_end] = map_coordinates(
|
|
1261
|
+
# data_real, [sample_rows, sample_cols], order=1, mode="constant", cval=0.0
|
|
1262
|
+
# )
|
|
1263
|
+
# out_imag[row_start:row_end] = map_coordinates(
|
|
1264
|
+
# data_imag, [sample_rows, sample_cols], order=1, mode="constant", cval=0.0
|
|
1265
|
+
# )
|
|
1266
|
+
|
|
1267
|
+
# return (out_real + 1j * out_imag).astype(np.complex64)
|
|
1180
1268
|
|
|
1181
1269
|
|
|
1182
1270
|
def _interpolate(orbit, t):
|
|
@@ -3,32 +3,27 @@ TOPS deburst — remove real burst-boundary overlap/redundancy, matching
|
|
|
3
3
|
SNAP's own S-1 TOPS Deburst operator.
|
|
4
4
|
|
|
5
5
|
Algorithm confirmed directly against ESA's own STEP documentation for
|
|
6
|
-
TOPSARDeburstOp
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
burst. For each range cell, the merging time is quantised to the
|
|
13
|
-
nearest output azimuth cell."
|
|
6
|
+
TOPSARDeburstOp:
|
|
7
|
+
"In the azimuth direction, bursts are merged according to their
|
|
8
|
+
zero Doppler time... The merge time is determined by the average
|
|
9
|
+
of the last line of the first burst and the first line of the next
|
|
10
|
+
burst. For each range cell, the merging time is quantised to the
|
|
11
|
+
nearest output azimuth cell."
|
|
14
12
|
|
|
15
13
|
Why this matters: without deburst, adjacent bursts' real overlap
|
|
16
14
|
regions both remain in the data, and the SAME ground area gets imaged
|
|
17
15
|
twice by two different (radiometrically and phase-wise inconsistent)
|
|
18
|
-
parts of the antenna beam pattern
|
|
19
|
-
of visible striping and degraded coherence at burst boundaries
|
|
20
|
-
was never being corrected anywhere in this pipeline before this module
|
|
21
|
-
(pygeofetch's own existing ESD refinement docstring already admits this
|
|
22
|
-
directly: "has no effect on already-deburst/stripmap data" -- data
|
|
23
|
-
passed through it was never actually deburst).
|
|
16
|
+
parts of the antenna beam pattern — this is a real, confirmed source
|
|
17
|
+
of visible striping and degraded coherence at burst boundaries.
|
|
24
18
|
"""
|
|
25
|
-
|
|
26
19
|
from __future__ import annotations
|
|
27
20
|
|
|
28
21
|
import logging
|
|
29
22
|
from datetime import timedelta
|
|
30
23
|
from typing import List, Tuple
|
|
31
24
|
|
|
25
|
+
import numpy as np
|
|
26
|
+
|
|
32
27
|
from pygeofetch.insar.annotation import SwathTiming
|
|
33
28
|
|
|
34
29
|
logger = logging.getLogger("pygeofetch.insar.deburst")
|
|
@@ -41,23 +36,9 @@ def compute_burst_row_ranges(
|
|
|
41
36
|
Compute each burst's real, kept (non-overlapping) row range, in
|
|
42
37
|
that burst's own local 0-indexed row coordinates.
|
|
43
38
|
|
|
44
|
-
Verified before use: on a small, controlled synthetic case with
|
|
45
|
-
known burst timing, the computed ranges left no gaps and no
|
|
46
|
-
duplicate coverage, and their total length matched an
|
|
47
|
-
independently-computed expected total (span from the first burst's
|
|
48
|
-
start to the last burst's last line, at one line per
|
|
49
|
-
azimuth_time_interval_s) exactly.
|
|
50
|
-
|
|
51
39
|
Args:
|
|
52
|
-
swath_timing:
|
|
53
|
-
|
|
54
|
-
azimuth_time_interval_s: Real per-line time spacing, from the
|
|
55
|
-
same annotation XML's SLCGeometry
|
|
56
|
-
(imageAnnotation/imageInformation/azimuthTimeInterval)
|
|
57
|
-
-- must be the SAME value used for both, since
|
|
58
|
-
the cut-time calculation converts between the
|
|
59
|
-
two bursts' local row coordinates via this
|
|
60
|
-
shared spacing.
|
|
40
|
+
swath_timing: Real burst metadata from annotation.parse_burst_info().
|
|
41
|
+
azimuth_time_interval_s: Real per-line time spacing.
|
|
61
42
|
|
|
62
43
|
Returns:
|
|
63
44
|
List of (keep_start_row, keep_end_row) inclusive, local to
|
|
@@ -72,23 +53,26 @@ def compute_burst_row_ranges(
|
|
|
72
53
|
if n == 1:
|
|
73
54
|
return [(0, lines_per_burst - 1)]
|
|
74
55
|
|
|
75
|
-
# Real cut row, in each side's own local coordinates
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
cut_in_next = [None] * n # cut row (local to burst i) starting burst i's kept range
|
|
56
|
+
# Real cut row, in each side's own local coordinates
|
|
57
|
+
cut_in_prev = [None] * n
|
|
58
|
+
cut_in_next = [None] * n
|
|
79
59
|
|
|
80
60
|
for i in range(n - 1):
|
|
81
61
|
burst_i_last_line_time = bursts[i].azimuth_time + timedelta(
|
|
82
62
|
seconds=(lines_per_burst - 1) * azimuth_time_interval_s
|
|
83
63
|
)
|
|
84
64
|
burst_next_first_line_time = bursts[i + 1].azimuth_time
|
|
85
|
-
cut_time = burst_i_last_line_time + (
|
|
65
|
+
cut_time = burst_i_last_line_time + (
|
|
66
|
+
burst_next_first_line_time - burst_i_last_line_time
|
|
67
|
+
) / 2
|
|
86
68
|
|
|
87
69
|
cut_row_in_i = round(
|
|
88
|
-
(cut_time - bursts[i].azimuth_time).total_seconds()
|
|
70
|
+
(cut_time - bursts[i].azimuth_time).total_seconds()
|
|
71
|
+
/ azimuth_time_interval_s
|
|
89
72
|
)
|
|
90
73
|
cut_row_in_next = round(
|
|
91
|
-
(cut_time - bursts[i + 1].azimuth_time).total_seconds()
|
|
74
|
+
(cut_time - bursts[i + 1].azimuth_time).total_seconds()
|
|
75
|
+
/ azimuth_time_interval_s
|
|
92
76
|
)
|
|
93
77
|
|
|
94
78
|
cut_in_prev[i] = cut_row_in_i
|
|
@@ -100,11 +84,10 @@ def compute_burst_row_ranges(
|
|
|
100
84
|
keep_end = (lines_per_burst - 1) if i == n - 1 else cut_in_prev[i]
|
|
101
85
|
keep_start = max(0, min(keep_start, lines_per_burst - 1))
|
|
102
86
|
keep_end = max(0, min(keep_end, lines_per_burst - 1))
|
|
87
|
+
|
|
103
88
|
if keep_start > keep_end:
|
|
104
89
|
logger.warning(
|
|
105
|
-
"Burst %d: computed keep range is empty (start=%d > end=%d)
|
|
106
|
-
"— unusually large real overlap or unusual burst timing; "
|
|
107
|
-
"this burst will contribute no rows to the debursted result.",
|
|
90
|
+
"Burst %d: computed keep range is empty (start=%d > end=%d)",
|
|
108
91
|
i, keep_start, keep_end,
|
|
109
92
|
)
|
|
110
93
|
ranges.append((keep_start, keep_end))
|
|
@@ -112,30 +95,27 @@ def compute_burst_row_ranges(
|
|
|
112
95
|
return ranges
|
|
113
96
|
|
|
114
97
|
|
|
115
|
-
def deburst_array(
|
|
98
|
+
def deburst_array(
|
|
99
|
+
data: np.ndarray,
|
|
100
|
+
swath_timing: SwathTiming,
|
|
101
|
+
azimuth_time_interval_s: float,
|
|
102
|
+
row_offset: int = 0,
|
|
103
|
+
) -> Tuple[np.ndarray, int]:
|
|
116
104
|
"""
|
|
117
105
|
Apply real deburst to an array: remove each burst's redundant
|
|
118
106
|
overlap rows and concatenate the kept, non-overlapping portions
|
|
119
|
-
into one continuous array
|
|
120
|
-
image genuinely shrinks; this is not a masking operation).
|
|
107
|
+
into one continuous array.
|
|
121
108
|
|
|
122
109
|
Args:
|
|
123
|
-
data:
|
|
124
|
-
|
|
125
|
-
stack — n_bursts * lines_per_burst rows (or the
|
|
126
|
-
real full-scene extent if row_offset is used).
|
|
127
|
-
swath_timing: Real burst metadata from annotation.parse_burst_info().
|
|
110
|
+
data: 2D array (complex SLC, real-valued raster, etc.)
|
|
111
|
+
swath_timing: Real burst metadata from annotation.parse_burst_info().
|
|
128
112
|
azimuth_time_interval_s: Real per-line time spacing.
|
|
129
|
-
row_offset:
|
|
130
|
-
|
|
131
|
-
burst stack. Default 0 assumes `data` already starts
|
|
132
|
-
at the full-scene burst stack's row 0.
|
|
113
|
+
row_offset: If `data` is an already-cropped extract, the real
|
|
114
|
+
row offset of `data`'s row 0 within the full-scene burst stack.
|
|
133
115
|
|
|
134
116
|
Returns:
|
|
135
117
|
(debursted_data, first_kept_full_scene_row)
|
|
136
118
|
"""
|
|
137
|
-
import numpy as np
|
|
138
|
-
|
|
139
119
|
if not isinstance(swath_timing, SwathTiming):
|
|
140
120
|
raise TypeError(
|
|
141
121
|
f"deburst_array expects a SwathTiming object, got "
|
|
@@ -176,8 +156,10 @@ def deburst_array(data, swath_timing: SwathTiming, azimuth_time_interval_s: floa
|
|
|
176
156
|
)
|
|
177
157
|
|
|
178
158
|
debursted = np.concatenate(chunks, axis=0)
|
|
159
|
+
|
|
179
160
|
logger.info(
|
|
180
161
|
"Deburst: %d bursts -> %d output rows (input was %d rows)",
|
|
181
162
|
len(swath_timing.bursts), debursted.shape[0], data.shape[0],
|
|
182
163
|
)
|
|
183
|
-
|
|
164
|
+
|
|
165
|
+
return debursted, first_kept_full_scene_row
|