Repository: dlr-eoc/ukis-pysat

Issue:
get_aoi() should be able to handle geo_interfaces
**Is your feature request related to a problem? Please describe.**
I want to stream in bytes to directly load an AOI without IO.

**Describe the solution you'd like**
A [geo_interface](https://gist.github.com/sgillies/2217756) should be created on client side and [prep_aoi()](https://github.com/dlr-eoc/ukis-pysat/blob/master/ukis_pysat/data.py#L413) should handle the rest.

**Describe alternatives you've considered**
Just a simlpe dictionary, but a geo_interface is more flexible.


Discussion:
codecov-io: # [Codecov](https://codecov.io/gh/dlr-eoc/ukis-pysat/pull/126?src=pr&el=h1) Report
> Merging [#126](https://codecov.io/gh/dlr-eoc/ukis-pysat/pull/126?src=pr&el=desc) (e1cf611) into [master](https://codecov.io/gh/dlr-eoc/ukis-pysat/commit/ff91db3c853319434f81db355b324f825ed69c61?el=desc) (ff91db3) will **increase** coverage by `0.19%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/dlr-eoc/ukis-pysat/pull/126/graphs/tree.svg?width=650&height=150&src=pr&token=6LYPGRGMXV)](https://codecov.io/gh/dlr-eoc/ukis-pysat/pull/126?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #126      +/-   ##
==========================================
+ Coverage   80.64%   80.83%   +0.19%     
==========================================
  Files          11       11              
  Lines         904      913       +9     
==========================================
+ Hits          729      738       +9     
  Misses        175      175              
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `80.83% <100.00%> (+0.19%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/dlr-eoc/ukis-pysat/pull/126?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [tests/test\_data.py](https://codecov.io/gh/dlr-eoc/ukis-pysat/pull/126/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9kYXRhLnB5) | `92.47% <100.00%> (+0.51%)` | :arrow_up: |
| [ukis\_pysat/data.py](https://codecov.io/gh/dlr-eoc/ukis-pysat/pull/126/diff?src=pr&el=tree#diff-dWtpc19weXNhdC9kYXRhLnB5) | `58.88% <100.00%> (+0.63%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/dlr-eoc/ukis-pysat/pull/126?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/dlr-eoc/ukis-pysat/pull/126?src=pr&el=footer). Last update [ff91db3...e1cf611](https://codecov.io/gh/dlr-eoc/ukis-pysat/pull/126?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 6d21190..8cb01d9 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -16,14 +16,18 @@ something was changed
 [master] (2021-**-**)
 ---------------------
 
+[1.1.0] (2021-03-29)
+---------------------
+
 Changed
 ^^^^^^^
-- ``data``: change default scihup endpoint `/dhus` to `/apihub`#110
+- ``data``: prep_aoi() is not private and supports __geo_interface__ #113
+- ``data``: change default scihup endpoint `/dhus` to `/apihub` #110
 - ``data``: mocked all interaction with Hubs for unit testing #116
 - ``data``: EarthExplorer changed to new M2M API due to dependency update #120
-- split unit tests #110
-- ``data``: removed hard dependency for landsatxplore & pylandsat #104
+- ``data``: removed hard dependency for `landsatxplore` & `pylandsat` #104
 - ``data``: `acquisitiondate` and `ingestiondate` have been removed #124
+- split unit tests #110
 
 Fixed
 ^^^^^
diff --git a/requirements.txt b/requirements.txt
index 379643c..c687c1c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,8 +1,6 @@
 ## data
-landsatxplore>=0.13.0
 numpy
 pillow
-pylandsat>=0.6.0
 pystac[validation]>=0.5.5
 python-dateutil
 requests
@@ -18,7 +16,9 @@ shapely
 ## dev
 dask[array]
 fiona
+landsatxplore>=0.13.0
 pandas
+pylandsat>=0.6.0
 pyproj>=3.0.0
 requests_mock
 utm>=0.7.0
\ No newline at end of file
diff --git a/ukis_pysat/__init__.py b/ukis_pysat/__init__.py
index d10af32..eee00e3 100644
--- a/ukis_pysat/__init__.py
+++ b/ukis_pysat/__init__.py
@@ -1,1 +1,1 @@
-__version__ = "1.0.2"
+__version__ = "1.1.0"
diff --git a/ukis_pysat/data.py b/ukis_pysat/data.py
index 4ebb07c..708da90 100644
--- a/ukis_pysat/data.py
+++ b/ukis_pysat/data.py
@@ -18,6 +18,7 @@ try:
     from PIL import Image
     from pystac.extensions import sat
     from shapely import geometry, wkt, ops
+    from shapely.geometry import shape, mapping
 except ImportError as e:
     msg = (
         "ukis_pysat.data dependencies are not installed.\n\n"
@@ -134,7 +135,7 @@ class Source:
         if self.src == Datahub.STAC_local:
             # query STAC Catalog for metadata
             catalog = self._init_catalog()
-            geom = self.prep_aoi(aoi)
+            geom = self._prep_aoi(aoi)
             for item in self.api.get_all_items():
                 if item.ext.eo.cloud_cover and cloud_cover:
                     if not cloud_cover[0] <= item.ext.eo.cloud_cover < cloud_cover[1]:
@@ -157,7 +158,7 @@ class Source:
 
         elif self.src == Datahub.EarthExplorer:
             # query EarthExplorer for metadata
-            bbox = self.prep_aoi(aoi).bounds
+            bbox = self._prep_aoi(aoi).bounds
             kwargs = {}
             if cloud_cover:
                 kwargs["max_cloud_cover"] = cloud_cover[1]
@@ -176,7 +177,7 @@ class Source:
             if cloud_cover and platform != platform.Sentinel1:
                 kwargs["cloudcoverpercentage"] = cloud_cover
             products = self.api.query(
-                area=self.prep_aoi(aoi).wkt,
+                area=self._prep_aoi(aoi).wkt,
                 date=date,
                 platformname=platform.value,
                 **kwargs,
@@ -401,10 +402,11 @@ class Source:
             out_file.write(str(ul_y) + "\n")
 
     @staticmethod
-    def prep_aoi(aoi):
+    def _prep_aoi(aoi):
         """Converts aoi to Shapely Polygon and reprojects to WGS84.
 
-        :param aoi: Area of interest as Geojson file, WKT string or bounding box in lat lon coordinates (String, Tuple)
+        :param aoi: Area of interest as Geojson file, WKT string or bounding box in lat lon coordinates (String,
+        Tuple, __geo_interface__)
         :return: Shapely Polygon
         """
         # check if handed object is a string
@@ -429,11 +431,14 @@ class Source:
             else:
                 aoi = wkt.loads(aoi)
 
+        elif isinstance(aoi, dict):
+            aoi = shape(aoi)
+
         elif isinstance(aoi, tuple):
             aoi = geometry.box(aoi[0], aoi[1], aoi[2], aoi[3])
 
         else:
-            raise TypeError(f"aoi must be of type string, Path or tuple")
+            raise TypeError(f"aoi must be of type string, Path, tuple or __geo_interface__")
 
         return aoi
 


Test patch:
diff --git a/tests/test_data.py b/tests/test_data.py
index 7ae0f60..9be4daf 100644
--- a/tests/test_data.py
+++ b/tests/test_data.py
@@ -5,6 +5,7 @@ from tempfile import gettempdir
 
 import pystac
 import requests_mock
+from shapely.geometry import Polygon
 
 from ukis_pysat.data import Source
 from ukis_pysat.members import Datahub, Platform
@@ -43,7 +44,21 @@ class DataTest(unittest.TestCase):
     def test_exception_false_aoi(self):
         with Source(datahub=Datahub.STAC_local, catalog=catalog_path) as src:
             with self.assertRaises(TypeError, msg=f"aoi must be of type string or tuple"):
-                src.prep_aoi(1)
+                src._prep_aoi(1)
+
+    def test_aoi_geointerface(self):
+        geom = Source._prep_aoi(
+            {
+                "type": "Polygon",
+                "coordinates": (
+                    ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0), (0.0, 0.0)),
+                    ((0.1, 0.1), (0.1, 0.2), (0.2, 0.2), (0.2, 0.1), (0.1, 0.1)),
+                ),
+            }
+        )
+        self.assertIsInstance(geom, Polygon)
+        self.assertEqual(tuple(geom.exterior.coords), ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0), (0.0, 0.0)))
+        self.assertEqual(len(geom.interiors), 1)
 
     @unittest.skip("Skip until we find a better test or this also runs with Github Actions")
     def test_query_metadata_stac_local(self):