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
+
1
15
import asyncio
2
16
from pathlib import Path
3
17
from typing import AsyncIterator , Awaitable , Optional , Tuple , Type , TypeVar , Union , cast
18
+ from planet .clients .base import _BaseClient
4
19
from planet .constants import PLANET_BASE_URL
5
20
from planet .exceptions import MissingResource
6
21
from 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
8
23
from uuid import UUID
9
24
10
25
BASE_URL = f'{ PLANET_BASE_URL } /basemaps/v1'
@@ -39,7 +54,7 @@ def _is_uuid(val: str) -> bool:
39
54
return False
40
55
41
56
42
- class MosaicsClient :
57
+ class MosaicsClient ( _BaseClient ) :
43
58
"""High-level asynchronous access to Planet's Mosaics API.
44
59
45
60
Example:
@@ -49,7 +64,7 @@ class MosaicsClient:
49
64
>>>
50
65
>>> async def main():
51
66
... async with Session() as sess:
52
- ... cl = sess.client('data ')
67
+ ... cl = sess.client('mosaics ')
53
68
... # use client here
54
69
...
55
70
>>> asyncio.run(main())
@@ -63,18 +78,10 @@ def __init__(self, session: Session, base_url: Optional[str] = None):
63
78
base_url: The base URL to use. Defaults to production Mosaics
64
79
base url.
65
80
"""
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 )
75
82
76
83
def _url (self , path : str ) -> str :
77
- return f"{ BASE_URL } /{ path } "
84
+ return f"{ self . _base_url } /{ path } "
78
85
79
86
async def _get_by_name (self , path : str , pager : Type [Paged ],
80
87
name : str ) -> dict :
@@ -88,7 +95,12 @@ async def _get_by_name(self, path: str, pager: Type[Paged],
88
95
listing = response .json ()[pager .ITEMS_KEY ]
89
96
if len (listing ):
90
97
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 ))
92
104
93
105
async def _get_by_id (self , path : str , id : str ) -> dict :
94
106
response = await self ._session .request (method = "GET" ,
@@ -130,7 +142,7 @@ async def list_series(
130
142
name_contains : Optional [str ] = None ,
131
143
interval : Optional [str ] = None ,
132
144
acquired_gt : Optional [str ] = None ,
133
- acquired_lt : Optional [str ] = None ) -> AsyncIterator [dict ]:
145
+ acquired_lt : Optional [str ] = None ) -> AsyncIterator [Series ]:
134
146
"""
135
147
List the series you have access to.
136
148
@@ -157,7 +169,7 @@ async def list_series(
157
169
params = params ,
158
170
)
159
171
async for item in _SeriesPage (resp , self ._session .request ):
160
- yield item
172
+ yield Series ( item )
161
173
162
174
async def list_mosaics (
163
175
self ,
@@ -166,8 +178,7 @@ async def list_mosaics(
166
178
interval : Optional [str ] = None ,
167
179
acquired_gt : Optional [str ] = None ,
168
180
acquired_lt : Optional [str ] = None ,
169
- latest : bool = False ,
170
- ) -> AsyncIterator [dict ]:
181
+ ) -> AsyncIterator [Mosaic ]:
171
182
"""
172
183
List the mosaics you have access to.
173
184
@@ -188,15 +199,13 @@ async def list_mosaics(
188
199
params ["acquired__gt" ] = acquired_gt
189
200
if acquired_lt :
190
201
params ["acquired__lt" ] = acquired_lt
191
- if latest :
192
- params ["latest" ] = "yes"
193
202
resp = await self ._session .request (
194
203
method = 'GET' ,
195
204
url = self ._url ("mosaics" ),
196
205
params = params ,
197
206
)
198
207
async for item in _MosaicsPage (resp , self ._session .request ):
199
- yield item
208
+ yield Mosaic ( item )
200
209
201
210
async def list_series_mosaics (
202
211
self ,
@@ -206,7 +215,7 @@ async def list_series_mosaics(
206
215
acquired_gt : Optional [str ] = None ,
207
216
acquired_lt : Optional [str ] = None ,
208
217
latest : bool = False ,
209
- ) -> AsyncIterator [dict ]:
218
+ ) -> AsyncIterator [Mosaic ]:
210
219
"""
211
220
List the mosaics in a series.
212
221
@@ -218,6 +227,7 @@ async def list_series_mosaics(
218
227
print(m)
219
228
```
220
229
"""
230
+ series_id = series
221
231
if isinstance (series , Series ):
222
232
series_id = series ["id" ]
223
233
elif not _is_uuid (series ):
@@ -238,15 +248,15 @@ async def list_series_mosaics(
238
248
params = params ,
239
249
)
240
250
async for item in _MosaicsPage (resp , self ._session .request ):
241
- yield item
251
+ yield Mosaic ( item )
242
252
243
253
async def list_quads (self ,
244
254
/ ,
245
255
mosaic : Union [Mosaic , str ],
246
256
* ,
247
257
minimal : bool = False ,
248
258
bbox : Optional [BBox ] = None ,
249
- geometry : Optional [dict ] = None ,
259
+ geometry : Optional [Union [ dict , GeoInterface ] ] = None ,
250
260
summary : bool = False ) -> AsyncIterator [Quad ]:
251
261
"""
252
262
List the a mosaic's quads.
@@ -262,6 +272,8 @@ async def list_quads(self,
262
272
"""
263
273
mosaic = await self ._resolve_mosaic (mosaic )
264
274
if geometry :
275
+ if isinstance (geometry , GeoInterface ):
276
+ geometry = geometry .__geo_interface__
265
277
resp = await self ._quads_geometry (mosaic ,
266
278
geometry ,
267
279
minimal ,
@@ -364,14 +376,6 @@ async def download_quad(self,
364
376
directory : str = "." ,
365
377
overwrite : bool = False ,
366
378
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 )
375
379
"""
376
380
Download a quad to a directory.
377
381
@@ -382,6 +386,14 @@ async def download_quad(self,
382
386
await client.download_quad(quad)
383
387
```
384
388
"""
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 )
385
397
386
398
async def download_quads (self ,
387
399
/ ,
@@ -390,7 +402,8 @@ async def download_quads(self,
390
402
directory : Optional [str ] = None ,
391
403
overwrite : bool = False ,
392
404
bbox : Optional [BBox ] = None ,
393
- geometry : Optional [dict ] = None ,
405
+ geometry : Optional [Union [dict ,
406
+ GeoInterface ]] = None ,
394
407
progress_bar : bool = False ,
395
408
concurrency : int = 4 ):
396
409
"""
0 commit comments