PyGeoFetch 2.6.2.7__tar.gz → 2.6.2.8__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.2.7 → pygeofetch-2.6.2.8}/CHANGELOG.md +7 -7
- {pygeofetch-2.6.2.7/pygeofetch.egg-info → pygeofetch-2.6.2.8}/PKG-INFO +9 -9
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/README.md +8 -8
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/__init__.py +1 -1
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/auto_fallback.py +18 -2
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/meta.yaml +1 -1
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8/pygeofetch.egg-info}/PKG-INFO +9 -9
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch.egg-info/SOURCES.txt +1 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pyproject.toml +1 -1
- pygeofetch-2.6.2.8/tests/test_auto_fallback_fixed.py +226 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/LICENSE +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/MANIFEST.in +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/cli/__init__.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/cli/auth_commands.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/cli/config_commands.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/cli/download_commands.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/cli/index_commands.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/cli/main.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/cli/monitor_commands.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/cli/multisensor_commands.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/cli/optical_commands.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/cli/pipeline_process_commands.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/cli/postprocess_commands.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/cli/preprocess_commands.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/cli/sar_commands.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/cli/search_commands.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/config/__init__.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/config/defaults.yaml +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/config/settings.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/core/__init__.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/core/authenticator.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/core/cache_manager.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/core/data_organizer.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/core/downloader.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/core/engine.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/core/logging.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/core/monitor.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/core/orbits.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/core/scheduler.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/core/searcher.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/core/state.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/geotech/__init__.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/geotech/risk_mapper.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/__init__.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/advanced_safeguards.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/analysis.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/annotation.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/atmosphere.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/coregister.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/deburst.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/esd.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/extraction.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/flatearth.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/geolocation.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/gpu.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/interferogram.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/ionosphere.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/offset_geometry.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/offset_tracking.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/preflight.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/provenance.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/ps_selection.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/splitspectrum_diagnostic.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/stack_selection.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/synthetic.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/timeseries.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/unwrap.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/validate.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/visualize.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/insar/workflow.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/models/__init__.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/models/download_task.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/models/satellite_data.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/models/search_query.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/models/user_auth.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/multisensor/_init.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/multisensor/dem_differencing.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/multisensor/flood_mapping.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/multisensor/insar_optical_fusion.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/multisensor/terrain_corrected_change.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/multisensor/vegetation_disturbance.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/optical/__init__.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/optical/offset_tracking.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/processing/__init__.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/processing/base.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/processing/batch.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/processing/indices.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/processing/pipeline.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/processing/postprocessor.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/processing/preprocessor.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/processing/sar.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/processing/uq_exporter.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/processor/__init__.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/processor/indices.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/processor/landsat.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/processor/loader.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/processor/stacker.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/processor/timeseries.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/__init__.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/airbus_oneatlas.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/alaska_satellite_facility.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/aws_earth.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/base.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/copernicus.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/copernicus_nodes.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/digitalglobe.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/earth_explorer_additional.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/element84.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/eodag_provider.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/esa_scihub.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/geoserver_generic.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/google_earth_engine.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/inpe_cbers.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/isro_bhuvan.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/jaxa_earth.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/maxar_gbdx.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/nasa_earthdata.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/nasa_earthdata_cloud.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/noaa_big_data.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/opentopography.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/planet.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/planetary_computer.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/sentinel_hub.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/terrabotics.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/providers/usgs.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/sar/__init__.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/sar/_native.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/sar/_ost.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/sar/_sarxarray.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/sar/extraction.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/sar/math.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/sar/pipelines.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/sar/processor.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/stac_compute/__init__.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/stac_compute/lightweight_prefetch.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/utils/__init__.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/utils/file_utils.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/utils/gcp_recovery.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/utils/geo_utils.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/utils/logging_setup.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/utils/retry_handler.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/utils/sar_math.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/utils/validators.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/validation/__init__.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/validation/optical_validator.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/viz/__init__.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/viz/map.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/viz/plot.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch.egg-info/dependency_links.txt +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch.egg-info/entry_points.txt +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch.egg-info/not-zip-safe +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch.egg-info/requires.txt +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch.egg-info/top_level.txt +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/setup.cfg +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_advanced_safeguards.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_airbus_oneatlas.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_alaska_satellite_facility.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_atmospheric_circular_regression.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_auto_fallback.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_build_sbas_network-new.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_build_sbas_network.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_burst_metadata.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_burst_processing_integration.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_burst_sync_large_gap.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_burst_sync_prescreening.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_burst_synchronization.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_circuit_breaker_wiring.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_cli_optical_validation.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_copernicus_nodes.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_coregister_integration.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_coregister_upgrades (1).py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_coregister_upgrades.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_credential_encryption.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_data_organizer.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_dem_differencing_pipeline.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_digitalglobe.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_earth_explorer_additional.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_era5_atmospheric_correction.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_esa_scihub.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_extraction.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_flat_earth_phase.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_flood_mapping_pipeline.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_gcp_reprojection.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_geology_indices_and_rvi_fix.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_geoserver_generic.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_google_earth_engine.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_inpe_cbers.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_insar.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_insar_optical_fusion_pipeline.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_ionosphere.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_isro_bhuvan.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_jaxa_earth.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_lightweight_prefetch.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_map_preview_consolidation.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_models.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_monitor.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_nasa_earthdata_cloud_s3_fix.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_nasa_earthdata_public_search.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_noaa_big_data.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_offset_tracking.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_opentopography_pointcloud_usgs.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_optical_offset_tracking.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_optical_validation_wiring.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_optical_validator.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_optical_validator_stac.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_pgv_integration_contract.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_pipeline_process_export.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_planet_asset_type_fix.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_preflight_gate.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_preflight_spatial_baseline.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_provider_geometry_audit.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_provider_scene_disambiguation.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_providers.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_ps_selection.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_raster_collocation_integration.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_reference_pixel_and_despike.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_risk_mapper.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_risk_mapping.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_sar_pipelines.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_screen_stack_orchestration.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_search_download.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_sentinel_hub_catalog_fix.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_sign_convention.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_splitspectrum_diagnostic.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_stack_selection_consolidation.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_state.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_strain_and_fusion.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_terrain_corrected_change_pipeline.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_topo_r2_bug.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_uq_exporter.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_utils.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_vegetation_disturbance_pipeline.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_viz_plot.py +0 -0
- {pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/tests/test_wls_inversion.py +0 -0
|
@@ -20,7 +20,7 @@ date, and marked accordingly rather than filled in with invented
|
|
|
20
20
|
bullet points.
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
## [Unreleased] — v2.6.2.
|
|
23
|
+
## [Unreleased] — v2.6.2.8.4
|
|
24
24
|
|
|
25
25
|
```{note}
|
|
26
26
|
Not yet published to PyPI. This entry documents work completed and
|
|
@@ -130,7 +130,7 @@ audit and remain open for a follow-up pass.
|
|
|
130
130
|
|
|
131
131
|
---
|
|
132
132
|
|
|
133
|
-
## [2.6.2.
|
|
133
|
+
## [2.6.2.8.4] — 2026-08-25 (latest)
|
|
134
134
|
|
|
135
135
|
Confirmed via direct fetch of the current PyPI listing.
|
|
136
136
|
|
|
@@ -149,7 +149,7 @@ Confirmed via direct fetch of the current PyPI listing.
|
|
|
149
149
|
- `SearchQuery.sort_ascending: bool` — earlier examples showed a `sort_order: "asc"/"desc"` string field that doesn't exist on the model (the CLI's `--sort-order` flag does convert to this internally, so CLI docs were unaffected)
|
|
150
150
|
- `DownloadOptions.post_process` requires a list of `PostProcessAction` objects — passing plain strings raises a real `pydantic.ValidationError`
|
|
151
151
|
|
|
152
|
-
## [2.6.2.
|
|
152
|
+
## [2.6.2.8] — 2026-08-24
|
|
153
153
|
Real, verified release. Detailed notes not independently recovered from PyPI's release-history page (no distinct per-version notes exposed for this release specifically).
|
|
154
154
|
|
|
155
155
|
## [2.6.1] — 2026-08-22
|
|
@@ -261,9 +261,9 @@ recovered.
|
|
|
261
261
|
|
|
262
262
|
```{note}
|
|
263
263
|
The previous version of this changelog had an entry labeled
|
|
264
|
-
**"2.6.2.
|
|
264
|
+
**"2.6.2.8.4 — 2026-07-12"** describing 22+ providers, Sentinel-1C/1D
|
|
265
265
|
support, SLC search with provider routing, precise orbit file
|
|
266
|
-
management, 17 spectral indices, and various bug fixes. **2.6.2.
|
|
266
|
+
management, 17 spectral indices, and various bug fixes. **2.6.2.8.4 was
|
|
267
267
|
only ever released once, on 2026-08-25** — it was never released on
|
|
268
268
|
2026-07-12. The date matches this real release, **1.0.8**, exactly.
|
|
269
269
|
That content is reproduced below under the version it actually
|
|
@@ -406,8 +406,8 @@ The version of this file uploaded for this update had **5 entries
|
|
|
406
406
|
covering 5 dates**. PyPI's real release history has **54 releases**.
|
|
407
407
|
Specific issues found and fixed:
|
|
408
408
|
|
|
409
|
-
1. **Two different entries were both labeled `2.6.2.
|
|
410
|
-
2026-07-29 and 2026-07-12. **2.6.2.
|
|
409
|
+
1. **Two different entries were both labeled `2.6.2.8.4`**, dated
|
|
410
|
+
2026-07-29 and 2026-07-12. **2.6.2.8.4 was only ever released once**,
|
|
411
411
|
on 2026-08-25 (confirmed directly from the live PyPI listing).
|
|
412
412
|
2. **The 2026-07-12 entry's date exactly matches a real release**
|
|
413
413
|
— version **1.0.8** — and its content (22+ providers, Sentinel-1C/D,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pygeofetch
|
|
3
|
-
Version: 2.6.2.
|
|
3
|
+
Version: 2.6.2.8
|
|
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
|
|
@@ -167,13 +167,13 @@ Where pygeofetch goes further than most data-access libraries is in its **InSAR
|
|
|
167
167
|
|
|
168
168
|
| | |
|
|
169
169
|
|---|---|
|
|
170
|
-
|
|
|
171
|
-
|
|
|
172
|
-
|
|
|
173
|
-
|
|
|
174
|
-
|
|
|
175
|
-
|
|
|
176
|
-
|
|
|
170
|
+
| **Authenticated access** | 22 verified providers; credentials stored via a Fernet-encrypted local file by default, or the system keyring (Keychain / Credential Manager / Secret Service) as an opt-in alternative |
|
|
171
|
+
| **Federated search** | One query across all providers → STAC 1.0 GeoJSON / GeoParquet / CSV, with real footprint geometry where the provider supplies it |
|
|
172
|
+
| **Resilient downloads** | Parallel, resumable, checksum-verified, band-selective, atomic writes |
|
|
173
|
+
| **Preprocessing** | Atmospheric correction, cloud masking, reprojection, resampling, pan-sharpening, mosaicking |
|
|
174
|
+
| **17 spectral indices** | NDVI, EVI, SAVI, NDWI, MNDWI, NDBI, LST, Albedo, dNBR, GLCM texture, and more |
|
|
175
|
+
| **Verified InSAR chain** | Coregistration → phase removal → atmospheric/ionospheric correction → unwrapping → SBAS inversion |
|
|
176
|
+
| **YAML pipelines** | Chainable, schedulable, repeatable workflows with full run history |
|
|
177
177
|
|
|
178
178
|
---
|
|
179
179
|
|
|
@@ -369,7 +369,7 @@ If you use PyGeoFetch in your research or operational work, please cite it using
|
|
|
369
369
|
title = {PyGeoFetch: A Unified Python Framework for Multi-Provider Satellite Data Acquisition, Pre-download Quality Control, and Geospatial Processing},
|
|
370
370
|
year = {2026},
|
|
371
371
|
publisher = {Zenodo},
|
|
372
|
-
version = {2.6.2.
|
|
372
|
+
version = {2.6.2.8.1},
|
|
373
373
|
doi = {10.5281/zenodo.22087230},
|
|
374
374
|
url = {https://doi.org/10.5281/zenodo.22087230}
|
|
375
375
|
}
|
|
@@ -27,13 +27,13 @@ Where pygeofetch goes further than most data-access libraries is in its **InSAR
|
|
|
27
27
|
|
|
28
28
|
| | |
|
|
29
29
|
|---|---|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
30
|
+
| **Authenticated access** | 22 verified providers; credentials stored via a Fernet-encrypted local file by default, or the system keyring (Keychain / Credential Manager / Secret Service) as an opt-in alternative |
|
|
31
|
+
| **Federated search** | One query across all providers → STAC 1.0 GeoJSON / GeoParquet / CSV, with real footprint geometry where the provider supplies it |
|
|
32
|
+
| **Resilient downloads** | Parallel, resumable, checksum-verified, band-selective, atomic writes |
|
|
33
|
+
| **Preprocessing** | Atmospheric correction, cloud masking, reprojection, resampling, pan-sharpening, mosaicking |
|
|
34
|
+
| **17 spectral indices** | NDVI, EVI, SAVI, NDWI, MNDWI, NDBI, LST, Albedo, dNBR, GLCM texture, and more |
|
|
35
|
+
| **Verified InSAR chain** | Coregistration → phase removal → atmospheric/ionospheric correction → unwrapping → SBAS inversion |
|
|
36
|
+
| **YAML pipelines** | Chainable, schedulable, repeatable workflows with full run history |
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
@@ -229,7 +229,7 @@ If you use PyGeoFetch in your research or operational work, please cite it using
|
|
|
229
229
|
title = {PyGeoFetch: A Unified Python Framework for Multi-Provider Satellite Data Acquisition, Pre-download Quality Control, and Geospatial Processing},
|
|
230
230
|
year = {2026},
|
|
231
231
|
publisher = {Zenodo},
|
|
232
|
-
version = {2.6.2.
|
|
232
|
+
version = {2.6.2.8.1},
|
|
233
233
|
doi = {10.5281/zenodo.22087230},
|
|
234
234
|
url = {https://doi.org/10.5281/zenodo.22087230}
|
|
235
235
|
}
|
|
@@ -293,7 +293,23 @@ def run_multi_modal_deformation(
|
|
|
293
293
|
# received a scalar `pairs` list, not a per-pixel coherence raster
|
|
294
294
|
# -- build one the same real, honest way the multi-sensor
|
|
295
295
|
# notebooks do (mean across the real per-pair coherence arrays).
|
|
296
|
-
|
|
296
|
+
#
|
|
297
|
+
# Real, necessary edge case, confirmed directly: when `pairs` is
|
|
298
|
+
# genuinely empty (a total InSAR failure with zero usable pairs at
|
|
299
|
+
# all, not just low coherence), `np.mean([], axis=0)` silently
|
|
300
|
+
# degrades to a scalar `nan` with shape `()` rather than raising --
|
|
301
|
+
# which then fails the real shape check inside fuse_insar_optical
|
|
302
|
+
# with a confusing "all input arrays must share the same real
|
|
303
|
+
# shape" error that gives no hint the real cause was an empty
|
|
304
|
+
# pairs list. Build a properly-shaped all-NaN raster explicitly in
|
|
305
|
+
# this case instead: a real, honest signal of "zero real coherence
|
|
306
|
+
# information available," matching `velocity`'s own real shape so
|
|
307
|
+
# fuse_insar_optical's fusion logic runs correctly and correctly
|
|
308
|
+
# concludes optical-only, rather than crashing before it can.
|
|
309
|
+
if pairs:
|
|
310
|
+
mean_coherence_raster = np.mean([np.asarray(p.coherence) for p in pairs], axis=0)
|
|
311
|
+
else:
|
|
312
|
+
mean_coherence_raster = np.full(np.asarray(velocity).shape, np.nan)
|
|
297
313
|
|
|
298
314
|
fused = fuse_insar_optical(
|
|
299
315
|
insar_velocity=velocity,
|
|
@@ -329,4 +345,4 @@ def run_multi_modal_deformation(
|
|
|
329
345
|
"n_optical_windows_reliable": int(offset_result.reliable.sum()),
|
|
330
346
|
"n_optical_windows_total": int(offset_result.reliable.size),
|
|
331
347
|
},
|
|
332
|
-
)
|
|
348
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pygeofetch
|
|
3
|
-
Version: 2.6.2.
|
|
3
|
+
Version: 2.6.2.8
|
|
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
|
|
@@ -167,13 +167,13 @@ Where pygeofetch goes further than most data-access libraries is in its **InSAR
|
|
|
167
167
|
|
|
168
168
|
| | |
|
|
169
169
|
|---|---|
|
|
170
|
-
|
|
|
171
|
-
|
|
|
172
|
-
|
|
|
173
|
-
|
|
|
174
|
-
|
|
|
175
|
-
|
|
|
176
|
-
|
|
|
170
|
+
| **Authenticated access** | 22 verified providers; credentials stored via a Fernet-encrypted local file by default, or the system keyring (Keychain / Credential Manager / Secret Service) as an opt-in alternative |
|
|
171
|
+
| **Federated search** | One query across all providers → STAC 1.0 GeoJSON / GeoParquet / CSV, with real footprint geometry where the provider supplies it |
|
|
172
|
+
| **Resilient downloads** | Parallel, resumable, checksum-verified, band-selective, atomic writes |
|
|
173
|
+
| **Preprocessing** | Atmospheric correction, cloud masking, reprojection, resampling, pan-sharpening, mosaicking |
|
|
174
|
+
| **17 spectral indices** | NDVI, EVI, SAVI, NDWI, MNDWI, NDBI, LST, Albedo, dNBR, GLCM texture, and more |
|
|
175
|
+
| **Verified InSAR chain** | Coregistration → phase removal → atmospheric/ionospheric correction → unwrapping → SBAS inversion |
|
|
176
|
+
| **YAML pipelines** | Chainable, schedulable, repeatable workflows with full run history |
|
|
177
177
|
|
|
178
178
|
---
|
|
179
179
|
|
|
@@ -369,7 +369,7 @@ If you use PyGeoFetch in your research or operational work, please cite it using
|
|
|
369
369
|
title = {PyGeoFetch: A Unified Python Framework for Multi-Provider Satellite Data Acquisition, Pre-download Quality Control, and Geospatial Processing},
|
|
370
370
|
year = {2026},
|
|
371
371
|
publisher = {Zenodo},
|
|
372
|
-
version = {2.6.2.
|
|
372
|
+
version = {2.6.2.8.1},
|
|
373
373
|
doi = {10.5281/zenodo.22087230},
|
|
374
374
|
url = {https://doi.org/10.5281/zenodo.22087230}
|
|
375
375
|
}
|
|
@@ -155,6 +155,7 @@ tests/test_airbus_oneatlas.py
|
|
|
155
155
|
tests/test_alaska_satellite_facility.py
|
|
156
156
|
tests/test_atmospheric_circular_regression.py
|
|
157
157
|
tests/test_auto_fallback.py
|
|
158
|
+
tests/test_auto_fallback_fixed.py
|
|
158
159
|
tests/test_build_sbas_network-new.py
|
|
159
160
|
tests/test_build_sbas_network.py
|
|
160
161
|
tests/test_burst_metadata.py
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for pygeofetch.insar.auto_fallback.run_multi_modal_deformation.
|
|
3
|
+
|
|
4
|
+
Uses real synthetic rasters and a minimal real-shaped pair stand-in
|
|
5
|
+
(anything with a real `.coherence` array, matching what
|
|
6
|
+
InterferogramGenerator.process_pair() actually returns) rather than
|
|
7
|
+
mocks, so the real quality-metric computation and real fusion path
|
|
8
|
+
both get genuinely exercised.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
import pytest
|
|
17
|
+
import rasterio
|
|
18
|
+
from rasterio.transform import from_origin
|
|
19
|
+
|
|
20
|
+
from pygeofetch.insar.auto_fallback import (
|
|
21
|
+
SOURCE_INSAR_VALID,
|
|
22
|
+
SOURCE_OPTICAL_FALLBACK,
|
|
23
|
+
_compute_insar_quality_metrics,
|
|
24
|
+
run_multi_modal_deformation,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
REAL_CRS = "EPSG:32633"
|
|
28
|
+
GROUND_ORIGIN = (500000, 4000000)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class FakePair:
|
|
33
|
+
"""Minimal stand-in for a real InterferogramPair result -- only
|
|
34
|
+
needs the real `.coherence` attribute this router actually reads."""
|
|
35
|
+
|
|
36
|
+
coherence: np.ndarray
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _write_raster(path, data, transform):
|
|
40
|
+
profile = {
|
|
41
|
+
"driver": "GTiff",
|
|
42
|
+
"height": data.shape[0],
|
|
43
|
+
"width": data.shape[1],
|
|
44
|
+
"count": 1,
|
|
45
|
+
"dtype": "float32",
|
|
46
|
+
"crs": REAL_CRS,
|
|
47
|
+
"transform": transform,
|
|
48
|
+
}
|
|
49
|
+
with rasterio.open(path, "w", **profile) as dst:
|
|
50
|
+
dst.write(data.astype("float32"), 1)
|
|
51
|
+
return path
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class TestQualityMetrics:
|
|
55
|
+
def test_all_finite_velocity_gives_full_reliable_fraction(self):
|
|
56
|
+
velocity = np.zeros((10, 10))
|
|
57
|
+
pairs = [FakePair(coherence=np.full((10, 10), 0.8))]
|
|
58
|
+
reliable_fraction, mean_coherence = _compute_insar_quality_metrics(
|
|
59
|
+
velocity, pairs
|
|
60
|
+
)
|
|
61
|
+
assert reliable_fraction == 1.0
|
|
62
|
+
assert mean_coherence == pytest.approx(0.8)
|
|
63
|
+
|
|
64
|
+
def test_nan_pixels_correctly_excluded_from_reliable_fraction(self):
|
|
65
|
+
velocity = np.full((10, 10), 1.0)
|
|
66
|
+
velocity[:5, :] = np.nan # real, half the pixels genuinely underdetermined
|
|
67
|
+
pairs = [FakePair(coherence=np.full((10, 10), 0.5))]
|
|
68
|
+
reliable_fraction, _ = _compute_insar_quality_metrics(velocity, pairs)
|
|
69
|
+
assert reliable_fraction == pytest.approx(0.5)
|
|
70
|
+
|
|
71
|
+
def test_mean_coherence_averages_across_real_pairs(self):
|
|
72
|
+
velocity = np.zeros((5, 5))
|
|
73
|
+
pairs = [
|
|
74
|
+
FakePair(coherence=np.full((5, 5), 0.9)),
|
|
75
|
+
FakePair(coherence=np.full((5, 5), 0.3)),
|
|
76
|
+
]
|
|
77
|
+
_, mean_coherence = _compute_insar_quality_metrics(velocity, pairs)
|
|
78
|
+
assert mean_coherence == pytest.approx(0.6)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class TestHighQualityInsarSkipsOptical:
|
|
82
|
+
def test_fallback_not_triggered_when_insar_is_good(self):
|
|
83
|
+
velocity = np.full((20, 20), -5.0) # fully valid, no NaN
|
|
84
|
+
pairs = [FakePair(coherence=np.full((20, 20), 0.7))] # real, high coherence
|
|
85
|
+
|
|
86
|
+
result = run_multi_modal_deformation(velocity, pairs)
|
|
87
|
+
|
|
88
|
+
assert result.success
|
|
89
|
+
assert not result.used_optical_fallback
|
|
90
|
+
assert result.insar_reliable_fraction == 1.0
|
|
91
|
+
assert result.insar_mean_coherence == pytest.approx(0.7)
|
|
92
|
+
assert (result.processing_source_mask == SOURCE_INSAR_VALID).all()
|
|
93
|
+
|
|
94
|
+
def test_optical_inputs_not_required_when_insar_is_good(self):
|
|
95
|
+
"""Real, deliberate efficiency check: no optical_reference_path
|
|
96
|
+
needed at all when InSAR already clears both thresholds."""
|
|
97
|
+
velocity = np.full((10, 10), -2.0)
|
|
98
|
+
pairs = [FakePair(coherence=np.full((10, 10), 0.9))]
|
|
99
|
+
result = run_multi_modal_deformation(velocity, pairs) # no optical args passed
|
|
100
|
+
assert result.success
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class TestTotalInsarFailureWithEmptyPairs:
|
|
104
|
+
"""Real, previously-crashing edge case: a total InSAR failure where
|
|
105
|
+
literally zero pairs survived (not just low-coherence ones) --
|
|
106
|
+
confirmed to have raised a confusing shape-mismatch error rather
|
|
107
|
+
than the correct, honest optical-only result, since
|
|
108
|
+
np.mean([], axis=0) silently degrades to a scalar NaN instead of a
|
|
109
|
+
properly-shaped array."""
|
|
110
|
+
|
|
111
|
+
def test_empty_pairs_with_nan_velocity_falls_back_cleanly(self, tmp_path):
|
|
112
|
+
rng = np.random.default_rng(3)
|
|
113
|
+
shape = (20, 20)
|
|
114
|
+
velocity = np.full(shape, np.nan) # real, honest "InSAR produced nothing" placeholder
|
|
115
|
+
transform = from_origin(*GROUND_ORIGIN, 10, 10)
|
|
116
|
+
|
|
117
|
+
ref_path = _write_raster(tmp_path / "ref.tif", rng.normal(1000, 50, (40, 40)), transform)
|
|
118
|
+
sec_path = _write_raster(tmp_path / "sec.tif", rng.normal(1000, 50, (40, 40)), transform)
|
|
119
|
+
|
|
120
|
+
result = run_multi_modal_deformation(
|
|
121
|
+
velocity, pairs=[], # genuinely empty -- not just low-coherence pairs
|
|
122
|
+
optical_reference_path=ref_path, optical_secondary_path=sec_path,
|
|
123
|
+
reference_transform=transform, insar_transform=transform, insar_crs=REAL_CRS,
|
|
124
|
+
window_size=16, step_size=8,
|
|
125
|
+
)
|
|
126
|
+
assert result.success, result.error
|
|
127
|
+
assert result.used_optical_fallback
|
|
128
|
+
assert result.insar_reliable_fraction == 0.0
|
|
129
|
+
assert result.insar_mean_coherence == 0.0
|
|
130
|
+
# Real, correct consequence: with InSAR velocity entirely
|
|
131
|
+
# invalid, every pixel where optical actually has real,
|
|
132
|
+
# windowed coverage must be optical-fallback. Pixels where
|
|
133
|
+
# optical ALSO has no real coverage (a genuine edge effect near
|
|
134
|
+
# the grid border, not a bug -- see fuse_insar_optical's own
|
|
135
|
+
# real handling of "both sources invalid") fall through to the
|
|
136
|
+
# blended/transition label by that function's own existing
|
|
137
|
+
# convention, not a new behavior introduced by this fix.
|
|
138
|
+
has_real_optical_coverage = np.isfinite(result.optical_displacement_magnitude)
|
|
139
|
+
assert (result.processing_source_mask[has_real_optical_coverage] == SOURCE_OPTICAL_FALLBACK).all()
|
|
140
|
+
assert not (result.processing_source_mask == SOURCE_INSAR_VALID).any()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class TestLowQualityInsarTriggersRealFallback:
|
|
144
|
+
def test_low_coherence_triggers_fallback_and_fuses(self, tmp_path):
|
|
145
|
+
rng = np.random.default_rng(1)
|
|
146
|
+
transform = from_origin(*GROUND_ORIGIN, 10, 10)
|
|
147
|
+
|
|
148
|
+
velocity = np.full((20, 20), -10.0)
|
|
149
|
+
pairs = [FakePair(coherence=np.full((20, 20), 0.15))] # real, low coherence
|
|
150
|
+
|
|
151
|
+
ref_data = rng.normal(1000, 100, (40, 40)).astype("float32")
|
|
152
|
+
sec_data = ref_data + rng.normal(0, 5, (40, 40)).astype("float32")
|
|
153
|
+
ref_path = _write_raster(
|
|
154
|
+
tmp_path / "ref.tif", ref_data, from_origin(*GROUND_ORIGIN, 10, 10)
|
|
155
|
+
)
|
|
156
|
+
sec_path = _write_raster(
|
|
157
|
+
tmp_path / "sec.tif", sec_data, from_origin(*GROUND_ORIGIN, 10, 10)
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
with rasterio.open(ref_path) as src:
|
|
161
|
+
reference_transform = src.transform
|
|
162
|
+
|
|
163
|
+
result = run_multi_modal_deformation(
|
|
164
|
+
velocity,
|
|
165
|
+
pairs,
|
|
166
|
+
optical_reference_path=ref_path,
|
|
167
|
+
optical_secondary_path=sec_path,
|
|
168
|
+
reference_transform=reference_transform,
|
|
169
|
+
insar_transform=transform,
|
|
170
|
+
insar_crs=REAL_CRS,
|
|
171
|
+
window_size=16,
|
|
172
|
+
step_size=8,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
assert result.success, result.error
|
|
176
|
+
assert result.used_optical_fallback
|
|
177
|
+
assert result.insar_mean_coherence == pytest.approx(0.15)
|
|
178
|
+
# Real, correct consequence of uniformly low coherence: fully
|
|
179
|
+
# optical-dominant, matching fuse_insar_optical's own real,
|
|
180
|
+
# documented behavior at this coherence level.
|
|
181
|
+
assert (result.processing_source_mask == SOURCE_OPTICAL_FALLBACK).mean() > 0.5
|
|
182
|
+
|
|
183
|
+
def test_missing_optical_inputs_raises_clear_error(self):
|
|
184
|
+
velocity = np.full((10, 10), -5.0)
|
|
185
|
+
pairs = [FakePair(coherence=np.full((10, 10), 0.1))] # real, low coherence
|
|
186
|
+
|
|
187
|
+
with pytest.raises(ValueError, match="optical_reference_path"):
|
|
188
|
+
run_multi_modal_deformation(
|
|
189
|
+
velocity, pairs
|
|
190
|
+
) # no optical args -- must raise
|
|
191
|
+
|
|
192
|
+
def test_or_not_and_low_coverage_alone_triggers_fallback(self, tmp_path):
|
|
193
|
+
"""Real, deliberate OR logic: even with high coherence, low
|
|
194
|
+
real coverage (many NaN pixels) alone must trigger fallback."""
|
|
195
|
+
rng = np.random.default_rng(2)
|
|
196
|
+
velocity = np.full((20, 20), np.nan)
|
|
197
|
+
velocity[:2, :] = -5.0 # only 10% real coverage
|
|
198
|
+
pairs = [FakePair(coherence=np.full((20, 20), 0.9))] # coherence itself is fine
|
|
199
|
+
|
|
200
|
+
ref_path = _write_raster(
|
|
201
|
+
tmp_path / "ref.tif",
|
|
202
|
+
rng.normal(1000, 50, (40, 40)),
|
|
203
|
+
from_origin(*GROUND_ORIGIN, 10, 10),
|
|
204
|
+
)
|
|
205
|
+
sec_path = _write_raster(
|
|
206
|
+
tmp_path / "sec.tif",
|
|
207
|
+
rng.normal(1000, 50, (40, 40)),
|
|
208
|
+
from_origin(*GROUND_ORIGIN, 10, 10),
|
|
209
|
+
)
|
|
210
|
+
with rasterio.open(ref_path) as src:
|
|
211
|
+
reference_transform = src.transform
|
|
212
|
+
|
|
213
|
+
result = run_multi_modal_deformation(
|
|
214
|
+
velocity,
|
|
215
|
+
pairs,
|
|
216
|
+
optical_reference_path=ref_path,
|
|
217
|
+
optical_secondary_path=sec_path,
|
|
218
|
+
reference_transform=reference_transform,
|
|
219
|
+
insar_transform=from_origin(*GROUND_ORIGIN, 10, 10),
|
|
220
|
+
insar_crs=REAL_CRS,
|
|
221
|
+
window_size=16,
|
|
222
|
+
step_size=8,
|
|
223
|
+
)
|
|
224
|
+
assert (
|
|
225
|
+
result.used_optical_fallback
|
|
226
|
+
) # triggered by coverage alone, despite good coherence
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pygeofetch-2.6.2.7 → pygeofetch-2.6.2.8}/pygeofetch/multisensor/terrain_corrected_change.py
RENAMED
|
File without changes
|
|
File without changes
|