geobox 2.0.0__py3-none-any.whl → 2.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
geobox/aio/raster.py CHANGED
@@ -1,869 +1,1273 @@
1
- import os
2
- from urllib.parse import urljoin, urlencode
3
- from typing import Optional, Dict, List, Optional, Union, TYPE_CHECKING
4
- import mimetypes
5
- import requests
6
- import sys
7
-
8
- from .base import AsyncBase
9
- from .task import Task
10
- from ..utils import clean_data, join_url_params
11
-
12
-
13
- if TYPE_CHECKING:
14
- from . import AsyncGeoboxClient
15
- from .user import User
16
- from ..api import GeoboxClient as SyncGeoboxClient
17
- from ..raster import Raster as SyncRaster
18
-
19
-
20
- class Raster(AsyncBase):
21
-
22
- BASE_ENDPOINT: str = 'rasters/'
23
-
24
- def __init__(self,
25
- api: 'AsyncGeoboxClient',
26
- uuid: str,
27
- data: Optional[Dict] = {}):
28
- """
29
- Constructs all the necessary attributes for the Raster object.
30
-
31
- Args:
32
- api (AsyncGeoboxClient): The API instance.
33
- uuid (str): The UUID of the raster.
34
- data (Dict, optional): The raster data.
35
- """
36
- super().__init__(api, uuid=uuid, data=data)
37
-
38
-
39
- @classmethod
40
- async def get_rasters(cls, api: 'AsyncGeoboxClient', **kwargs) -> Union[List['Raster'], int]:
41
- """
42
- [async] Get all rasters.
43
-
44
- Args:
45
- api (AsyncGeoboxClient): The API instance.
46
-
47
- Keyword Args:
48
- terrain (bool): whether to get terrain rasters.
49
- q (str): query filter based on OGC CQL standard. e.g. "field1 LIKE '%GIS%' AND created_at > '2021-01-01'"
50
- search (str): search term for keyword-based searching among search_fields or all textual fields if search_fields does not have value. NOTE: if q param is defined this param will be ignored.
51
- search_fields (str): comma separated list of fields for searching
52
- order_by (str): comma separated list of fields for sorting results [field1 A|D, field2 A|D, …]. e.g. name A, type D. NOTE: "A" denotes ascending order and "D" denotes descending order.
53
- return_count (bool): whether to return the total count of rasters. default is False.
54
- skip (int): number of rasters to skip. minimum is 0.
55
- limit (int): number of rasters to return. minimum is 1.
56
- user_id (int): user id to show the rasters of the user. privileges required.
57
- shared (bool): whether to return shared rasters. default is False.
58
-
59
- Returns:
60
- List[Raster] | int: A list of Raster objects or the total count of rasters.
61
-
62
- Example:
63
- >>> from geobox.aio import AsyncGeoboxClient
64
- >>> from geobox.aio.raster import Raster
65
- >>> async with AsyncGeoboxClient() as client:
66
- >>> rasters = await Raster.get_rasters(client, terrain=True, q="name LIKE '%GIS%'")
67
- or
68
- >>> rasters = await client.get_rasters(terrain=True, q="name LIKE '%GIS%'")
69
- """
70
- params = {
71
- 'terrain': kwargs.get('terrain', None),
72
- 'f': 'json',
73
- 'q': kwargs.get('q', None),
74
- 'search': kwargs.get('search', None),
75
- 'search_fields': kwargs.get('search_fields', None),
76
- 'order_by': kwargs.get('order_by', None),
77
- 'return_count': kwargs.get('return_count', False),
78
- 'skip': kwargs.get('skip', 0),
79
- 'limit': kwargs.get('limit', 100),
80
- 'user_id': kwargs.get('user_id', None),
81
- 'shared': kwargs.get('shared', False)
82
- }
83
- return await super()._get_list(api, cls.BASE_ENDPOINT, params, factory_func=lambda api, item: Raster(api, item['uuid'], item))
84
-
85
-
86
-
87
- @classmethod
88
- async def get_rasters_by_ids(cls, api: 'AsyncGeoboxClient', ids: List[str], user_id: int = None) -> List['Raster']:
89
- """
90
- [async] Get rasters by their IDs.
91
-
92
- Args:
93
- api (AsyncGeoboxClient): The API instance.
94
- ids (List[str]): The IDs of the rasters.
95
- user_id (int, optional): specific user. privileges required.
96
-
97
- Returns:
98
- List['Raster']: A list of Raster objects.
99
-
100
- Example:
101
- >>> from geobox.aio import AsyncGeoboxClient
102
- >>> from geobox.aio.raster import Raster
103
- >>> async with AsyncGeoboxClient() as client:
104
- >>> rasters = await Raster.get_rasters_by_ids(client, ids=['123', '456'])
105
- or
106
- >>> rasters = await client.get_rasters_by_ids(ids=['123', '456'])
107
- """
108
- params = {
109
- 'ids': ids,
110
- 'user_id': user_id,
111
- }
112
- endpoint = urljoin(cls.BASE_ENDPOINT, 'get-rasters/')
113
-
114
- return await super()._get_list_by_ids(api, endpoint, params, factory_func=lambda api, item: Raster(api, item['uuid'], item))
115
-
116
-
117
- @classmethod
118
- async def get_raster(cls, api: 'AsyncGeoboxClient', uuid: str, user_id: int = None) -> 'Raster':
119
- """
120
- [async] Get a raster by its UUID.
121
-
122
- Args:
123
- api (AsyncGeoboxClient): The API instance.
124
- uuid (str): The UUID of the raster.
125
- user_id (int, optional): specific user. privileges required.
126
-
127
- Returns:
128
- Raster: A Raster object.
129
-
130
- Example:
131
- >>> from geobox.aio import AsyncGeoboxClient
132
- >>> from geobox.aio.raster import Raster
133
- >>> async with AsyncGeoboxClient() as client:
134
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
135
- or
136
- >>> raster = await client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
137
- """
138
- params = {
139
- 'f': 'json',
140
- 'user_id': user_id
141
- }
142
- return await super()._get_detail(api, cls.BASE_ENDPOINT, uuid, params, factory_func=lambda api, item: Raster(api, item['uuid'], item))
143
-
144
-
145
- @classmethod
146
- async def get_raster_by_name(cls, api: 'AsyncGeoboxClient', name: str, user_id: int = None) -> Union['Raster', None]:
147
- """
148
- [async] Get a raster by name
149
-
150
- Args:
151
- api (AsyncGeoboxClient): The AsyncGeoboxClient instance for making requests.
152
- name (str): the name of the raster to get
153
- user_id (int, optional): specific user. privileges required.
154
-
155
- Returns:
156
- Raster | None: returns the raster if a raster matches the given name, else None
157
-
158
- Example:
159
- >>> from geobox.aio import AsyncGeoboxClient
160
- >>> from geobox.aio.raster import Raster
161
- >>> async with AsyncGeoboxClient() as client:
162
- >>> raster = await Raster.get_raster_by_name(client, name='test')
163
- or
164
- >>> raster = await client.get_raster_by_name(name='test')
165
- """
166
- rasters = await cls.get_rasters(api, q=f"name = '{name}'", user_id=user_id)
167
- if rasters and rasters[0].name == name:
168
- return rasters[0]
169
- else:
170
- return None
171
-
172
-
173
- async def update(self, **kwargs) -> None:
174
- """
175
- [async] Update the raster.
176
-
177
- Keyword Args:
178
- name (str): The name of the raster.
179
- display_name (str): The display name of the raster.
180
- description (str): The description of the raster.
181
-
182
- Returns:
183
- None
184
-
185
- Example:
186
- >>> from geobox.aio import AsyncGeoboxClient
187
- >>> from geobox.aio.raster import Raster
188
- >>> async with AsyncGeoboxClient() as client:
189
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
190
- >>> await raster.update(name="new_name")
191
- """
192
- params = {
193
- 'name': kwargs.get('name'),
194
- 'display_name': kwargs.get('display_name'),
195
- 'description': kwargs.get('description')
196
- }
197
- return await super()._update(self.endpoint, params)
198
-
199
-
200
- async def delete(self) -> None:
201
- """
202
- [async] Delete the raster.
203
-
204
- Returns:
205
- None
206
-
207
- Example:
208
- >>> from geobox.aio import AsyncGeoboxClient
209
- >>> from geobox.aio.raster import Raster
210
- >>> async with AsyncGeoboxClient() as client:
211
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
212
- >>> await raster.delete()
213
- """
214
- await super().delete(self.endpoint)
215
-
216
-
217
- @property
218
- def thumbnail(self) -> str:
219
- """
220
- Get the thumbnail of the raster.
221
-
222
- Returns:
223
- str: The url of the thumbnail.
224
-
225
- Example:
226
- >>> from geobox.aio import AsyncGeoboxClient
227
- >>> from geobox.aio.raster import Raster
228
- >>> async with AsyncGeoboxClient() as client:
229
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
230
- >>> raster.thumbnail
231
- """
232
- return super().thumbnail(format='')
233
-
234
-
235
- @property
236
- async def info(self) -> Dict:
237
- """
238
- [async] Get the info of the raster.
239
-
240
- Returns:
241
- Dict: The info of the raster.
242
-
243
- Example:
244
- >>> from geobox.aio import AsyncGeoboxClient
245
- >>> from geobox.aio.raster import Raster
246
- >>> async with AsyncGeoboxClient() as client:
247
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
248
- >>> await raster.info
249
- """
250
- endpoint = urljoin(self.endpoint, 'info/')
251
- return await self.api.get(endpoint)
252
-
253
-
254
- async def get_statistics(self, indexes: str = None) -> Dict:
255
- """
256
- [async] Get the statistics of the raster.
257
-
258
- Args:
259
- indexes (str): list of comma separated band indexes. e.g. 1, 2, 3
260
-
261
- Returns:
262
- Dict: The statistics of the raster.
263
-
264
- Example:
265
- >>> from geobox.aio import AsyncGeoboxClient
266
- >>> from geobox.aio.raster import Raster
267
- >>> async with AsyncGeoboxClient() as client:
268
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
269
- >>> await raster.get_statistics(indexes='1, 2, 3')
270
- """
271
- params = clean_data({
272
- 'indexes': indexes,
273
- })
274
- query_string = urlencode(params)
275
- endpoint = urljoin(self.endpoint, f'statistics/?{query_string}')
276
- return await self.api.get(endpoint)
277
-
278
-
279
- async def get_point(self, lat: float, lng: float) -> Dict:
280
- """
281
- [async] Get the point of the raster.
282
-
283
- Args:
284
- lat (float): The latitude of the point. minimum is -90, maximum is 90.
285
- lng (float): The longitude of the point. minimum is -180, maximum is 180.
286
-
287
- Returns:
288
- Dict: The point of the raster.
289
-
290
- Example:
291
- >>> from geobox.aio import AsyncGeoboxClient
292
- >>> from geobox.aio.raster import Raster
293
- >>> async with AsyncGeoboxClient() as client:
294
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
295
- >>> await raster.get_point(lat=60, lng=50)
296
- """
297
- if lat < -90 or lat > 90:
298
- raise ValueError("lat must be between -90 and 90")
299
- if lng < -180 or lng > 180:
300
- raise ValueError("lng must be between -180 and 180")
301
-
302
- params = clean_data({
303
- 'lat': lat,
304
- 'lng': lng,
305
- })
306
- query_string = urlencode(params)
307
- endpoint = urljoin(self.endpoint, f'point?{query_string}')
308
- return await self.api.get(endpoint)
309
-
310
-
311
- def _get_save_path(self, save_path: str = None) -> str:
312
- """
313
- Get the path where the file should be saved.
314
-
315
- Args:
316
- save_path (str, optional): The path to save the file.
317
-
318
- Returns:
319
- str: The path where the file is saved.
320
-
321
- Raises:
322
- ValueError: If save_path does not end with a '/'.
323
- """
324
- # If save_path is provided, check if it ends with a '/'
325
- if save_path and save_path.endswith('/'):
326
- return f'{save_path}'
327
-
328
- if save_path and not save_path.endswith('/'):
329
- raise ValueError("save_path must end with a '/'")
330
-
331
- return os.getcwd()
332
-
333
-
334
- def _get_file_name(self, response: requests.Response) -> str:
335
- """
336
- Get the file name from the response.
337
-
338
- Args:
339
- response (requests.Response): The response of the request.
340
-
341
- Returns:
342
- str: The file name
343
- """
344
- if 'Content-Disposition' in response.headers and 'filename=' in response.headers['Content-Disposition']:
345
- file_name = response.headers['Content-Disposition'].split('filename=')[-1].strip().strip('"')
346
-
347
- else:
348
- content_type = response.headers.get("Content-Type", "")
349
- file_name = f'{self.name}{mimetypes.guess_extension(content_type.split(";")[0])}'
350
-
351
- return file_name
352
-
353
-
354
- def _create_progress_bar(self) -> 'tqdm':
355
- """Creates a progress bar for the task."""
356
- try:
357
- from tqdm.auto import tqdm
358
- except ImportError:
359
- from .api import logger
360
- logger.warning("[tqdm] extra is required to show the progress bar. install with: pip insatll geobox[tqdm]")
361
- return None
362
-
363
- return tqdm(unit="B",
364
- total=int(self.size),
365
- file=sys.stdout,
366
- dynamic_ncols=True,
367
- desc="Downloading",
368
- unit_scale=True,
369
- unit_divisor=1024,
370
- ascii=True
371
- )
372
-
373
-
374
- async def download(self, save_path: str = None, progress_bar: bool = True) -> str:
375
- """
376
- [async] Download the raster.
377
-
378
- Args:
379
- save_path (str, optional): Path where the file should be saved.
380
- If not provided, it saves to the current working directory
381
- using the original filename and appropriate extension.
382
- progress_bar (bool, optional): Whether to show a progress bar. default: True
383
-
384
- Returns:
385
- str: The path to save the raster.
386
-
387
- Raises:
388
- ValueError: If file_uuid is not set
389
- OSError: If there are issues with file operations
390
-
391
- Example:
392
- >>> from geobox.aio import AsyncGeoboxClient
393
- >>> from geobox.aio.raster import Raster
394
- >>> async with AsyncGeoboxClient() as client:
395
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
396
- >>> await raster.download(save_path="path/to/save/")
397
- """
398
- if not self.uuid:
399
- raise ValueError("Raster UUID is required to download the raster file")
400
-
401
- save_path = self._get_save_path(save_path)
402
- os.makedirs(os.path.dirname(save_path), exist_ok=True)
403
-
404
- endpoint = urljoin(self.api.base_url, f"{self.endpoint}download/")
405
-
406
- async with self.api.session.session.get(endpoint) as response:
407
- file_name = self._get_file_name(response)
408
- full_path = f"{save_path}/{file_name}"
409
- with open(full_path, 'wb') as f:
410
- pbar = self._create_progress_bar() if progress_bar else None
411
- async for chunk in response.content.iter_chunked(8192):
412
- f.write(chunk)
413
- if pbar:
414
- pbar.update(len(chunk))
415
- pbar.refresh()
416
- if pbar:
417
- pbar.close()
418
-
419
- return os.path.abspath(full_path)
420
-
421
-
422
- async def get_content_file(self, save_path: str = None, progress_bar: bool = True) -> str:
423
- """
424
- [async] Get Raster Content URL
425
-
426
- Args:
427
- save_path (str, optional): Path where the file should be saved.
428
- If not provided, it saves to the current working directory
429
- using the original filename and appropriate extension.
430
- progress_bar (bool, optional): Whether to show a progress bar. default: True
431
-
432
- Returns:
433
- str: The path to save the raster.
434
-
435
- Raises:
436
- ValueError: If uuid is not set
437
- OSError: If there are issues with file operations
438
-
439
- Examples:
440
- >>> from geobox.aio import AsyncGeoboxClient
441
- >>> from geobox.aio.raster import Raster
442
- >>> async with AsyncGeoboxClient() as client:
443
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
444
- >>> raster_tiff = await raste.get_content_file()
445
- """
446
- if not self.uuid:
447
- raise ValueError("Raster UUID is required to download the raster content")
448
-
449
- save_path = self._get_save_path(save_path)
450
- os.makedirs(os.path.dirname(save_path), exist_ok=True)
451
-
452
- endpoint = urljoin(self.api.base_url, f"{self.endpoint}content/")
453
-
454
- async with self.api.session.session.get(endpoint) as response:
455
- file_name = self._get_file_name(response)
456
- full_path = f"{save_path}/{file_name}"
457
- with open(full_path, 'wb') as f:
458
- pbar = self._create_progress_bar() if progress_bar else None
459
- async for chunk in response.content.iter_chunked(8192):
460
- f.write(chunk)
461
- if pbar:
462
- pbar.update(len(chunk))
463
- pbar.refresh()
464
- if pbar:
465
- pbar.close()
466
-
467
- return os.path.abspath(full_path)
468
-
469
-
470
- def get_render_png_url(self, x: int, y: int, z: int, **kwargs) -> str:
471
- """
472
- Get the PNG URL of the raster.
473
-
474
- Args:
475
- x (int): The x coordinate of the tile.
476
- y (int): The y coordinate of the tile.
477
- z (int): The zoom level of the tile.
478
-
479
- Keyword Args:
480
- indexes (str, optional): list of comma separated band indexes to be rendered. e.g. 1, 2, 3
481
- nodata (int, optional)
482
- expression (str, optional): band math expression. e.g. b1*b2+b3
483
- rescale (List, optional): comma (',') separated Min,Max range. Can set multiple time for multiple bands.
484
- color_formula (str, optional): Color formula. e.g. gamma R 0.5
485
- colormap_name (str, optional)
486
- colormap (str, optional): JSON encoded custom Colormap. e.g. {"0": "#ff0000", "1": "#00ff00"} or [[[0, 100], "#ff0000"], [[100, 200], "#00ff00"]]
487
-
488
- Returns:
489
- str: The PNG Render URL of the raster.
490
-
491
- Example:
492
- >>> from geobox.aio import AsyncGeoboxClient
493
- >>> from geobox.aio.raster import Raster
494
- >>> async with AsyncGeoboxClient() as client:
495
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
496
- >>> raster.get_tile_render_url(x=10, y=20, z=1)
497
- """
498
- params = clean_data({
499
- 'indexes': kwargs.get('indexes'),
500
- 'nodata': kwargs.get('nodata'),
501
- 'expression': kwargs.get('expression'),
502
- 'rescale': kwargs.get('rescale'),
503
- 'color_formula': kwargs.get('color_formula'),
504
- 'colormap_name': kwargs.get('colormap_name'),
505
- 'colormap': kwargs.get('colormap')
506
- })
507
- query_string = urlencode(params)
508
- endpoint = f'{self.api.base_url}{self.endpoint}render/{z}/{x}/{y}.png'
509
- if query_string:
510
- endpoint = f'{endpoint}?{query_string}'
511
-
512
- if not self.api.access_token and self.api.apikey:
513
- endpoint = join_url_params(endpoint, {"apikey": self.api.apikey})
514
-
515
- return endpoint
516
-
517
-
518
- def get_tile_pbf_url(self, x: int = '{x}', y: int = '{y}', z: int = '{z}', indexes: str = None) -> str:
519
- """
520
- Get the URL of the tile.
521
-
522
- Args:
523
- x (int, optional): The x coordinate of the tile.
524
- y (int, optional): The y coordinate of the tile.
525
- z (int, optional): The zoom level of the tile.
526
- indexes (str, optional): list of comma separated band indexes to be rendered. e.g. 1, 2, 3
527
-
528
- Returns:
529
- str: The URL of the tile.
530
-
531
- Example:
532
- >>> from geobox.aio import AsyncGeoboxClient
533
- >>> from geobox.aio.raster import Raster
534
- >>> async with AsyncGeoboxClient() as client:
535
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
536
- >>> raster.get_tile_pbf_url(x=10, y=20, z=1)
537
- """
538
- params = clean_data({
539
- 'indexes': indexes
540
- })
541
- query_string = urlencode(params)
542
- endpoint = urljoin(self.api.base_url, f'{self.endpoint}tiles/{z}/{x}/{y}.pbf')
543
- endpoint = urljoin(endpoint, f'?{query_string}')
544
-
545
- if not self.api.access_token and self.api.apikey:
546
- endpoint = join_url_params(endpoint, {"apikey": self.api.apikey})
547
-
548
- return endpoint
549
-
550
-
551
- def get_tile_png_url(self, x: int = 'x', y: int = 'y', z: int = 'z') -> str:
552
- """
553
- Get the URL of the tile.
554
-
555
- Args:
556
- x (int, optional): The x coordinate of the tile.
557
- y (int, optional): The y coordinate of the tile.
558
- z (int, optional): The zoom level of the tile.
559
-
560
- Returns:
561
- str: The URL of the tile.
562
-
563
- Example:
564
- >>> from geobox.aio import AsyncGeoboxClient
565
- >>> from geobox.aio.raster import Raster
566
- >>> async with AsyncGeoboxClient() as client:
567
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
568
- >>> raster.get_tile_png_url(x=10, y=20, z=1)
569
- """
570
- endpoint = f'{self.api.base_url}{self.endpoint}tiles/{z}/{x}/{y}.png'
571
-
572
- if not self.api.access_token and self.api.apikey:
573
- endpoint = f'{endpoint}?apikey={self.api.apikey}'
574
-
575
- return endpoint
576
-
577
-
578
- async def get_tile_json(self) -> Dict:
579
- """
580
- [async] Get the tile JSON of the raster.
581
-
582
- Returns:
583
- Dict: The tile JSON of the raster.
584
-
585
- Example:
586
- >>> from geobox.aio import AsyncGeoboxClient
587
- >>> from geobox.aio.raster import Raster
588
- >>> async with AsyncGeoboxClient() as client:
589
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
590
- >>> await raster.get_tile_json()
591
- """
592
- endpoint = urljoin(self.endpoint, 'tilejson.json')
593
- return await self.api.get(endpoint)
594
-
595
-
596
- def wmts(self, scale: int = None) -> str:
597
- """
598
- Get the WMTS URL
599
-
600
- Args:
601
- scale (int, optional): The scale of the raster. values are: 1, 2
602
-
603
- Returns:
604
- str: the raster WMTS URL
605
-
606
- Example:
607
- >>> from geobox.aio import AsyncGeoboxClient
608
- >>> from geobox.aio.raster import Raster
609
- >>> async with AsyncGeoboxClient() as client:
610
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
611
- >>> raster.wmts(scale=1)
612
- """
613
- endpoint = urljoin(self.api.base_url, f'{self.endpoint}wmts/')
614
- if scale:
615
- endpoint = f"{endpoint}?scale={scale}"
616
-
617
- if not self.api.access_token and self.api.apikey:
618
- endpoint = join_url_params(endpoint, {"apikey": self.api.apikey})
619
-
620
- return endpoint
621
-
622
-
623
- @property
624
- async def settings(self) -> Dict:
625
- """
626
- [async] Get the settings of the raster.
627
-
628
- Returns:
629
- Dict: The settings of the raster.
630
-
631
- Example:
632
- >>> from geobox.aio import AsyncGeoboxClient
633
- >>> from geobox.aio.raster import Raster
634
- >>> async with AsyncGeoboxClient() as client:
635
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
636
- >>> await raster.settings
637
- """
638
- return await super()._get_settings(self.endpoint)
639
-
640
-
641
- async def update_settings(self, settings: Dict) -> Dict:
642
- """
643
- [async] Update the settings
644
-
645
- settings (Dict): settings dictionary
646
-
647
- Returns:
648
- Dict: updated settings
649
-
650
- Example:
651
- >>> from geobox.aio import AsyncGeoboxClient
652
- >>> async with AsyncGeoboxClient() as client:
653
- >>> raster1 = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
654
- >>> raster2 = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
655
- >>> raster1.update_settings(raster2.settings)
656
- """
657
- return await super()._set_settings(self.endpoint, settings)
658
-
659
-
660
- async def set_settings(self, **kwargs) -> None:
661
- """
662
- [async] Set the settings of the raster.
663
-
664
- Keyword Args:
665
- nodata (int): The nodata value of the raster.
666
- indexes (list[int]): The indexes of the raster.
667
- rescale (list[int]): The rescale of the raster.
668
- colormap_name (str): The colormap name of the raster.
669
- color_formula (str): The color formula of the raster.
670
- expression (str): The expression of the raster.
671
- exaggeraion (int): The exaggeraion of the raster.
672
- min_zoom (int): The min zoom of the raster.
673
- max_zoom (int): The max zoom of the raster.
674
- use_cache (bool): Whether to use cache of the raster.
675
- cache_until_zoom (int): The cache until zoom of the raster.
676
-
677
-
678
- Example:
679
- >>> from geobox.aio import AsyncGeoboxClient
680
- >>> from geobox.aio.raster import Raster
681
- >>> async with AsyncGeoboxClient() as client:
682
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
683
- >>> await raster.set_settings(nodata=0,
684
- ... indexes=[1],
685
- ... rescale=[[0, 10000]],
686
- ... colormap_name='gist_rainbow',
687
- ... color_formula='Gamma R 0.5',
688
- ... expression='b1 * 2',
689
- ... exaggeraion=10,
690
- ... min_zoom=0,
691
- ... max_zoom=22,
692
- ... use_cache=True,
693
- ... cache_until_zoom=17)
694
- """
695
- visual_settings = {
696
- 'nodata', 'indexes', 'rescale', 'colormap_name',
697
- 'color_formula', 'expression', 'exaggeraion'
698
- }
699
- tile_settings = {
700
- 'min_zoom', 'max_zoom', 'use_cache', 'cache_until_zoom'
701
- }
702
-
703
- settings = await self.settings
704
-
705
- for key, value in kwargs.items():
706
- if key in visual_settings:
707
- settings['visual_settings'][key] = value
708
- elif key in tile_settings:
709
- settings['tile_settings'][key] = value
710
-
711
-
712
- return await super()._set_settings(self.endpoint, settings)
713
-
714
-
715
- async def share(self, users: List['User']) -> None:
716
- """
717
- [async] Shares the raster with specified users.
718
-
719
- Args:
720
- users (List[User]): The list of user objects to share the raster with.
721
-
722
- Returns:
723
- None
724
-
725
- Example:
726
- >>> from geobox.aio import AsyncGeoboxClient
727
- >>> from geobox.aio.raster import Raster
728
- >>> async with AsyncGeoboxClient() as client:
729
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
730
- >>> users = await client.search_users(search="John")
731
- >>> await raster.share(users=users)
732
- """
733
- await super()._share(self.endpoint, users)
734
-
735
-
736
- async def unshare(self, users: List['User']) -> None:
737
- """
738
- [async] Unshares the raster with specified users.
739
-
740
- Args:
741
- users (List[User]): The list of user objects to unshare the raster with.
742
-
743
- Returns:
744
- None
745
-
746
- Example:
747
- >>> from geobox.aio import AsyncGeoboxClient
748
- >>> from geobox.aio.raster import Raster
749
- >>> async with AsyncGeoboxClient() as client:
750
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
751
- >>> users = await client.search_users(search="John")
752
- >>> await raster.unshare(users=users)
753
- """
754
- await super()._unshare(self.endpoint, users)
755
-
756
-
757
- async def get_shared_users(self, search: str = None, skip: int = 0, limit: int = 10) -> List['User']:
758
- """
759
- [async] Retrieves the list of users the raster is shared with.
760
-
761
- Args:
762
- search (str, optional): The search query.
763
- skip (int, optional): The number of users to skip.
764
- limit (int, optional): The maximum number of users to retrieve.
765
-
766
- Returns:
767
- List[User]: The list of shared users.
768
-
769
- Example:
770
- >>> from geobox.aio import AsyncGeoboxClient
771
- >>> from geobox.aio.raster import Raster
772
- >>> async with AsyncGeoboxClient() as client:
773
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
774
- >>> await raster.get_shared_users(search='John', skip=0, limit=10)
775
- """
776
- params = {
777
- 'search': search,
778
- 'skip': skip,
779
- 'limit': limit
780
- }
781
- return await super()._get_shared_users(self.endpoint, params)
782
-
783
-
784
- async def seed_cache(self, from_zoom: int = None, to_zoom: int = None, extent: List[int] = None, workers: int = 1) -> List['Task']:
785
- """
786
- [async] Seed the cache of the raster.
787
-
788
- Args:
789
- from_zoom (int, optional): The from zoom of the raster.
790
- to_zoom (int, optional): The to zoom of the raster.
791
- extent (List[int], optional): The extent of the raster.
792
- workers (int, optional): The number of workers to use. default is 1.
793
-
794
- Returns:
795
- Task: The task of the seed cache.
796
-
797
- Example:
798
- >>> from geobox.aio import AsyncGeoboxClient
799
- >>> from geobox.aio.raster import Raster
800
- >>> async with AsyncGeoboxClient() as client:
801
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
802
- >>> task = await raster.seed_cache(from_zoom=0, to_zoom=22, extent=[0, 0, 100, 100], workers=1)
803
- """
804
- data = {
805
- 'from_zoom': from_zoom,
806
- 'to_zoom': to_zoom,
807
- 'extent': extent,
808
- 'workers': workers
809
- }
810
- return await super()._seed_cache(self.endpoint, data)
811
-
812
-
813
- async def clear_cache(self) -> None:
814
- """
815
- [async] Clear the cache of the raster.
816
-
817
- Returns:
818
- None
819
-
820
- Example:
821
- >>> from geobox.aio import AsyncGeoboxClient
822
- >>> from geobox.aio.raster import Raster
823
- >>> async with AsyncGeoboxClient() as client:
824
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
825
- >>> await raster.clear_cache()
826
- """
827
- await super()._clear_cache(self.endpoint)
828
-
829
-
830
- @property
831
- async def cache_size(self) -> int:
832
- """
833
- [async] Get the size of the cache of the raster.
834
-
835
- Returns:
836
- int: The size of the cache of the raster.
837
-
838
- Example:
839
- >>> from geobox.aio import AsyncGeoboxClient
840
- >>> from geobox.aio.raster import Raster
841
- >>> async with AsyncGeoboxClient() as client:
842
- >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
843
- >>> await raster.cache_size
844
- """
845
- return await super()._cache_size(self.endpoint)
846
-
847
-
848
- def to_sync(self, sync_client: 'SyncGeoboxClient') -> 'SyncRaster':
849
- """
850
- Switch to sync version of the raster instance to have access to the sync methods
851
-
852
- Args:
853
- sync_client (SyncGeoboxClient): The sync version of the GeoboxClient instance for making requests.
854
-
855
- Returns:
856
- geobox.raster.Raster: the sync instance of the raster.
857
-
858
- Example:
859
- >>> from geobox import Geoboxclient
860
- >>> from geobox.aio import AsyncGeoboxClient
861
- >>> from geobox.aio.raster import Raster
862
- >>> client = GeoboxClient()
863
- >>> async with AsyncGeoboxClient() as async_client:
864
- >>> raster = await Raster.get_raster(async_client, uuid="12345678-1234-5678-1234-567812345678")
865
- >>> sync_raster = raster.to_sync(client)
866
- """
867
- from ..raster import Raster as SyncRaster
868
-
869
- return SyncRaster(api=sync_client, uuid=self.uuid, data=self.data)
1
+ import os
2
+ from urllib.parse import urljoin, urlencode
3
+ from typing import Optional, Dict, List, Optional, Union, TYPE_CHECKING
4
+ import mimetypes
5
+ import requests
6
+ import sys
7
+
8
+ from .base import AsyncBase
9
+ from .task import Task
10
+ from .vectorlayer import VectorLayer
11
+ from .view import VectorLayerView
12
+ from ..utils import clean_data, join_url_params
13
+ from ..enums import PolygonizeConnectivity, AnalysisResampleMethod, SlopeUnit, AnalysisAlgorithm, AnalysisDataType, RangeBound, DistanceUnit
14
+
15
+ if TYPE_CHECKING:
16
+ from . import AsyncGeoboxClient
17
+ from .user import User
18
+ from ..api import GeoboxClient as SyncGeoboxClient
19
+ from ..raster import Raster as SyncRaster
20
+
21
+
22
+ class Raster(AsyncBase):
23
+
24
+ BASE_ENDPOINT: str = 'rasters/'
25
+
26
+ def __init__(self,
27
+ api: 'AsyncGeoboxClient',
28
+ uuid: str,
29
+ data: Optional[Dict] = {}):
30
+ """
31
+ Constructs all the necessary attributes for the Raster object.
32
+
33
+ Args:
34
+ api (AsyncGeoboxClient): The API instance.
35
+ uuid (str): The UUID of the raster.
36
+ data (Dict, optional): The raster data.
37
+ """
38
+ super().__init__(api, uuid=uuid, data=data)
39
+
40
+
41
+ @classmethod
42
+ async def get_rasters(cls, api: 'AsyncGeoboxClient', **kwargs) -> Union[List['Raster'], int]:
43
+ """
44
+ [async] Get all rasters.
45
+
46
+ Args:
47
+ api (AsyncGeoboxClient): The API instance.
48
+
49
+ Keyword Args:
50
+ terrain (bool): whether to get terrain rasters.
51
+ q (str): query filter based on OGC CQL standard. e.g. "field1 LIKE '%GIS%' AND created_at > '2021-01-01'"
52
+ search (str): search term for keyword-based searching among search_fields or all textual fields if search_fields does not have value. NOTE: if q param is defined this param will be ignored.
53
+ search_fields (str): comma separated list of fields for searching
54
+ order_by (str): comma separated list of fields for sorting results [field1 A|D, field2 A|D, …]. e.g. name A, type D. NOTE: "A" denotes ascending order and "D" denotes descending order.
55
+ return_count (bool): whether to return the total count of rasters. default is False.
56
+ skip (int): number of rasters to skip. minimum is 0.
57
+ limit (int): number of rasters to return. minimum is 1.
58
+ user_id (int): user id to show the rasters of the user. privileges required.
59
+ shared (bool): whether to return shared rasters. default is False.
60
+
61
+ Returns:
62
+ List[Raster] | int: A list of Raster objects or the total count of rasters.
63
+
64
+ Example:
65
+ >>> from geobox.aio import AsyncGeoboxClient
66
+ >>> from geobox.aio.raster import Raster
67
+ >>> async with AsyncGeoboxClient() as client:
68
+ >>> rasters = await Raster.get_rasters(client, terrain=True, q="name LIKE '%GIS%'")
69
+ or
70
+ >>> rasters = await client.get_rasters(terrain=True, q="name LIKE '%GIS%'")
71
+ """
72
+ params = {
73
+ 'terrain': kwargs.get('terrain', None),
74
+ 'f': 'json',
75
+ 'q': kwargs.get('q', None),
76
+ 'search': kwargs.get('search', None),
77
+ 'search_fields': kwargs.get('search_fields', None),
78
+ 'order_by': kwargs.get('order_by', None),
79
+ 'return_count': kwargs.get('return_count', False),
80
+ 'skip': kwargs.get('skip', 0),
81
+ 'limit': kwargs.get('limit', 100),
82
+ 'user_id': kwargs.get('user_id', None),
83
+ 'shared': kwargs.get('shared', False)
84
+ }
85
+ return await super()._get_list(api, cls.BASE_ENDPOINT, params, factory_func=lambda api, item: Raster(api, item['uuid'], item))
86
+
87
+
88
+
89
+ @classmethod
90
+ async def get_rasters_by_ids(cls, api: 'AsyncGeoboxClient', ids: List[str], user_id: int = None) -> List['Raster']:
91
+ """
92
+ [async] Get rasters by their IDs.
93
+
94
+ Args:
95
+ api (AsyncGeoboxClient): The API instance.
96
+ ids (List[str]): The IDs of the rasters.
97
+ user_id (int, optional): specific user. privileges required.
98
+
99
+ Returns:
100
+ List['Raster']: A list of Raster objects.
101
+
102
+ Example:
103
+ >>> from geobox.aio import AsyncGeoboxClient
104
+ >>> from geobox.aio.raster import Raster
105
+ >>> async with AsyncGeoboxClient() as client:
106
+ >>> rasters = await Raster.get_rasters_by_ids(client, ids=['123', '456'])
107
+ or
108
+ >>> rasters = await client.get_rasters_by_ids(ids=['123', '456'])
109
+ """
110
+ params = {
111
+ 'ids': ids,
112
+ 'user_id': user_id,
113
+ }
114
+ endpoint = urljoin(cls.BASE_ENDPOINT, 'get-rasters/')
115
+
116
+ return await super()._get_list_by_ids(api, endpoint, params, factory_func=lambda api, item: Raster(api, item['uuid'], item))
117
+
118
+
119
+ @classmethod
120
+ async def get_raster(cls, api: 'AsyncGeoboxClient', uuid: str, user_id: int = None) -> 'Raster':
121
+ """
122
+ [async] Get a raster by its UUID.
123
+
124
+ Args:
125
+ api (AsyncGeoboxClient): The API instance.
126
+ uuid (str): The UUID of the raster.
127
+ user_id (int, optional): specific user. privileges required.
128
+
129
+ Returns:
130
+ Raster: A Raster object.
131
+
132
+ Example:
133
+ >>> from geobox.aio import AsyncGeoboxClient
134
+ >>> from geobox.aio.raster import Raster
135
+ >>> async with AsyncGeoboxClient() as client:
136
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
137
+ or
138
+ >>> raster = await client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
139
+ """
140
+ params = {
141
+ 'f': 'json',
142
+ 'user_id': user_id
143
+ }
144
+ return await super()._get_detail(api, cls.BASE_ENDPOINT, uuid, params, factory_func=lambda api, item: Raster(api, item['uuid'], item))
145
+
146
+
147
+ @classmethod
148
+ async def get_raster_by_name(cls, api: 'AsyncGeoboxClient', name: str, user_id: int = None) -> Union['Raster', None]:
149
+ """
150
+ [async] Get a raster by name
151
+
152
+ Args:
153
+ api (AsyncGeoboxClient): The AsyncGeoboxClient instance for making requests.
154
+ name (str): the name of the raster to get
155
+ user_id (int, optional): specific user. privileges required.
156
+
157
+ Returns:
158
+ Raster | None: returns the raster if a raster matches the given name, else None
159
+
160
+ Example:
161
+ >>> from geobox.aio import AsyncGeoboxClient
162
+ >>> from geobox.aio.raster import Raster
163
+ >>> async with AsyncGeoboxClient() as client:
164
+ >>> raster = await Raster.get_raster_by_name(client, name='test')
165
+ or
166
+ >>> raster = await client.get_raster_by_name(name='test')
167
+ """
168
+ rasters = await cls.get_rasters(api, q=f"name = '{name}'", user_id=user_id)
169
+ if rasters and rasters[0].name == name:
170
+ return rasters[0]
171
+ else:
172
+ return None
173
+
174
+
175
+ async def update(self, **kwargs) -> None:
176
+ """
177
+ [async] Update the raster.
178
+
179
+ Keyword Args:
180
+ name (str): The name of the raster.
181
+ display_name (str): The display name of the raster.
182
+ description (str): The description of the raster.
183
+
184
+ Returns:
185
+ None
186
+
187
+ Example:
188
+ >>> from geobox.aio import AsyncGeoboxClient
189
+ >>> from geobox.aio.raster import Raster
190
+ >>> async with AsyncGeoboxClient() as client:
191
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
192
+ >>> await raster.update(name="new_name")
193
+ """
194
+ params = {
195
+ 'name': kwargs.get('name'),
196
+ 'display_name': kwargs.get('display_name'),
197
+ 'description': kwargs.get('description')
198
+ }
199
+ return await super()._update(self.endpoint, params)
200
+
201
+
202
+ async def delete(self) -> None:
203
+ """
204
+ [async] Delete the raster.
205
+
206
+ Returns:
207
+ None
208
+
209
+ Example:
210
+ >>> from geobox.aio import AsyncGeoboxClient
211
+ >>> from geobox.aio.raster import Raster
212
+ >>> async with AsyncGeoboxClient() as client:
213
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
214
+ >>> await raster.delete()
215
+ """
216
+ await super().delete(self.endpoint)
217
+
218
+
219
+ @property
220
+ def thumbnail(self) -> str:
221
+ """
222
+ Get the thumbnail of the raster.
223
+
224
+ Returns:
225
+ str: The url of the thumbnail.
226
+
227
+ Example:
228
+ >>> from geobox.aio import AsyncGeoboxClient
229
+ >>> from geobox.aio.raster import Raster
230
+ >>> async with AsyncGeoboxClient() as client:
231
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
232
+ >>> raster.thumbnail
233
+ """
234
+ return super().thumbnail(format='')
235
+
236
+
237
+ @property
238
+ async def info(self) -> Dict:
239
+ """
240
+ [async] Get the info of the raster.
241
+
242
+ Returns:
243
+ Dict: The info of the raster.
244
+
245
+ Example:
246
+ >>> from geobox.aio import AsyncGeoboxClient
247
+ >>> from geobox.aio.raster import Raster
248
+ >>> async with AsyncGeoboxClient() as client:
249
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
250
+ >>> await raster.info
251
+ """
252
+ endpoint = urljoin(self.endpoint, 'info/')
253
+ return await self.api.get(endpoint)
254
+
255
+
256
+ async def get_statistics(self, indexes: str = None) -> Dict:
257
+ """
258
+ [async] Get the statistics of the raster.
259
+
260
+ Args:
261
+ indexes (str): list of comma separated band indexes. e.g. 1, 2, 3
262
+
263
+ Returns:
264
+ Dict: The statistics of the raster.
265
+
266
+ Example:
267
+ >>> from geobox.aio import AsyncGeoboxClient
268
+ >>> from geobox.aio.raster import Raster
269
+ >>> async with AsyncGeoboxClient() as client:
270
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
271
+ >>> await raster.get_statistics(indexes='1, 2, 3')
272
+ """
273
+ params = clean_data({
274
+ 'indexes': indexes,
275
+ })
276
+ query_string = urlencode(params)
277
+ endpoint = urljoin(self.endpoint, f'statistics/?{query_string}')
278
+ return await self.api.get(endpoint)
279
+
280
+
281
+ async def get_point(self, lat: float, lng: float) -> Dict:
282
+ """
283
+ [async] Get the point of the raster.
284
+
285
+ Args:
286
+ lat (float): The latitude of the point. minimum is -90, maximum is 90.
287
+ lng (float): The longitude of the point. minimum is -180, maximum is 180.
288
+
289
+ Returns:
290
+ Dict: The point of the raster.
291
+
292
+ Example:
293
+ >>> from geobox.aio import AsyncGeoboxClient
294
+ >>> from geobox.aio.raster import Raster
295
+ >>> async with AsyncGeoboxClient() as client:
296
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
297
+ >>> await raster.get_point(lat=60, lng=50)
298
+ """
299
+ if lat < -90 or lat > 90:
300
+ raise ValueError("lat must be between -90 and 90")
301
+ if lng < -180 or lng > 180:
302
+ raise ValueError("lng must be between -180 and 180")
303
+
304
+ params = clean_data({
305
+ 'lat': lat,
306
+ 'lng': lng,
307
+ })
308
+ query_string = urlencode(params)
309
+ endpoint = urljoin(self.endpoint, f'point?{query_string}')
310
+ return await self.api.get(endpoint)
311
+
312
+
313
+ def _get_save_path(self, save_path: str = None) -> str:
314
+ """
315
+ Get the path where the file should be saved.
316
+
317
+ Args:
318
+ save_path (str, optional): The path to save the file.
319
+
320
+ Returns:
321
+ str: The path where the file is saved.
322
+
323
+ Raises:
324
+ ValueError: If save_path does not end with a '/'.
325
+ """
326
+ # If save_path is provided, check if it ends with a '/'
327
+ if save_path and save_path.endswith('/'):
328
+ return f'{save_path}'
329
+
330
+ if save_path and not save_path.endswith('/'):
331
+ raise ValueError("save_path must end with a '/'")
332
+
333
+ return os.getcwd()
334
+
335
+
336
+ def _get_file_name(self, response: requests.Response) -> str:
337
+ """
338
+ Get the file name from the response.
339
+
340
+ Args:
341
+ response (requests.Response): The response of the request.
342
+
343
+ Returns:
344
+ str: The file name
345
+ """
346
+ if 'Content-Disposition' in response.headers and 'filename=' in response.headers['Content-Disposition']:
347
+ file_name = response.headers['Content-Disposition'].split('filename=')[-1].strip().strip('"')
348
+
349
+ else:
350
+ content_type = response.headers.get("Content-Type", "")
351
+ file_name = f'{self.name}{mimetypes.guess_extension(content_type.split(";")[0])}'
352
+
353
+ return file_name
354
+
355
+
356
+ def _create_progress_bar(self) -> 'tqdm':
357
+ """Creates a progress bar for the task."""
358
+ try:
359
+ from tqdm.auto import tqdm
360
+ except ImportError:
361
+ from .api import logger
362
+ logger.warning("[tqdm] extra is required to show the progress bar. install with: pip insatll geobox[tqdm]")
363
+ return None
364
+
365
+ return tqdm(unit="B",
366
+ total=int(self.size),
367
+ file=sys.stdout,
368
+ dynamic_ncols=True,
369
+ desc="Downloading",
370
+ unit_scale=True,
371
+ unit_divisor=1024,
372
+ ascii=True
373
+ )
374
+
375
+
376
+ async def download(self, save_path: str = None, progress_bar: bool = True) -> str:
377
+ """
378
+ [async] Download the raster.
379
+
380
+ Args:
381
+ save_path (str, optional): Path where the file should be saved.
382
+ If not provided, it saves to the current working directory
383
+ using the original filename and appropriate extension.
384
+ progress_bar (bool, optional): Whether to show a progress bar. default: True
385
+
386
+ Returns:
387
+ str: The path to save the raster.
388
+
389
+ Raises:
390
+ ValueError: If file_uuid is not set
391
+ OSError: If there are issues with file operations
392
+
393
+ Example:
394
+ >>> from geobox.aio import AsyncGeoboxClient
395
+ >>> from geobox.aio.raster import Raster
396
+ >>> async with AsyncGeoboxClient() as client:
397
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
398
+ >>> await raster.download(save_path="path/to/save/")
399
+ """
400
+ if not self.uuid:
401
+ raise ValueError("Raster UUID is required to download the raster file")
402
+
403
+ save_path = self._get_save_path(save_path)
404
+ os.makedirs(os.path.dirname(save_path), exist_ok=True)
405
+
406
+ endpoint = urljoin(self.api.base_url, f"{self.endpoint}download/")
407
+
408
+ async with self.api.session.session.get(endpoint) as response:
409
+ file_name = self._get_file_name(response)
410
+ full_path = f"{save_path}/{file_name}"
411
+ with open(full_path, 'wb') as f:
412
+ pbar = self._create_progress_bar() if progress_bar else None
413
+ async for chunk in response.content.iter_chunked(8192):
414
+ f.write(chunk)
415
+ if pbar:
416
+ pbar.update(len(chunk))
417
+ pbar.refresh()
418
+ if pbar:
419
+ pbar.close()
420
+
421
+ return os.path.abspath(full_path)
422
+
423
+
424
+ async def get_content_file(self, save_path: str = None, progress_bar: bool = True) -> str:
425
+ """
426
+ [async] Get Raster Content URL
427
+
428
+ Args:
429
+ save_path (str, optional): Path where the file should be saved.
430
+ If not provided, it saves to the current working directory
431
+ using the original filename and appropriate extension.
432
+ progress_bar (bool, optional): Whether to show a progress bar. default: True
433
+
434
+ Returns:
435
+ str: The path to save the raster.
436
+
437
+ Raises:
438
+ ValueError: If uuid is not set
439
+ OSError: If there are issues with file operations
440
+
441
+ Examples:
442
+ >>> from geobox.aio import AsyncGeoboxClient
443
+ >>> from geobox.aio.raster import Raster
444
+ >>> async with AsyncGeoboxClient() as client:
445
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
446
+ >>> raster_tiff = await raste.get_content_file()
447
+ """
448
+ if not self.uuid:
449
+ raise ValueError("Raster UUID is required to download the raster content")
450
+
451
+ save_path = self._get_save_path(save_path)
452
+ os.makedirs(os.path.dirname(save_path), exist_ok=True)
453
+
454
+ endpoint = urljoin(self.api.base_url, f"{self.endpoint}content/")
455
+
456
+ async with self.api.session.session.get(endpoint) as response:
457
+ file_name = self._get_file_name(response)
458
+ full_path = f"{save_path}/{file_name}"
459
+ with open(full_path, 'wb') as f:
460
+ pbar = self._create_progress_bar() if progress_bar else None
461
+ async for chunk in response.content.iter_chunked(8192):
462
+ f.write(chunk)
463
+ if pbar:
464
+ pbar.update(len(chunk))
465
+ pbar.refresh()
466
+ if pbar:
467
+ pbar.close()
468
+
469
+ return os.path.abspath(full_path)
470
+
471
+
472
+ def get_render_png_url(self, x: int, y: int, z: int, **kwargs) -> str:
473
+ """
474
+ Get the PNG URL of the raster.
475
+
476
+ Args:
477
+ x (int): The x coordinate of the tile.
478
+ y (int): The y coordinate of the tile.
479
+ z (int): The zoom level of the tile.
480
+
481
+ Keyword Args:
482
+ indexes (str, optional): list of comma separated band indexes to be rendered. e.g. 1, 2, 3
483
+ nodata (int, optional)
484
+ expression (str, optional): band math expression. e.g. b1*b2+b3
485
+ rescale (List, optional): comma (',') separated Min,Max range. Can set multiple time for multiple bands.
486
+ color_formula (str, optional): Color formula. e.g. gamma R 0.5
487
+ colormap_name (str, optional)
488
+ colormap (str, optional): JSON encoded custom Colormap. e.g. {"0": "#ff0000", "1": "#00ff00"} or [[[0, 100], "#ff0000"], [[100, 200], "#00ff00"]]
489
+
490
+ Returns:
491
+ str: The PNG Render URL of the raster.
492
+
493
+ Example:
494
+ >>> from geobox.aio import AsyncGeoboxClient
495
+ >>> from geobox.aio.raster import Raster
496
+ >>> async with AsyncGeoboxClient() as client:
497
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
498
+ >>> raster.get_tile_render_url(x=10, y=20, z=1)
499
+ """
500
+ params = clean_data({
501
+ 'indexes': kwargs.get('indexes'),
502
+ 'nodata': kwargs.get('nodata'),
503
+ 'expression': kwargs.get('expression'),
504
+ 'rescale': kwargs.get('rescale'),
505
+ 'color_formula': kwargs.get('color_formula'),
506
+ 'colormap_name': kwargs.get('colormap_name'),
507
+ 'colormap': kwargs.get('colormap')
508
+ })
509
+ query_string = urlencode(params)
510
+ endpoint = f'{self.api.base_url}{self.endpoint}render/{z}/{x}/{y}.png'
511
+ if query_string:
512
+ endpoint = f'{endpoint}?{query_string}'
513
+
514
+ if not self.api.access_token and self.api.apikey:
515
+ endpoint = join_url_params(endpoint, {"apikey": self.api.apikey})
516
+
517
+ return endpoint
518
+
519
+
520
+ def get_tile_pbf_url(self, x: int = '{x}', y: int = '{y}', z: int = '{z}', indexes: str = None) -> str:
521
+ """
522
+ Get the URL of the tile.
523
+
524
+ Args:
525
+ x (int, optional): The x coordinate of the tile.
526
+ y (int, optional): The y coordinate of the tile.
527
+ z (int, optional): The zoom level of the tile.
528
+ indexes (str, optional): list of comma separated band indexes to be rendered. e.g. 1, 2, 3
529
+
530
+ Returns:
531
+ str: The URL of the tile.
532
+
533
+ Example:
534
+ >>> from geobox.aio import AsyncGeoboxClient
535
+ >>> from geobox.aio.raster import Raster
536
+ >>> async with AsyncGeoboxClient() as client:
537
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
538
+ >>> raster.get_tile_pbf_url(x=10, y=20, z=1)
539
+ """
540
+ params = clean_data({
541
+ 'indexes': indexes
542
+ })
543
+ query_string = urlencode(params)
544
+ endpoint = urljoin(self.api.base_url, f'{self.endpoint}tiles/{z}/{x}/{y}.pbf')
545
+ endpoint = urljoin(endpoint, f'?{query_string}')
546
+
547
+ if not self.api.access_token and self.api.apikey:
548
+ endpoint = join_url_params(endpoint, {"apikey": self.api.apikey})
549
+
550
+ return endpoint
551
+
552
+
553
+ def get_tile_png_url(self, x: int = 'x', y: int = 'y', z: int = 'z') -> str:
554
+ """
555
+ Get the URL of the tile.
556
+
557
+ Args:
558
+ x (int, optional): The x coordinate of the tile.
559
+ y (int, optional): The y coordinate of the tile.
560
+ z (int, optional): The zoom level of the tile.
561
+
562
+ Returns:
563
+ str: The URL of the tile.
564
+
565
+ Example:
566
+ >>> from geobox.aio import AsyncGeoboxClient
567
+ >>> from geobox.aio.raster import Raster
568
+ >>> async with AsyncGeoboxClient() as client:
569
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
570
+ >>> raster.get_tile_png_url(x=10, y=20, z=1)
571
+ """
572
+ endpoint = f'{self.api.base_url}{self.endpoint}tiles/{z}/{x}/{y}.png'
573
+
574
+ if not self.api.access_token and self.api.apikey:
575
+ endpoint = f'{endpoint}?apikey={self.api.apikey}'
576
+
577
+ return endpoint
578
+
579
+
580
+ async def get_tile_json(self) -> Dict:
581
+ """
582
+ [async] Get the tile JSON of the raster.
583
+
584
+ Returns:
585
+ Dict: The tile JSON of the raster.
586
+
587
+ Example:
588
+ >>> from geobox.aio import AsyncGeoboxClient
589
+ >>> from geobox.aio.raster import Raster
590
+ >>> async with AsyncGeoboxClient() as client:
591
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
592
+ >>> await raster.get_tile_json()
593
+ """
594
+ endpoint = urljoin(self.endpoint, 'tilejson.json')
595
+ return await self.api.get(endpoint)
596
+
597
+
598
+ def wmts(self, scale: int = None) -> str:
599
+ """
600
+ Get the WMTS URL
601
+
602
+ Args:
603
+ scale (int, optional): The scale of the raster. values are: 1, 2
604
+
605
+ Returns:
606
+ str: the raster WMTS URL
607
+
608
+ Example:
609
+ >>> from geobox.aio import AsyncGeoboxClient
610
+ >>> from geobox.aio.raster import Raster
611
+ >>> async with AsyncGeoboxClient() as client:
612
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
613
+ >>> raster.wmts(scale=1)
614
+ """
615
+ endpoint = urljoin(self.api.base_url, f'{self.endpoint}wmts/')
616
+ if scale:
617
+ endpoint = f"{endpoint}?scale={scale}"
618
+
619
+ if not self.api.access_token and self.api.apikey:
620
+ endpoint = join_url_params(endpoint, {"apikey": self.api.apikey})
621
+
622
+ return endpoint
623
+
624
+
625
+ @property
626
+ async def settings(self) -> Dict:
627
+ """
628
+ [async] Get the settings of the raster.
629
+
630
+ Returns:
631
+ Dict: The settings of the raster.
632
+
633
+ Example:
634
+ >>> from geobox.aio import AsyncGeoboxClient
635
+ >>> from geobox.aio.raster import Raster
636
+ >>> async with AsyncGeoboxClient() as client:
637
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
638
+ >>> await raster.settings
639
+ """
640
+ return await super()._get_settings(self.endpoint)
641
+
642
+
643
+ async def update_settings(self, settings: Dict) -> Dict:
644
+ """
645
+ [async] Update the settings
646
+
647
+ settings (Dict): settings dictionary
648
+
649
+ Returns:
650
+ Dict: updated settings
651
+
652
+ Example:
653
+ >>> from geobox.aio import AsyncGeoboxClient
654
+ >>> async with AsyncGeoboxClient() as client:
655
+ >>> raster1 = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
656
+ >>> raster2 = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
657
+ >>> raster1.update_settings(raster2.settings)
658
+ """
659
+ return await super()._set_settings(self.endpoint, settings)
660
+
661
+
662
+ async def set_settings(self, **kwargs) -> None:
663
+ """
664
+ [async] Set the settings of the raster.
665
+
666
+ Keyword Args:
667
+ nodata (int): The nodata value of the raster.
668
+ indexes (list[int]): The indexes of the raster.
669
+ rescale (list[int]): The rescale of the raster.
670
+ colormap_name (str): The colormap name of the raster.
671
+ color_formula (str): The color formula of the raster.
672
+ expression (str): The expression of the raster.
673
+ exaggeraion (int): The exaggeraion of the raster.
674
+ min_zoom (int): The min zoom of the raster.
675
+ max_zoom (int): The max zoom of the raster.
676
+ use_cache (bool): Whether to use cache of the raster.
677
+ cache_until_zoom (int): The cache until zoom of the raster.
678
+
679
+
680
+ Example:
681
+ >>> from geobox.aio import AsyncGeoboxClient
682
+ >>> from geobox.aio.raster import Raster
683
+ >>> async with AsyncGeoboxClient() as client:
684
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
685
+ >>> await raster.set_settings(nodata=0,
686
+ ... indexes=[1],
687
+ ... rescale=[[0, 10000]],
688
+ ... colormap_name='gist_rainbow',
689
+ ... color_formula='Gamma R 0.5',
690
+ ... expression='b1 * 2',
691
+ ... exaggeraion=10,
692
+ ... min_zoom=0,
693
+ ... max_zoom=22,
694
+ ... use_cache=True,
695
+ ... cache_until_zoom=17)
696
+ """
697
+ visual_settings = {
698
+ 'nodata', 'indexes', 'rescale', 'colormap_name',
699
+ 'color_formula', 'expression', 'exaggeraion'
700
+ }
701
+ tile_settings = {
702
+ 'min_zoom', 'max_zoom', 'use_cache', 'cache_until_zoom'
703
+ }
704
+
705
+ settings = await self.settings
706
+
707
+ for key, value in kwargs.items():
708
+ if key in visual_settings:
709
+ settings['visual_settings'][key] = value
710
+ elif key in tile_settings:
711
+ settings['tile_settings'][key] = value
712
+
713
+
714
+ return await super()._set_settings(self.endpoint, settings)
715
+
716
+
717
+ async def share(self, users: List['User']) -> None:
718
+ """
719
+ [async] Shares the raster with specified users.
720
+
721
+ Args:
722
+ users (List[User]): The list of user objects to share the raster with.
723
+
724
+ Returns:
725
+ None
726
+
727
+ Example:
728
+ >>> from geobox.aio import AsyncGeoboxClient
729
+ >>> from geobox.aio.raster import Raster
730
+ >>> async with AsyncGeoboxClient() as client:
731
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
732
+ >>> users = await client.search_users(search="John")
733
+ >>> await raster.share(users=users)
734
+ """
735
+ await super()._share(self.endpoint, users)
736
+
737
+
738
+ async def unshare(self, users: List['User']) -> None:
739
+ """
740
+ [async] Unshares the raster with specified users.
741
+
742
+ Args:
743
+ users (List[User]): The list of user objects to unshare the raster with.
744
+
745
+ Returns:
746
+ None
747
+
748
+ Example:
749
+ >>> from geobox.aio import AsyncGeoboxClient
750
+ >>> from geobox.aio.raster import Raster
751
+ >>> async with AsyncGeoboxClient() as client:
752
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
753
+ >>> users = await client.search_users(search="John")
754
+ >>> await raster.unshare(users=users)
755
+ """
756
+ await super()._unshare(self.endpoint, users)
757
+
758
+
759
+ async def get_shared_users(self, search: str = None, skip: int = 0, limit: int = 10) -> List['User']:
760
+ """
761
+ [async] Retrieves the list of users the raster is shared with.
762
+
763
+ Args:
764
+ search (str, optional): The search query.
765
+ skip (int, optional): The number of users to skip.
766
+ limit (int, optional): The maximum number of users to retrieve.
767
+
768
+ Returns:
769
+ List[User]: The list of shared users.
770
+
771
+ Example:
772
+ >>> from geobox.aio import AsyncGeoboxClient
773
+ >>> from geobox.aio.raster import Raster
774
+ >>> async with AsyncGeoboxClient() as client:
775
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
776
+ >>> await raster.get_shared_users(search='John', skip=0, limit=10)
777
+ """
778
+ params = {
779
+ 'search': search,
780
+ 'skip': skip,
781
+ 'limit': limit
782
+ }
783
+ return await super()._get_shared_users(self.endpoint, params)
784
+
785
+
786
+ async def seed_cache(self, from_zoom: int = None, to_zoom: int = None, extent: List[int] = None, workers: int = 1) -> List['Task']:
787
+ """
788
+ [async] Seed the cache of the raster.
789
+
790
+ Args:
791
+ from_zoom (int, optional): The from zoom of the raster.
792
+ to_zoom (int, optional): The to zoom of the raster.
793
+ extent (List[int], optional): The extent of the raster.
794
+ workers (int, optional): The number of workers to use. default is 1.
795
+
796
+ Returns:
797
+ Task: The task of the seed cache.
798
+
799
+ Example:
800
+ >>> from geobox.aio import AsyncGeoboxClient
801
+ >>> from geobox.aio.raster import Raster
802
+ >>> async with AsyncGeoboxClient() as client:
803
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
804
+ >>> task = await raster.seed_cache(from_zoom=0, to_zoom=22, extent=[0, 0, 100, 100], workers=1)
805
+ """
806
+ data = {
807
+ 'from_zoom': from_zoom,
808
+ 'to_zoom': to_zoom,
809
+ 'extent': extent,
810
+ 'workers': workers
811
+ }
812
+ return await super()._seed_cache(self.endpoint, data)
813
+
814
+
815
+ async def clear_cache(self) -> None:
816
+ """
817
+ [async] Clear the cache of the raster.
818
+
819
+ Returns:
820
+ None
821
+
822
+ Example:
823
+ >>> from geobox.aio import AsyncGeoboxClient
824
+ >>> from geobox.aio.raster import Raster
825
+ >>> async with AsyncGeoboxClient() as client:
826
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
827
+ >>> await raster.clear_cache()
828
+ """
829
+ await super()._clear_cache(self.endpoint)
830
+
831
+
832
+ @property
833
+ async def cache_size(self) -> int:
834
+ """
835
+ [async] Get the size of the cache of the raster.
836
+
837
+ Returns:
838
+ int: The size of the cache of the raster.
839
+
840
+ Example:
841
+ >>> from geobox.aio import AsyncGeoboxClient
842
+ >>> from geobox.aio.raster import Raster
843
+ >>> async with AsyncGeoboxClient() as client:
844
+ >>> raster = await Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
845
+ >>> await raster.cache_size
846
+ """
847
+ return await super()._cache_size(self.endpoint)
848
+
849
+
850
+ def to_sync(self, sync_client: 'SyncGeoboxClient') -> 'SyncRaster':
851
+ """
852
+ Switch to sync version of the raster instance to have access to the sync methods
853
+
854
+ Args:
855
+ sync_client (SyncGeoboxClient): The sync version of the GeoboxClient instance for making requests.
856
+
857
+ Returns:
858
+ geobox.raster.Raster: the sync instance of the raster.
859
+
860
+ Example:
861
+ >>> from geobox import Geoboxclient
862
+ >>> from geobox.aio import AsyncGeoboxClient
863
+ >>> from geobox.aio.raster import Raster
864
+ >>> client = GeoboxClient()
865
+ >>> async with AsyncGeoboxClient() as async_client:
866
+ >>> raster = await Raster.get_raster(async_client, uuid="12345678-1234-5678-1234-567812345678")
867
+ >>> sync_raster = raster.to_sync(client)
868
+ """
869
+ from ..raster import Raster as SyncRaster
870
+
871
+ return SyncRaster(api=sync_client, uuid=self.uuid, data=self.data)
872
+
873
+
874
+ async def polygonize(self,
875
+ output_layer_name: str,
876
+ band_index: int = 1,
877
+ value_field: Optional[str] = None,
878
+ mask_nodata: bool = False,
879
+ connectivity: PolygonizeConnectivity = PolygonizeConnectivity.connected_4,
880
+ keep_values: Optional[str] = None,
881
+ layer_name: Optional[str] = None,
882
+ user_id: Optional[int] = None) -> 'Task':
883
+ """
884
+ [async] Convert a raster to vector polygons
885
+
886
+ vectorizes a raster (polygonize) to a vector dataset (*.gpkg). Only users with Publisher role or higher can perform this operation
887
+
888
+ Args:
889
+ api (GeoboxClient): The GeoboxClient instance for making requests
890
+ raster (Raster): Raster instance
891
+ output_layer_name (str): Name for the output vector layer.
892
+ band_index (int, optional): Raster band to polygonize. default: 1
893
+ value_field (str, optional): Name of attribute field storing the pixel value. default: None
894
+ mask_nodata (bool, optional): If True, NoData pixels are excluded using the band mask. default: False
895
+ connectivity (PolygonizeConnectivity, optional): 4 or 8 connectivity for region grouping. default: PolygonizeConnectivity.connected_4
896
+ keep_values (str, optional): JSON array of values to keep (e.g., '[1,2,3]'). default: None
897
+ layer_name (str, optional): Output layer name. default: None
898
+ user_id (int, optional): specific user. priviledges required!
899
+
900
+ Returns:
901
+ Task: task instance of the process
902
+
903
+ Example:
904
+ >>> from geobox.aio import AsyncGeoboxClient
905
+ >>> async with AsyncGeoboxClient() as client:
906
+ >>> raster = await client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
907
+ >>> task = raster.polygonize(output_layer_name='test')
908
+ """
909
+ from .analysis import Analysis
910
+ return await Analysis.polygonize(self.api,
911
+ self,
912
+ output_layer_name=output_layer_name,
913
+ band_index=band_index,
914
+ value_field=value_field,
915
+ mask_nodata=mask_nodata,
916
+ connectivity=connectivity,
917
+ keep_values=keep_values,
918
+ layer_name=layer_name,
919
+ user_id=user_id)
920
+
921
+
922
+ async def clip(self,
923
+ layer: Union[VectorLayer, VectorLayerView],
924
+ output_raster_name: str,
925
+ where: Optional[str] = None,
926
+ dst_nodata: int = -9999,
927
+ crop: bool = True,
928
+ resample: AnalysisResampleMethod = AnalysisResampleMethod.near,
929
+ user_id: Optional[int] = None) -> 'Task':
930
+ """
931
+ [async] Clip a raster using a vector layer as a mask
932
+
933
+ clips a raster dataset using a vector layer as the clipping boundary. Only users with Publisher role or higher can perform this operation
934
+
935
+ Args:
936
+ api (GeoboxClient): The GeoboxClient instance for making requests
937
+ raster (Raster): Raster instance
938
+ layer (VectorLayer | VectorLayerView): VectorLayer or VectorLayerView instance
939
+ output_raster_name (str): Name for the output raster dataset
940
+ where (str, optional): Optional attribute filter, e.g. 'VEG=forest'.
941
+ dst_nodata (int, optional): Output NoData value. default: -9999
942
+ crop (bool, optional): True=shrink extent to polygon(s); False=keep full extent but mask outside. default: True
943
+ resample (AnalysisResampleMethod, optional): Resampling method: 'near', 'bilinear', 'cubic', 'lanczos', etc. default: AnalysisResampleMethod.near
944
+ user_id (int, optional): specific user. priviledges required!
945
+
946
+ Returns:
947
+ Task: task instance of the process
948
+
949
+ Example:
950
+ >>> from geobox.aio import AsyncGeoboxClient
951
+ >>> async with AsyncGeoboxClient() as client:
952
+ >>> raster = await client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
953
+ >>> vector = await client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
954
+ >>> await raster.clip(layer=vector, output_raster_name='test')
955
+ """
956
+ from .analysis import Analysis
957
+ return await Analysis.clip(self.api,
958
+ self,
959
+ layer=layer,
960
+ output_raster_name=output_raster_name,
961
+ where=where,
962
+ dst_nodata=dst_nodata,
963
+ crop=crop,
964
+ resample=resample,
965
+ user_id=user_id)
966
+
967
+
968
+ async def slope(self,
969
+ output_raster_name: str,
970
+ slope_units: SlopeUnit = SlopeUnit.degree,
971
+ algorithm: AnalysisAlgorithm = AnalysisAlgorithm.Horn,
972
+ scale: int = 1,
973
+ compute_edges: bool = True,
974
+ nodata_out: int = -9999,
975
+ user_id: Optional[int] = None) -> 'Task':
976
+ """
977
+ [async] Calculate slope from a DEM raster.
978
+
979
+ This endpoint creates a slope raster from a Digital Elevation Model (DEM). Only users with Publisher role or higher can perform this operation.
980
+
981
+ Args:
982
+ output_raster_name (str): Name for the output raster dataset.
983
+ slope_units (SlopeUnit, optional): Slope units: 'degree' or 'percent'. default: SlopeUnit.degree
984
+ algorithm (AnalysisAlgorithm, optional): Algorithm: 'Horn' or 'ZevenbergenThorne'. default: AnalysisAlgorithm.Horn
985
+ scale (int, optional): Ratio of vertical units to horizontal units. default: 1
986
+ compute_edges (bool, optional): Whether to compute edges. default: True
987
+ nodata (int, optional): NoData value for the output raster. default = -9999
988
+ user_id (int, optional): specific user. priviledges required!
989
+
990
+ Returns:
991
+ Task: task instance of the process
992
+
993
+ Example:
994
+ >>> from geobox.aio import AsyncGeoboxClient
995
+ >>> async with AsyncGeoboxClient() as client:
996
+ >>> raster = await client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
997
+ >>> task = await raster.slope(output_raster_name='test')
998
+ """
999
+ from .analysis import Analysis
1000
+ return await Analysis.slope(self.api,
1001
+ self,
1002
+ slope_units=slope_units,
1003
+ output_raster_name=output_raster_name,
1004
+ algorithm=algorithm,
1005
+ scale=scale,
1006
+ compute_edges=compute_edges,
1007
+ nodata_out=nodata_out,
1008
+ user_id=user_id)
1009
+
1010
+
1011
+ async def aspect(self,
1012
+ output_raster_name: str,
1013
+ algorithm: AnalysisAlgorithm = AnalysisAlgorithm.Horn,
1014
+ trigonometric: bool = False,
1015
+ zero_for_flat: bool = True,
1016
+ compute_edges: bool = True,
1017
+ nodata_out: int = -9999,
1018
+ user_id: Optional[int] = None) -> 'Task':
1019
+ """
1020
+ [async] Calculate aspect from a DEM raster.
1021
+
1022
+ it creates an aspect raster (degrees 0–360) from a Digital Elevation Model (DEM).
1023
+ Only users with Publisher role or higher can perform this operation.
1024
+
1025
+ Args:
1026
+ api (GeoboxClient): The GeoboxClient instance for making requests
1027
+ raster (Raster): DEM Raster instance
1028
+ output_raster_name (str): Name for the output raster dataset.
1029
+ algorithm (AnalysisAlgorithm, optional): Algorithm: 'Horn' or 'ZevenbergenThorne'. default: AnalysisAlgorithm.Horn
1030
+ trigonometric (bool, optional): False: azimuth (0°=N, 90°=E, clockwise); True: 0°=E, counter-clockwise. default: False
1031
+ zero_for_flat (bool, optional): Set flats (slope==0) to 0 instead of NoData. default: True
1032
+ compute_edges (bool, optional): Whether to compute edges. default: True
1033
+ nodata (int, optional): NoData value for the output raster. default = -9999
1034
+ user_id (int, optional): specific user. priviledges required!
1035
+
1036
+ Returns:
1037
+ Task: task instance of the process
1038
+
1039
+ Example:
1040
+ >>> from geobox.aio import AsyncGeoboxClient
1041
+ >>> async with AsyncGeoboxClient() as client:
1042
+ >>> raster = await client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
1043
+ >>> await raster.aspect(output_raster_name='test')
1044
+ """
1045
+ from .analysis import Analysis
1046
+ return await Analysis.aspect(self.api,
1047
+ self,
1048
+ output_raster_name=output_raster_name,
1049
+ algorithm=algorithm,
1050
+ trigonometric=trigonometric,
1051
+ zero_for_flat=zero_for_flat,
1052
+ compute_edge=compute_edges,
1053
+ nodata_out=nodata_out,
1054
+ user_id=user_id)
1055
+
1056
+
1057
+ async def reclassify(self,
1058
+ output_raster_name: str,
1059
+ rules: str,
1060
+ default_value: Optional[int] = None,
1061
+ nodata_in: int = -9999,
1062
+ nodata_out: int = -9999,
1063
+ out_dtype: AnalysisDataType = AnalysisDataType.int16,
1064
+ inclusive: RangeBound = RangeBound.left,
1065
+ user_id: Optional[int] = None) -> 'Task':
1066
+ """
1067
+ [async] Reclassify a raster using value mapping or class breaks.
1068
+
1069
+ This endpoint reclassifies raster values according to specified rules.
1070
+ Only users with Publisher role or higher can perform this operation.
1071
+
1072
+ Args:
1073
+ output_raster_name (str): Name for the output reclassified raster dataset.
1074
+ rules (str): JSON string containing reclassification rules.
1075
+ For mode='exact', it should be a dict {old_value: new_value}.
1076
+ For mode='range', it should be a list of (low, high, new_value).
1077
+ Example for mode='exact': '{"1": 10, "2": 20, "3": 30}'.
1078
+ Example for mode='range': '[[0, 10, 1], [10, 20, 2], [20, 30, 3]]'.
1079
+ the method would detect the mode type based on the rules input.
1080
+ default_value (str, optional): Value to assign when a pixel matches no rule.
1081
+ nodata_in (int, optional): NoData of input. If None, tries to get from the input raster.
1082
+ nodata_out (int, optional): NoData value to set on output band.
1083
+ out_dtype (AnalysisDataType, optional): Output data type. default: AnalysisDataType.int16
1084
+ inclusive (RangeBound, optional): Range bound semantics for mode='range': 'left', 'right', 'both', 'neither'. default: RangeBound.left
1085
+ user_id (int, optional): specific user. priviledges required!
1086
+
1087
+ Returns:
1088
+ Task: task instance of the process
1089
+
1090
+ Example:
1091
+ >>> from geobox.aio import AsyncGeoboxClient
1092
+ >>> async with AsyncGeoboxClient() as client:
1093
+ >>> raster = await client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
1094
+ >>> await raster.reclassify(output_raster_name='test', rules='{"1": 10, "2": 20, "3": 30}')
1095
+ """
1096
+ from .analysis import Analysis
1097
+ return await Analysis.reclassify(self.api,
1098
+ self,
1099
+ output_raster_name=output_raster_name,
1100
+ rules=rules,
1101
+ default_value=default_value,
1102
+ nodata_in=nodata_in,
1103
+ nodata_out=nodata_out,
1104
+ out_dtype=out_dtype,
1105
+ inclusive=inclusive,
1106
+ user_id=user_id)
1107
+
1108
+
1109
+ async def resample(self,
1110
+ output_raster_name: str,
1111
+ out_res: Optional[str] = None,
1112
+ scale_factor: Optional[str] = None,
1113
+ match_raster_uuid: Optional[str] = None,
1114
+ resample_method: AnalysisResampleMethod = AnalysisResampleMethod.near,
1115
+ dst_nodata: int = -9999,
1116
+ user_id: Optional[int] = None) -> 'Task':
1117
+ """
1118
+ [async] Resample a raster to a different resolution.
1119
+
1120
+ it resamples a raster using GDAL Warp.
1121
+ Exactly one of out_res, scale_factor, or match_raster_uuid must be provided.
1122
+ Only users with Publisher role or higher can perform this operation.
1123
+
1124
+ Args:
1125
+ output_raster_name (str): Name for the output reclassified raster dataset.
1126
+ out_res (str, optional): Output resolution as 'x_res,y_res' (e.g., '10,10').
1127
+ scale_factor (int, optional): Scale factor (e.g., 2.0 for 2x finer resolution).
1128
+ match_raster_uuid (str, optional): UUID of reference raster to match resolution/extent.
1129
+ resample_method (AnalysisResampleMethod, optional): Resampling method: 'near', 'bilinear', 'cubic', 'lanczos', etc.
1130
+ dst_nodata (int, optional): Output NoData value.
1131
+ user_id (int, optional): specific user. priviledges required!
1132
+
1133
+ Returns:
1134
+ Task: task instance of the process
1135
+
1136
+ Example:
1137
+ >>> from geobox.aio import AsyncGeoboxClient
1138
+ >>> async with AsyncGeoboxClient() as client:
1139
+ >>> raster = await client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
1140
+ >>> await raster.resample(output_raster_name='test')
1141
+ """
1142
+ from .analysis import Analysis
1143
+ return await Analysis.resample(self.api,
1144
+ self,
1145
+ output_raster_name=output_raster_name,
1146
+ out_res=out_res,
1147
+ scale_factor=scale_factor,
1148
+ match_raster_uuid=match_raster_uuid,
1149
+ resample_method=resample_method,
1150
+ dst_nodata=dst_nodata,
1151
+ user_id=user_id)
1152
+
1153
+
1154
+ async def fill_nodata(self,
1155
+ output_raster_name: str,
1156
+ band: Union[int, str] = 1,
1157
+ nodata: Optional[int] = None,
1158
+ max_search_dist: Optional[int] = None,
1159
+ smoothing_iterations: Optional[int] = None,
1160
+ mask_raster_uuid: Optional[str] = None,
1161
+ user_id: Optional[int] = None) -> 'Task':
1162
+ """
1163
+ [async] Fill NoData regions in a raster using GDAL's FillNodata algorithm.
1164
+
1165
+ it fills gaps (NoData regions) in a raster by interpolating values from surrounding valid pixels.
1166
+ This is commonly used for data cleaning and gap filling in remote sensing and elevation data.
1167
+ Only users with Publisher role or higher can perform this operation.
1168
+
1169
+ Args:
1170
+ output_raster_name (str): Name for the output filled raster dataset.
1171
+ band (int | str): 1-based band index to process or 'all' to process all bands. default: 1
1172
+ nodata (int, optional): NoData value to use. If None, uses the band's existing NoData.
1173
+ max_search_dist (int, optoinal): Maximum distance in pixels to search for valid data.
1174
+ smoothing_iterations (int, optional): Number of smoothing iterations to apply.
1175
+ mask_raster_uuid (str, optional): Optional UUID of a mask raster (0=masked, >0=valid).
1176
+ user_id (int, optional): specific user. priviledges required!
1177
+
1178
+ Returns:
1179
+ Task: task instance of the process
1180
+
1181
+ Example:
1182
+ >>> from geobox.aio import AsyncGeoboxClient
1183
+ >>> async with AsyncGeoboxClient() as client:
1184
+ >>> raster = await client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
1185
+ >>> task = await raster.fill_nodata(output_raster_name='test')
1186
+ """
1187
+ from .analysis import Analysis
1188
+ return await Analysis.fill_nodata(self.api,
1189
+ self,
1190
+ output_raster_name=output_raster_name,
1191
+ band=band,
1192
+ nodata=nodata,
1193
+ max_search_dist=max_search_dist,
1194
+ smoothing_iterations=smoothing_iterations,
1195
+ mask_raster_uuid=mask_raster_uuid,
1196
+ user_id=user_id)
1197
+
1198
+
1199
+ async def proximity(self,
1200
+ output_raster_name: str,
1201
+ dist_units: DistanceUnit = DistanceUnit.GEO,
1202
+ burn_value: int = 1,
1203
+ nodata: int = -9999,
1204
+ user_id: Optional[int] = None) -> 'Task':
1205
+ """
1206
+ [async] Create a proximity (distance) raster from a raster layer.
1207
+
1208
+ it creates a raster showing the distance from each pixel to the nearest pixel in the input raster layer.
1209
+ Only users with Publisher role or higher can perform this operation.
1210
+
1211
+ Args:
1212
+ output_raster_name (str): Name for the output proximity raster dataset.
1213
+ dist_units (DistanceUnit, optional): Distance units: 'GEO' for georeferenced units, 'PIXEL' for pixels. default: DistanceUnit.GEO
1214
+ burn_value (int, optional): Value treated as targets (distance 0). default: 1
1215
+ nodata (int, optional): NoData value to use in the output raster. default: -9999
1216
+ user_id (int, optional): specific user. priviledges required!
1217
+
1218
+ Returns:
1219
+ Task: task instance of the process
1220
+
1221
+ Example:
1222
+ >>> from geobox.aio import AsyncGeoboxClient
1223
+ >>> async with AsyncGeoboxClient() as client:
1224
+ >>> raster = await client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
1225
+ >>> task = await raster.proximity(output_raster_name='test')
1226
+ """
1227
+ from .analysis import Analysis
1228
+ return await Analysis.proximity(self.api,
1229
+ self,
1230
+ output_raster_name=output_raster_name,
1231
+ dist_units=dist_units,
1232
+ burn_value=burn_value,
1233
+ nodata=nodata,
1234
+ user_id=user_id)
1235
+
1236
+
1237
+ async def profile(self,
1238
+ polyline: List,
1239
+ number_of_samples: int = 100,
1240
+ output_epsg: Optional[int] = None,
1241
+ include_distance: bool = True,
1242
+ treat_nodata_as_null: bool = True) -> Dict:
1243
+ """
1244
+ [async] Create a profile form a raster along a path
1245
+
1246
+ Args:
1247
+ polyline (List): Path coordinates as [x, y] pairs. Use raster CRS unless `output_epsg` is provided.
1248
+ number_of_samples (int, optional): Number of samples along the path. default: 100.
1249
+ output_epsg (int, optional): EPSG code for output coordinates. If None, use raster CRS.
1250
+ include_distance (bool, optional): Include cumulative distance for each sample. default: True.
1251
+ treat_nodata_as_null (bool, optional): Treat NoData pixels as null values. default: True.
1252
+
1253
+ Returns:
1254
+ Dict: the profile result as geojson
1255
+
1256
+ Example:
1257
+ >>> from geobox.aio import AsyncGeoboxClient
1258
+ >>> async with AsyncGeoboxClient() as client:
1259
+ >>> raster = await client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
1260
+ >>> task = await raster.profile(polyline=[[0, 0], [10, 10]], number_of_samples=200)
1261
+ """
1262
+ endpoint = f"{self.endpoint}profile/"
1263
+
1264
+ data = clean_data({
1265
+ 'polyline': polyline,
1266
+ 'number_of_samples': number_of_samples,
1267
+ 'output_epsg': output_epsg,
1268
+ 'include_distance': include_distance,
1269
+ 'treat_nodata_as_null': treat_nodata_as_null
1270
+ })
1271
+
1272
+ response = await self.api.post(endpoint=endpoint, payload=data)
1273
+ return response