PyGeoFetch 1.2.0__tar.gz → 1.4.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 (112) hide show
  1. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/CHANGELOG.md +2 -2
  2. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/LICENSE +1 -1
  3. {pygeofetch-1.2.0/pygeofetch.egg-info → pygeofetch-1.4.0}/PKG-INFO +22 -3
  4. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/README.md +19 -0
  5. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/__init__.py +1 -1
  6. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/cli/search_commands.py +30 -1
  7. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/core/authenticator.py +1 -1
  8. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/core/downloader.py +398 -54
  9. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/core/logging.py +1 -1
  10. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/core/orbits.py +1 -1
  11. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/insar/__init__.py +1 -1
  12. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/insar/atmosphere.py +1 -1
  13. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/insar/interferogram.py +1 -1
  14. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/insar/timeseries.py +1 -1
  15. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/meta.yaml +1 -1
  16. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/models/satellite_data.py +2 -2
  17. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/processing/preprocessor.py +152 -8
  18. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/processing/sar.py +45 -8
  19. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/processor/__init__.py +4 -1
  20. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/processor/landsat.py +71 -12
  21. pygeofetch-1.4.0/pygeofetch/processor/timeseries.py +436 -0
  22. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/airbus_oneatlas.py +20 -4
  23. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/aws_earth.py +36 -8
  24. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/copernicus.py +88 -8
  25. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/esa_scihub.py +20 -4
  26. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/google_earth_engine.py +20 -4
  27. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/usgs.py +162 -16
  28. pygeofetch-1.4.0/pygeofetch/sar/__init__.py +22 -0
  29. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/sar/_native.py +5 -4
  30. pygeofetch-1.4.0/pygeofetch/sar/extraction.py +189 -0
  31. pygeofetch-1.4.0/pygeofetch/viz/plot.py +657 -0
  32. {pygeofetch-1.2.0 → pygeofetch-1.4.0/pygeofetch.egg-info}/PKG-INFO +22 -3
  33. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch.egg-info/SOURCES.txt +4 -1
  34. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pyproject.toml +4 -2
  35. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/tests/test_insar.py +1 -1
  36. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/tests/test_pgv_integration_contract.py +1 -1
  37. pygeofetch-1.4.0/tests/test_viz_plot.py +321 -0
  38. pygeofetch-1.2.0/pygeofetch/sar/__init__.py +0 -18
  39. pygeofetch-1.2.0/pygeofetch/viz/plot.py +0 -239
  40. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/MANIFEST.in +0 -0
  41. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/cli/__init__.py +0 -0
  42. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/cli/auth_commands.py +0 -0
  43. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/cli/config_commands.py +0 -0
  44. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/cli/download_commands.py +0 -0
  45. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/cli/index_commands.py +0 -0
  46. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/cli/main.py +0 -0
  47. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/cli/pipeline_process_commands.py +0 -0
  48. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/cli/postprocess_commands.py +0 -0
  49. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/cli/preprocess_commands.py +0 -0
  50. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/cli/sar_commands.py +0 -0
  51. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/config/__init__.py +0 -0
  52. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/config/defaults.yaml +0 -0
  53. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/config/settings.py +0 -0
  54. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/core/__init__.py +0 -0
  55. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/core/cache_manager.py +0 -0
  56. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/core/engine.py +0 -0
  57. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/core/scheduler.py +0 -0
  58. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/core/searcher.py +0 -0
  59. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/insar/extraction.py +0 -0
  60. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/insar/unwrap.py +0 -0
  61. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/models/__init__.py +0 -0
  62. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/models/download_task.py +0 -0
  63. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/models/search_query.py +0 -0
  64. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/models/user_auth.py +0 -0
  65. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/processing/__init__.py +0 -0
  66. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/processing/base.py +0 -0
  67. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/processing/batch.py +0 -0
  68. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/processing/indices.py +0 -0
  69. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/processing/pipeline.py +0 -0
  70. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/processing/postprocessor.py +0 -0
  71. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/processor/indices.py +0 -0
  72. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/processor/loader.py +0 -0
  73. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/processor/stacker.py +0 -0
  74. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/__init__.py +0 -0
  75. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/alaska_satellite_facility.py +0 -0
  76. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/base.py +0 -0
  77. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/digitalglobe.py +0 -0
  78. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/earth_explorer_additional.py +0 -0
  79. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/element84.py +0 -0
  80. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/eodag_provider.py +0 -0
  81. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/geoserver_generic.py +0 -0
  82. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/inpe_cbers.py +0 -0
  83. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/isro_bhuvan.py +0 -0
  84. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/jaxa_earth.py +0 -0
  85. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/maxar_gbdx.py +0 -0
  86. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/nasa_earthdata.py +0 -0
  87. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/nasa_earthdata_cloud.py +0 -0
  88. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/noaa_big_data.py +0 -0
  89. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/opentopography.py +0 -0
  90. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/planet.py +0 -0
  91. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/planetary_computer.py +0 -0
  92. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/sentinel_hub.py +0 -0
  93. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/providers/terrabotics.py +0 -0
  94. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/sar/_ost.py +0 -0
  95. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/sar/_sarxarray.py +0 -0
  96. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/sar/processor.py +0 -0
  97. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/utils/__init__.py +0 -0
  98. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/utils/file_utils.py +0 -0
  99. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/utils/gcp_recovery.py +0 -0
  100. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/utils/geo_utils.py +0 -0
  101. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/utils/logging_setup.py +0 -0
  102. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/utils/retry_handler.py +0 -0
  103. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/utils/validators.py +0 -0
  104. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/viz/__init__.py +0 -0
  105. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch/viz/map.py +0 -0
  106. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch.egg-info/dependency_links.txt +0 -0
  107. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch.egg-info/entry_points.txt +0 -0
  108. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch.egg-info/not-zip-safe +0 -0
  109. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch.egg-info/requires.txt +0 -0
  110. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/pygeofetch.egg-info/top_level.txt +0 -0
  111. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/setup.cfg +0 -0
  112. {pygeofetch-1.2.0 → pygeofetch-1.4.0}/tests/test_extraction.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/1.0.0/).
4
+ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.4.0/).
5
5
 
6
- ## [1.0.0] — 2026-07-12
6
+ ## [1.4.0] — 2026-07-12
7
7
 
8
8
  ### Added
9
9
  - Federated search across 22+ satellite data providers
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 SatelliteBridge Contributors
3
+ Copyright (c) 2026 Samuel Appiah Kubi
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygeofetch
3
- Version: 1.2.0
3
+ Version: 1.4.0
4
4
  Summary: Universal satellite data pipeline + geospatial processing platform
5
- Author-email: pygeofetch Contributors <pygeofetch@example.com>
5
+ Author-email: Samuel Appiah Kubi <appiahkubis14@gmail.com>
6
6
  License: MIT License
7
7
 
8
- Copyright (c) 2024 SatelliteBridge Contributors
8
+ Copyright (c) 2026 Samuel Appiah Kubi
9
9
 
10
10
  Permission is hereby granted, free of charge, to any person obtaining a copy
11
11
  of this software and associated documentation files (the "Software"), to deal
@@ -605,6 +605,25 @@ jupyter lab
605
605
 
606
606
  ---
607
607
 
608
+ <p align="center">
609
+ <img src="docs/images/trend_map.png" width="48%" />
610
+ <img src="docs/images/trend_classification.png" width="48%" />
611
+ </p>
612
+ <p align="center"><em>NDVI trend (2018–2024) and severity classification for the Obuasi Municipal District, computed end-to-end with PyGeoFetch — real USGS Landsat data, boundary-clipped before processing.</em></p>
613
+
614
+ 32.0% of the Obuasi Municipal District shows measurable vegetation decline over 2018–2024 (9.2% strong decline + 22.8% moderate decline), against 57.5% stable and 10.5% showing an increasing trend.
615
+
616
+ That headline number matters less than where it's concentrated. The decline isn't scattered randomly across the district — it forms a clear, spatially coherent cluster in the western portion of the AOI (roughly west of -1.70° longitude), visible as a dense, contiguous red-and-orange zone in both the trend map and the classification map. Real land degradation signals tend to look like this — clustered along mining concessions, roads, or river corridors — whereas sensor noise or processing artifacts tend to scatter more randomly across the whole scene. The fact that this pattern holds together spatially is a reasonable indicator that it reflects something real on the ground, not just pixel-level noise.
617
+
618
+ That said, an NDVI trend map on its own can't distinguish why vegetation declined — illegal small-scale mining (galamsey), selective logging, agricultural land clearing, and urban expansion can all produce a similar signature. Given Obuasi's well-documented galamsey activity specifically concentrated in this district, the spatial pattern here is consistent with mining-driven clearance — but confirming that specific cause would need either higher-resolution imagery, ground verification, or cross-referencing against known concession boundaries, not this analysis alone.
619
+
620
+ Two things worth flagging honestly rather than glossing over:
621
+
622
+ The scattered dark-blue linear and blob features running through the map (most visibly the river-like feature crossing the upper-central area) are very likely water bodies, not vegetation gain. NDVI trend over open water isn't a meaningful signal — surface reflectance there is driven by turbidity and water level, not plant health — so those features should be excluded from the "increasing" interpretation, not read as reforestation.
623
+ The small white gaps scattered through the trend map (several distinct circular patches) are nodata — pixels that didn't have enough valid, cloud-free observations across all six dates to fit a reliable trend. Worth being upfront that the 32%/57.5%/10.5% breakdown is computed over the valid pixels only, not the full district area including these gaps.
624
+
625
+ The eastern two-thirds of the district, by contrast, is overwhelmingly stable (dominant green in the classification map), which is itself a useful negative result — it suggests the decline is genuinely localized to a specific area rather than reflecting a district-wide drought or seasonal artifact that would show up everywhere.
626
+
608
627
  ## 📋 Documentation
609
628
 
610
629
  Full documentation: **https://appiahkubis14.github.io/pygeofetch-docs/**
@@ -470,6 +470,25 @@ jupyter lab
470
470
 
471
471
  ---
472
472
 
473
+ <p align="center">
474
+ <img src="docs/images/trend_map.png" width="48%" />
475
+ <img src="docs/images/trend_classification.png" width="48%" />
476
+ </p>
477
+ <p align="center"><em>NDVI trend (2018–2024) and severity classification for the Obuasi Municipal District, computed end-to-end with PyGeoFetch — real USGS Landsat data, boundary-clipped before processing.</em></p>
478
+
479
+ 32.0% of the Obuasi Municipal District shows measurable vegetation decline over 2018–2024 (9.2% strong decline + 22.8% moderate decline), against 57.5% stable and 10.5% showing an increasing trend.
480
+
481
+ That headline number matters less than where it's concentrated. The decline isn't scattered randomly across the district — it forms a clear, spatially coherent cluster in the western portion of the AOI (roughly west of -1.70° longitude), visible as a dense, contiguous red-and-orange zone in both the trend map and the classification map. Real land degradation signals tend to look like this — clustered along mining concessions, roads, or river corridors — whereas sensor noise or processing artifacts tend to scatter more randomly across the whole scene. The fact that this pattern holds together spatially is a reasonable indicator that it reflects something real on the ground, not just pixel-level noise.
482
+
483
+ That said, an NDVI trend map on its own can't distinguish why vegetation declined — illegal small-scale mining (galamsey), selective logging, agricultural land clearing, and urban expansion can all produce a similar signature. Given Obuasi's well-documented galamsey activity specifically concentrated in this district, the spatial pattern here is consistent with mining-driven clearance — but confirming that specific cause would need either higher-resolution imagery, ground verification, or cross-referencing against known concession boundaries, not this analysis alone.
484
+
485
+ Two things worth flagging honestly rather than glossing over:
486
+
487
+ The scattered dark-blue linear and blob features running through the map (most visibly the river-like feature crossing the upper-central area) are very likely water bodies, not vegetation gain. NDVI trend over open water isn't a meaningful signal — surface reflectance there is driven by turbidity and water level, not plant health — so those features should be excluded from the "increasing" interpretation, not read as reforestation.
488
+ The small white gaps scattered through the trend map (several distinct circular patches) are nodata — pixels that didn't have enough valid, cloud-free observations across all six dates to fit a reliable trend. Worth being upfront that the 32%/57.5%/10.5% breakdown is computed over the valid pixels only, not the full district area including these gaps.
489
+
490
+ The eastern two-thirds of the district, by contrast, is overwhelmingly stable (dominant green in the classification map), which is itself a useful negative result — it suggests the decline is genuinely localized to a specific area rather than reflecting a district-wide drought or seasonal artifact that would show up everywhere.
491
+
473
492
  ## 📋 Documentation
474
493
 
475
494
  Full documentation: **https://appiahkubis14.github.io/pygeofetch-docs/**
@@ -30,7 +30,7 @@ CLI::
30
30
  pygeofetch doctor
31
31
  """
32
32
 
33
- __version__ = "1.0.0"
33
+ __version__ = "1.4.0"
34
34
  __author__ = "PyGeoFetch Contributors"
35
35
  __license__ = "MIT"
36
36
 
@@ -53,6 +53,22 @@ def search() -> None:
53
53
  default=None,
54
54
  help="Comma-separated satellite names e.g. Sentinel-2,Landsat-8.",
55
55
  )
56
+ @click.option(
57
+ "--product-type",
58
+ default=None,
59
+ help=(
60
+ "SAR product type e.g. GRD, SLC, GRD-COG. Currently only "
61
+ "meaningfully filters Copernicus (OData-based, not STAC) — "
62
+ "STAC providers like planetary_computer/element84 offer only "
63
+ "pre-processed Sentinel-1 RTC via their collection mapping, not "
64
+ "raw GRD/SLC, so this has no effect there regardless of value."
65
+ ),
66
+ )
67
+ @click.option(
68
+ "--polarisation",
69
+ default=None,
70
+ help='SAR polarisation e.g. VV, VH, HH, HV. Same Copernicus-only caveat as --product-type.',
71
+ )
56
72
  @click.option("--max-results", "-n", default=100, show_default=True)
57
73
  @click.option(
58
74
  "--sort-by",
@@ -98,6 +114,8 @@ def search_run(
98
114
  processing_level,
99
115
  providers,
100
116
  satellites,
117
+ product_type,
118
+ polarisation,
101
119
  max_results,
102
120
  sort_by,
103
121
  sort_order,
@@ -196,7 +214,16 @@ def search_run(
196
214
 
197
215
  query = SearchQuery(
198
216
  bbox=bbox_obj,
199
- geometry_geojson=geometry_geojson,
217
+ # Was "geometry_geojson=geometry_geojson" — a SEPARATE, unused
218
+ # field that nothing in the codebase actually reads. Every
219
+ # provider (and the model's own to_stac_params()/
220
+ # has_spatial_filter() methods) reads query.geometry, not
221
+ # query.geometry_geojson. This meant --geometry-file silently
222
+ # never applied real polygon-based spatial filtering at all —
223
+ # it fell back to the bbox approximation computed just above
224
+ # (still functional, but loses the actual polygon precision
225
+ # that geometry-based filtering exists to provide).
226
+ geometry=geometry_geojson,
200
227
  start_date=start_date,
201
228
  end_date=end_date,
202
229
  cloud_cover_min=cloud_min,
@@ -205,6 +232,8 @@ def search_run(
205
232
  resolution_max_m=res_max,
206
233
  processing_levels=pl_list,
207
234
  satellites=sat_list,
235
+ product_type=product_type,
236
+ polarisation=polarisation,
208
237
  max_results=max_results,
209
238
  providers=provider_list or [],
210
239
  sort_by=sort_by,
@@ -492,4 +492,4 @@ class AuthManager:
492
492
  "auth_type": creds.auth_type.value,
493
493
  "username": creds.username,
494
494
  }
495
- return result
495
+ return result