PyGeoFetch 2.6.2.9__tar.gz → 2.6.3.0__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.9 → pygeofetch-2.6.3.0}/CHANGELOG.md +7 -7
- {pygeofetch-2.6.2.9/pygeofetch.egg-info → pygeofetch-2.6.3.0}/PKG-INFO +2 -2
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/README.md +1 -1
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/__init__.py +1 -1
- pygeofetch-2.6.3.0/pygeofetch/cli/classify_commands.py +112 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/cli/index_commands.py +64 -22
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/cli/main.py +95 -1
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/cli/postprocess_commands.py +48 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/core/engine.py +2 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/core/searcher.py +32 -87
- pygeofetch-2.6.3.0/pygeofetch/insar/feasibility.py +306 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/meta.yaml +1 -1
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/processing/base.py +104 -1
- pygeofetch-2.6.3.0/pygeofetch/processing/classification.py +367 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/processing/indices.py +101 -15
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/processing/postprocessor.py +169 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/aws_earth.py +10 -1
- pygeofetch-2.6.3.0/pygeofetch/utils/geocoding.py +132 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0/pygeofetch.egg-info}/PKG-INFO +2 -2
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch.egg-info/SOURCES.txt +4 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pyproject.toml +1 -1
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/LICENSE +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/MANIFEST.in +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/cli/__init__.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/cli/auth_commands.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/cli/config_commands.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/cli/download_commands.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/cli/monitor_commands.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/cli/multisensor_commands.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/cli/optical_commands.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/cli/pipeline_process_commands.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/cli/preprocess_commands.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/cli/sar_commands.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/cli/search_commands.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/config/__init__.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/config/defaults.yaml +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/config/settings.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/core/__init__.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/core/authenticator.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/core/cache_manager.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/core/data_organizer.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/core/downloader.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/core/logging.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/core/monitor.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/core/orbits.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/core/scheduler.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/core/state.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/geotech/__init__.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/geotech/risk_mapper.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/__init__.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/advanced_safeguards.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/analysis.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/annotation.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/atmosphere.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/auto_fallback.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/coregister.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/deburst.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/esd.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/extraction.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/flatearth.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/geolocation.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/gpu.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/interferogram.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/ionosphere.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/offset_geometry.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/offset_tracking.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/preflight.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/provenance.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/ps_selection.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/splitspectrum_diagnostic.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/stack_selection.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/synthetic.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/timeseries.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/unwrap.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/validate.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/visualize.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/insar/workflow.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/models/__init__.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/models/download_task.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/models/satellite_data.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/models/search_query.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/models/user_auth.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/multisensor/_init.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/multisensor/dem_differencing.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/multisensor/flood_mapping.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/multisensor/insar_optical_fusion.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/multisensor/terrain_corrected_change.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/multisensor/vegetation_disturbance.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/optical/__init__.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/optical/offset_tracking.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/processing/__init__.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/processing/batch.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/processing/pipeline.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/processing/preprocessor.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/processing/sar.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/processing/uq_exporter.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/processor/__init__.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/processor/indices.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/processor/landsat.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/processor/loader.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/processor/stacker.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/processor/timeseries.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/__init__.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/airbus_oneatlas.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/alaska_satellite_facility.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/base.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/copernicus.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/copernicus_nodes.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/digitalglobe.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/earth_explorer_additional.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/element84.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/eodag_provider.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/esa_scihub.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/geoserver_generic.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/google_earth_engine.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/inpe_cbers.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/isro_bhuvan.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/jaxa_earth.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/maxar_gbdx.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/nasa_earthdata.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/nasa_earthdata_cloud.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/noaa_big_data.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/opentopography.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/planet.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/planetary_computer.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/sentinel_hub.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/terrabotics.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/providers/usgs.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/sar/__init__.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/sar/_native.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/sar/_ost.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/sar/_sarxarray.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/sar/extraction.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/sar/math.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/sar/pipelines.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/sar/processor.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/stac_compute/__init__.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/stac_compute/lightweight_prefetch.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/utils/__init__.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/utils/file_utils.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/utils/gcp_recovery.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/utils/geo_utils.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/utils/logging_setup.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/utils/retry_handler.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/utils/sar_math.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/utils/validators.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/validation/__init__.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/validation/optical_validator.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/viz/__init__.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/viz/map.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch/viz/plot.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch.egg-info/dependency_links.txt +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch.egg-info/entry_points.txt +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch.egg-info/not-zip-safe +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch.egg-info/requires.txt +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/pygeofetch.egg-info/top_level.txt +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/setup.cfg +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_advanced_safeguards.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_airbus_oneatlas.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_alaska_satellite_facility.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_atmospheric_circular_regression.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_auto_fallback.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_auto_fallback_fixed.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_build_sbas_network-new.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_build_sbas_network.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_burst_metadata.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_burst_processing_integration.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_burst_sync_large_gap.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_burst_sync_prescreening.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_burst_synchronization.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_circuit_breaker_wiring.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_cli_optical_validation.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_copernicus_nodes.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_coregister_integration.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_coregister_upgrades (1).py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_coregister_upgrades.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_credential_encryption.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_data_organizer.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_dem_differencing_pipeline.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_digitalglobe.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_earth_explorer_additional.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_era5_atmospheric_correction.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_esa_scihub.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_extraction.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_flat_earth_phase.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_flood_mapping_pipeline.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_gcp_reprojection.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_geology_indices_and_rvi_fix.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_geoserver_generic.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_google_earth_engine.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_inpe_cbers.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_insar.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_insar_optical_fusion_pipeline.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_ionosphere.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_isro_bhuvan.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_jaxa_earth.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_lightweight_prefetch.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_map_preview_consolidation.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_models.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_monitor.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_nasa_earthdata_cloud_s3_fix.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_nasa_earthdata_public_search.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_noaa_big_data.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_offset_tracking.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_opentopography_pointcloud_usgs.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_optical_offset_tracking.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_optical_validation_wiring.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_optical_validator.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_optical_validator_stac.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_pgv_integration_contract.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_pipeline_process_export.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_planet_asset_type_fix.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_preflight_gate.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_preflight_spatial_baseline.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_provider_geometry_audit.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_provider_scene_disambiguation.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_providers.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_ps_selection.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_raster_collocation_integration.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_reference_pixel_and_despike.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_risk_mapper.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_risk_mapping.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_sar_pipelines.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_screen_stack_orchestration.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_search_download.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_sentinel_hub_catalog_fix.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_sign_convention.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_splitspectrum_diagnostic.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_stack_selection_consolidation.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_state.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_strain_and_fusion.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_terrain_corrected_change_pipeline.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_topo_r2_bug.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_uq_exporter.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_utils.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_vegetation_disturbance_pipeline.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/tests/test_viz_plot.py +0 -0
- {pygeofetch-2.6.2.9 → pygeofetch-2.6.3.0}/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.
|
|
23
|
+
## [Unreleased] — v2.6.3.0.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.
|
|
133
|
+
## [2.6.3.0.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.
|
|
152
|
+
## [2.6.3.0] — 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.
|
|
264
|
+
**"2.6.3.0.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.
|
|
266
|
+
management, 17 spectral indices, and various bug fixes. **2.6.3.0.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.
|
|
410
|
-
2026-07-29 and 2026-07-12. **2.6.
|
|
409
|
+
1. **Two different entries were both labeled `2.6.3.0.4`**, dated
|
|
410
|
+
2026-07-29 and 2026-07-12. **2.6.3.0.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.
|
|
3
|
+
Version: 2.6.3.0
|
|
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
|
|
@@ -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.
|
|
372
|
+
version = {2.6.3.0.1},
|
|
373
373
|
doi = {10.5281/zenodo.22087230},
|
|
374
374
|
url = {https://doi.org/10.5281/zenodo.22087230}
|
|
375
375
|
}
|
|
@@ -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.
|
|
232
|
+
version = {2.6.3.0.1},
|
|
233
233
|
doi = {10.5281/zenodo.22087230},
|
|
234
234
|
url = {https://doi.org/10.5281/zenodo.22087230}
|
|
235
235
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""CLI commands for pixel classification."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
# from pygeofetch.cli.index_commands import clip_options, _parse_bbox
|
|
11
|
+
|
|
12
|
+
console = Console()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _engine():
|
|
16
|
+
from pygeofetch.core.engine import PyGeoFetch
|
|
17
|
+
|
|
18
|
+
return PyGeoFetch(log_level="WARNING")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _pr(result, name):
|
|
22
|
+
if result.success:
|
|
23
|
+
size_mb = (
|
|
24
|
+
result.output_path.stat().st_size / (1024 * 1024)
|
|
25
|
+
if result.output_path and result.output_path.exists()
|
|
26
|
+
else 0
|
|
27
|
+
)
|
|
28
|
+
console.print(
|
|
29
|
+
f"[green]✓[/] {name} → {result.output_path}"
|
|
30
|
+
f" ({size_mb:.1f} MB, {result.duration_seconds:.2f}s)"
|
|
31
|
+
)
|
|
32
|
+
meta = result.metadata or {}
|
|
33
|
+
for k, v in meta.items():
|
|
34
|
+
if k != "classification_report":
|
|
35
|
+
console.print(f" {k}: {v}")
|
|
36
|
+
else:
|
|
37
|
+
console.print(f"[red]✗[/] {name} failed: {result.error}")
|
|
38
|
+
sys.exit(1)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@click.group()
|
|
42
|
+
def classify() -> None:
|
|
43
|
+
"""Pixel classification — unsupervised K-means and supervised Random Forest/SVM."""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@classify.command("kmeans")
|
|
47
|
+
@click.argument("inputs", nargs=-1, type=click.Path(exists=True))
|
|
48
|
+
@click.option("--clusters", "-k", default=5, show_default=True, type=int, help="Number of clusters.")
|
|
49
|
+
@click.option("--output", "-o", default=None)
|
|
50
|
+
@click.option("--random-state", default=42, show_default=True, type=int)
|
|
51
|
+
# @clip_options
|
|
52
|
+
def kmeans_cmd(inputs, clusters, output, random_state, bbox, geometry, geometry_crs):
|
|
53
|
+
"""Unsupervised K-means clustering. No training data needed.
|
|
54
|
+
|
|
55
|
+
Example: pygeofetch classify kmeans B02.tif B03.tif B04.tif B08.tif -k 5
|
|
56
|
+
"""
|
|
57
|
+
if len(inputs) < 1:
|
|
58
|
+
console.print("[red]Provide at least 1 input band raster[/]")
|
|
59
|
+
sys.exit(1)
|
|
60
|
+
e = _engine()
|
|
61
|
+
r = e.classify.kmeans(
|
|
62
|
+
inputs=list(inputs), n_clusters=clusters, output=output, random_state=random_state,
|
|
63
|
+
bbox=bbox, geometry=geometry, geometry_crs=geometry_crs,
|
|
64
|
+
)
|
|
65
|
+
_pr(r, f"K-means ({clusters} clusters)")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@classify.command("train")
|
|
69
|
+
@click.argument("inputs", nargs=-1, type=click.Path(exists=True))
|
|
70
|
+
@click.option("--training-data", required=True, type=click.Path(exists=True), help="Vector file (GeoJSON/Shapefile) with labeled training geometries.")
|
|
71
|
+
@click.option("--class-field", required=True, help="Attribute column holding the real class label.")
|
|
72
|
+
@click.option("--output-model", "-o", required=True, help="Where to save the trained model.")
|
|
73
|
+
@click.option("--model-type", default="random_forest", type=click.Choice(["random_forest", "svm"]), show_default=True)
|
|
74
|
+
@click.option("--n-estimators", default=100, show_default=True, type=int, help="Random Forest tree count.")
|
|
75
|
+
@click.option("--test-size", default=0.2, show_default=True, type=float, help="Fraction of samples held out for real accuracy reporting.")
|
|
76
|
+
def train_cmd(inputs, training_data, class_field, output_model, model_type, n_estimators, test_size):
|
|
77
|
+
"""Train a supervised classifier from labeled vector training samples.
|
|
78
|
+
|
|
79
|
+
Example: pygeofetch classify train B02.tif B03.tif B04.tif B08.tif
|
|
80
|
+
--training-data samples.geojson --class-field land_cover -o model.joblib
|
|
81
|
+
"""
|
|
82
|
+
if len(inputs) < 1:
|
|
83
|
+
console.print("[red]Provide at least 1 input band raster[/]")
|
|
84
|
+
sys.exit(1)
|
|
85
|
+
e = _engine()
|
|
86
|
+
r = e.classify.train(
|
|
87
|
+
inputs=list(inputs), training_data=training_data, class_field=class_field,
|
|
88
|
+
output_model=output_model, model_type=model_type, n_estimators=n_estimators,
|
|
89
|
+
test_size=test_size,
|
|
90
|
+
)
|
|
91
|
+
_pr(r, f"train ({model_type})")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@classify.command("predict")
|
|
95
|
+
@click.argument("inputs", nargs=-1, type=click.Path(exists=True))
|
|
96
|
+
@click.option("--model", "-m", required=True, type=click.Path(exists=True), help="A model trained via 'classify train'.")
|
|
97
|
+
@click.option("--output", "-o", default=None)
|
|
98
|
+
# @clip_options
|
|
99
|
+
def predict_cmd(inputs, model, output, bbox, geometry, geometry_crs):
|
|
100
|
+
"""Apply a trained model to a real raster stack, producing a classification map.
|
|
101
|
+
|
|
102
|
+
Example: pygeofetch classify predict B02.tif B03.tif B04.tif B08.tif -m model.joblib
|
|
103
|
+
"""
|
|
104
|
+
if len(inputs) < 1:
|
|
105
|
+
console.print("[red]Provide at least 1 input band raster[/]")
|
|
106
|
+
sys.exit(1)
|
|
107
|
+
e = _engine()
|
|
108
|
+
r = e.classify.predict(
|
|
109
|
+
inputs=list(inputs), model=model, output=output,
|
|
110
|
+
bbox=bbox, geometry=geometry, geometry_crs=geometry_crs,
|
|
111
|
+
)
|
|
112
|
+
_pr(r, "predict")
|
|
@@ -16,6 +16,37 @@ def _engine():
|
|
|
16
16
|
return PyGeoFetch(log_level="WARNING")
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
def _parse_bbox(ctx, param, value):
|
|
20
|
+
if value is None:
|
|
21
|
+
return None
|
|
22
|
+
try:
|
|
23
|
+
parts = [float(x.strip()) for x in value.split(",")]
|
|
24
|
+
except ValueError:
|
|
25
|
+
raise click.BadParameter("must be 4 comma-separated numbers: minx,miny,maxx,maxy") from None
|
|
26
|
+
if len(parts) != 4:
|
|
27
|
+
raise click.BadParameter("must be exactly 4 comma-separated numbers: minx,miny,maxx,maxy")
|
|
28
|
+
return tuple(parts)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def clip_options(f):
|
|
32
|
+
"""Shared --bbox/--geometry/--geometry-crs options, added to every
|
|
33
|
+
index command whose underlying method supports post-computation
|
|
34
|
+
clipping."""
|
|
35
|
+
f = click.option(
|
|
36
|
+
"--bbox", callback=_parse_bbox, default=None,
|
|
37
|
+
help="Clip the result to minx,miny,maxx,maxy (comma-separated).",
|
|
38
|
+
)(f)
|
|
39
|
+
f = click.option(
|
|
40
|
+
"--geometry", default=None, type=click.Path(exists=True),
|
|
41
|
+
help="Clip the result to a GeoJSON file's geometry instead of a bbox.",
|
|
42
|
+
)(f)
|
|
43
|
+
f = click.option(
|
|
44
|
+
"--geometry-crs", default="EPSG:4326", show_default=True,
|
|
45
|
+
help="CRS of --bbox/--geometry coordinates.",
|
|
46
|
+
)(f)
|
|
47
|
+
return f
|
|
48
|
+
|
|
49
|
+
|
|
19
50
|
def _pr(result, name):
|
|
20
51
|
if result.success:
|
|
21
52
|
size_mb = (
|
|
@@ -55,7 +86,8 @@ def index() -> None:
|
|
|
55
86
|
help="Process in memory-safe tiles for large rasters (e.g. drone orthomosaics) that exceed available RAM or the 4GB GeoTIFF limit.",
|
|
56
87
|
)
|
|
57
88
|
@click.option("--tile-size", default=1024, show_default=True, type=int, help="Tile edge length in pixels when --chunked is set.")
|
|
58
|
-
|
|
89
|
+
@clip_options
|
|
90
|
+
def ndvi_cmd(red, nir, output, chunked, tile_size, bbox, geometry, geometry_crs):
|
|
59
91
|
"""NDVI — Normalized Difference Vegetation Index. Range: -1 to +1. Vegetation > 0.3.
|
|
60
92
|
|
|
61
93
|
Pass the SAME path to --red and --nir to auto-detect both bands from
|
|
@@ -63,7 +95,7 @@ def ndvi_cmd(red, nir, output, chunked, tile_size):
|
|
|
63
95
|
real, embedded band descriptions.
|
|
64
96
|
"""
|
|
65
97
|
e = _engine()
|
|
66
|
-
r = e.indices.ndvi(red=red, nir=nir, output=output, chunked=chunked, tile_size=tile_size)
|
|
98
|
+
r = e.indices.ndvi(red=red, nir=nir, output=output, chunked=chunked, tile_size=tile_size, bbox=bbox, geometry=geometry, geometry_crs=geometry_crs)
|
|
67
99
|
_pr(r, "NDVI")
|
|
68
100
|
|
|
69
101
|
|
|
@@ -80,7 +112,8 @@ def ndvi_cmd(red, nir, output, chunked, tile_size):
|
|
|
80
112
|
help="Process in memory-safe tiles for large rasters that exceed available RAM or the 4GB GeoTIFF limit.",
|
|
81
113
|
)
|
|
82
114
|
@click.option("--tile-size", default=1024, show_default=True, type=int, help="Tile edge length in pixels when --chunked is set.")
|
|
83
|
-
|
|
115
|
+
@clip_options
|
|
116
|
+
def ndre_cmd(rededge, nir, output, chunked, tile_size, bbox, geometry, geometry_crs):
|
|
84
117
|
"""NDRE — Normalized Difference Red Edge Index. Range: -1 to +1.
|
|
85
118
|
|
|
86
119
|
Less prone to saturation than NDVI in dense canopy -- useful for
|
|
@@ -89,7 +122,7 @@ def ndre_cmd(rededge, nir, output, chunked, tile_size):
|
|
|
89
122
|
both bands from one merged multi-band image.
|
|
90
123
|
"""
|
|
91
124
|
e = _engine()
|
|
92
|
-
r = e.indices.ndre(rededge=rededge, nir=nir, output=output, chunked=chunked, tile_size=tile_size)
|
|
125
|
+
r = e.indices.ndre(rededge=rededge, nir=nir, output=output, chunked=chunked, tile_size=tile_size, bbox=bbox, geometry=geometry, geometry_crs=geometry_crs)
|
|
93
126
|
_pr(r, "NDRE")
|
|
94
127
|
|
|
95
128
|
|
|
@@ -100,14 +133,15 @@ def ndre_cmd(rededge, nir, output, chunked, tile_size):
|
|
|
100
133
|
@click.option("--output", "-o", default=None)
|
|
101
134
|
@click.option("--chunked", is_flag=True, help="Process in memory-safe tiles for large rasters.")
|
|
102
135
|
@click.option("--tile-size", default=1024, show_default=True, type=int)
|
|
103
|
-
|
|
136
|
+
@clip_options
|
|
137
|
+
def evi_cmd(blue, red, nir, output, chunked, tile_size, bbox, geometry, geometry_crs):
|
|
104
138
|
"""EVI — Enhanced Vegetation Index. Better than NDVI in high-biomass areas.
|
|
105
139
|
|
|
106
140
|
Pass the SAME path to --blue, --red, and --nir to auto-detect all
|
|
107
141
|
three bands from one merged multi-band image.
|
|
108
142
|
"""
|
|
109
143
|
e = _engine()
|
|
110
|
-
r = e.indices.evi(blue=blue, red=red, nir=nir, output=output, chunked=chunked, tile_size=tile_size)
|
|
144
|
+
r = e.indices.evi(blue=blue, red=red, nir=nir, output=output, chunked=chunked, tile_size=tile_size, bbox=bbox, geometry=geometry, geometry_crs=geometry_crs)
|
|
111
145
|
_pr(r, "EVI")
|
|
112
146
|
|
|
113
147
|
|
|
@@ -118,14 +152,15 @@ def evi_cmd(blue, red, nir, output, chunked, tile_size):
|
|
|
118
152
|
@click.option("--output", "-o", default=None)
|
|
119
153
|
@click.option("--chunked", is_flag=True, help="Process in memory-safe tiles for large rasters.")
|
|
120
154
|
@click.option("--tile-size", default=1024, show_default=True, type=int)
|
|
121
|
-
|
|
155
|
+
@clip_options
|
|
156
|
+
def savi_cmd(red, nir, soil_l, output, chunked, tile_size, bbox, geometry, geometry_crs):
|
|
122
157
|
"""SAVI — Soil Adjusted Vegetation Index. Reduces soil brightness influence.
|
|
123
158
|
|
|
124
159
|
Pass the SAME path to --red and --nir to auto-detect both bands
|
|
125
160
|
from one merged multi-band image.
|
|
126
161
|
"""
|
|
127
162
|
e = _engine()
|
|
128
|
-
r = e.indices.savi(red=red, nir=nir, L=soil_l, output=output, chunked=chunked, tile_size=tile_size)
|
|
163
|
+
r = e.indices.savi(red=red, nir=nir, L=soil_l, output=output, chunked=chunked, tile_size=tile_size, bbox=bbox, geometry=geometry, geometry_crs=geometry_crs)
|
|
129
164
|
_pr(r, "SAVI")
|
|
130
165
|
|
|
131
166
|
|
|
@@ -135,14 +170,15 @@ def savi_cmd(red, nir, soil_l, output, chunked, tile_size):
|
|
|
135
170
|
@click.option("--output", "-o", default=None)
|
|
136
171
|
@click.option("--chunked", is_flag=True, help="Process in memory-safe tiles for large rasters.")
|
|
137
172
|
@click.option("--tile-size", default=1024, show_default=True, type=int)
|
|
138
|
-
|
|
173
|
+
@clip_options
|
|
174
|
+
def ndwi_cmd(green, nir, output, chunked, tile_size, bbox, geometry, geometry_crs):
|
|
139
175
|
"""NDWI — Normalized Difference Water Index. Water > 0.
|
|
140
176
|
|
|
141
177
|
Pass the SAME path to --green and --nir to auto-detect both bands
|
|
142
178
|
from one merged multi-band image.
|
|
143
179
|
"""
|
|
144
180
|
e = _engine()
|
|
145
|
-
r = e.indices.ndwi(green=green, nir=nir, output=output, chunked=chunked, tile_size=tile_size)
|
|
181
|
+
r = e.indices.ndwi(green=green, nir=nir, output=output, chunked=chunked, tile_size=tile_size, bbox=bbox, geometry=geometry, geometry_crs=geometry_crs)
|
|
146
182
|
_pr(r, "NDWI")
|
|
147
183
|
|
|
148
184
|
|
|
@@ -152,14 +188,15 @@ def ndwi_cmd(green, nir, output, chunked, tile_size):
|
|
|
152
188
|
@click.option("--output", "-o", default=None)
|
|
153
189
|
@click.option("--chunked", is_flag=True, help="Process in memory-safe tiles for large rasters.")
|
|
154
190
|
@click.option("--tile-size", default=1024, show_default=True, type=int)
|
|
155
|
-
|
|
191
|
+
@clip_options
|
|
192
|
+
def mndwi_cmd(green, swir1, output, chunked, tile_size, bbox, geometry, geometry_crs):
|
|
156
193
|
"""MNDWI — Modified NDWI. Better separation of water from built-up areas.
|
|
157
194
|
|
|
158
195
|
Pass the SAME path to --green and --swir1 to auto-detect both
|
|
159
196
|
bands from one merged multi-band image.
|
|
160
197
|
"""
|
|
161
198
|
e = _engine()
|
|
162
|
-
r = e.indices.mndwi(green=green, swir1=swir1, output=output, chunked=chunked, tile_size=tile_size)
|
|
199
|
+
r = e.indices.mndwi(green=green, swir1=swir1, output=output, chunked=chunked, tile_size=tile_size, bbox=bbox, geometry=geometry, geometry_crs=geometry_crs)
|
|
163
200
|
_pr(r, "MNDWI")
|
|
164
201
|
|
|
165
202
|
|
|
@@ -169,14 +206,15 @@ def mndwi_cmd(green, swir1, output, chunked, tile_size):
|
|
|
169
206
|
@click.option("--output", "-o", default=None)
|
|
170
207
|
@click.option("--chunked", is_flag=True, help="Process in memory-safe tiles for large rasters.")
|
|
171
208
|
@click.option("--tile-size", default=1024, show_default=True, type=int)
|
|
172
|
-
|
|
209
|
+
@clip_options
|
|
210
|
+
def ndbi_cmd(nir, swir1, output, chunked, tile_size, bbox, geometry, geometry_crs):
|
|
173
211
|
"""NDBI — Normalized Difference Built-up Index. Urban > 0.
|
|
174
212
|
|
|
175
213
|
Pass the SAME path to --nir and --swir1 to auto-detect both bands
|
|
176
214
|
from one merged multi-band image.
|
|
177
215
|
"""
|
|
178
216
|
e = _engine()
|
|
179
|
-
r = e.indices.ndbi(nir=nir, swir1=swir1, output=output, chunked=chunked, tile_size=tile_size)
|
|
217
|
+
r = e.indices.ndbi(nir=nir, swir1=swir1, output=output, chunked=chunked, tile_size=tile_size, bbox=bbox, geometry=geometry, geometry_crs=geometry_crs)
|
|
180
218
|
_pr(r, "NDBI")
|
|
181
219
|
|
|
182
220
|
|
|
@@ -186,14 +224,15 @@ def ndbi_cmd(nir, swir1, output, chunked, tile_size):
|
|
|
186
224
|
@click.option("--output", "-o", default=None)
|
|
187
225
|
@click.option("--chunked", is_flag=True, help="Process in memory-safe tiles for large rasters.")
|
|
188
226
|
@click.option("--tile-size", default=1024, show_default=True, type=int)
|
|
189
|
-
|
|
227
|
+
@clip_options
|
|
228
|
+
def ndsi_cmd(green, swir1, output, chunked, tile_size, bbox, geometry, geometry_crs):
|
|
190
229
|
"""NDSI — Snow Index. Snow > 0.4.
|
|
191
230
|
|
|
192
231
|
Pass the SAME path to --green and --swir1 to auto-detect both
|
|
193
232
|
bands from one merged multi-band image.
|
|
194
233
|
"""
|
|
195
234
|
e = _engine()
|
|
196
|
-
r = e.indices.ndsi(green=green, swir1=swir1, output=output, chunked=chunked, tile_size=tile_size)
|
|
235
|
+
r = e.indices.ndsi(green=green, swir1=swir1, output=output, chunked=chunked, tile_size=tile_size, bbox=bbox, geometry=geometry, geometry_crs=geometry_crs)
|
|
197
236
|
_pr(r, "NDSI")
|
|
198
237
|
|
|
199
238
|
|
|
@@ -203,14 +242,15 @@ def ndsi_cmd(green, swir1, output, chunked, tile_size):
|
|
|
203
242
|
@click.option("--output", "-o", default=None)
|
|
204
243
|
@click.option("--chunked", is_flag=True, help="Process in memory-safe tiles for large rasters.")
|
|
205
244
|
@click.option("--tile-size", default=1024, show_default=True, type=int)
|
|
206
|
-
|
|
245
|
+
@clip_options
|
|
246
|
+
def ndmi_cmd(nir, swir1, output, chunked, tile_size, bbox, geometry, geometry_crs):
|
|
207
247
|
"""NDMI — Moisture Index. Sensitive to canopy water content.
|
|
208
248
|
|
|
209
249
|
Pass the SAME path to --nir and --swir1 to auto-detect both bands
|
|
210
250
|
from one merged multi-band image.
|
|
211
251
|
"""
|
|
212
252
|
e = _engine()
|
|
213
|
-
r = e.indices.ndmi(nir=nir, swir1=swir1, output=output, chunked=chunked, tile_size=tile_size)
|
|
253
|
+
r = e.indices.ndmi(nir=nir, swir1=swir1, output=output, chunked=chunked, tile_size=tile_size, bbox=bbox, geometry=geometry, geometry_crs=geometry_crs)
|
|
214
254
|
_pr(r, "NDMI")
|
|
215
255
|
|
|
216
256
|
|
|
@@ -220,14 +260,15 @@ def ndmi_cmd(nir, swir1, output, chunked, tile_size):
|
|
|
220
260
|
@click.option("--output", "-o", default=None)
|
|
221
261
|
@click.option("--chunked", is_flag=True, help="Process in memory-safe tiles for large rasters.")
|
|
222
262
|
@click.option("--tile-size", default=1024, show_default=True, type=int)
|
|
223
|
-
|
|
263
|
+
@clip_options
|
|
264
|
+
def nbr_cmd(nir, swir2, output, chunked, tile_size, bbox, geometry, geometry_crs):
|
|
224
265
|
"""NBR — Normalized Burn Ratio. Use dNBR for burn severity.
|
|
225
266
|
|
|
226
267
|
Pass the SAME path to --nir and --swir2 to auto-detect both bands
|
|
227
268
|
from one merged multi-band image.
|
|
228
269
|
"""
|
|
229
270
|
e = _engine()
|
|
230
|
-
r = e.indices.nbr(nir=nir, swir2=swir2, output=output, chunked=chunked, tile_size=tile_size)
|
|
271
|
+
r = e.indices.nbr(nir=nir, swir2=swir2, output=output, chunked=chunked, tile_size=tile_size, bbox=bbox, geometry=geometry, geometry_crs=geometry_crs)
|
|
231
272
|
_pr(r, "NBR")
|
|
232
273
|
|
|
233
274
|
|
|
@@ -351,10 +392,11 @@ def albedo_cmd(inputs, sensor, output):
|
|
|
351
392
|
help="Process in memory-safe tiles for large rasters (e.g. drone orthomosaics) that exceed available RAM or the 4GB GeoTIFF limit.",
|
|
352
393
|
)
|
|
353
394
|
@click.option("--tile-size", default=1024, show_default=True, type=int, help="Tile edge length in pixels when --chunked is set.")
|
|
354
|
-
|
|
395
|
+
@clip_options
|
|
396
|
+
def band_math_cmd(inputs, expr, output, chunked, tile_size, bbox, geometry, geometry_crs):
|
|
355
397
|
"""Arbitrary band arithmetic. Example: --expr '(B[1]-B[0])/(B[1]+B[0])'."""
|
|
356
398
|
e = _engine()
|
|
357
|
-
r = e.indices.band_math(inputs=list(inputs), expression=expr, output=output, chunked=chunked, tile_size=tile_size)
|
|
399
|
+
r = e.indices.band_math(inputs=list(inputs), expression=expr, output=output, chunked=chunked, tile_size=tile_size, bbox=bbox, geometry=geometry, geometry_crs=geometry_crs)
|
|
358
400
|
_pr(r, f"band-math ({expr[:40]})")
|
|
359
401
|
|
|
360
402
|
|
|
@@ -27,6 +27,7 @@ from pygeofetch.cli.config_commands import config
|
|
|
27
27
|
from pygeofetch.cli.download_commands import download
|
|
28
28
|
from pygeofetch.cli.index_commands import index
|
|
29
29
|
from pygeofetch.cli.multisensor_commands import multisensor
|
|
30
|
+
from pygeofetch.cli.classify_commands import classify
|
|
30
31
|
from pygeofetch.cli.optical_commands import optical
|
|
31
32
|
from pygeofetch.cli.pipeline_process_commands import proc_pipeline
|
|
32
33
|
from pygeofetch.cli.postprocess_commands import post
|
|
@@ -107,6 +108,7 @@ cli.add_command(sar)
|
|
|
107
108
|
cli.add_command(proc_pipeline)
|
|
108
109
|
cli.add_command(optical)
|
|
109
110
|
cli.add_command(multisensor)
|
|
111
|
+
cli.add_command(classify)
|
|
110
112
|
|
|
111
113
|
|
|
112
114
|
# ---------------------------------------------------------------------------
|
|
@@ -212,6 +214,98 @@ def version(as_json: bool) -> None:
|
|
|
212
214
|
)
|
|
213
215
|
|
|
214
216
|
|
|
217
|
+
# ---------------------------------------------------------------------------
|
|
218
|
+
# GEOCODE
|
|
219
|
+
# ---------------------------------------------------------------------------
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@cli.command()
|
|
223
|
+
@click.argument("query")
|
|
224
|
+
@click.option("--limit", default=1, show_default=True, type=int, help="Max results to return.")
|
|
225
|
+
def geocode(query: str, limit: int) -> None:
|
|
226
|
+
"""Look up a place name and get its coordinates and bounding box.
|
|
227
|
+
|
|
228
|
+
Example: pygeofetch geocode "Nairobi, Kenya"
|
|
229
|
+
|
|
230
|
+
The printed bbox is ready to paste directly into a SearchQuery.
|
|
231
|
+
"""
|
|
232
|
+
from pygeofetch.utils.geocoding import geocode as _geocode
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
results = _geocode(query, limit=limit)
|
|
236
|
+
except Exception as exc:
|
|
237
|
+
console.print(f"[red]✗[/] geocode failed: {exc}")
|
|
238
|
+
sys.exit(1)
|
|
239
|
+
|
|
240
|
+
if not results:
|
|
241
|
+
console.print(f"[yellow]No results found for[/] {query!r}")
|
|
242
|
+
return
|
|
243
|
+
|
|
244
|
+
for r in results:
|
|
245
|
+
console.print(f"[green]{r['display_name']}[/]")
|
|
246
|
+
console.print(f" lat/lon: {r['lat']}, {r['lon']}")
|
|
247
|
+
console.print(f" bbox (min_lon, min_lat, max_lon, max_lat): {r['bbox']}")
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@cli.command(name="reverse-geocode")
|
|
251
|
+
@click.argument("lat", type=float)
|
|
252
|
+
@click.argument("lon", type=float)
|
|
253
|
+
def reverse_geocode_cmd(lat: float, lon: float) -> None:
|
|
254
|
+
"""Look up the real place name at a coordinate.
|
|
255
|
+
|
|
256
|
+
Example: pygeofetch reverse-geocode -1.286389 36.817223
|
|
257
|
+
"""
|
|
258
|
+
from pygeofetch.utils.geocoding import reverse_geocode as _reverse_geocode
|
|
259
|
+
|
|
260
|
+
try:
|
|
261
|
+
place = _reverse_geocode(lat, lon)
|
|
262
|
+
except Exception as exc:
|
|
263
|
+
console.print(f"[red]✗[/] reverse-geocode failed: {exc}")
|
|
264
|
+
sys.exit(1)
|
|
265
|
+
|
|
266
|
+
if place is None:
|
|
267
|
+
console.print(f"[yellow]No real place found at[/] ({lat}, {lon})")
|
|
268
|
+
return
|
|
269
|
+
console.print(f"[green]{place['display_name']}[/]")
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
# ---------------------------------------------------------------------------
|
|
273
|
+
# INSAR FEASIBILITY
|
|
274
|
+
# ---------------------------------------------------------------------------
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
@cli.command(name="assess-feasibility")
|
|
278
|
+
@click.option("--bbox", required=True, help="min_lon,min_lat,max_lon,max_lat")
|
|
279
|
+
@click.option("--start-date", required=True)
|
|
280
|
+
@click.option("--end-date", required=True)
|
|
281
|
+
@click.option("--max-temporal-baseline", default=72, show_default=True, type=int)
|
|
282
|
+
@click.option("--deep-check", is_flag=True, help="Also run real, live burst-sync screening on the best track (costs real network calls).")
|
|
283
|
+
def assess_feasibility_cmd(bbox, start_date, end_date, max_temporal_baseline, deep_check) -> None:
|
|
284
|
+
"""Assess InSAR feasibility for an AOI before downloading anything.
|
|
285
|
+
|
|
286
|
+
Example: pygeofetch assess-feasibility --bbox 18.85,50.15,19.15,50.35 \\
|
|
287
|
+
--start-date 2016-01-01 --end-date 2021-06-30
|
|
288
|
+
|
|
289
|
+
Reports the real, largest temporally-connected component per track
|
|
290
|
+
-- an honest upper bound, not a guarantee InSAR will work.
|
|
291
|
+
"""
|
|
292
|
+
from pygeofetch.core.engine import PyGeoFetch
|
|
293
|
+
from pygeofetch.insar.feasibility import assess_insar_feasibility
|
|
294
|
+
|
|
295
|
+
try:
|
|
296
|
+
bbox_tuple = tuple(float(x) for x in bbox.split(","))
|
|
297
|
+
except ValueError:
|
|
298
|
+
console.print("[red]--bbox must be 4 comma-separated numbers[/]")
|
|
299
|
+
sys.exit(1)
|
|
300
|
+
|
|
301
|
+
client = PyGeoFetch(log_level="WARNING")
|
|
302
|
+
report = assess_insar_feasibility(
|
|
303
|
+
client, bbox_tuple, start_date, end_date,
|
|
304
|
+
max_temporal_baseline_days=max_temporal_baseline, deep_check=deep_check,
|
|
305
|
+
)
|
|
306
|
+
report.print_summary()
|
|
307
|
+
|
|
308
|
+
|
|
215
309
|
# ---------------------------------------------------------------------------
|
|
216
310
|
# DOCTOR
|
|
217
311
|
# ---------------------------------------------------------------------------
|
|
@@ -923,4 +1017,4 @@ def main() -> None:
|
|
|
923
1017
|
|
|
924
1018
|
|
|
925
1019
|
if __name__ == "__main__":
|
|
926
|
-
main()
|
|
1020
|
+
main()
|
|
@@ -183,3 +183,51 @@ def cog_cmd(input, compress, blocksize, output):
|
|
|
183
183
|
e = _engine()
|
|
184
184
|
r = e.post.cog(input, compress=compress, blocksize=blocksize, output=output)
|
|
185
185
|
_pr(r, f"COG ({compress}, {blocksize}px)")
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@post.command("overlay")
|
|
189
|
+
@click.argument("left", type=click.Path(exists=True))
|
|
190
|
+
@click.argument("right", type=click.Path(exists=True))
|
|
191
|
+
@click.option("--output", "-o", required=True)
|
|
192
|
+
@click.option("--how", default="intersection", show_default=True,
|
|
193
|
+
type=click.Choice(["intersection", "union", "difference", "symmetric_difference", "identity"]))
|
|
194
|
+
def overlay_cmd(left, right, output, how):
|
|
195
|
+
"""Real polygon overlay between two vector layers.
|
|
196
|
+
|
|
197
|
+
Example: pygeofetch post overlay flood.geojson parcels.geojson -o flooded_parcels.geojson --how intersection
|
|
198
|
+
"""
|
|
199
|
+
e = _engine()
|
|
200
|
+
r = e.post.overlay(left, right, output=output, how=how)
|
|
201
|
+
_pr(r, f"overlay ({how})")
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@post.command("dissolve")
|
|
205
|
+
@click.argument("input", type=click.Path(exists=True))
|
|
206
|
+
@click.option("--output", "-o", required=True)
|
|
207
|
+
@click.option("--by", default=None, help="Attribute column to group by before dissolving.")
|
|
208
|
+
@click.option("--aggfunc", default="first", show_default=True, help="How to aggregate other columns within each group.")
|
|
209
|
+
def dissolve_cmd(input, output, by, aggfunc):
|
|
210
|
+
"""Merge polygons sharing the same attribute value into single geometries.
|
|
211
|
+
|
|
212
|
+
Example: pygeofetch post dissolve parcels.geojson -o zones.geojson --by land_use
|
|
213
|
+
"""
|
|
214
|
+
e = _engine()
|
|
215
|
+
r = e.post.dissolve(input, output=output, by=by, aggfunc=aggfunc)
|
|
216
|
+
_pr(r, f"dissolve (by={by})")
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@post.command("spatial-join")
|
|
220
|
+
@click.argument("left", type=click.Path(exists=True))
|
|
221
|
+
@click.argument("right", type=click.Path(exists=True))
|
|
222
|
+
@click.option("--output", "-o", required=True)
|
|
223
|
+
@click.option("--how", default="inner", show_default=True, type=click.Choice(["inner", "left", "right"]))
|
|
224
|
+
@click.option("--predicate", default="intersects", show_default=True,
|
|
225
|
+
type=click.Choice(["intersects", "contains", "within", "touches", "crosses", "overlaps"]))
|
|
226
|
+
def spatial_join_cmd(left, right, output, how, predicate):
|
|
227
|
+
"""Attach attributes from one vector layer to another by real spatial relationship.
|
|
228
|
+
|
|
229
|
+
Example: pygeofetch post spatial-join points.geojson zones.geojson -o joined.geojson --predicate within
|
|
230
|
+
"""
|
|
231
|
+
e = _engine()
|
|
232
|
+
r = e.post.spatial_join(left, right, output=output, how=how, predicate=predicate)
|
|
233
|
+
_pr(r, f"spatial-join ({predicate})")
|
|
@@ -40,6 +40,7 @@ from pygeofetch.models.satellite_data import SatelliteData
|
|
|
40
40
|
from pygeofetch.processing.batch import BatchProcessor
|
|
41
41
|
from pygeofetch.processing.indices import SpectralIndices
|
|
42
42
|
from pygeofetch.processing.postprocessor import PostProcessor
|
|
43
|
+
from pygeofetch.processing.classification import Classifier
|
|
43
44
|
from pygeofetch.processing.preprocessor import Preprocessor
|
|
44
45
|
from pygeofetch.processing.sar import SARProcessor
|
|
45
46
|
from pygeofetch.utils.geo_utils import _normalise_satellite_name
|
|
@@ -156,6 +157,7 @@ class PyGeoFetch:
|
|
|
156
157
|
self.preprocess = Preprocessor()
|
|
157
158
|
self.indices = SpectralIndices()
|
|
158
159
|
self.post = PostProcessor()
|
|
160
|
+
self.classify = Classifier()
|
|
159
161
|
self.sar = SARProcessor()
|
|
160
162
|
self.batch = BatchProcessor(engine=self)
|
|
161
163
|
|