PyGeoFetch 2.6.2.8__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.
Files changed (239) hide show
  1. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/CHANGELOG.md +7 -7
  2. {pygeofetch-2.6.2.8/pygeofetch.egg-info → pygeofetch-2.6.3.0}/PKG-INFO +2 -2
  3. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/README.md +1 -1
  4. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/__init__.py +1 -1
  5. pygeofetch-2.6.3.0/pygeofetch/cli/classify_commands.py +112 -0
  6. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/cli/index_commands.py +161 -30
  7. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/cli/main.py +95 -1
  8. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/cli/postprocess_commands.py +48 -0
  9. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/core/engine.py +2 -0
  10. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/core/searcher.py +32 -87
  11. pygeofetch-2.6.3.0/pygeofetch/insar/feasibility.py +306 -0
  12. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/meta.yaml +1 -1
  13. pygeofetch-2.6.3.0/pygeofetch/processing/base.py +651 -0
  14. pygeofetch-2.6.3.0/pygeofetch/processing/classification.py +367 -0
  15. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/processing/indices.py +444 -61
  16. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/processing/postprocessor.py +169 -0
  17. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/aws_earth.py +10 -1
  18. pygeofetch-2.6.3.0/pygeofetch/utils/geocoding.py +132 -0
  19. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0/pygeofetch.egg-info}/PKG-INFO +2 -2
  20. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch.egg-info/SOURCES.txt +4 -0
  21. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pyproject.toml +1 -1
  22. pygeofetch-2.6.2.8/pygeofetch/processing/base.py +0 -338
  23. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/LICENSE +0 -0
  24. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/MANIFEST.in +0 -0
  25. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/cli/__init__.py +0 -0
  26. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/cli/auth_commands.py +0 -0
  27. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/cli/config_commands.py +0 -0
  28. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/cli/download_commands.py +0 -0
  29. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/cli/monitor_commands.py +0 -0
  30. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/cli/multisensor_commands.py +0 -0
  31. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/cli/optical_commands.py +0 -0
  32. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/cli/pipeline_process_commands.py +0 -0
  33. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/cli/preprocess_commands.py +0 -0
  34. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/cli/sar_commands.py +0 -0
  35. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/cli/search_commands.py +0 -0
  36. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/config/__init__.py +0 -0
  37. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/config/defaults.yaml +0 -0
  38. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/config/settings.py +0 -0
  39. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/core/__init__.py +0 -0
  40. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/core/authenticator.py +0 -0
  41. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/core/cache_manager.py +0 -0
  42. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/core/data_organizer.py +0 -0
  43. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/core/downloader.py +0 -0
  44. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/core/logging.py +0 -0
  45. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/core/monitor.py +0 -0
  46. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/core/orbits.py +0 -0
  47. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/core/scheduler.py +0 -0
  48. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/core/state.py +0 -0
  49. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/geotech/__init__.py +0 -0
  50. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/geotech/risk_mapper.py +0 -0
  51. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/__init__.py +0 -0
  52. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/advanced_safeguards.py +0 -0
  53. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/analysis.py +0 -0
  54. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/annotation.py +0 -0
  55. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/atmosphere.py +0 -0
  56. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/auto_fallback.py +0 -0
  57. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/coregister.py +0 -0
  58. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/deburst.py +0 -0
  59. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/esd.py +0 -0
  60. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/extraction.py +0 -0
  61. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/flatearth.py +0 -0
  62. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/geolocation.py +0 -0
  63. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/gpu.py +0 -0
  64. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/interferogram.py +0 -0
  65. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/ionosphere.py +0 -0
  66. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/offset_geometry.py +0 -0
  67. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/offset_tracking.py +0 -0
  68. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/preflight.py +0 -0
  69. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/provenance.py +0 -0
  70. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/ps_selection.py +0 -0
  71. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/splitspectrum_diagnostic.py +0 -0
  72. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/stack_selection.py +0 -0
  73. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/synthetic.py +0 -0
  74. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/timeseries.py +0 -0
  75. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/unwrap.py +0 -0
  76. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/validate.py +0 -0
  77. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/visualize.py +0 -0
  78. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/insar/workflow.py +0 -0
  79. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/models/__init__.py +0 -0
  80. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/models/download_task.py +0 -0
  81. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/models/satellite_data.py +0 -0
  82. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/models/search_query.py +0 -0
  83. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/models/user_auth.py +0 -0
  84. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/multisensor/_init.py +0 -0
  85. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/multisensor/dem_differencing.py +0 -0
  86. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/multisensor/flood_mapping.py +0 -0
  87. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/multisensor/insar_optical_fusion.py +0 -0
  88. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/multisensor/terrain_corrected_change.py +0 -0
  89. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/multisensor/vegetation_disturbance.py +0 -0
  90. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/optical/__init__.py +0 -0
  91. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/optical/offset_tracking.py +0 -0
  92. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/processing/__init__.py +0 -0
  93. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/processing/batch.py +0 -0
  94. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/processing/pipeline.py +0 -0
  95. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/processing/preprocessor.py +0 -0
  96. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/processing/sar.py +0 -0
  97. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/processing/uq_exporter.py +0 -0
  98. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/processor/__init__.py +0 -0
  99. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/processor/indices.py +0 -0
  100. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/processor/landsat.py +0 -0
  101. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/processor/loader.py +0 -0
  102. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/processor/stacker.py +0 -0
  103. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/processor/timeseries.py +0 -0
  104. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/__init__.py +0 -0
  105. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/airbus_oneatlas.py +0 -0
  106. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/alaska_satellite_facility.py +0 -0
  107. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/base.py +0 -0
  108. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/copernicus.py +0 -0
  109. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/copernicus_nodes.py +0 -0
  110. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/digitalglobe.py +0 -0
  111. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/earth_explorer_additional.py +0 -0
  112. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/element84.py +0 -0
  113. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/eodag_provider.py +0 -0
  114. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/esa_scihub.py +0 -0
  115. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/geoserver_generic.py +0 -0
  116. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/google_earth_engine.py +0 -0
  117. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/inpe_cbers.py +0 -0
  118. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/isro_bhuvan.py +0 -0
  119. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/jaxa_earth.py +0 -0
  120. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/maxar_gbdx.py +0 -0
  121. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/nasa_earthdata.py +0 -0
  122. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/nasa_earthdata_cloud.py +0 -0
  123. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/noaa_big_data.py +0 -0
  124. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/opentopography.py +0 -0
  125. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/planet.py +0 -0
  126. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/planetary_computer.py +0 -0
  127. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/sentinel_hub.py +0 -0
  128. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/terrabotics.py +0 -0
  129. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/providers/usgs.py +0 -0
  130. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/sar/__init__.py +0 -0
  131. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/sar/_native.py +0 -0
  132. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/sar/_ost.py +0 -0
  133. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/sar/_sarxarray.py +0 -0
  134. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/sar/extraction.py +0 -0
  135. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/sar/math.py +0 -0
  136. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/sar/pipelines.py +0 -0
  137. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/sar/processor.py +0 -0
  138. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/stac_compute/__init__.py +0 -0
  139. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/stac_compute/lightweight_prefetch.py +0 -0
  140. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/utils/__init__.py +0 -0
  141. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/utils/file_utils.py +0 -0
  142. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/utils/gcp_recovery.py +0 -0
  143. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/utils/geo_utils.py +0 -0
  144. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/utils/logging_setup.py +0 -0
  145. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/utils/retry_handler.py +0 -0
  146. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/utils/sar_math.py +0 -0
  147. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/utils/validators.py +0 -0
  148. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/validation/__init__.py +0 -0
  149. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/validation/optical_validator.py +0 -0
  150. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/viz/__init__.py +0 -0
  151. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/viz/map.py +0 -0
  152. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch/viz/plot.py +0 -0
  153. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch.egg-info/dependency_links.txt +0 -0
  154. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch.egg-info/entry_points.txt +0 -0
  155. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch.egg-info/not-zip-safe +0 -0
  156. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch.egg-info/requires.txt +0 -0
  157. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/pygeofetch.egg-info/top_level.txt +0 -0
  158. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/setup.cfg +0 -0
  159. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_advanced_safeguards.py +0 -0
  160. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_airbus_oneatlas.py +0 -0
  161. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_alaska_satellite_facility.py +0 -0
  162. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_atmospheric_circular_regression.py +0 -0
  163. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_auto_fallback.py +0 -0
  164. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_auto_fallback_fixed.py +0 -0
  165. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_build_sbas_network-new.py +0 -0
  166. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_build_sbas_network.py +0 -0
  167. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_burst_metadata.py +0 -0
  168. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_burst_processing_integration.py +0 -0
  169. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_burst_sync_large_gap.py +0 -0
  170. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_burst_sync_prescreening.py +0 -0
  171. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_burst_synchronization.py +0 -0
  172. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_circuit_breaker_wiring.py +0 -0
  173. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_cli_optical_validation.py +0 -0
  174. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_copernicus_nodes.py +0 -0
  175. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_coregister_integration.py +0 -0
  176. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_coregister_upgrades (1).py +0 -0
  177. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_coregister_upgrades.py +0 -0
  178. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_credential_encryption.py +0 -0
  179. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_data_organizer.py +0 -0
  180. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_dem_differencing_pipeline.py +0 -0
  181. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_digitalglobe.py +0 -0
  182. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_earth_explorer_additional.py +0 -0
  183. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_era5_atmospheric_correction.py +0 -0
  184. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_esa_scihub.py +0 -0
  185. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_extraction.py +0 -0
  186. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_flat_earth_phase.py +0 -0
  187. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_flood_mapping_pipeline.py +0 -0
  188. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_gcp_reprojection.py +0 -0
  189. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_geology_indices_and_rvi_fix.py +0 -0
  190. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_geoserver_generic.py +0 -0
  191. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_google_earth_engine.py +0 -0
  192. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_inpe_cbers.py +0 -0
  193. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_insar.py +0 -0
  194. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_insar_optical_fusion_pipeline.py +0 -0
  195. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_ionosphere.py +0 -0
  196. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_isro_bhuvan.py +0 -0
  197. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_jaxa_earth.py +0 -0
  198. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_lightweight_prefetch.py +0 -0
  199. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_map_preview_consolidation.py +0 -0
  200. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_models.py +0 -0
  201. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_monitor.py +0 -0
  202. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_nasa_earthdata_cloud_s3_fix.py +0 -0
  203. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_nasa_earthdata_public_search.py +0 -0
  204. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_noaa_big_data.py +0 -0
  205. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_offset_tracking.py +0 -0
  206. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_opentopography_pointcloud_usgs.py +0 -0
  207. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_optical_offset_tracking.py +0 -0
  208. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_optical_validation_wiring.py +0 -0
  209. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_optical_validator.py +0 -0
  210. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_optical_validator_stac.py +0 -0
  211. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_pgv_integration_contract.py +0 -0
  212. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_pipeline_process_export.py +0 -0
  213. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_planet_asset_type_fix.py +0 -0
  214. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_preflight_gate.py +0 -0
  215. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_preflight_spatial_baseline.py +0 -0
  216. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_provider_geometry_audit.py +0 -0
  217. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_provider_scene_disambiguation.py +0 -0
  218. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_providers.py +0 -0
  219. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_ps_selection.py +0 -0
  220. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_raster_collocation_integration.py +0 -0
  221. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_reference_pixel_and_despike.py +0 -0
  222. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_risk_mapper.py +0 -0
  223. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_risk_mapping.py +0 -0
  224. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_sar_pipelines.py +0 -0
  225. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_screen_stack_orchestration.py +0 -0
  226. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_search_download.py +0 -0
  227. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_sentinel_hub_catalog_fix.py +0 -0
  228. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_sign_convention.py +0 -0
  229. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_splitspectrum_diagnostic.py +0 -0
  230. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_stack_selection_consolidation.py +0 -0
  231. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_state.py +0 -0
  232. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_strain_and_fusion.py +0 -0
  233. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_terrain_corrected_change_pipeline.py +0 -0
  234. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_topo_r2_bug.py +0 -0
  235. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_uq_exporter.py +0 -0
  236. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_utils.py +0 -0
  237. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_vegetation_disturbance_pipeline.py +0 -0
  238. {pygeofetch-2.6.2.8 → pygeofetch-2.6.3.0}/tests/test_viz_plot.py +0 -0
  239. {pygeofetch-2.6.2.8 → 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.2.8.4
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.2.8.4] — 2026-08-25 (latest)
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.2.8] — 2026-08-24
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.2.8.4 — 2026-07-12"** describing 22+ providers, Sentinel-1C/1D
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.2.8.4 was
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.2.8.4`**, dated
410
- 2026-07-29 and 2026-07-12. **2.6.2.8.4 was only ever released once**,
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.2.8
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.2.8.1},
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.2.8.1},
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
  }
@@ -30,7 +30,7 @@ CLI::
30
30
  pygeofetch doctor
31
31
  """
32
32
 
33
- __version__ = "2.6.2.8.4"
33
+ __version__ = "2.6.3.0.4"
34
34
  __author__ = "PyGeoFetch Contributors"
35
35
  __license__ = "MIT"
36
36
 
@@ -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 = (
@@ -50,22 +81,67 @@ def index() -> None:
50
81
  "--nir", required=True, type=click.Path(exists=True), help="NIR band (e.g. B08.tif)"
51
82
  )
52
83
  @click.option("--output", "-o", default=None)
53
- def ndvi_cmd(red, nir, output):
54
- """NDVI — Normalized Difference Vegetation Index. Range: -1 to +1. Vegetation > 0.3."""
84
+ @click.option(
85
+ "--chunked", is_flag=True,
86
+ help="Process in memory-safe tiles for large rasters (e.g. drone orthomosaics) that exceed available RAM or the 4GB GeoTIFF limit.",
87
+ )
88
+ @click.option("--tile-size", default=1024, show_default=True, type=int, help="Tile edge length in pixels when --chunked is set.")
89
+ @clip_options
90
+ def ndvi_cmd(red, nir, output, chunked, tile_size, bbox, geometry, geometry_crs):
91
+ """NDVI — Normalized Difference Vegetation Index. Range: -1 to +1. Vegetation > 0.3.
92
+
93
+ Pass the SAME path to --red and --nir to auto-detect both bands from
94
+ one merged multi-band image (e.g. a drone orthomosaic), using its
95
+ real, embedded band descriptions.
96
+ """
55
97
  e = _engine()
56
- r = e.indices.ndvi(red=red, nir=nir, output=output)
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)
57
99
  _pr(r, "NDVI")
58
100
 
59
101
 
102
+ @index.command("ndre")
103
+ @click.option(
104
+ "--rededge", required=True, type=click.Path(exists=True), help="Red edge band"
105
+ )
106
+ @click.option(
107
+ "--nir", required=True, type=click.Path(exists=True), help="NIR band"
108
+ )
109
+ @click.option("--output", "-o", default=None)
110
+ @click.option(
111
+ "--chunked", is_flag=True,
112
+ help="Process in memory-safe tiles for large rasters that exceed available RAM or the 4GB GeoTIFF limit.",
113
+ )
114
+ @click.option("--tile-size", default=1024, show_default=True, type=int, help="Tile edge length in pixels when --chunked is set.")
115
+ @clip_options
116
+ def ndre_cmd(rededge, nir, output, chunked, tile_size, bbox, geometry, geometry_crs):
117
+ """NDRE — Normalized Difference Red Edge Index. Range: -1 to +1.
118
+
119
+ Less prone to saturation than NDVI in dense canopy -- useful for
120
+ crop stress and chlorophyll monitoring from drone multispectral
121
+ sensors. Pass the SAME path to --rededge and --nir to auto-detect
122
+ both bands from one merged multi-band image.
123
+ """
124
+ e = _engine()
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)
126
+ _pr(r, "NDRE")
127
+
128
+
60
129
  @index.command("evi")
61
130
  @click.option("--blue", required=True, type=click.Path(exists=True))
62
131
  @click.option("--red", required=True, type=click.Path(exists=True))
63
132
  @click.option("--nir", required=True, type=click.Path(exists=True))
64
133
  @click.option("--output", "-o", default=None)
65
- def evi_cmd(blue, red, nir, output):
66
- """EVI — Enhanced Vegetation Index. Better than NDVI in high-biomass areas."""
134
+ @click.option("--chunked", is_flag=True, help="Process in memory-safe tiles for large rasters.")
135
+ @click.option("--tile-size", default=1024, show_default=True, type=int)
136
+ @clip_options
137
+ def evi_cmd(blue, red, nir, output, chunked, tile_size, bbox, geometry, geometry_crs):
138
+ """EVI — Enhanced Vegetation Index. Better than NDVI in high-biomass areas.
139
+
140
+ Pass the SAME path to --blue, --red, and --nir to auto-detect all
141
+ three bands from one merged multi-band image.
142
+ """
67
143
  e = _engine()
68
- r = e.indices.evi(blue=blue, red=red, nir=nir, output=output)
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)
69
145
  _pr(r, "EVI")
70
146
 
71
147
 
@@ -74,10 +150,17 @@ def evi_cmd(blue, red, nir, output):
74
150
  @click.option("--nir", required=True, type=click.Path(exists=True))
75
151
  @click.option("--soil-l", default=0.5, show_default=True, type=float)
76
152
  @click.option("--output", "-o", default=None)
77
- def savi_cmd(red, nir, soil_l, output):
78
- """SAVI — Soil Adjusted Vegetation Index. Reduces soil brightness influence."""
153
+ @click.option("--chunked", is_flag=True, help="Process in memory-safe tiles for large rasters.")
154
+ @click.option("--tile-size", default=1024, show_default=True, type=int)
155
+ @clip_options
156
+ def savi_cmd(red, nir, soil_l, output, chunked, tile_size, bbox, geometry, geometry_crs):
157
+ """SAVI — Soil Adjusted Vegetation Index. Reduces soil brightness influence.
158
+
159
+ Pass the SAME path to --red and --nir to auto-detect both bands
160
+ from one merged multi-band image.
161
+ """
79
162
  e = _engine()
80
- r = e.indices.savi(red=red, nir=nir, L=soil_l, output=output)
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)
81
164
  _pr(r, "SAVI")
82
165
 
83
166
 
@@ -85,10 +168,17 @@ def savi_cmd(red, nir, soil_l, output):
85
168
  @click.option("--green", required=True, type=click.Path(exists=True))
86
169
  @click.option("--nir", required=True, type=click.Path(exists=True))
87
170
  @click.option("--output", "-o", default=None)
88
- def ndwi_cmd(green, nir, output):
89
- """NDWI — Normalized Difference Water Index. Water > 0."""
171
+ @click.option("--chunked", is_flag=True, help="Process in memory-safe tiles for large rasters.")
172
+ @click.option("--tile-size", default=1024, show_default=True, type=int)
173
+ @clip_options
174
+ def ndwi_cmd(green, nir, output, chunked, tile_size, bbox, geometry, geometry_crs):
175
+ """NDWI — Normalized Difference Water Index. Water > 0.
176
+
177
+ Pass the SAME path to --green and --nir to auto-detect both bands
178
+ from one merged multi-band image.
179
+ """
90
180
  e = _engine()
91
- r = e.indices.ndwi(green=green, nir=nir, output=output)
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)
92
182
  _pr(r, "NDWI")
93
183
 
94
184
 
@@ -96,10 +186,17 @@ def ndwi_cmd(green, nir, output):
96
186
  @click.option("--green", required=True, type=click.Path(exists=True))
97
187
  @click.option("--swir1", required=True, type=click.Path(exists=True))
98
188
  @click.option("--output", "-o", default=None)
99
- def mndwi_cmd(green, swir1, output):
100
- """MNDWI — Modified NDWI. Better separation of water from built-up areas."""
189
+ @click.option("--chunked", is_flag=True, help="Process in memory-safe tiles for large rasters.")
190
+ @click.option("--tile-size", default=1024, show_default=True, type=int)
191
+ @clip_options
192
+ def mndwi_cmd(green, swir1, output, chunked, tile_size, bbox, geometry, geometry_crs):
193
+ """MNDWI — Modified NDWI. Better separation of water from built-up areas.
194
+
195
+ Pass the SAME path to --green and --swir1 to auto-detect both
196
+ bands from one merged multi-band image.
197
+ """
101
198
  e = _engine()
102
- r = e.indices.mndwi(green=green, swir1=swir1, output=output)
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)
103
200
  _pr(r, "MNDWI")
104
201
 
105
202
 
@@ -107,10 +204,17 @@ def mndwi_cmd(green, swir1, output):
107
204
  @click.option("--nir", required=True, type=click.Path(exists=True))
108
205
  @click.option("--swir1", required=True, type=click.Path(exists=True))
109
206
  @click.option("--output", "-o", default=None)
110
- def ndbi_cmd(nir, swir1, output):
111
- """NDBI — Normalized Difference Built-up Index. Urban > 0."""
207
+ @click.option("--chunked", is_flag=True, help="Process in memory-safe tiles for large rasters.")
208
+ @click.option("--tile-size", default=1024, show_default=True, type=int)
209
+ @clip_options
210
+ def ndbi_cmd(nir, swir1, output, chunked, tile_size, bbox, geometry, geometry_crs):
211
+ """NDBI — Normalized Difference Built-up Index. Urban > 0.
212
+
213
+ Pass the SAME path to --nir and --swir1 to auto-detect both bands
214
+ from one merged multi-band image.
215
+ """
112
216
  e = _engine()
113
- r = e.indices.ndbi(nir=nir, swir1=swir1, output=output)
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)
114
218
  _pr(r, "NDBI")
115
219
 
116
220
 
@@ -118,10 +222,17 @@ def ndbi_cmd(nir, swir1, output):
118
222
  @click.option("--green", required=True, type=click.Path(exists=True))
119
223
  @click.option("--swir1", required=True, type=click.Path(exists=True))
120
224
  @click.option("--output", "-o", default=None)
121
- def ndsi_cmd(green, swir1, output):
122
- """NDSI — Snow Index. Snow > 0.4."""
225
+ @click.option("--chunked", is_flag=True, help="Process in memory-safe tiles for large rasters.")
226
+ @click.option("--tile-size", default=1024, show_default=True, type=int)
227
+ @clip_options
228
+ def ndsi_cmd(green, swir1, output, chunked, tile_size, bbox, geometry, geometry_crs):
229
+ """NDSI — Snow Index. Snow > 0.4.
230
+
231
+ Pass the SAME path to --green and --swir1 to auto-detect both
232
+ bands from one merged multi-band image.
233
+ """
123
234
  e = _engine()
124
- r = e.indices.ndsi(green=green, swir1=swir1, output=output)
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)
125
236
  _pr(r, "NDSI")
126
237
 
127
238
 
@@ -129,10 +240,17 @@ def ndsi_cmd(green, swir1, output):
129
240
  @click.option("--nir", required=True, type=click.Path(exists=True))
130
241
  @click.option("--swir1", required=True, type=click.Path(exists=True))
131
242
  @click.option("--output", "-o", default=None)
132
- def ndmi_cmd(nir, swir1, output):
133
- """NDMI — Moisture Index. Sensitive to canopy water content."""
243
+ @click.option("--chunked", is_flag=True, help="Process in memory-safe tiles for large rasters.")
244
+ @click.option("--tile-size", default=1024, show_default=True, type=int)
245
+ @clip_options
246
+ def ndmi_cmd(nir, swir1, output, chunked, tile_size, bbox, geometry, geometry_crs):
247
+ """NDMI — Moisture Index. Sensitive to canopy water content.
248
+
249
+ Pass the SAME path to --nir and --swir1 to auto-detect both bands
250
+ from one merged multi-band image.
251
+ """
134
252
  e = _engine()
135
- r = e.indices.ndmi(nir=nir, swir1=swir1, output=output)
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)
136
254
  _pr(r, "NDMI")
137
255
 
138
256
 
@@ -140,10 +258,17 @@ def ndmi_cmd(nir, swir1, output):
140
258
  @click.option("--nir", required=True, type=click.Path(exists=True))
141
259
  @click.option("--swir2", required=True, type=click.Path(exists=True))
142
260
  @click.option("--output", "-o", default=None)
143
- def nbr_cmd(nir, swir2, output):
144
- """NBR — Normalized Burn Ratio. Use dNBR for burn severity."""
261
+ @click.option("--chunked", is_flag=True, help="Process in memory-safe tiles for large rasters.")
262
+ @click.option("--tile-size", default=1024, show_default=True, type=int)
263
+ @clip_options
264
+ def nbr_cmd(nir, swir2, output, chunked, tile_size, bbox, geometry, geometry_crs):
265
+ """NBR — Normalized Burn Ratio. Use dNBR for burn severity.
266
+
267
+ Pass the SAME path to --nir and --swir2 to auto-detect both bands
268
+ from one merged multi-band image.
269
+ """
145
270
  e = _engine()
146
- r = e.indices.nbr(nir=nir, swir2=swir2, output=output)
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)
147
272
  _pr(r, "NBR")
148
273
 
149
274
 
@@ -262,10 +387,16 @@ def albedo_cmd(inputs, sensor, output):
262
387
  "--expr", "-e", required=True, help="Python expression. Bands as B[0], B[1], ..."
263
388
  )
264
389
  @click.option("--output", "-o", default=None)
265
- def band_math_cmd(inputs, expr, output):
390
+ @click.option(
391
+ "--chunked", is_flag=True,
392
+ help="Process in memory-safe tiles for large rasters (e.g. drone orthomosaics) that exceed available RAM or the 4GB GeoTIFF limit.",
393
+ )
394
+ @click.option("--tile-size", default=1024, show_default=True, type=int, help="Tile edge length in pixels when --chunked is set.")
395
+ @clip_options
396
+ def band_math_cmd(inputs, expr, output, chunked, tile_size, bbox, geometry, geometry_crs):
266
397
  """Arbitrary band arithmetic. Example: --expr '(B[1]-B[0])/(B[1]+B[0])'."""
267
398
  e = _engine()
268
- r = e.indices.band_math(inputs=list(inputs), expression=expr, output=output)
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)
269
400
  _pr(r, f"band-math ({expr[:40]})")
270
401
 
271
402
 
@@ -276,4 +407,4 @@ def stack_cmd(inputs, output):
276
407
  """Stack single-band rasters into a multi-band GeoTIFF."""
277
408
  e = _engine()
278
409
  r = e.indices.stack(inputs=list(inputs), output=output)
279
- _pr(r, f"stack ({len(inputs)} bands)")
410
+ _pr(r, f"stack ({len(inputs)} bands)")
@@ -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})")