Repository: CS-SI/eodag
Pull request #653 (2023-02-21 16:11:45)

feat: EOProduct defaultGeometry property
Better handle missing geometry when building [EOProduct](https://eodag.readthedocs.io/en/stable/api_reference/eoproduct.html#eodag.api.product._product.EOProduct):
- raises `MisconfiguredError` with explicit message on missing geometry and no `defaultGeometry` configured
- uses `properties["defaultGeometry"]` as default geometry if exists

github-actions[bot]: ## Unit Test Results
    2 files  ±0      2 suites  ±0   5m 23s [:stopwatch:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v1.20/README.md#the-symbols "duration of all tests") +5s
363 tests +1  360 [:heavy_check_mark:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v1.20/README.md#the-symbols "passed tests") +1  3 [:zzz:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v1.20/README.md#the-symbols "skipped / disabled tests") ±0  0 [:x:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v1.20/README.md#the-symbols "failed tests") ±0 
726 runs  +2  720 [:heavy_check_mark:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v1.20/README.md#the-symbols "passed tests") +2  6 [:zzz:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v1.20/README.md#the-symbols "skipped / disabled tests") ±0  0 [:x:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v1.20/README.md#the-symbols "failed tests") ±0 

Results for commit 188616b2. ± Comparison against base commit 7a372ebe.

[test-results]:data:application/gzip;base64,H4sIAM/u9GMC/02MTQ6DIBgFr2JYdwEflJ9epgGEhFSlQViZ3r1gLbqceS+zIR8mt6LHALcBrSXkDmNJOoe4VKRAq6hTbiPlnZ5rsXZX+FSv8G6qC6/DVMX5cCnFdJhUltYUwA/4JwXgbn7F/rgEd772bJznkCsgIiUn3IBiGqSXyjgumFIjxndOtfRMaCM1EPT5AvFzokUFAQAA

github-actions[bot]: <strong> </strong>

| File | Coverage |   |
| - | :-: | :-: |
| **All files** | `88%` | :white_check_mark: |
| eodag/api/product/\_product.py | `85%` | :white_check_mark: |
| tests/units/test\_eoproduct.py | `99%` | :white_check_mark: |

<strong> </strong>

| File | Coverage |   |
| - | :-: | :-: |
| **All files** | `87%` | :white_check_mark: |
| eodag/api/product/\_product.py | `85%` | :white_check_mark: |
| tests/units/test\_eoproduct.py | `99%` | :white_check_mark: |
| eodag/api/product/\_product.py | `85%` | :white_check_mark: |
| tests/units/test\_eoproduct.py | `99%` | :white_check_mark: |

_Minimum allowed coverage is `70%`_

<p align="right">Generated by :monkey: cobertura-action against 188616b294a28f89be67499d00563a8f47ab8a21 </p>

Patch:
diff --git a/eodag/api/product/_product.py b/eodag/api/product/_product.py
index a0a3d78c..afe0b8f2 100644
--- a/eodag/api/product/_product.py
+++ b/eodag/api/product/_product.py
@@ -92,7 +92,17 @@ class EOProduct(object):
             for key, value in properties.items()
             if key != "geometry" and value not in [NOT_MAPPED, NOT_AVAILABLE]
         }
-        product_geometry = properties["geometry"]
+        if "geometry" not in properties or (
+            properties["geometry"] == NOT_AVAILABLE
+            and "defaultGeometry" not in properties
+        ):
+            raise MisconfiguredError(
+                f"No geometry available to build EOProduct(id={properties.get('id', None)}, provider={provider})"
+            )
+        elif properties["geometry"] == NOT_AVAILABLE:
+            product_geometry = properties["defaultGeometry"]
+        else:
+            product_geometry = properties["geometry"]
         # Let's try 'latmin lonmin latmax lonmax'
         if isinstance(product_geometry, str):
             bbox_pattern = re.compile(


Test patch:
diff --git a/tests/units/test_eoproduct.py b/tests/units/test_eoproduct.py
index fa428d78..bbee65a8 100644
--- a/tests/units/test_eoproduct.py
+++ b/tests/units/test_eoproduct.py
@@ -30,10 +30,12 @@ from shapely import geometry
 from tests import EODagTestCase
 from tests.context import (
     DEFAULT_STREAM_REQUESTS_TIMEOUT,
+    NOT_AVAILABLE,
     DatasetDriver,
     Download,
     EOProduct,
     HTTPDownload,
+    MisconfiguredError,
     ProgressCallback,
     config,
 )
@@ -53,12 +55,23 @@ class TestEOProduct(EODagTestCase):
             shutil.rmtree(self.output_dir)
 
     def test_eoproduct_search_intersection_geom(self):
-        """EOProduct search_intersection attr must be it's geom when no bbox_or_intersect param given"""  # noqa
+        """EOProduct search_intersection attr must be it's geom when no bbox_or_intersect param given"""
         product = self._dummy_product()
         self.assertEqual(product.geometry, product.search_intersection)
 
+    def test_eoproduct_default_geom(self):
+        """EOProduct needs a geometry or can use confired defaultGeometry by default"""
+
+        with self.assertRaisesRegex(MisconfiguredError, "No geometry available"):
+            self._dummy_product(properties={"geometry": NOT_AVAILABLE})
+
+        product = self._dummy_product(
+            properties={"geometry": NOT_AVAILABLE, "defaultGeometry": "0 0 1 1"}
+        )
+        self.assertEqual(product.geometry.bounds, (0.0, 0.0, 1.0, 1.0))
+
     def test_eoproduct_search_intersection_none(self):
-        """EOProduct search_intersection attr must be None if shapely.errors.GEOSException when intersecting"""  # noqa
+        """EOProduct search_intersection attr must be None if shapely.errors.GEOSException when intersecting"""
         # Invalid geometry
         self.eoproduct_props["geometry"] = {
             "type": "Polygon",