Repository: planetlabs/planet-client-python
Pull request #59 (2015-08-10 22:47:04)

Mosaic rebase


Patch:
diff --git a/planet/api/client.py b/planet/api/client.py
index 012fd30..edd887c 100644
--- a/planet/api/client.py
+++ b/planet/api/client.py
@@ -124,7 +124,7 @@ class Client(object):
 
         .. todo:: Pagination
         """
-        return self._get('mosaics').get_body()
+        return self._get('mosaics', models.Mosaics).get_body()
 
     def get_mosaic(self, name):
         """
@@ -135,6 +135,31 @@ class Client(object):
         """
         return self._get('mosaics/%s' % name).get_body()
 
+    def get_mosaic_quads(self, name, intersects=None, count=50):
+        """
+        Get metadata for mosaic quads.
+
+        :param name:
+            Mosaic name as retured by `list_mosaics`.
+        :param intersects:
+            WKT or GeoJSON describing a region of interest.
+        :param count:
+            Number of results to return.
+        """
+
+        params = {}
+        if intersects:
+            params['intersects'] = intersects
+        if count:
+            params['count'] = count
+
+        return self._get('mosaics/%s/quads/' % name, params=params).get_body()
+
+    def fetch_mosaic_quad_geotiffs(self, mosaic_name, quad_ids, callback=None):
+        pt = 'mosaics/%s/quads/%s/full'
+        paths = [pt % (mosaic_name, qid) for qid in quad_ids]
+        return self._download_many(paths, {}, callback)
+
     def get_workspaces(self):
         return self._get('workspaces').get_body()
 
diff --git a/planet/api/models.py b/planet/api/models.py
index 09ab8d7..94cd407 100644
--- a/planet/api/models.py
+++ b/planet/api/models.py
@@ -121,13 +121,13 @@ class JSON(Body):
         return self.response.json()
 
 
-class Scenes(JSON):
+class Paged(JSON):
 
     def next(self):
         links = self.get()['links']
         next = links.get('next', None)
         if next:
-            request = Request(next, self._request.auth, body_type=Scenes)
+            request = Request(next, self._request.auth, body_type=type(self))
             return self._dispatcher.response(request).get_body()
 
     def iter(self, pages=None):
@@ -144,5 +144,13 @@ class Scenes(JSON):
             pages -= 1
 
 
+class Scenes(Paged):
+    pass
+
+
+class Mosaics(Paged):
+    pass
+
+
 class Image(Body):
     pass
diff --git a/planet/scripts/__init__.py b/planet/scripts/__init__.py
index 9ba28dd..d31fc46 100644
--- a/planet/scripts/__init__.py
+++ b/planet/scripts/__init__.py
@@ -363,6 +363,34 @@ def get_mosaic(mosaic_name, pretty):
     echo_json_response(call_and_wrap(client().get_mosaic, mosaic_name), pretty)
 
 
+@pretty
+@cli.command('mosaic-quads')
+@click.argument('mosaic_name', nargs=1)
+@click.option('--count', type=click.INT, required=False,
+              help="Set the number of returned quads.")
+def get_mosaic_quads(mosaic_name, count, pretty):
+    """
+    Get quad info for the specified mosaic
+    """
+    echo_json_response(
+        call_and_wrap(client().get_mosaic_quads,
+                      mosaic_name, count=count), pretty)
+
+
+@cli.command('download-quads')
+@dest_dir
+@click.argument('mosaic_name', nargs=1)
+@click.argument('quad_ids', nargs=-1)
+def download_quads(mosaic_name, quad_ids, dest):
+    """
+    Download quad geotiffs
+    """
+    quad_ids = read(quad_ids, split=True)
+    futures = call_and_wrap(client().fetch_mosaic_quad_geotiffs, mosaic_name,
+                            quad_ids, api.write_to_file(dest))
+    check_futures(futures)
+
+
 @pretty
 @cli.command('list-workspaces')
 def list_workspaces(pretty):


Test patch:
diff --git a/tests/fixtures/get-mosaic-quads.json b/tests/fixtures/get-mosaic-quads.json
new file mode 100644
index 0000000..ccaaa72
--- /dev/null
+++ b/tests/fixtures/get-mosaic-quads.json
@@ -0,0 +1,140 @@
+{
+  "type": "FeatureCollection",
+  "features": [
+    {
+      "geometry": {
+        "type": "Polygon",
+        "coordinates": [
+          [
+            [
+              -45.8789062436,
+              -11.1784018721
+            ],
+            [
+              -45.8789062436,
+              -11.3507967208
+            ],
+            [
+              -45.7031249936,
+              -11.3507967208
+            ],
+            [
+              -45.7031249936,
+              -11.1784018721
+            ],
+            [
+              -45.8789062436,
+              -11.1784018721
+            ]
+          ]
+        ]
+      },
+      "type": "Feature",
+      "id": "L15-0763E-0959N",
+      "properties": {
+        "updated": "2015-07-20T17:10:41.309135+00:00",
+        "num_input_scenes": 17,
+        "links": {
+          "self": "https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/L15-0763E-0959N",
+          "full": "https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/L15-0763E-0959N/full",
+          "thumbnail": "https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/L15-0763E-0959N/thumb",
+          "mosaic": "https://api.planet.com/v0/mosaics/color_balance_mosaic",
+          "scenes": "https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/L15-0763E-0959N/scenes/"
+        },
+        "percent_covered": 90.03
+      }
+    },
+    {
+      "geometry": {
+        "type": "Polygon",
+        "coordinates": [
+          [
+            [
+              -72.5976562399,
+              -51.0690166544
+            ],
+            [
+              -72.5976562399,
+              -51.1793429741
+            ],
+            [
+              -72.4218749899,
+              -51.1793429741
+            ],
+            [
+              -72.4218749899,
+              -51.0690166544
+            ],
+            [
+              -72.5976562399,
+              -51.0690166544
+            ]
+          ]
+        ]
+      },
+      "type": "Feature",
+      "id": "L15-0611E-0684N",
+      "properties": {
+        "updated": "2015-07-20T16:13:37.104462+00:00",
+        "num_input_scenes": 43,
+        "links": {
+          "self": "https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/L15-0611E-0684N",
+          "full": "https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/L15-0611E-0684N/full",
+          "thumbnail": "https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/L15-0611E-0684N/thumb",
+          "mosaic": "https://api.planet.com/v0/mosaics/color_balance_mosaic",
+          "scenes": "https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/L15-0611E-0684N/scenes/"
+        },
+        "percent_covered": 100
+      }
+    },
+    {
+      "geometry": {
+        "type": "Polygon",
+        "coordinates": [
+          [
+            [
+              -72.4218749899,
+              -51.0690166544
+            ],
+            [
+              -72.4218749899,
+              -51.1793429741
+            ],
+            [
+              -72.2460937399,
+              -51.1793429741
+            ],
+            [
+              -72.2460937399,
+              -51.0690166544
+            ],
+            [
+              -72.4218749899,
+              -51.0690166544
+            ]
+          ]
+        ]
+      },
+      "type": "Feature",
+      "id": "L15-0612E-0684N",
+      "properties": {
+        "updated": "2015-07-20T16:11:54.962205+00:00",
+        "num_input_scenes": 35,
+        "links": {
+          "self": "https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/L15-0612E-0684N",
+          "full": "https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/L15-0612E-0684N/full",
+          "thumbnail": "https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/L15-0612E-0684N/thumb",
+          "mosaic": "https://api.planet.com/v0/mosaics/color_balance_mosaic",
+          "scenes": "https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/L15-0612E-0684N/scenes/"
+        },
+        "percent_covered": 100
+      }
+    }
+  ],
+  "links": {
+    "next": "https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/?count=3&_page.updated.lt=2015-07-20T16%3A11%3A54.962205%2B00%3A00",
+    "self": "https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/?count=3",
+    "prev": "https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/?count=3&_page.updated.gt=2015-07-20T17%3A10%3A41.309135%2B00%3A00",
+    "first": "https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/?count=3"
+  }
+}
diff --git a/tests/test_mosaics.py b/tests/test_mosaics.py
index d275fa1..7c04b61 100644
--- a/tests/test_mosaics.py
+++ b/tests/test_mosaics.py
@@ -11,50 +11,60 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-import os
+from _common import read_fixture
 import json
-import unittest
-from requests_mock import Mocker
+import os
 from planet import api
+from requests_mock import Mocker
+
+
+client = api.Client(api_key='xyz')
+
+
+def test_list_mosaics():
+
+    with Mocker() as m:
+
+        text = read_fixture('list-mosaics.json')
+        uri = os.path.join(client.base_url, 'mosaics')
+        m.get(uri, text=text, status_code=200)
 
+        r = client.list_mosaics()
 
-TEST_DIR = os.path.dirname(os.path.realpath(__file__))
-FIXTURE_DIR = os.path.join(TEST_DIR, 'fixtures')
+        assert r.response.status_code == 200
+        assert r.get() == json.loads(text)
 
 
-class TestMosaics(unittest.TestCase):
+def test_get_mosaic():
 
-    def setUp(self):
+    mosaic_name = 'color_balance_mosaic'
 
-        self.client = api.Client(api_key='xyz')
+    with Mocker() as m:
 
-    def test_list_mosaics(self):
+        text = read_fixture('get-mosaic.json')
+        uri = os.path.join(client.base_url,
+                           'mosaics/%s' % mosaic_name)
+        m.get(uri, text=text, status_code=200)
 
-        fixture_path = os.path.join(FIXTURE_DIR, 'list-mosaics.json')
-        with Mocker() as m, open(fixture_path) as f:
+        r = client.get_mosaic(mosaic_name)
 
-            text = f.read()
-            uri = os.path.join(self.client.base_url, 'mosaics')
-            m.get(uri, text=text, status_code=200)
+        assert r.response.status_code == 200
+        assert r.get() == json.loads(text)
 
-            r = self.client.list_mosaics()
 
-            assert r.response.status_code == 200
-            assert r.get() == json.loads(text)
+def test_get_mosaic_quads():
 
-    def test_get_mosaic(self):
+    mosaic_name = 'color_balance_mosaic'
 
-        mosaic_name = 'color_balance_mosaic'
+    with Mocker() as m:
 
-        fixture_path = os.path.join(FIXTURE_DIR, 'get-mosaic.json')
-        with Mocker() as m, open(fixture_path) as f:
+        text = read_fixture('get-mosaic-quads.json')
+        uri = os.path.join(client.base_url,
+                           'mosaics/%s/quads/?count=50' % mosaic_name)
 
-            text = f.read()
-            uri = os.path.join(self.client.base_url,
-                               'mosaics/%s' % mosaic_name)
-            m.get(uri, text=text, status_code=200)
+        m.get(uri, text=text, status_code=200)
 
-            r = self.client.get_mosaic(mosaic_name)
+        r = client.get_mosaic_quads(mosaic_name, count=50)
 
-            assert r.response.status_code == 200
-            assert r.get() == json.loads(text)
+        assert r.response.status_code == 200
+        assert r.get() == json.loads(text)