PyGeoFetch 1.9.2__tar.gz → 1.9.3__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 (141) hide show
  1. pygeofetch-1.9.3/CHANGELOG.md +195 -0
  2. {pygeofetch-1.9.2/pygeofetch.egg-info → pygeofetch-1.9.3}/PKG-INFO +130 -38
  3. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/README.md +119 -28
  4. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/__init__.py +1 -1
  5. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/downloader.py +80 -25
  6. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/__init__.py +37 -2
  7. pygeofetch-1.9.3/pygeofetch/insar/annotation.py +461 -0
  8. pygeofetch-1.9.3/pygeofetch/insar/atmosphere.py +555 -0
  9. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/coregister.py +105 -32
  10. pygeofetch-1.9.3/pygeofetch/insar/deburst.py +191 -0
  11. pygeofetch-1.9.3/pygeofetch/insar/esd.py +282 -0
  12. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/extraction.py +124 -1
  13. pygeofetch-1.9.3/pygeofetch/insar/flatearth.py +243 -0
  14. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/interferogram.py +544 -7
  15. pygeofetch-1.9.3/pygeofetch/insar/ionosphere.py +675 -0
  16. pygeofetch-1.9.3/pygeofetch/insar/splitspectrum_diagnostic.py +171 -0
  17. pygeofetch-1.9.3/pygeofetch/insar/synthetic.py +391 -0
  18. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/timeseries.py +73 -46
  19. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/unwrap.py +249 -0
  20. pygeofetch-1.9.3/pygeofetch/insar/workflow.py +282 -0
  21. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/meta.yaml +1 -1
  22. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/airbus_oneatlas.py +10 -0
  23. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/alaska_satellite_facility.py +10 -0
  24. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/aws_earth.py +5 -2
  25. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/copernicus.py +30 -10
  26. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/digitalglobe.py +10 -0
  27. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/earth_explorer_additional.py +10 -0
  28. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/element84.py +13 -2
  29. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/eodag_provider.py +26 -3
  30. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/esa_scihub.py +10 -0
  31. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/geoserver_generic.py +10 -0
  32. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/google_earth_engine.py +10 -0
  33. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/inpe_cbers.py +10 -0
  34. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/isro_bhuvan.py +12 -2
  35. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/jaxa_earth.py +10 -0
  36. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/maxar_gbdx.py +10 -0
  37. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/nasa_earthdata.py +36 -1
  38. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/nasa_earthdata_cloud.py +3 -2
  39. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/noaa_big_data.py +10 -0
  40. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/planet.py +1 -0
  41. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/planetary_computer.py +5 -2
  42. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/sentinel_hub.py +5 -4
  43. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/terrabotics.py +8 -0
  44. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/usgs.py +2 -1
  45. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/viz/map.py +101 -2
  46. {pygeofetch-1.9.2 → pygeofetch-1.9.3/pygeofetch.egg-info}/PKG-INFO +130 -38
  47. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch.egg-info/SOURCES.txt +22 -0
  48. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch.egg-info/requires.txt +7 -5
  49. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pyproject.toml +32 -12
  50. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/setup.cfg +1 -1
  51. pygeofetch-1.9.3/tests/test_atmospheric_circular_regression.py +104 -0
  52. pygeofetch-1.9.3/tests/test_burst_metadata.py +116 -0
  53. pygeofetch-1.9.3/tests/test_burst_processing_integration.py +166 -0
  54. pygeofetch-1.9.3/tests/test_era5_atmospheric_correction.py +92 -0
  55. pygeofetch-1.9.3/tests/test_flat_earth_phase.py +162 -0
  56. pygeofetch-1.9.3/tests/test_gcp_reprojection.py +129 -0
  57. pygeofetch-1.9.3/tests/test_ionosphere.py +387 -0
  58. pygeofetch-1.9.3/tests/test_models.py +146 -0
  59. pygeofetch-1.9.3/tests/test_provider_geometry_audit (1).py +200 -0
  60. pygeofetch-1.9.3/tests/test_provider_geometry_audit.py +177 -0
  61. pygeofetch-1.9.3/tests/test_provider_scene_disambiguation.py +115 -0
  62. pygeofetch-1.9.3/tests/test_providers.py +88 -0
  63. pygeofetch-1.9.3/tests/test_search_download.py +126 -0
  64. pygeofetch-1.9.3/tests/test_splitspectrum_diagnostic.py +106 -0
  65. pygeofetch-1.9.3/tests/test_utils.py +186 -0
  66. pygeofetch-1.9.2/CHANGELOG.md +0 -42
  67. pygeofetch-1.9.2/pygeofetch/insar/annotation.py +0 -184
  68. pygeofetch-1.9.2/pygeofetch/insar/atmosphere.py +0 -308
  69. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/LICENSE +0 -0
  70. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/MANIFEST.in +0 -0
  71. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/__init__.py +0 -0
  72. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/auth_commands.py +0 -0
  73. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/config_commands.py +0 -0
  74. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/download_commands.py +0 -0
  75. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/index_commands.py +0 -0
  76. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/main.py +0 -0
  77. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/pipeline_process_commands.py +0 -0
  78. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/postprocess_commands.py +0 -0
  79. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/preprocess_commands.py +0 -0
  80. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/sar_commands.py +0 -0
  81. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/cli/search_commands.py +0 -0
  82. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/config/__init__.py +0 -0
  83. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/config/defaults.yaml +0 -0
  84. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/config/settings.py +0 -0
  85. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/__init__.py +0 -0
  86. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/authenticator.py +0 -0
  87. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/cache_manager.py +0 -0
  88. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/engine.py +0 -0
  89. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/logging.py +0 -0
  90. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/orbits.py +0 -0
  91. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/scheduler.py +0 -0
  92. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/core/searcher.py +0 -0
  93. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/geolocation.py +0 -0
  94. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/gpu.py +0 -0
  95. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/validate.py +0 -0
  96. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/insar/visualize.py +0 -0
  97. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/models/__init__.py +0 -0
  98. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/models/download_task.py +0 -0
  99. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/models/satellite_data.py +0 -0
  100. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/models/search_query.py +0 -0
  101. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/models/user_auth.py +0 -0
  102. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processing/__init__.py +0 -0
  103. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processing/base.py +0 -0
  104. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processing/batch.py +0 -0
  105. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processing/indices.py +0 -0
  106. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processing/pipeline.py +0 -0
  107. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processing/postprocessor.py +0 -0
  108. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processing/preprocessor.py +0 -0
  109. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processing/sar.py +0 -0
  110. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processor/__init__.py +0 -0
  111. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processor/indices.py +0 -0
  112. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processor/landsat.py +0 -0
  113. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processor/loader.py +0 -0
  114. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processor/stacker.py +0 -0
  115. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/processor/timeseries.py +0 -0
  116. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/__init__.py +0 -0
  117. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/base.py +0 -0
  118. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/providers/opentopography.py +0 -0
  119. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/sar/__init__.py +0 -0
  120. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/sar/_native.py +0 -0
  121. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/sar/_ost.py +0 -0
  122. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/sar/_sarxarray.py +0 -0
  123. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/sar/extraction.py +0 -0
  124. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/sar/processor.py +0 -0
  125. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/utils/__init__.py +0 -0
  126. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/utils/file_utils.py +0 -0
  127. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/utils/gcp_recovery.py +0 -0
  128. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/utils/geo_utils.py +0 -0
  129. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/utils/logging_setup.py +0 -0
  130. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/utils/retry_handler.py +0 -0
  131. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/utils/validators.py +0 -0
  132. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/viz/__init__.py +0 -0
  133. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch/viz/plot.py +0 -0
  134. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch.egg-info/dependency_links.txt +0 -0
  135. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch.egg-info/entry_points.txt +0 -0
  136. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch.egg-info/not-zip-safe +0 -0
  137. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/pygeofetch.egg-info/top_level.txt +0 -0
  138. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/tests/test_extraction.py +0 -0
  139. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/tests/test_insar.py +0 -0
  140. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/tests/test_pgv_integration_contract.py +0 -0
  141. {pygeofetch-1.9.2 → pygeofetch-1.9.3}/tests/test_viz_plot.py +0 -0
@@ -0,0 +1,195 @@
1
+ # Changelog
2
+
3
+ All notable changes to PyGeoFetch are documented here.
4
+ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.9.3/).
5
+
6
+ ## [1.9.4] — 2026-07-29
7
+
8
+ ### Added
9
+ - Major SNAPHU unwrapping improvements for urban InSAR applications
10
+ - 8x4 multilooking strategy for dense urban fringe reduction (32x noise reduction)
11
+ - Goldstein phase filter (alpha=0.5) for noise suppression
12
+ - Tile handling for large scenes (>1500x1500 pixels) with configurable overlap
13
+ - Per-panel colormap and range control in `plot_comparison`
14
+ - Multi-panel comparison for mixed continuous/categorical data
15
+ - Common grid resampling for SBAS time series with varying swath widths
16
+ - Coherence threshold masking (0.3) for reliable pixel filtering
17
+ - Auto-selection of reference pixel based on mean coherence
18
+ - LOS to vertical displacement conversion with configurable incidence angle
19
+
20
+ ### Changed
21
+ - **SNAPHU `nlooks`**: increased from 2.0 to 25.0–100.0 (12.5x phase variance reduction)
22
+ - **`coherence_window`**: now defaults to 5 for urban areas (was 11)
23
+ - **`min_conncomp_frac`**: lowered to 0.0001 for urban disconnected patches
24
+ - **`min_region_size`**: reduced to 20 for smaller reliable components
25
+ - **SNAPHU cost mode**: added `smooth` mode support for dense fringes
26
+ - **SNAPHU `init_method`**: optimized MCF with tile parameters
27
+
28
+ ### Deprecated
29
+ - ESD with arbitrary scaling (0.01) — use proper Doppler centroid implementation instead
30
+ - `nlooks=2.0` default — use 25.0–100.0 for urban areas
31
+
32
+ ### Fixed
33
+ - **CRITICAL**: SNAPHU 100% unreliable-pixels issue, even with coherence 0.3–0.4
34
+ - Phase variance reduced from 6.38 to 0.51 rad² (12.5x improvement)
35
+ - Shape mismatch errors in SBAS network inversion
36
+ - Missing tile parameters causing memory errors for large scenes
37
+ - ESD phase discontinuities from incorrect scaling factor
38
+ - Double plotting issue in Jupyter notebooks
39
+ - Coherence dtype handling for SNAPHU (float32 vs uint16)
40
+
41
+ ### Performance
42
+ - 87.1% reliable pixels achieved on urban InSAR data (was 0%)
43
+ - 16x faster processing with optimized tile parameters
44
+ - Reduced memory footprint for large scenes with tiling
45
+
46
+ ### Results (Mexico City Test Case)
47
+ - Best pair (2024-12-26 → 2025-01-07): 87.1% reliable
48
+ - Coherence range improved: 0.13–0.16 → 0.266–0.417
49
+ - Vertical velocity: −40 to +10 cm/year (Mexico City subsidence)
50
+ - Mean coherence: 0.302 across all pairs
51
+
52
+ ### Migration Guide
53
+ ```python
54
+ # Before (v1.9.3)
55
+ unwrapper = PhaseUnwrapper(cost_mode="defo", init_method="mcf")
56
+ unwrapped = unwrapper.unwrap(phase, coherence, nlooks=2.0)
57
+
58
+ # After (v1.10.0)
59
+ from pygeofetch.insar.unwrap import multilook
60
+
61
+ phase_ml = multilook(phase, 8, 4, wrapped_phase=True)
62
+ coh_ml = multilook(coherence, 8, 4, wrapped_phase=False)
63
+
64
+ unwrapper = PhaseUnwrapper(cost_mode="smooth", init_method="mcf")
65
+ unwrapped = unwrapper.unwrap(
66
+ phase_ml, coh_ml,
67
+ nlooks=100.0,
68
+ min_conncomp_frac=0.0001,
69
+ min_region_size=20
70
+ )
71
+ ```
72
+
73
+ ---
74
+
75
+ ## [1.9.3] — 2026-07-12
76
+
77
+ ### Added
78
+ - Federated search across 22+ satellite data providers
79
+ - Sentinel-1C and Sentinel-1D constellation support (active since May/April 2026)
80
+ - SLC product type search with automatic provider routing
81
+ - Precise orbit file management (POEORB/RESORB) for InSAR workflows
82
+ - 17 spectral indices: NDVI, EVI, SAVI, NDWI, MNDWI, NDBI, NDSI, NDMI, NBR, dNBR, TCT, PCA, Texture, LST, Albedo, Band Math, Stack
83
+ - SAR processing: despeckle, calibrate, flood mapping, coherence
84
+ - 41-step chainable processing pipeline builder
85
+ - YAML pipeline definitions with cron scheduling
86
+ - Cloud Optimized GeoTIFF output
87
+ - Clean search result tables with ANSI-aware column alignment
88
+ - Live download progress with Jupyter notebook HTML widget support
89
+ - Credential redaction in all log output
90
+
91
+ ### Fixed
92
+ - `AuthManager.add_credentials()` now accepts dict form (BUG 1)
93
+ - `download()` length/order contract guaranteed (BUG 2)
94
+ - Partial downloads detected via file validation (BUG 3)
95
+ - CRS identity transform detection after reprojection (BUG 4)
96
+ - `resolve_band_keys` missing import in `aws_earth` provider
97
+ - Band alias conflict between Sentinel-2 and Landsat naming conventions
98
+ - Copernicus `product_type` OData filter now correctly applied
99
+ - USGS M2M API authentication payload (`authType`, `catalogId` fields)
100
+ - ZIP archive validation no longer attempts rasterio open on archives
101
+
102
+ ### Providers
103
+ - Copernicus Dataspace (Sentinel-1/2/3/5P, SLC + GRD)
104
+ - AWS Earth (Sentinel-2, Landsat via STAC)
105
+ - Planetary Computer (Sentinel-2, Landsat, MODIS)
106
+ - Element84 Earth Search (STAC)
107
+ - USGS Earth Explorer (Landsat 1–9, ASTER, SRTM)
108
+ - NASA Earthdata (MODIS, ICESat-2, GEDI)
109
+ - Alaska SAR Facility (Sentinel-1 SLC, ALOS PALSAR)
110
+ - Planet Labs, Sentinel Hub, Maxar, Airbus, OpenTopography, and more
111
+
112
+ ---
113
+
114
+ ## [1.9.2] — 2026-06-15
115
+
116
+ ### Added
117
+ - Batch processing support for multiple scenes
118
+ - Parallel download with configurable thread count
119
+ - Progress tracking with ETA estimation
120
+ - Automatic retry on network failures (3 attempts)
121
+ - Checksum verification for downloaded files
122
+
123
+ ### Fixed
124
+ - Memory leak in large STAC search results
125
+ - Timeout issues with slow API responses
126
+ - Incorrect band ordering in Sentinel-2 L2A products
127
+ - CRS transformation for MODIS sinusoidal projection
128
+
129
+ ### Performance
130
+ - 40% faster STAC searches with pagination optimization
131
+ - 60% faster download with parallel connections
132
+ - Reduced memory usage for large raster processing
133
+
134
+ ---
135
+
136
+ ## [1.9.1] — 2026-05-20
137
+
138
+ ### Added
139
+ - Interactive 3D terrain visualization with PyVista
140
+ - Time series plotting for SBAS results
141
+ - Phase unwrapping with SNAPHU integration
142
+ - Coherence estimation with variable window sizes
143
+ - Automatic DEM download and clipping
144
+
145
+ ### Fixed
146
+ - GeoJSON parsing for irregular geometries
147
+ - Reprojection issues with rotated coordinate systems
148
+ - Nodata handling in raster arithmetic
149
+ - Date parsing for various timestamp formats
150
+
151
+ ### Dependencies
152
+ - Minimum numpy version: 1.21.0
153
+ - Minimum rasterio version: 1.2.0
154
+ - Optional: `pyvista[jupyter]` for 3D terrain
155
+
156
+ ---
157
+
158
+ ## [1.9.0] — 2026-04-25
159
+
160
+ ### Added
161
+ - Initial public release
162
+ - Core geospatial processing capabilities
163
+ - Multi-provider data search and download
164
+ - Basic SAR processing workflow
165
+ - Spectral index calculation suite
166
+ - Cloud-optimized GeoTIFF support
167
+ - Documentation and examples
168
+ - CI/CD pipeline with tests
169
+ - PyPI package distribution
170
+
171
+ ### Providers
172
+ - Copernicus Dataspace
173
+ - AWS Earth
174
+ - Planetary Computer
175
+ - USGS Earth Explorer
176
+
177
+ ### Features
178
+ - Search by polygon, date range, and cloud cover
179
+ - Download with progress tracking
180
+ - Reprojection and resampling
181
+ - Raster arithmetic and band math
182
+ - Time series analysis
183
+ - Visualization tools
184
+
185
+ ---
186
+
187
+ ## Summary of All Versions
188
+
189
+ | Version | Date | Key Changes |
190
+ |---------|------------|-------------|
191
+ | **1.9.4** | 2026-07-29 | SNAPHU unwrapping fixes, 87.1% reliable pixels |
192
+ | **1.9.3** | 2026-07-12 | 22 providers, 17 indices, Sentinel-1C/D |
193
+ | **1.9.2** | 2026-06-15 | Batch processing, parallel downloads |
194
+ | **1.9.1** | 2026-05-20 | 3D terrain, SNAPHU integration, time series |
195
+ | **1.9.0** | 2026-04-25 | Initial public release |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygeofetch
3
- Version: 1.9.2
3
+ Version: 1.9.3
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
@@ -25,10 +25,10 @@ License: MIT License
25
25
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
26
  SOFTWARE.
27
27
 
28
- Project-URL: Homepage, https://github.com/pygeofetch/pygeofetch
29
- Project-URL: Documentation, https://pygeofetch.readthedocs.io
30
- Project-URL: Repository, https://github.com/pygeofetch/pygeofetch
31
- Project-URL: Bug Tracker, https://github.com/pygeofetch/pygeofetch/issues
28
+ Project-URL: Homepage, https://github.com/EOCoreINT
29
+ Project-URL: Documentation, https://github.com/appiahkubis14/pygeofetch-docs
30
+ Project-URL: Repository, https://github.com/EOCoreINT/pygeofetch
31
+ Project-URL: Bug Tracker, https://github.com/EOCoreINT/pygeofetch/issues
32
32
  Keywords: satellite,remote-sensing,GIS,earth-observation,geospatial,SAR,Sentinel,Landsat
33
33
  Classifier: Development Status :: 5 - Production/Stable
34
34
  Classifier: Intended Audience :: Science/Research
@@ -55,6 +55,8 @@ Requires-Dist: requests>=2.31
55
55
  Requires-Dist: anyio>=4.2
56
56
  Requires-Dist: numpy>=1.26
57
57
  Requires-Dist: tqdm>=4.66
58
+ Requires-Dist: okada-wrapper
59
+ Requires-Dist: unlzw3
58
60
  Provides-Extra: geo
59
61
  Requires-Dist: rasterio>=1.3; extra == "geo"
60
62
  Requires-Dist: geopandas>=0.14; extra == "geo"
@@ -94,17 +96,18 @@ Requires-Dist: plotly>=5.14.0; extra == "viz"
94
96
  Requires-Dist: whitebox>=2.3.0; extra == "viz"
95
97
  Requires-Dist: whiteboxgui>=2.3.0; extra == "viz"
96
98
  Requires-Dist: geemap>=0.28.0; extra == "viz"
97
- Requires-Dist: osmnx>=1.9.2; extra == "viz"
98
- Requires-Dist: contextily>=1.9.2; extra == "viz"
99
+ Requires-Dist: osmnx>=1.9.3; extra == "viz"
100
+ Requires-Dist: contextily>=1.9.3; extra == "viz"
99
101
  Requires-Dist: xyzservices>=2023.10.0; extra == "viz"
100
102
  Requires-Dist: mapclassify>=2.5.0; extra == "viz"
101
103
  Requires-Dist: geoplot>=0.5.0; extra == "viz"
102
104
  Requires-Dist: mplleaflet>=0.0.5; extra == "viz"
103
105
  Requires-Dist: branca>=0.6.0; extra == "viz"
104
106
  Requires-Dist: plotly-geo>=1.0.0; extra == "viz"
105
- Requires-Dist: keplergl>=0.3.0; extra == "viz"
106
107
  Requires-Dist: streamlit>=1.28.0; extra == "viz"
107
108
  Requires-Dist: pyvista; extra == "viz"
109
+ Provides-Extra: viz-3d
110
+ Requires-Dist: keplergl>=0.3.0; extra == "viz-3d"
108
111
  Provides-Extra: cloud
109
112
  Requires-Dist: boto3>=1.34; extra == "cloud"
110
113
  Requires-Dist: pystac>=1.9; extra == "cloud"
@@ -126,8 +129,6 @@ Requires-Dist: twine>=5.0; extra == "dev"
126
129
  Provides-Extra: insar-full
127
130
  Requires-Dist: snaphu>=0.1; extra == "insar-full"
128
131
  Requires-Dist: scipy>=1.12; extra == "insar-full"
129
- Requires-Dist: mintpy>=1.5; extra == "insar-full"
130
- Requires-Dist: pyaps3>=0.3; extra == "insar-full"
131
132
  Provides-Extra: full
132
133
  Requires-Dist: pygeofetch[cloud,geo,notebook,processor,providers,sar,schedule,viz]; extra == "full"
133
134
  Provides-Extra: all
@@ -491,45 +492,136 @@ Every stage supports automatic visualization (`auto_visualize=True`) and optiona
491
492
 
492
493
  See [`pygeofetch/insar/README.md`](pygeofetch/insar/README.md) for the full processing chain, verification methodology, and current limitations.
493
494
 
494
-
495
495
  ```python
496
- # InSAR — search to SBAS time series
496
+ from pygeofetch.insar import InterferogramGenerator
497
+
498
+ gen = InterferogramGenerator(use_gpu=False)
499
+ result = gen.process_pair(
500
+ reference="slc_ref.tif",
501
+ secondary="slc_sec.tif",
502
+ dem="dem.tif",
503
+ reference_safe_zip="S1A_..._ref.SAFE.zip",
504
+ secondary_safe_zip="S1A_..._sec.SAFE.zip",
505
+ reference_orbit_file="ref.EOF",
506
+ secondary_orbit_file="sec.EOF",
507
+ )
508
+ result.save("./output", auto_visualize=True)
509
+ ```
510
+
511
+ ### Without the new orbit-based inputs (falls back safely)
512
+
513
+ ```python
514
+ result = gen.process_pair(reference="slc_ref.tif", secondary="slc_sec.tif", dem="dem.tif")
515
+ # Logs: "Using shape-based coregistration fallback ..."
516
+ ```
517
+
518
+ ### Full chain, search to SBAS
519
+
520
+ ```python
521
+ from pathlib import Path
522
+ import numpy as np
523
+ from pygeofetch import PyGeoFetch
524
+ from pygeofetch.models.search_query import SearchQuery, BoundingBox
525
+ from pygeofetch.models.download_task import DownloadOptions
526
+ from pygeofetch.processing.preprocessor import Preprocessor
527
+ from pygeofetch.core.orbits import fetch_orbit_file
497
528
  from pygeofetch.insar import (
498
- InterferogramGenerator, PhaseUnwrapper, AtmosphericCorrector,
499
- SBASTimeSeries, DataValidator,
529
+ SLCExtractor, InterferogramGenerator, AtmosphericCorrector,
530
+ PhaseUnwrapper, SBASTimeSeries, DataValidator, multilook,
500
531
  )
501
532
  from pygeofetch.insar.timeseries import InterferogramPair
502
- from pygeofetch.core.orbits import fetch_orbit_file
503
-
504
- # Coregistration + interferogram formation (real orbit-based when a DEM,
505
- # both SAFE archives, and both orbit files are supplied; falls back to
506
- # shape-based resampling otherwise)
533
+
534
+ client = PyGeoFetch()
535
+ client.add_credentials("copernicus", username="you@example.com", password="...")
536
+ client.add_credentials("opentopography", api_key="...")
537
+
538
+ aoi = BoundingBox(min_lon=-1.75, max_lon=-1.63, min_lat=6.15, max_lat=6.24)
539
+ output_dir = Path("./insar_output")
540
+
541
+ # Search — real geometry/bbox filtering, not the dead geometry_geojson= field
542
+ scenes = {}
543
+ for start, end in [("2026-01-01", "2026-01-08"), ("2026-01-13", "2026-01-20")]:
544
+ results = client.search(
545
+ SearchQuery(bbox=aoi, start_date=start, end_date=end,
546
+ product_type="SLC", polarisation="VV", max_results=1),
547
+ providers=["copernicus"],
548
+ )
549
+ if results:
550
+ scenes[str(results[0].datetime.date())] = results[0]
551
+
552
+ # Download
553
+ downloads = {
554
+ label: client.download([scene], destination=output_dir / "raw" / label,
555
+ options=DownloadOptions(resume=True))[0]
556
+ for label, scene in scenes.items()
557
+ }
558
+
559
+ # Real orbit files — use the real product name, not the catalog ID
560
+ orbits = {
561
+ label: fetch_orbit_file(
562
+ product_name=scene.properties.get("name", scene.id),
563
+ output_dir=str(output_dir / "orbits"), orbit_type="precise",
564
+ )
565
+ for label, scene in scenes.items()
566
+ }
567
+
568
+ # Real DEM, clipped to the AOI
569
+ dem_result = client.download(
570
+ client.search(SearchQuery(bbox=aoi), providers=["opentopography"])[:1],
571
+ destination=output_dir / "dem",
572
+ )[0]
573
+ dem_path = Preprocessor().clip(dem_result.output_path, bbox=aoi,
574
+ output=str(output_dir / "dem" / "clipped.tif")).output_path
575
+
576
+ # AOI-cropped extraction (not the full sub-swath)
577
+ extractor = SLCExtractor(polarisation="VV")
578
+ slcs = {
579
+ label: extractor.extract_scene(dl.output_path, aoi=aoi,
580
+ output_dir=output_dir / "slc" / label, label=label)
581
+ for label, dl in downloads.items()
582
+ }
583
+ dates = sorted(slcs)
584
+
585
+ # Interferogram formation — real orbit-based coregistration when all four
586
+ # inputs are available; falls back to shape-based resampling otherwise
507
587
  gen = InterferogramGenerator(coherence_window=5, esd_enabled=True, use_gpu=False)
588
+ d1, d2 = dates[0], dates[1]
508
589
  result = gen.process_pair(
509
- reference="slc_ref.tif", secondary="slc_sec.tif", dem="dem.tif",
510
- reference_safe_zip="ref.SAFE.zip", secondary_safe_zip="sec.SAFE.zip",
511
- reference_orbit_file=fetch_orbit_file("S1A_..._ref"),
512
- secondary_orbit_file=fetch_orbit_file("S1A_..._sec"),
590
+ reference=slcs[d1], secondary=slcs[d2], dem=dem_path,
591
+ reference_date=d1, secondary_date=d2,
592
+ reference_safe_zip=downloads[d1].output_path, secondary_safe_zip=downloads[d2].output_path,
593
+ reference_orbit_file=orbits[d1], secondary_orbit_file=orbits[d2],
513
594
  )
514
- result.save("./output", auto_visualize=True)
515
-
516
- # Atmospheric correction + phase unwrapping
595
+ result.save(output_dir / "interferograms", auto_visualize=True)
596
+
597
+ # Atmospheric correction — return_metadata=True to know what actually happened
517
598
  atm = AtmosphericCorrector(method="elevation")
518
- corrected_phase = atm.correct(result.interferogram, dem="dem.tif")
519
-
599
+ corrected_phase, atm_meta = atm.correct(
600
+ np.angle(result.interferogram), dem=dem_path, return_metadata=True
601
+ )
602
+
603
+ # Unwrapping — multilook first; wrapped_phase must be explicit (True for
604
+ # phase, False for coherence — never guessed from dtype)
605
+ phase_ml = multilook(corrected_phase, 4, 1, wrapped_phase=True)
606
+ coherence_ml = multilook(result.coherence, 4, 1, wrapped_phase=False)
520
607
  unwrapper = PhaseUnwrapper(cost_mode="defo", init_method="mcf")
521
- unwrapped, conncomp = unwrapper.unwrap(corrected_phase, result.coherence)
522
-
523
- # SBAS time series inversion across a network of interferograms
524
- pairs = [
525
- InterferogramPair("2026-01-01", "2026-01-13", unwrapped, result.coherence),
526
- # ... additional pairs forming a connected network
527
- ]
528
- sbas = SBASTimeSeries(wavelength_m=0.05546576, reference_date="2026-01-01")
529
- ts_result = sbas.invert(pairs, reference_pixel=(10, 15)) # known-stable pixel
530
- ts_result.save("./timeseries", auto_visualize=True)
608
+ unwrapped, conncomp = unwrapper.unwrap(phase_ml, coherence_ml, nlooks=4.0)
609
+
610
+ # SBAS inversion
611
+ pairs = [InterferogramPair(d1, d2, unwrapped, coherence_ml)]
612
+ network_check = DataValidator.validate_sbas_network(pairs, dates)
613
+ sbas = SBASTimeSeries(wavelength_m=0.05546576, reference_date=dates[0], use_gpu=False)
614
+ ts_result = sbas.invert(pairs, reference_pixel=(0, 0))
615
+ ts_result.save(output_dir / "timeseries", auto_visualize=True)
531
616
  print(f"Mean velocity: {ts_result.velocity.mean()*1000:.1f} mm/year")
532
617
  ```
618
+
619
+ <!-- A real, runnable two-date example — extend the search date list and repeat
620
+ the interferogram/unwrapping steps per consecutive pair for a full SBAS
621
+ network. See `23_accra_urban_deformation_sbas.ipynb` for the complete,
622
+ real, multi-date worked version of this exact chain. -->
623
+
624
+ ---
533
625
 
534
626
  ## 🖥️ Complete CLI Reference
535
627
 
@@ -355,45 +355,136 @@ Every stage supports automatic visualization (`auto_visualize=True`) and optiona
355
355
 
356
356
  See [`pygeofetch/insar/README.md`](pygeofetch/insar/README.md) for the full processing chain, verification methodology, and current limitations.
357
357
 
358
-
359
358
  ```python
360
- # InSAR — search to SBAS time series
359
+ from pygeofetch.insar import InterferogramGenerator
360
+
361
+ gen = InterferogramGenerator(use_gpu=False)
362
+ result = gen.process_pair(
363
+ reference="slc_ref.tif",
364
+ secondary="slc_sec.tif",
365
+ dem="dem.tif",
366
+ reference_safe_zip="S1A_..._ref.SAFE.zip",
367
+ secondary_safe_zip="S1A_..._sec.SAFE.zip",
368
+ reference_orbit_file="ref.EOF",
369
+ secondary_orbit_file="sec.EOF",
370
+ )
371
+ result.save("./output", auto_visualize=True)
372
+ ```
373
+
374
+ ### Without the new orbit-based inputs (falls back safely)
375
+
376
+ ```python
377
+ result = gen.process_pair(reference="slc_ref.tif", secondary="slc_sec.tif", dem="dem.tif")
378
+ # Logs: "Using shape-based coregistration fallback ..."
379
+ ```
380
+
381
+ ### Full chain, search to SBAS
382
+
383
+ ```python
384
+ from pathlib import Path
385
+ import numpy as np
386
+ from pygeofetch import PyGeoFetch
387
+ from pygeofetch.models.search_query import SearchQuery, BoundingBox
388
+ from pygeofetch.models.download_task import DownloadOptions
389
+ from pygeofetch.processing.preprocessor import Preprocessor
390
+ from pygeofetch.core.orbits import fetch_orbit_file
361
391
  from pygeofetch.insar import (
362
- InterferogramGenerator, PhaseUnwrapper, AtmosphericCorrector,
363
- SBASTimeSeries, DataValidator,
392
+ SLCExtractor, InterferogramGenerator, AtmosphericCorrector,
393
+ PhaseUnwrapper, SBASTimeSeries, DataValidator, multilook,
364
394
  )
365
395
  from pygeofetch.insar.timeseries import InterferogramPair
366
- from pygeofetch.core.orbits import fetch_orbit_file
367
-
368
- # Coregistration + interferogram formation (real orbit-based when a DEM,
369
- # both SAFE archives, and both orbit files are supplied; falls back to
370
- # shape-based resampling otherwise)
396
+
397
+ client = PyGeoFetch()
398
+ client.add_credentials("copernicus", username="you@example.com", password="...")
399
+ client.add_credentials("opentopography", api_key="...")
400
+
401
+ aoi = BoundingBox(min_lon=-1.75, max_lon=-1.63, min_lat=6.15, max_lat=6.24)
402
+ output_dir = Path("./insar_output")
403
+
404
+ # Search — real geometry/bbox filtering, not the dead geometry_geojson= field
405
+ scenes = {}
406
+ for start, end in [("2026-01-01", "2026-01-08"), ("2026-01-13", "2026-01-20")]:
407
+ results = client.search(
408
+ SearchQuery(bbox=aoi, start_date=start, end_date=end,
409
+ product_type="SLC", polarisation="VV", max_results=1),
410
+ providers=["copernicus"],
411
+ )
412
+ if results:
413
+ scenes[str(results[0].datetime.date())] = results[0]
414
+
415
+ # Download
416
+ downloads = {
417
+ label: client.download([scene], destination=output_dir / "raw" / label,
418
+ options=DownloadOptions(resume=True))[0]
419
+ for label, scene in scenes.items()
420
+ }
421
+
422
+ # Real orbit files — use the real product name, not the catalog ID
423
+ orbits = {
424
+ label: fetch_orbit_file(
425
+ product_name=scene.properties.get("name", scene.id),
426
+ output_dir=str(output_dir / "orbits"), orbit_type="precise",
427
+ )
428
+ for label, scene in scenes.items()
429
+ }
430
+
431
+ # Real DEM, clipped to the AOI
432
+ dem_result = client.download(
433
+ client.search(SearchQuery(bbox=aoi), providers=["opentopography"])[:1],
434
+ destination=output_dir / "dem",
435
+ )[0]
436
+ dem_path = Preprocessor().clip(dem_result.output_path, bbox=aoi,
437
+ output=str(output_dir / "dem" / "clipped.tif")).output_path
438
+
439
+ # AOI-cropped extraction (not the full sub-swath)
440
+ extractor = SLCExtractor(polarisation="VV")
441
+ slcs = {
442
+ label: extractor.extract_scene(dl.output_path, aoi=aoi,
443
+ output_dir=output_dir / "slc" / label, label=label)
444
+ for label, dl in downloads.items()
445
+ }
446
+ dates = sorted(slcs)
447
+
448
+ # Interferogram formation — real orbit-based coregistration when all four
449
+ # inputs are available; falls back to shape-based resampling otherwise
371
450
  gen = InterferogramGenerator(coherence_window=5, esd_enabled=True, use_gpu=False)
451
+ d1, d2 = dates[0], dates[1]
372
452
  result = gen.process_pair(
373
- reference="slc_ref.tif", secondary="slc_sec.tif", dem="dem.tif",
374
- reference_safe_zip="ref.SAFE.zip", secondary_safe_zip="sec.SAFE.zip",
375
- reference_orbit_file=fetch_orbit_file("S1A_..._ref"),
376
- secondary_orbit_file=fetch_orbit_file("S1A_..._sec"),
453
+ reference=slcs[d1], secondary=slcs[d2], dem=dem_path,
454
+ reference_date=d1, secondary_date=d2,
455
+ reference_safe_zip=downloads[d1].output_path, secondary_safe_zip=downloads[d2].output_path,
456
+ reference_orbit_file=orbits[d1], secondary_orbit_file=orbits[d2],
377
457
  )
378
- result.save("./output", auto_visualize=True)
379
-
380
- # Atmospheric correction + phase unwrapping
458
+ result.save(output_dir / "interferograms", auto_visualize=True)
459
+
460
+ # Atmospheric correction — return_metadata=True to know what actually happened
381
461
  atm = AtmosphericCorrector(method="elevation")
382
- corrected_phase = atm.correct(result.interferogram, dem="dem.tif")
383
-
462
+ corrected_phase, atm_meta = atm.correct(
463
+ np.angle(result.interferogram), dem=dem_path, return_metadata=True
464
+ )
465
+
466
+ # Unwrapping — multilook first; wrapped_phase must be explicit (True for
467
+ # phase, False for coherence — never guessed from dtype)
468
+ phase_ml = multilook(corrected_phase, 4, 1, wrapped_phase=True)
469
+ coherence_ml = multilook(result.coherence, 4, 1, wrapped_phase=False)
384
470
  unwrapper = PhaseUnwrapper(cost_mode="defo", init_method="mcf")
385
- unwrapped, conncomp = unwrapper.unwrap(corrected_phase, result.coherence)
386
-
387
- # SBAS time series inversion across a network of interferograms
388
- pairs = [
389
- InterferogramPair("2026-01-01", "2026-01-13", unwrapped, result.coherence),
390
- # ... additional pairs forming a connected network
391
- ]
392
- sbas = SBASTimeSeries(wavelength_m=0.05546576, reference_date="2026-01-01")
393
- ts_result = sbas.invert(pairs, reference_pixel=(10, 15)) # known-stable pixel
394
- ts_result.save("./timeseries", auto_visualize=True)
471
+ unwrapped, conncomp = unwrapper.unwrap(phase_ml, coherence_ml, nlooks=4.0)
472
+
473
+ # SBAS inversion
474
+ pairs = [InterferogramPair(d1, d2, unwrapped, coherence_ml)]
475
+ network_check = DataValidator.validate_sbas_network(pairs, dates)
476
+ sbas = SBASTimeSeries(wavelength_m=0.05546576, reference_date=dates[0], use_gpu=False)
477
+ ts_result = sbas.invert(pairs, reference_pixel=(0, 0))
478
+ ts_result.save(output_dir / "timeseries", auto_visualize=True)
395
479
  print(f"Mean velocity: {ts_result.velocity.mean()*1000:.1f} mm/year")
396
480
  ```
481
+
482
+ <!-- A real, runnable two-date example — extend the search date list and repeat
483
+ the interferogram/unwrapping steps per consecutive pair for a full SBAS
484
+ network. See `23_accra_urban_deformation_sbas.ipynb` for the complete,
485
+ real, multi-date worked version of this exact chain. -->
486
+
487
+ ---
397
488
 
398
489
  ## 🖥️ Complete CLI Reference
399
490
 
@@ -30,7 +30,7 @@ CLI::
30
30
  pygeofetch doctor
31
31
  """
32
32
 
33
- __version__ = "1.9.2"
33
+ __version__ = "1.9.3"
34
34
  __author__ = "PyGeoFetch Contributors"
35
35
  __license__ = "MIT"
36
36