PyGeoFetch 2.6.1__tar.gz → 2.6.2.1__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.6.1 → pygeofetch-2.6.2.1}/CHANGELOG.md +5 -5
- {pygeofetch-2.6.1/pygeofetch.egg-info → pygeofetch-2.6.2.1}/PKG-INFO +25 -28
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/README.md +23 -27
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/__init__.py +1 -1
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/cli/auth_commands.py +6 -3
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/cli/main.py +10 -6
- pygeofetch-2.6.2.1/pygeofetch/cli/monitor_commands.py +141 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/cli/search_commands.py +1 -1
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/core/authenticator.py +5 -3
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/core/downloader.py +31 -13
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/core/engine.py +1 -1
- pygeofetch-2.6.2.1/pygeofetch/core/monitor.py +164 -0
- pygeofetch-2.6.2.1/pygeofetch/core/state.py +246 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/__init__.py +175 -48
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/analysis.py +62 -40
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/annotation.py +116 -70
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/atmosphere.py +168 -70
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/coregister.py +126 -58
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/deburst.py +39 -12
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/esd.py +65 -28
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/extraction.py +126 -67
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/flatearth.py +45 -18
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/geolocation.py +176 -27
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/gpu.py +3 -1
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/interferogram.py +502 -193
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/ionosphere.py +118 -45
- pygeofetch-2.6.2.1/pygeofetch/insar/offset_geometry.py +203 -0
- pygeofetch-2.6.2.1/pygeofetch/insar/offset_tracking.py +440 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/preflight.py +288 -169
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/provenance.py +2 -1
- pygeofetch-2.6.2.1/pygeofetch/insar/ps_selection.py +359 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/splitspectrum_diagnostic.py +12 -4
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/stack_selection.py +179 -77
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/synthetic.py +53 -17
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/timeseries.py +663 -268
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/unwrap.py +408 -156
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/validate.py +146 -9
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/visualize.py +15 -5
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/insar/workflow.py +102 -29
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/meta.yaml +1 -1
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/models/satellite_data.py +5 -3
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/processing/indices.py +5 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/processing/preprocessor.py +55 -25
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/processing/sar.py +13 -4
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/processor/indices.py +4 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/processor/stacker.py +4 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/processor/timeseries.py +41 -13
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/airbus_oneatlas.py +17 -4
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/alaska_satellite_facility.py +5 -2
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/aws_earth.py +1 -1
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/copernicus.py +19 -8
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/copernicus_nodes.py +73 -23
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/digitalglobe.py +5 -2
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/earth_explorer_additional.py +5 -2
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/element84.py +1 -1
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/eodag_provider.py +9 -5
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/esa_scihub.py +17 -4
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/geoserver_generic.py +5 -2
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/google_earth_engine.py +17 -4
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/inpe_cbers.py +5 -2
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/isro_bhuvan.py +5 -2
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/jaxa_earth.py +5 -2
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/maxar_gbdx.py +5 -2
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/nasa_earthdata.py +6 -4
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/nasa_earthdata_cloud.py +2 -2
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/noaa_big_data.py +5 -2
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/opentopography.py +6 -2
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/planet.py +4 -3
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/planetary_computer.py +1 -1
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/sentinel_hub.py +11 -7
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/terrabotics.py +4 -3
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/usgs.py +20 -5
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/sar/__init__.py +1 -1
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/sar/_native.py +1 -1
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/sar/extraction.py +8 -4
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/utils/gcp_recovery.py +1 -3
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/viz/map.py +82 -33
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/viz/plot.py +91 -38
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1/pygeofetch.egg-info}/PKG-INFO +25 -28
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch.egg-info/SOURCES.txt +13 -4
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch.egg-info/requires.txt +1 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pyproject.toml +9 -2
- pygeofetch-2.6.1/tests/test_atmospheric_circular_regression (1).py → pygeofetch-2.6.2.1/tests/test_atmospheric_circular_regression.py +21 -6
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_build_sbas_network.py +68 -38
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_burst_metadata.py +20 -6
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_burst_processing_integration.py +110 -31
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_burst_sync_large_gap.py +49 -35
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_burst_sync_prescreening.py +68 -43
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_burst_synchronization.py +56 -36
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_copernicus_nodes.py +63 -36
- pygeofetch-2.6.2.1/tests/test_coregister_integration.py +353 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_coregister_upgrades.py +110 -47
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_era5_atmospheric_correction.py +17 -6
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_extraction.py +46 -12
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_flat_earth_phase.py +72 -24
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_gcp_reprojection.py +28 -8
- pygeofetch-2.6.2.1/tests/test_insar.py +691 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_ionosphere.py +102 -33
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_map_preview_consolidation.py +41 -24
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_models.py +10 -3
- pygeofetch-2.6.2.1/tests/test_monitor.py +134 -0
- pygeofetch-2.6.2.1/tests/test_offset_tracking.py +273 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_pgv_integration_contract.py +42 -42
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_preflight_gate.py +398 -107
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_provider_geometry_audit.py +26 -12
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_provider_scene_disambiguation.py +31 -12
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_providers.py +1 -0
- pygeofetch-2.6.2.1/tests/test_ps_selection.py +299 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_raster_collocation_integration.py +75 -36
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_reference_pixel_and_despike.py +64 -38
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_risk_mapping.py +99 -43
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_screen_stack_orchestration.py +68 -41
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_search_download.py +3 -1
- pygeofetch-2.6.2.1/tests/test_sign_convention.py +209 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_splitspectrum_diagnostic.py +7 -2
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_stack_selection_consolidation.py +104 -45
- pygeofetch-2.6.2.1/tests/test_state.py +132 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_topo_r2_bug.py +42 -21
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/tests/test_utils.py +2 -1
- pygeofetch-2.6.2.1/tests/test_wls_inversion.py +237 -0
- pygeofetch-2.6.1/tests/test_atmospheric_circular_regression.py +0 -104
- pygeofetch-2.6.1/tests/test_coregister_integration.py +0 -354
- pygeofetch-2.6.1/tests/test_insar.py +0 -578
- pygeofetch-2.6.1/tests/test_preflight_gate (1).py +0 -319
- pygeofetch-2.6.1/tests/test_provider_geometry_audit (1).py +0 -200
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/LICENSE +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/MANIFEST.in +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/cli/__init__.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/cli/config_commands.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/cli/download_commands.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/cli/index_commands.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/cli/pipeline_process_commands.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/cli/postprocess_commands.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/cli/preprocess_commands.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/cli/sar_commands.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/config/__init__.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/config/defaults.yaml +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/config/settings.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/core/__init__.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/core/cache_manager.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/core/logging.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/core/orbits.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/core/scheduler.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/core/searcher.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/models/__init__.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/models/download_task.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/models/search_query.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/models/user_auth.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/processing/__init__.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/processing/base.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/processing/batch.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/processing/pipeline.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/processing/postprocessor.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/processor/__init__.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/processor/landsat.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/processor/loader.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/__init__.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/providers/base.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/sar/_ost.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/sar/_sarxarray.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/sar/processor.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/utils/__init__.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/utils/file_utils.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/utils/geo_utils.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/utils/logging_setup.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/utils/retry_handler.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/utils/validators.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch/viz/__init__.py +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch.egg-info/dependency_links.txt +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch.egg-info/entry_points.txt +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch.egg-info/not-zip-safe +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/pygeofetch.egg-info/top_level.txt +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/setup.cfg +0 -0
- {pygeofetch-2.6.1 → pygeofetch-2.6.2.1}/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.6.1/).
|
|
4
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/2.6.2.1/).
|
|
5
5
|
|
|
6
|
-
## [2.6.1] — 2026-07-29
|
|
6
|
+
## [2.6.2.1] — 2026-07-29
|
|
7
7
|
|
|
8
8
|
### Added
|
|
9
9
|
- Major SNAPHU unwrapping improvements for urban InSAR applications
|
|
@@ -72,7 +72,7 @@ unwrapped = unwrapper.unwrap(
|
|
|
72
72
|
|
|
73
73
|
---
|
|
74
74
|
|
|
75
|
-
## [2.6.1] — 2026-07-12
|
|
75
|
+
## [2.6.2.1] — 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.6.1** | 2026-07-29 | SNAPHU unwrapping fixes, 87.1% reliable pixels |
|
|
192
|
-
| **2.6.1** | 2026-07-12 | 22 providers, 17 indices, Sentinel-1C/D |
|
|
191
|
+
| **2.6.2.1** | 2026-07-29 | SNAPHU unwrapping fixes, 87.1% reliable pixels |
|
|
192
|
+
| **2.6.2.1** | 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.6.1
|
|
3
|
+
Version: 2.6.2.1
|
|
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
|
|
@@ -121,6 +121,7 @@ Requires-Dist: pytest>=8.0; extra == "dev"
|
|
|
121
121
|
Requires-Dist: pytest-cov>=4.1; extra == "dev"
|
|
122
122
|
Requires-Dist: pytest-mock>=3.12; extra == "dev"
|
|
123
123
|
Requires-Dist: ruff>=0.3; extra == "dev"
|
|
124
|
+
Requires-Dist: black<25.0,>=24.0; extra == "dev"
|
|
124
125
|
Requires-Dist: mypy>=1.8; extra == "dev"
|
|
125
126
|
Requires-Dist: build>=1.0; extra == "dev"
|
|
126
127
|
Requires-Dist: twine>=5.0; extra == "dev"
|
|
@@ -135,11 +136,10 @@ Dynamic: license-file
|
|
|
135
136
|
|
|
136
137
|
<div align="center">
|
|
137
138
|
|
|
138
|
-
<img src="https://raw.githubusercontent.com/EOCoreINT/pygeofetch/main/icon/pygeofetch_logo.png" alt="PyGeoFetch Logo" width="350">
|
|
139
|
-
|
|
140
139
|
[](https://pypi.org/project/pygeofetch/)
|
|
141
140
|
[](https://pypi.org/project/pygeofetch/)
|
|
142
141
|
[](https://opensource.org/licenses/MIT)
|
|
142
|
+
[](https://doi.org/10.5281/zenodo.22087230)
|
|
143
143
|
[](https://github.com/appiahkubis14/pygeofetch/actions)
|
|
144
144
|
[](https://codecov.io/gh/appiahkubis14/pygeofetch)
|
|
145
145
|
|
|
@@ -147,6 +147,10 @@ Dynamic: license-file
|
|
|
147
147
|
|
|
148
148
|
[Quick Start](#-quick-start) · [Mexico City Case Study](#-case-study-mapping-mexico-city-land-subsidence-with-insar) · [Case Study Repo ↗](https://github.com/EOCoreINT/mexico-subsidence-project) · [Documentation](https://appiahkubis14.github.io/pygeofetch-docs/) · [Notebooks](#-notebooks)
|
|
149
149
|
|
|
150
|
+
<br>
|
|
151
|
+
<img src="https://raw.githubusercontent.com/EOCoreINT/pygeofetch/main/icon/pygeofetch_logo.png" alt="PyGeoFetch Logo" width="350">
|
|
152
|
+
<br>
|
|
153
|
+
|
|
150
154
|
</div>
|
|
151
155
|
|
|
152
156
|
---
|
|
@@ -213,7 +217,7 @@ Every provider has been directly verified to return at minimum a correct boundin
|
|
|
213
217
|
pip install pygeofetch # core — free providers work immediately
|
|
214
218
|
pip install "pygeofetch[geo]" # + rasterio, geopandas, shapely
|
|
215
219
|
pip install "pygeofetch[insar]" # + native SBAS inversion, phase unwrapping
|
|
216
|
-
pip install "pygeofetch[insar-full]" # +
|
|
220
|
+
pip install "pygeofetch[insar-full]" # + For Linux Users
|
|
217
221
|
pip install "pygeofetch[all]" # everything
|
|
218
222
|
```
|
|
219
223
|
|
|
@@ -365,29 +369,22 @@ Run `pygeofetch --help` or any subcommand with `--help` for the complete, curren
|
|
|
365
369
|
|
|
366
370
|
---
|
|
367
371
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
| `provider_search_footprint_test.ipynb` | Search and footprint-map validation across all 24 providers |
|
|
385
|
-
|
|
386
|
-
```bash
|
|
387
|
-
cd notebooks/ && jupyter lab
|
|
388
|
-
``` -->
|
|
389
|
-
|
|
390
|
-
---
|
|
372
|
+
## 📚 Citation
|
|
373
|
+
|
|
374
|
+
If you use PyGeoFetch in your research or operational work, please cite it using the following:
|
|
375
|
+
|
|
376
|
+
### Software Citation
|
|
377
|
+
```bibtex
|
|
378
|
+
@software{pygeofetch_2026,
|
|
379
|
+
author = {Appiah Kubi, Samuel},
|
|
380
|
+
title = {PyGeoFetch: A Unified Python Framework for Multi-Provider Satellite Data Acquisition, Pre-download Quality Control, and Geospatial Processing},
|
|
381
|
+
year = {2026},
|
|
382
|
+
publisher = {Zenodo},
|
|
383
|
+
version = {2.6.2.1},
|
|
384
|
+
doi = {10.5281/zenodo.22087230},
|
|
385
|
+
url = {https://doi.org/10.5281/zenodo.22087230}
|
|
386
|
+
}
|
|
387
|
+
```
|
|
391
388
|
|
|
392
389
|
## Documentation
|
|
393
390
|
|
|
@@ -400,7 +397,7 @@ Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). Good first
|
|
|
400
397
|
```bash
|
|
401
398
|
git clone git@github.com:EOCoreINT/pygeofetch.git && cd pygeofetch
|
|
402
399
|
pip install -e ".[dev,all]" && pytest tests/unit/ -v
|
|
403
|
-
|
|
400
|
+
|
|
404
401
|
|
|
405
402
|
## License
|
|
406
403
|
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<img src="https://raw.githubusercontent.com/EOCoreINT/pygeofetch/main/icon/pygeofetch_logo.png" alt="PyGeoFetch Logo" width="350">
|
|
4
|
-
|
|
5
3
|
[](https://pypi.org/project/pygeofetch/)
|
|
6
4
|
[](https://pypi.org/project/pygeofetch/)
|
|
7
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://doi.org/10.5281/zenodo.22087230)
|
|
8
7
|
[](https://github.com/appiahkubis14/pygeofetch/actions)
|
|
9
8
|
[](https://codecov.io/gh/appiahkubis14/pygeofetch)
|
|
10
9
|
|
|
@@ -12,6 +11,10 @@
|
|
|
12
11
|
|
|
13
12
|
[Quick Start](#-quick-start) · [Mexico City Case Study](#-case-study-mapping-mexico-city-land-subsidence-with-insar) · [Case Study Repo ↗](https://github.com/EOCoreINT/mexico-subsidence-project) · [Documentation](https://appiahkubis14.github.io/pygeofetch-docs/) · [Notebooks](#-notebooks)
|
|
14
13
|
|
|
14
|
+
<br>
|
|
15
|
+
<img src="https://raw.githubusercontent.com/EOCoreINT/pygeofetch/main/icon/pygeofetch_logo.png" alt="PyGeoFetch Logo" width="350">
|
|
16
|
+
<br>
|
|
17
|
+
|
|
15
18
|
</div>
|
|
16
19
|
|
|
17
20
|
---
|
|
@@ -78,7 +81,7 @@ Every provider has been directly verified to return at minimum a correct boundin
|
|
|
78
81
|
pip install pygeofetch # core — free providers work immediately
|
|
79
82
|
pip install "pygeofetch[geo]" # + rasterio, geopandas, shapely
|
|
80
83
|
pip install "pygeofetch[insar]" # + native SBAS inversion, phase unwrapping
|
|
81
|
-
pip install "pygeofetch[insar-full]" # +
|
|
84
|
+
pip install "pygeofetch[insar-full]" # + For Linux Users
|
|
82
85
|
pip install "pygeofetch[all]" # everything
|
|
83
86
|
```
|
|
84
87
|
|
|
@@ -230,29 +233,22 @@ Run `pygeofetch --help` or any subcommand with `--help` for the complete, curren
|
|
|
230
233
|
|
|
231
234
|
---
|
|
232
235
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
| `provider_search_footprint_test.ipynb` | Search and footprint-map validation across all 24 providers |
|
|
250
|
-
|
|
251
|
-
```bash
|
|
252
|
-
cd notebooks/ && jupyter lab
|
|
253
|
-
``` -->
|
|
254
|
-
|
|
255
|
-
---
|
|
236
|
+
## 📚 Citation
|
|
237
|
+
|
|
238
|
+
If you use PyGeoFetch in your research or operational work, please cite it using the following:
|
|
239
|
+
|
|
240
|
+
### Software Citation
|
|
241
|
+
```bibtex
|
|
242
|
+
@software{pygeofetch_2026,
|
|
243
|
+
author = {Appiah Kubi, Samuel},
|
|
244
|
+
title = {PyGeoFetch: A Unified Python Framework for Multi-Provider Satellite Data Acquisition, Pre-download Quality Control, and Geospatial Processing},
|
|
245
|
+
year = {2026},
|
|
246
|
+
publisher = {Zenodo},
|
|
247
|
+
version = {2.6.2.1},
|
|
248
|
+
doi = {10.5281/zenodo.22087230},
|
|
249
|
+
url = {https://doi.org/10.5281/zenodo.22087230}
|
|
250
|
+
}
|
|
251
|
+
```
|
|
256
252
|
|
|
257
253
|
## Documentation
|
|
258
254
|
|
|
@@ -265,7 +261,7 @@ Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). Good first
|
|
|
265
261
|
```bash
|
|
266
262
|
git clone git@github.com:EOCoreINT/pygeofetch.git && cd pygeofetch
|
|
267
263
|
pip install -e ".[dev,all]" && pytest tests/unit/ -v
|
|
268
|
-
|
|
264
|
+
|
|
269
265
|
|
|
270
266
|
## License
|
|
271
267
|
|
|
@@ -221,8 +221,11 @@ def auth_remove(provider: str, yes: bool) -> None:
|
|
|
221
221
|
click.confirm(f"Remove credentials for {provider!r}?", abort=True)
|
|
222
222
|
|
|
223
223
|
mgr = AuthManager()
|
|
224
|
-
mgr.
|
|
225
|
-
|
|
224
|
+
removed = mgr.remove(provider)
|
|
225
|
+
if removed:
|
|
226
|
+
console.print(f"[green]Credentials for [bold]{provider}[/] removed.[/]")
|
|
227
|
+
else:
|
|
228
|
+
console.print(f"[yellow]No stored credentials found for {provider!r}.[/]")
|
|
226
229
|
|
|
227
230
|
|
|
228
231
|
@auth.command(name="export")
|
|
@@ -250,7 +253,7 @@ def auth_export(provider: str | None, output: str) -> None:
|
|
|
250
253
|
from pygeofetch.core.authenticator import AuthManager
|
|
251
254
|
|
|
252
255
|
mgr = AuthManager()
|
|
253
|
-
data = mgr.export_credentials(
|
|
256
|
+
data = mgr.export_credentials(providers=[provider] if provider else None)
|
|
254
257
|
|
|
255
258
|
with open(output, "w") as f:
|
|
256
259
|
json.dump(data, f, indent=2, default=str)
|
|
@@ -484,9 +484,11 @@ def providers_info(provider_id: str, as_json: bool) -> None:
|
|
|
484
484
|
table.add_row("Auth type", caps.auth_type)
|
|
485
485
|
table.add_row(
|
|
486
486
|
"Auth status",
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
487
|
+
(
|
|
488
|
+
f"[green]{auth_status}[/]"
|
|
489
|
+
if "✓" in auth_status or "🌐" in auth_status
|
|
490
|
+
else f"[red]{auth_status}[/]"
|
|
491
|
+
),
|
|
490
492
|
)
|
|
491
493
|
table.add_row("Satellites", ", ".join(caps.satellites) if caps.satellites else "—")
|
|
492
494
|
table.add_row("SAR support", "[green]Yes[/]" if caps.supports_sar else "No")
|
|
@@ -495,9 +497,11 @@ def providers_info(provider_id: str, as_json: bool) -> None:
|
|
|
495
497
|
table.add_row("CQL2 filter", "[green]Yes[/]" if caps.supports_cql2 else "No")
|
|
496
498
|
table.add_row(
|
|
497
499
|
"Resolution",
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
500
|
+
(
|
|
501
|
+
f"{caps.resolution_min_m}m – {caps.resolution_max_m}m"
|
|
502
|
+
if caps.resolution_min_m
|
|
503
|
+
else "Varies"
|
|
504
|
+
),
|
|
501
505
|
)
|
|
502
506
|
table.add_row("Regions", ", ".join(caps.regions) if caps.regions else "global")
|
|
503
507
|
table.add_row("Endpoint", caps.endpoint_url or "—")
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pygeofetch monitor — real CLI entry point for Feature 4 (Automated
|
|
3
|
+
Monitoring).
|
|
4
|
+
|
|
5
|
+
HONEST SCOPE, stated plainly rather than implied: this command wires
|
|
6
|
+
together the real, tested pieces already built --
|
|
7
|
+
pygeofetch.core.state.ProjectState and
|
|
8
|
+
pygeofetch.core.monitor.plan_monitoring_run -- to do real search and
|
|
9
|
+
real incremental planning (what's new, what pairs are needed). It
|
|
10
|
+
does NOT itself call download, coregistration, interferogram
|
|
11
|
+
formation, or invert_weighted() end to end here; those are the
|
|
12
|
+
existing, separately-tested real pipeline stages, and wiring all of
|
|
13
|
+
them into one CLI command is a real, substantial integration task of
|
|
14
|
+
its own that deserves the same direct verification already given to
|
|
15
|
+
each individual piece, not a rushed final assembly. What this command
|
|
16
|
+
does today: real search, real state-aware new-date detection, real
|
|
17
|
+
incremental pair planning, real persistence of results — genuinely
|
|
18
|
+
useful on its own for seeing what a scheduled run would actually do,
|
|
19
|
+
and the concrete foundation the remaining wiring builds on next.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import logging
|
|
25
|
+
|
|
26
|
+
import click
|
|
27
|
+
from rich.console import Console
|
|
28
|
+
|
|
29
|
+
from pygeofetch.core.monitor import plan_monitoring_run
|
|
30
|
+
from pygeofetch.core.state import ProjectState, RunSummary
|
|
31
|
+
|
|
32
|
+
console = Console()
|
|
33
|
+
logger = logging.getLogger("pygeofetch.cli.monitor")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@click.group(
|
|
37
|
+
name="monitor", help="Automated, incremental InSAR monitoring (Feature 4)."
|
|
38
|
+
)
|
|
39
|
+
def monitor():
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@monitor.command(
|
|
44
|
+
name="run",
|
|
45
|
+
help="Run one real monitoring cycle: search for new scenes, plan incremental processing.",
|
|
46
|
+
)
|
|
47
|
+
@click.option(
|
|
48
|
+
"--state-db",
|
|
49
|
+
required=True,
|
|
50
|
+
type=click.Path(),
|
|
51
|
+
help="Path to this project's real state database.",
|
|
52
|
+
)
|
|
53
|
+
@click.option(
|
|
54
|
+
"--bbox",
|
|
55
|
+
required=True,
|
|
56
|
+
type=str,
|
|
57
|
+
help="Real AOI bounding box, 'minlon,minlat,maxlon,maxlat'.",
|
|
58
|
+
)
|
|
59
|
+
@click.option(
|
|
60
|
+
"--satellites",
|
|
61
|
+
default="Sentinel-1A,Sentinel-1B",
|
|
62
|
+
help="Comma-separated real satellite names.",
|
|
63
|
+
)
|
|
64
|
+
@click.option(
|
|
65
|
+
"--n-neighbors",
|
|
66
|
+
default=3,
|
|
67
|
+
type=int,
|
|
68
|
+
help="Real neighbors each new date connects to (spec default: 3).",
|
|
69
|
+
)
|
|
70
|
+
@click.option(
|
|
71
|
+
"--cron-daily",
|
|
72
|
+
is_flag=True,
|
|
73
|
+
help="Marker flag for scheduled invocation (real cron job); does not change behavior, documents intent in logs.",
|
|
74
|
+
)
|
|
75
|
+
@click.pass_context
|
|
76
|
+
def monitor_run(ctx, state_db, bbox, satellites, n_neighbors, cron_daily):
|
|
77
|
+
client = ctx.obj.get("client") if ctx.obj else None
|
|
78
|
+
if client is None:
|
|
79
|
+
from pygeofetch import PyGeoFetch
|
|
80
|
+
|
|
81
|
+
client = PyGeoFetch()
|
|
82
|
+
|
|
83
|
+
state = ProjectState(state_db)
|
|
84
|
+
|
|
85
|
+
minlon, minlat, maxlon, maxlat = (float(x) for x in bbox.split(","))
|
|
86
|
+
sat_list = [s.strip() for s in satellites.split(",")]
|
|
87
|
+
|
|
88
|
+
from pygeofetch.models.search_query import SearchQuery
|
|
89
|
+
|
|
90
|
+
query = SearchQuery(
|
|
91
|
+
bbox=(minlon, minlat, maxlon, maxlat),
|
|
92
|
+
satellites=sat_list,
|
|
93
|
+
product_type="SLC",
|
|
94
|
+
)
|
|
95
|
+
if state.last_download_timestamp:
|
|
96
|
+
query.start_date = state.last_download_timestamp
|
|
97
|
+
|
|
98
|
+
console.print(
|
|
99
|
+
f"[bold]pygeofetch monitor[/bold] — {'scheduled' if cron_daily else 'manual'} run"
|
|
100
|
+
)
|
|
101
|
+
console.print(f"State: {state_db}")
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
results = client.search(query)
|
|
105
|
+
except Exception as exc:
|
|
106
|
+
state.record_run(RunSummary(status="failed", detail=f"search failed: {exc}"))
|
|
107
|
+
console.print(f"[red]Search failed:[/red] {exc}")
|
|
108
|
+
raise
|
|
109
|
+
|
|
110
|
+
available_dates = sorted(
|
|
111
|
+
{str(r.datetime)[:10] for r in results if getattr(r, "datetime", None)}
|
|
112
|
+
)
|
|
113
|
+
plan = plan_monitoring_run(state, available_dates, n_neighbors=n_neighbors)
|
|
114
|
+
|
|
115
|
+
console.print(plan.message)
|
|
116
|
+
if plan.new_dates:
|
|
117
|
+
console.print(f" New dates: {plan.new_dates}")
|
|
118
|
+
console.print(f" New pairs needed: {len(plan.new_pairs)}")
|
|
119
|
+
|
|
120
|
+
state.record_run(
|
|
121
|
+
RunSummary(
|
|
122
|
+
status="success" if plan.network_changed else "no_new_scenes",
|
|
123
|
+
n_new_scenes=len(plan.new_dates),
|
|
124
|
+
detail=plan.message,
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return plan
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@monitor.command(name="history", help="Show real run history for a monitoring project.")
|
|
132
|
+
@click.option("--state-db", required=True, type=click.Path(exists=True))
|
|
133
|
+
@click.option("--limit", default=20, type=int)
|
|
134
|
+
def monitor_history(state_db, limit):
|
|
135
|
+
state = ProjectState(state_db)
|
|
136
|
+
runs = state.run_history(limit=limit)
|
|
137
|
+
if not runs:
|
|
138
|
+
console.print("No real runs recorded yet.")
|
|
139
|
+
return
|
|
140
|
+
for run in runs:
|
|
141
|
+
console.print(f"[{run['status']}] {run['started_at']} — {run['detail']}")
|
|
@@ -67,7 +67,7 @@ def search() -> None:
|
|
|
67
67
|
@click.option(
|
|
68
68
|
"--polarisation",
|
|
69
69
|
default=None,
|
|
70
|
-
help=
|
|
70
|
+
help="SAR polarisation e.g. VV, VH, HH, HV. Same Copernicus-only caveat as --product-type.",
|
|
71
71
|
)
|
|
72
72
|
@click.option("--max-results", "-n", default=100, show_default=True)
|
|
73
73
|
@click.option(
|
|
@@ -447,9 +447,11 @@ class AuthManager:
|
|
|
447
447
|
{
|
|
448
448
|
"provider": provider,
|
|
449
449
|
"has_session": session is not None and session.is_valid,
|
|
450
|
-
"session_expires":
|
|
451
|
-
|
|
452
|
-
|
|
450
|
+
"session_expires": (
|
|
451
|
+
session.expires_at.isoformat()
|
|
452
|
+
if session and session.expires_at
|
|
453
|
+
else None
|
|
454
|
+
),
|
|
453
455
|
}
|
|
454
456
|
)
|
|
455
457
|
return result
|
|
@@ -25,7 +25,7 @@ import hashlib
|
|
|
25
25
|
import time
|
|
26
26
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
27
27
|
from pathlib import Path
|
|
28
|
-
from typing import TYPE_CHECKING, Any, Callable, Dict
|
|
28
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, List, cast
|
|
29
29
|
|
|
30
30
|
from pygeofetch.core.logging import DownloadProgress, get_logger
|
|
31
31
|
from pygeofetch.models.download_task import (
|
|
@@ -365,16 +365,19 @@ class AdaptiveDownloader:
|
|
|
365
365
|
sum(1 for r in results if r and r.success)
|
|
366
366
|
sum(1 for r in results if r and not r.success)
|
|
367
367
|
dp.__exit__(None, None, None)
|
|
368
|
-
assert len(results) == len(
|
|
369
|
-
|
|
370
|
-
)
|
|
368
|
+
assert len(results) == len(
|
|
369
|
+
data_list
|
|
370
|
+
), f"BUG: result count {len(results)} != input count {len(data_list)}"
|
|
371
371
|
sum(
|
|
372
372
|
r.bytes_downloaded / (1024 * 1024)
|
|
373
373
|
for r in results
|
|
374
374
|
if r and r.success and r.bytes_downloaded
|
|
375
375
|
)
|
|
376
376
|
# summary printed by DownloadProgress footer
|
|
377
|
-
|
|
377
|
+
# The loop above has already replaced every None slot with a
|
|
378
|
+
# real DownloadResult (failure placeholder), so this is
|
|
379
|
+
# genuinely list[DownloadResult] at this point.
|
|
380
|
+
return cast(List[DownloadResult], results)
|
|
378
381
|
|
|
379
382
|
def _get_provider(self, provider_id: str) -> Any:
|
|
380
383
|
"""Get a provider instance with a fresh authenticated session."""
|
|
@@ -889,7 +892,7 @@ class AdaptiveDownloader:
|
|
|
889
892
|
from pygeofetch.processor import SpectralIndex
|
|
890
893
|
except ImportError as exc:
|
|
891
894
|
raise ImportError(
|
|
892
|
-
f
|
|
895
|
+
f"{index_name} requires the processor extra: "
|
|
893
896
|
f'pip install "pygeofetch[processor]"'
|
|
894
897
|
) from exc
|
|
895
898
|
|
|
@@ -904,7 +907,10 @@ class AdaptiveDownloader:
|
|
|
904
907
|
(
|
|
905
908
|
p
|
|
906
909
|
for p in result.output_paths
|
|
907
|
-
if any(
|
|
910
|
+
if any(
|
|
911
|
+
pat in p.name or pat.lower() in p.name.lower()
|
|
912
|
+
for pat in patterns
|
|
913
|
+
)
|
|
908
914
|
),
|
|
909
915
|
None,
|
|
910
916
|
)
|
|
@@ -914,8 +920,15 @@ class AdaptiveDownloader:
|
|
|
914
920
|
f"{band_name} (looked for {patterns} in filenames)"
|
|
915
921
|
)
|
|
916
922
|
with rasterio.open(path) as src:
|
|
917
|
-
arr =
|
|
918
|
-
|
|
923
|
+
arr = (
|
|
924
|
+
src.read(
|
|
925
|
+
1,
|
|
926
|
+
out_shape=ref_shape,
|
|
927
|
+
resampling=rasterio.enums.Resampling.bilinear,
|
|
928
|
+
)
|
|
929
|
+
if ref_shape
|
|
930
|
+
else src.read(1)
|
|
931
|
+
)
|
|
919
932
|
if ref_shape is None:
|
|
920
933
|
ref_shape = arr.shape
|
|
921
934
|
profile = src.profile.copy()
|
|
@@ -929,6 +942,12 @@ class AdaptiveDownloader:
|
|
|
929
942
|
arr = np.where(arr == src.nodata, np.nan, arr)
|
|
930
943
|
band_arrays[band_name] = arr
|
|
931
944
|
|
|
945
|
+
assert ref_shape is not None and profile is not None, (
|
|
946
|
+
f"{index_name}: required_bands was empty or the loop never "
|
|
947
|
+
f"ran -- ref_shape/profile must be set by the first band "
|
|
948
|
+
f"processed above."
|
|
949
|
+
)
|
|
950
|
+
|
|
932
951
|
si = SpectralIndex(prefer_spyndex=False)
|
|
933
952
|
index_arr = np.asarray(si.compute(index_name, **band_arrays), dtype=np.float32)
|
|
934
953
|
# Any band being NaN at a pixel must make the index NaN there too,
|
|
@@ -950,7 +969,6 @@ class AdaptiveDownloader:
|
|
|
950
969
|
logger.info(f"{index_name} computed → {out_path}")
|
|
951
970
|
return result
|
|
952
971
|
|
|
953
|
-
|
|
954
972
|
def _find_existing_download(
|
|
955
973
|
self, data: "SatelliteData", destination: Path
|
|
956
974
|
) -> Path | None:
|
|
@@ -1230,7 +1248,8 @@ class AdaptiveDownloader:
|
|
|
1230
1248
|
"%s has no direct CRS/transform, using its %d "
|
|
1231
1249
|
"ground control points for reprojection instead "
|
|
1232
1250
|
"(common for raw SAR/GRD delivery).",
|
|
1233
|
-
source_path.name,
|
|
1251
|
+
source_path.name,
|
|
1252
|
+
len(src_gcps),
|
|
1234
1253
|
)
|
|
1235
1254
|
elif src_crs is None:
|
|
1236
1255
|
raise RuntimeError(
|
|
@@ -1301,7 +1320,6 @@ class AdaptiveDownloader:
|
|
|
1301
1320
|
)
|
|
1302
1321
|
raise RuntimeError(msg)
|
|
1303
1322
|
|
|
1304
|
-
|
|
1305
1323
|
def _backoff(self, attempt: int, options: DownloadOptions) -> float:
|
|
1306
1324
|
"""Calculate retry delay based on strategy."""
|
|
1307
1325
|
import random
|
|
@@ -1334,4 +1352,4 @@ class AdaptiveDownloader:
|
|
|
1334
1352
|
@property
|
|
1335
1353
|
def _result_total_size_mb(self) -> float:
|
|
1336
1354
|
"""Property used by result objects."""
|
|
1337
|
-
return 0.0
|
|
1355
|
+
return 0.0
|
|
@@ -484,4 +484,4 @@ class PyGeoFetch:
|
|
|
484
484
|
|
|
485
485
|
def __repr__(self) -> str:
|
|
486
486
|
authed = [item["provider"] for item in self.auth.list()]
|
|
487
|
-
return f"PyGeoFetch(authenticated={authed}, cache_entries={len(self.searcher.cache._cache)})"
|
|
487
|
+
return f"PyGeoFetch(authenticated={authed}, cache_entries={len(self.searcher.cache._cache)})"
|