1+ # Copyright 2025 Planet Labs PBC.
2+ #
3+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
4+ # use this file except in compliance with the License. You may obtain a copy of
5+ # the License at
6+ #
7+ # http://www.apache.org/licenses/LICENSE-2.0
8+ #
9+ # Unless required by applicable law or agreed to in writing, software
10+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+ # License for the specific language governing permissions and limitations under
13+ # the License.
14+
115import asyncio
216from pathlib import Path
3- from typing import AsyncIterator , Awaitable , Optional , Tuple , Type , TypeVar , Union , cast
17+ from typing import AsyncIterator , Optional , Tuple , Type , TypeVar , Union , cast
18+ from planet .clients .base import _BaseClient
419from planet .constants import PLANET_BASE_URL
520from planet .exceptions import MissingResource
621from planet .http import Session
7- from planet .models import Mosaic , Paged , Quad , Response , Series , StreamingBody
22+ from planet .models import GeoInterface , Mosaic , Paged , Quad , Response , Series , StreamingBody
823from uuid import UUID
924
1025BASE_URL = f'{ PLANET_BASE_URL } /basemaps/v1'
@@ -39,7 +54,7 @@ def _is_uuid(val: str) -> bool:
3954 return False
4055
4156
42- class MosaicsClient :
57+ class MosaicsClient ( _BaseClient ) :
4358 """High-level asynchronous access to Planet's Mosaics API.
4459
4560 Example:
@@ -49,7 +64,7 @@ class MosaicsClient:
4964 >>>
5065 >>> async def main():
5166 ... async with Session() as sess:
52- ... cl = sess.client('data ')
67+ ... cl = sess.client('mosaics ')
5368 ... # use client here
5469 ...
5570 >>> asyncio.run(main())
@@ -63,18 +78,10 @@ def __init__(self, session: Session, base_url: Optional[str] = None):
6378 base_url: The base URL to use. Defaults to production Mosaics
6479 base url.
6580 """
66- self ._session = session
67-
68- self ._base_url = base_url or BASE_URL
69- if self ._base_url .endswith ('/' ):
70- self ._base_url = self ._base_url [:- 1 ]
71-
72- def _call_sync (self , f : Awaitable [T ]) -> T :
73- """block on an async function call, using the call_sync method of the session"""
74- return self ._session ._call_sync (f )
81+ super ().__init__ (session , base_url or BASE_URL )
7582
7683 def _url (self , path : str ) -> str :
77- return f"{ BASE_URL } /{ path } "
84+ return f"{ self . _base_url } /{ path } "
7885
7986 async def _get_by_name (self , path : str , pager : Type [Paged ],
8087 name : str ) -> dict :
@@ -88,7 +95,12 @@ async def _get_by_name(self, path: str, pager: Type[Paged],
8895 listing = response .json ()[pager .ITEMS_KEY ]
8996 if len (listing ):
9097 return listing [0 ]
91- raise MissingResource (f"{ name } not found" )
98+ # mimic the response for 404 when search is empty
99+ resource = "Mosaic"
100+ if path == "series" :
101+ resource = "Series"
102+ raise MissingResource ('{"message":"%s Not Found: %s"}' %
103+ (resource , name ))
92104
93105 async def _get_by_id (self , path : str , id : str ) -> dict :
94106 response = await self ._session .request (method = "GET" ,
@@ -130,7 +142,7 @@ async def list_series(
130142 name_contains : Optional [str ] = None ,
131143 interval : Optional [str ] = None ,
132144 acquired_gt : Optional [str ] = None ,
133- acquired_lt : Optional [str ] = None ) -> AsyncIterator [dict ]:
145+ acquired_lt : Optional [str ] = None ) -> AsyncIterator [Series ]:
134146 """
135147 List the series you have access to.
136148
@@ -157,7 +169,7 @@ async def list_series(
157169 params = params ,
158170 )
159171 async for item in _SeriesPage (resp , self ._session .request ):
160- yield item
172+ yield Series ( item )
161173
162174 async def list_mosaics (
163175 self ,
@@ -166,8 +178,7 @@ async def list_mosaics(
166178 interval : Optional [str ] = None ,
167179 acquired_gt : Optional [str ] = None ,
168180 acquired_lt : Optional [str ] = None ,
169- latest : bool = False ,
170- ) -> AsyncIterator [dict ]:
181+ ) -> AsyncIterator [Mosaic ]:
171182 """
172183 List the mosaics you have access to.
173184
@@ -188,15 +199,13 @@ async def list_mosaics(
188199 params ["acquired__gt" ] = acquired_gt
189200 if acquired_lt :
190201 params ["acquired__lt" ] = acquired_lt
191- if latest :
192- params ["latest" ] = "yes"
193202 resp = await self ._session .request (
194203 method = 'GET' ,
195204 url = self ._url ("mosaics" ),
196205 params = params ,
197206 )
198207 async for item in _MosaicsPage (resp , self ._session .request ):
199- yield item
208+ yield Mosaic ( item )
200209
201210 async def list_series_mosaics (
202211 self ,
@@ -206,7 +215,7 @@ async def list_series_mosaics(
206215 acquired_gt : Optional [str ] = None ,
207216 acquired_lt : Optional [str ] = None ,
208217 latest : bool = False ,
209- ) -> AsyncIterator [dict ]:
218+ ) -> AsyncIterator [Mosaic ]:
210219 """
211220 List the mosaics in a series.
212221
@@ -218,6 +227,7 @@ async def list_series_mosaics(
218227 print(m)
219228 ```
220229 """
230+ series_id = series
221231 if isinstance (series , Series ):
222232 series_id = series ["id" ]
223233 elif not _is_uuid (series ):
@@ -238,15 +248,15 @@ async def list_series_mosaics(
238248 params = params ,
239249 )
240250 async for item in _MosaicsPage (resp , self ._session .request ):
241- yield item
251+ yield Mosaic ( item )
242252
243253 async def list_quads (self ,
244254 / ,
245255 mosaic : Union [Mosaic , str ],
246256 * ,
247257 minimal : bool = False ,
248258 bbox : Optional [BBox ] = None ,
249- geometry : Optional [dict ] = None ,
259+ geometry : Optional [Union [ dict , GeoInterface ] ] = None ,
250260 summary : bool = False ) -> AsyncIterator [Quad ]:
251261 """
252262 List the a mosaic's quads.
@@ -262,6 +272,8 @@ async def list_quads(self,
262272 """
263273 mosaic = await self ._resolve_mosaic (mosaic )
264274 if geometry :
275+ if isinstance (geometry , GeoInterface ):
276+ geometry = geometry .__geo_interface__
265277 resp = await self ._quads_geometry (mosaic ,
266278 geometry ,
267279 minimal ,
@@ -364,14 +376,6 @@ async def download_quad(self,
364376 directory : str = "." ,
365377 overwrite : bool = False ,
366378 progress_bar : bool = False ):
367- url = quad ["_links" ]["download" ]
368- Path (directory ).mkdir (exist_ok = True , parents = True )
369- async with self ._session .stream (method = 'GET' , url = url ) as resp :
370- body = StreamingBody (resp )
371- dest = Path (directory , body .name )
372- await body .write (dest ,
373- overwrite = overwrite ,
374- progress_bar = progress_bar )
375379 """
376380 Download a quad to a directory.
377381
@@ -382,6 +386,14 @@ async def download_quad(self,
382386 await client.download_quad(quad)
383387 ```
384388 """
389+ url = quad ["_links" ]["download" ]
390+ Path (directory ).mkdir (exist_ok = True , parents = True )
391+ async with self ._session .stream (method = 'GET' , url = url ) as resp :
392+ body = StreamingBody (resp )
393+ dest = Path (directory , body .name )
394+ await body .write (dest ,
395+ overwrite = overwrite ,
396+ progress_bar = progress_bar )
385397
386398 async def download_quads (self ,
387399 / ,
@@ -390,7 +402,8 @@ async def download_quads(self,
390402 directory : Optional [str ] = None ,
391403 overwrite : bool = False ,
392404 bbox : Optional [BBox ] = None ,
393- geometry : Optional [dict ] = None ,
405+ geometry : Optional [Union [dict ,
406+ GeoInterface ]] = None ,
394407 progress_bar : bool = False ,
395408 concurrency : int = 4 ):
396409 """
0 commit comments