PyGeoFetch 2.0.5__tar.gz → 2.0.7__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 (144) hide show
  1. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/CHANGELOG.md +6 -6
  2. {pygeofetch-2.0.5/pygeofetch.egg-info → pygeofetch-2.0.7}/PKG-INFO +1 -1
  3. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/__init__.py +2 -2
  4. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/cli/main.py +2 -2
  5. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/unwrap.py +94 -41
  6. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/meta.yaml +1 -1
  7. {pygeofetch-2.0.5 → pygeofetch-2.0.7/pygeofetch.egg-info}/PKG-INFO +1 -1
  8. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pyproject.toml +1 -1
  9. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/LICENSE +0 -0
  10. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/MANIFEST.in +0 -0
  11. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/README.md +0 -0
  12. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/cli/__init__.py +0 -0
  13. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/cli/auth_commands.py +0 -0
  14. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/cli/config_commands.py +0 -0
  15. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/cli/download_commands.py +0 -0
  16. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/cli/index_commands.py +0 -0
  17. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/cli/pipeline_process_commands.py +0 -0
  18. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/cli/postprocess_commands.py +0 -0
  19. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/cli/preprocess_commands.py +0 -0
  20. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/cli/sar_commands.py +0 -0
  21. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/cli/search_commands.py +0 -0
  22. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/config/__init__.py +0 -0
  23. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/config/defaults.yaml +0 -0
  24. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/config/settings.py +0 -0
  25. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/core/__init__.py +0 -0
  26. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/core/authenticator.py +0 -0
  27. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/core/cache_manager.py +0 -0
  28. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/core/downloader.py +0 -0
  29. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/core/engine.py +0 -0
  30. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/core/logging.py +0 -0
  31. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/core/orbits.py +0 -0
  32. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/core/scheduler.py +0 -0
  33. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/core/searcher.py +0 -0
  34. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/__init__.py +0 -0
  35. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/annotation.py +0 -0
  36. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/atmosphere.py +0 -0
  37. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/coregister.py +0 -0
  38. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/deburst.py +0 -0
  39. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/esd.py +0 -0
  40. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/extraction.py +0 -0
  41. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/flatearth.py +0 -0
  42. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/geolocation.py +0 -0
  43. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/gpu.py +0 -0
  44. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/interferogram.py +0 -0
  45. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/ionosphere.py +0 -0
  46. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/splitspectrum_diagnostic.py +0 -0
  47. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/stack_selection.py +0 -0
  48. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/synthetic.py +0 -0
  49. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/timeseries.py +0 -0
  50. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/validate.py +0 -0
  51. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/visualize.py +0 -0
  52. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/insar/workflow.py +0 -0
  53. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/models/__init__.py +0 -0
  54. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/models/download_task.py +0 -0
  55. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/models/satellite_data.py +0 -0
  56. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/models/search_query.py +0 -0
  57. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/models/user_auth.py +0 -0
  58. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/processing/__init__.py +0 -0
  59. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/processing/base.py +0 -0
  60. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/processing/batch.py +0 -0
  61. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/processing/indices.py +0 -0
  62. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/processing/pipeline.py +0 -0
  63. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/processing/postprocessor.py +0 -0
  64. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/processing/preprocessor.py +0 -0
  65. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/processing/sar.py +0 -0
  66. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/processor/__init__.py +0 -0
  67. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/processor/indices.py +0 -0
  68. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/processor/landsat.py +0 -0
  69. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/processor/loader.py +0 -0
  70. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/processor/stacker.py +0 -0
  71. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/processor/timeseries.py +0 -0
  72. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/__init__.py +0 -0
  73. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/airbus_oneatlas.py +0 -0
  74. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/alaska_satellite_facility.py +0 -0
  75. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/aws_earth.py +0 -0
  76. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/base.py +0 -0
  77. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/copernicus.py +0 -0
  78. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/digitalglobe.py +0 -0
  79. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/earth_explorer_additional.py +0 -0
  80. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/element84.py +0 -0
  81. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/eodag_provider.py +0 -0
  82. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/esa_scihub.py +0 -0
  83. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/geoserver_generic.py +0 -0
  84. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/google_earth_engine.py +0 -0
  85. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/inpe_cbers.py +0 -0
  86. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/isro_bhuvan.py +0 -0
  87. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/jaxa_earth.py +0 -0
  88. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/maxar_gbdx.py +0 -0
  89. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/nasa_earthdata.py +0 -0
  90. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/nasa_earthdata_cloud.py +0 -0
  91. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/noaa_big_data.py +0 -0
  92. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/opentopography.py +0 -0
  93. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/planet.py +0 -0
  94. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/planetary_computer.py +0 -0
  95. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/sentinel_hub.py +0 -0
  96. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/terrabotics.py +0 -0
  97. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/providers/usgs.py +0 -0
  98. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/sar/__init__.py +0 -0
  99. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/sar/_native.py +0 -0
  100. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/sar/_ost.py +0 -0
  101. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/sar/_sarxarray.py +0 -0
  102. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/sar/extraction.py +0 -0
  103. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/sar/processor.py +0 -0
  104. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/utils/__init__.py +0 -0
  105. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/utils/file_utils.py +0 -0
  106. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/utils/gcp_recovery.py +0 -0
  107. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/utils/geo_utils.py +0 -0
  108. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/utils/logging_setup.py +0 -0
  109. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/utils/retry_handler.py +0 -0
  110. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/utils/validators.py +0 -0
  111. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/viz/__init__.py +0 -0
  112. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/viz/map.py +0 -0
  113. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch/viz/plot.py +0 -0
  114. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch.egg-info/SOURCES.txt +0 -0
  115. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch.egg-info/dependency_links.txt +0 -0
  116. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch.egg-info/entry_points.txt +0 -0
  117. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch.egg-info/not-zip-safe +0 -0
  118. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch.egg-info/requires.txt +0 -0
  119. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/pygeofetch.egg-info/top_level.txt +0 -0
  120. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/setup.cfg +0 -0
  121. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_atmospheric_circular_regression (1).py +0 -0
  122. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_atmospheric_circular_regression.py +0 -0
  123. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_build_sbas_network.py +0 -0
  124. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_burst_metadata.py +0 -0
  125. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_burst_processing_integration.py +0 -0
  126. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_coregister_integration.py +0 -0
  127. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_coregister_upgrades.py +0 -0
  128. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_era5_atmospheric_correction.py +0 -0
  129. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_extraction.py +0 -0
  130. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_flat_earth_phase.py +0 -0
  131. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_gcp_reprojection.py +0 -0
  132. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_insar.py +0 -0
  133. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_ionosphere.py +0 -0
  134. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_models.py +0 -0
  135. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_pgv_integration_contract.py +0 -0
  136. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_provider_geometry_audit (1).py +0 -0
  137. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_provider_geometry_audit.py +0 -0
  138. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_provider_scene_disambiguation.py +0 -0
  139. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_providers.py +0 -0
  140. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_raster_collocation_integration.py +0 -0
  141. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_search_download.py +0 -0
  142. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_splitspectrum_diagnostic.py +0 -0
  143. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_utils.py +0 -0
  144. {pygeofetch-2.0.5 → pygeofetch-2.0.7}/tests/test_viz_plot.py +0 -0
@@ -1,9 +1,9 @@
1
1
  # Changelog
2
2
 
3
3
  All notable changes to PyGeoFetch are documented here.
4
- Format follows [Keep a Changelog](https://keepachangelog.com/en/2.0.5/).
4
+ Format follows [Keep a Changelog](https://keepachangelog.com/en/2.0.7/).
5
5
 
6
- ## [2.0.5] — 2026-07-29
6
+ ## [2.0.7] — 2026-07-29
7
7
 
8
8
  ### Added
9
9
  - Major SNAPHU unwrapping improvements for urban InSAR applications
@@ -51,7 +51,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/2.0.5/).
51
51
 
52
52
  ### Migration Guide
53
53
  ```python
54
- # Before (v2.0.5)
54
+ # Before (v2.0.7)
55
55
  unwrapper = PhaseUnwrapper(cost_mode="defo", init_method="mcf")
56
56
  unwrapped = unwrapper.unwrap(phase, coherence, nlooks=2.0)
57
57
 
@@ -72,7 +72,7 @@ unwrapped = unwrapper.unwrap(
72
72
 
73
73
  ---
74
74
 
75
- ## [2.0.5] — 2026-07-12
75
+ ## [2.0.7] — 2026-07-12
76
76
 
77
77
  ### Added
78
78
  - Federated search across 22+ satellite data providers
@@ -188,8 +188,8 @@ unwrapped = unwrapper.unwrap(
188
188
 
189
189
  | Version | Date | Key Changes |
190
190
  |---------|------------|-------------|
191
- | **2.0.5** | 2026-07-29 | SNAPHU unwrapping fixes, 87.1% reliable pixels |
192
- | **2.0.5** | 2026-07-12 | 22 providers, 17 indices, Sentinel-1C/D |
191
+ | **2.0.7** | 2026-07-29 | SNAPHU unwrapping fixes, 87.1% reliable pixels |
192
+ | **2.0.7** | 2026-07-12 | 22 providers, 17 indices, Sentinel-1C/D |
193
193
  | **1.9.2** | 2026-06-15 | Batch processing, parallel downloads |
194
194
  | **1.9.1** | 2026-05-20 | 3D terrain, SNAPHU integration, time series |
195
195
  | **1.9.0** | 2026-04-25 | Initial public release |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygeofetch
3
- Version: 2.0.5
3
+ Version: 2.0.7
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
@@ -1,5 +1,5 @@
1
1
  """
2
- PyGeoFetch v2.0.5 — Universal Satellite Data Pipeline.
2
+ PyGeoFetch v2.0.7 — Universal Satellite Data Pipeline.
3
3
 
4
4
  Unified access to 22+ satellite data providers with a consistent
5
5
  Python API and CLI. Supports authentication management, federated search,
@@ -30,7 +30,7 @@ CLI::
30
30
  pygeofetch doctor
31
31
  """
32
32
 
33
- __version__ = "2.0.5"
33
+ __version__ = "2.0.7"
34
34
  __author__ = "PyGeoFetch Contributors"
35
35
  __license__ = "MIT"
36
36
 
@@ -1,5 +1,5 @@
1
1
  """
2
- PyGeoFetch CLI v2.0.5 main entry point.
2
+ PyGeoFetch CLI v2.0.7 main entry point.
3
3
 
4
4
  Full command reference:
5
5
  pygeofetch auth — credential management
@@ -71,7 +71,7 @@ def cli(
71
71
  ) -> None:
72
72
  """
73
73
  \b
74
- PyGeoFetch v2.0.5 — Universal Satellite Data Pipeline
74
+ PyGeoFetch v2.0.7 — Universal Satellite Data Pipeline
75
75
  ===========================================================
76
76
  Unified access to 22+ satellite data providers.
77
77
 
@@ -214,7 +214,10 @@ def _ensure_snaphu_cli() -> str:
214
214
  # 3. Download if on Windows
215
215
  if platform.system() == "Windows":
216
216
  logger.info("SNAPHU executable not found. Downloading pre-compiled binary for Windows...")
217
- api_url = "https://api.github.com/repos/isce-framework/snaphu-py/releases/latest"
217
+
218
+ # The official isce-framework/snaphu-py does not provide Windows wheels/binaries.
219
+ # We use marsfan/SNAPHU-win which reliably hosts pre-compiled Windows binaries.
220
+ api_url = "https://api.github.com/repos/marsfan/SNAPHU-win/releases/latest"
218
221
  asset_url = None
219
222
 
220
223
  try:
@@ -224,29 +227,15 @@ def _ensure_snaphu_cli() -> str:
224
227
 
225
228
  for asset in data.get("assets", []):
226
229
  name = asset["name"].lower()
227
- if "win" in name and name.endswith(".zip"):
230
+ if name.endswith(".zip") and "snaphu" in name:
228
231
  asset_url = asset["browser_download_url"]
229
232
  break
230
-
231
- if not asset_url:
232
- # Fallback to scottstanie's repo which often hosts the windows binaries
233
- api_url2 = "https://api.github.com/repos/scottstanie/snaphu-py/releases/latest"
234
- req2 = urllib.request.Request(api_url2, headers={'User-Agent': 'PyGeoFetch'})
235
- with urllib.request.urlopen(req2, timeout=15) as response2:
236
- data2 = json.loads(response2.read().decode())
237
- for asset in data2.get("assets", []):
238
- if "win" in asset["name"].lower() and asset["name"].endswith(".zip"):
239
- asset_url = asset["browser_download_url"]
240
- break
241
233
  except Exception as e:
242
234
  logger.warning(f"Could not query GitHub API for SNAPHU binary: {e}")
243
235
 
244
236
  if not asset_url:
245
- raise RuntimeError(
246
- "Could not find a pre-compiled SNAPHU executable for Windows.\n"
247
- "Please install it manually and add it to your PATH, or install via pip:\n"
248
- " pip install snaphu"
249
- )
237
+ # Hardcoded fallback to the known working 2.0.7 release
238
+ asset_url = "https://github.com/marsfan/SNAPHU-win/releases/download/2.0.7/snaphu-v2.0.7.zip"
250
239
 
251
240
  logger.info(f"Downloading SNAPHU from {asset_url}...")
252
241
  try:
@@ -255,7 +244,10 @@ def _ensure_snaphu_cli() -> str:
255
244
  urllib.request.urlretrieve(asset_url, str(zip_path))
256
245
 
257
246
  with zipfile.ZipFile(zip_path, 'r') as zf:
258
- exe_files = [f for f in zf.namelist() if f.lower().endswith(".exe")]
247
+ # Look for snaphu.exe inside the zip (might be in a subfolder)
248
+ exe_files = [f for f in zf.namelist() if f.lower().endswith(".exe") and "snaphu" in f.lower()]
249
+ if not exe_files:
250
+ exe_files = [f for f in zf.namelist() if f.lower().endswith(".exe")]
259
251
  if not exe_files:
260
252
  raise RuntimeError("Downloaded zip did not contain an .exe file.")
261
253
 
@@ -276,7 +268,6 @@ def _ensure_snaphu_cli() -> str:
276
268
  "Or install the Python bindings: pip install snaphu"
277
269
  )
278
270
 
279
-
280
271
  def goldstein_filter(
281
272
  interferogram,
282
273
  alpha: float = 0.5,
@@ -461,13 +452,14 @@ class PhaseUnwrapper:
461
452
  """
462
453
  Fallback unwrapping using the SNAPHU CLI executable.
463
454
  Bypasses Python C-extension compilation issues on Windows entirely.
455
+ Includes auto-repair for the infamous 0xC0000135 (STATUS_DLL_NOT_FOUND) error.
464
456
  """
465
457
  import numpy as np
466
458
 
467
459
  cli_path = _ensure_snaphu_cli()
468
460
  height, width = igram.shape
469
461
 
470
- with tempfile.TemporaryDirectory() as tmp_dir:
462
+ def _run_snaphu(tmp_dir):
471
463
  tmp = Path(tmp_dir)
472
464
  igram_path = tmp / "igram.cpx"
473
465
  corr_path = tmp / "corr.cor"
@@ -482,29 +474,47 @@ class PhaseUnwrapper:
482
474
  # Write config file using relative paths to avoid Windows space issues
483
475
  nlooks_int = max(1, int(nlooks))
484
476
  conf_text = f"""INFILE igram.cpx
485
- INFILEFORMAT COMPLEX_DATA
486
- CORRFILE corr.cor
487
- CORRFILEFORMAT FLOAT_DATA
488
- OUTFILE unw.unw
489
- OUTFILEFORMAT FLOAT_DATA
490
- CONNCOMPFILE conncomp.cc
491
- CONNCOMPOUTFORMAT UINT_DATA
492
- LINELENGTH {width}
493
- STATCOSTMODE {self._cost_mode.upper()}
494
- INITMETHOD {self._init_method.upper()}
495
- NLOOKSRANGE {nlooks_int}
496
- NLOOKSAZ {nlooks_int}
497
- """
477
+ INFILEFORMAT COMPLEX_DATA
478
+ CORRFILE corr.cor
479
+ CORRFILEFORMAT FLOAT_DATA
480
+ OUTFILE unw.unw
481
+ OUTFILEFORMAT FLOAT_DATA
482
+ CONNCOMPFILE conncomp.cc
483
+ CONNCOMPOUTFORMAT UINT_DATA
484
+ LINELENGTH {width}
485
+ STATCOSTMODE {self._cost_mode.upper()}
486
+ INITMETHOD {self._init_method.upper()}
487
+ NLOOKSRANGE {nlooks_int}
488
+ NLOOKSAZ {nlooks_int}
489
+ """
498
490
  conf_path.write_text(conf_text)
499
491
 
500
492
  # Run SNAPHU from within the temp directory
501
- try:
502
- cmd = [cli_path, "-f", "snaphu.conf"]
503
- result = subprocess.run(cmd, capture_output=True, text=True, cwd=str(tmp), timeout=3600)
504
- if result.returncode != 0:
505
- raise RuntimeError(f"SNAPHU CLI failed with code {result.returncode}: {result.stderr}")
506
- except Exception as e:
507
- raise RuntimeError(f"SNAPHU CLI execution failed: {e}")
493
+ cmd = [cli_path, "-f", "snaphu.conf"]
494
+ result = subprocess.run(cmd, capture_output=True, text=True, cwd=str(tmp), timeout=3600)
495
+ return result, unw_path, cc_path
496
+
497
+ with tempfile.TemporaryDirectory() as tmp_dir:
498
+ result, unw_path, cc_path = _run_snaphu(tmp_dir)
499
+
500
+ # ═══════════════════════════════════════════════════════════════
501
+ # AUTO-REPAIR: Catch Windows STATUS_DLL_NOT_FOUND (0xC0000135)
502
+ # 3221225781 is the unsigned int representation of 0xC0000135
503
+ # -1073741515 is the signed int representation. We check both.
504
+ # ═══════════════════════════════════════════════════════════════
505
+ if result.returncode in (3221225781, -1073741515):
506
+ logger.warning(
507
+ "SNAPHU crashed with STATUS_DLL_NOT_FOUND (0xC0000135). "
508
+ "This means the Microsoft Visual C++ Redistributable is missing."
509
+ )
510
+ self._ensure_msvc_runtime()
511
+
512
+ # Retry after installation
513
+ logger.info("Retrying SNAPHU after MSVC runtime installation...")
514
+ result, unw_path, cc_path = _run_snaphu(tmp_dir)
515
+
516
+ if result.returncode != 0:
517
+ raise RuntimeError(f"SNAPHU CLI failed with code {result.returncode}: {result.stderr}")
508
518
 
509
519
  # Read outputs
510
520
  unw = np.fromfile(str(unw_path), dtype=np.float32).reshape((height, width))
@@ -521,6 +531,49 @@ class PhaseUnwrapper:
521
531
 
522
532
  return unw, conncomp.astype(np.int32)
523
533
 
534
+ def _ensure_msvc_runtime(self):
535
+ """
536
+ Fixes the 0xC0000135 (STATUS_DLL_NOT_FOUND) error by ensuring the
537
+ Microsoft Visual C++ Redistributable is installed.
538
+ """
539
+ import ctypes
540
+
541
+ # Check if vcruntime140.dll is already available in the system PATH
542
+ try:
543
+ ctypes.WinDLL("vcruntime140.dll")
544
+ return # Already installed
545
+ except OSError:
546
+ pass
547
+
548
+ logger.info("Downloading official Microsoft Visual C++ Redistributable...")
549
+ vc_url = "https://aka.ms/vs/17/release/vc_redist.x64.exe"
550
+
551
+ with tempfile.TemporaryDirectory() as tmp_dir:
552
+ installer_path = Path(tmp_dir) / "vc_redist.x64.exe"
553
+ try:
554
+ urllib.request.urlretrieve(vc_url, str(installer_path))
555
+ except Exception as e:
556
+ raise RuntimeError(
557
+ f"Failed to download MSVC installer: {e}\n"
558
+ "Please install manually: https://aka.ms/vs/17/release/vc_redist.x64.exe"
559
+ )
560
+
561
+ logger.info("Installing MSVC runtime (this may prompt for Administrator permission)...")
562
+ try:
563
+ # /quiet = no UI, /norestart = no reboot prompt, /install = install
564
+ result = subprocess.run(
565
+ [str(installer_path), "/install", "/quiet", "/norestart"],
566
+ timeout=120
567
+ )
568
+ # Return code 0 = success, 3010 = success but reboot required
569
+ if result.returncode not in (0, 3010):
570
+ logger.warning(f"Installer returned code {result.returncode}. You may need to install manually.")
571
+ except Exception as e:
572
+ raise RuntimeError(
573
+ f"Failed to run MSVC installer: {e}\n"
574
+ "Please install manually: https://aka.ms/vs/17/release/vc_redist.x64.exe"
575
+ )
576
+
524
577
  def unwrap(
525
578
  self,
526
579
  interferogram: Any,
@@ -1,4 +1,4 @@
1
- {% set version = "2.0.5" %}
1
+ {% set version = "2.0.7" %}
2
2
 
3
3
  package:
4
4
  name: pygeofetch
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygeofetch
3
- Version: 2.0.5
3
+ Version: 2.0.7
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pygeofetch"
7
- version = "2.0.5"
7
+ version = "2.0.7"
8
8
  description = "Universal satellite data pipeline + geospatial processing platform"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
File without changes
File without changes
File without changes
File without changes