geobox 2.0.1__py3-none-any.whl → 2.2.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.
Files changed (70) hide show
  1. geobox/__init__.py +61 -63
  2. geobox/aio/__init__.py +61 -63
  3. geobox/aio/api.py +489 -473
  4. geobox/aio/apikey.py +263 -263
  5. geobox/aio/attachment.py +341 -339
  6. geobox/aio/base.py +261 -262
  7. geobox/aio/basemap.py +196 -196
  8. geobox/aio/dashboard.py +340 -342
  9. geobox/aio/feature.py +23 -33
  10. geobox/aio/field.py +315 -321
  11. geobox/aio/file.py +72 -72
  12. geobox/aio/layout.py +340 -341
  13. geobox/aio/log.py +23 -23
  14. geobox/aio/map.py +1033 -1034
  15. geobox/aio/model3d.py +415 -415
  16. geobox/aio/mosaic.py +696 -696
  17. geobox/aio/plan.py +314 -314
  18. geobox/aio/query.py +693 -693
  19. geobox/aio/raster.py +907 -869
  20. geobox/aio/raster_analysis.py +740 -0
  21. geobox/aio/route.py +4 -4
  22. geobox/aio/scene.py +340 -342
  23. geobox/aio/settings.py +18 -18
  24. geobox/aio/task.py +404 -402
  25. geobox/aio/tile3d.py +337 -339
  26. geobox/aio/tileset.py +102 -103
  27. geobox/aio/usage.py +52 -51
  28. geobox/aio/user.py +506 -507
  29. geobox/aio/vector_tool.py +1968 -0
  30. geobox/aio/vectorlayer.py +315 -306
  31. geobox/aio/version.py +272 -273
  32. geobox/aio/view.py +1019 -983
  33. geobox/aio/workflow.py +340 -341
  34. geobox/api.py +18 -2
  35. geobox/apikey.py +262 -262
  36. geobox/attachment.py +336 -337
  37. geobox/base.py +384 -384
  38. geobox/basemap.py +194 -194
  39. geobox/dashboard.py +339 -341
  40. geobox/enums.py +432 -348
  41. geobox/feature.py +5 -5
  42. geobox/field.py +320 -320
  43. geobox/file.py +4 -4
  44. geobox/layout.py +339 -340
  45. geobox/log.py +4 -4
  46. geobox/map.py +1031 -1032
  47. geobox/model3d.py +410 -410
  48. geobox/mosaic.py +696 -696
  49. geobox/plan.py +313 -313
  50. geobox/query.py +691 -691
  51. geobox/raster.py +907 -863
  52. geobox/raster_analysis.py +737 -0
  53. geobox/scene.py +341 -342
  54. geobox/settings.py +194 -194
  55. geobox/task.py +399 -400
  56. geobox/tile3d.py +337 -338
  57. geobox/tileset.py +4 -4
  58. geobox/usage.py +3 -3
  59. geobox/user.py +503 -503
  60. geobox/vector_tool.py +1968 -0
  61. geobox/vectorlayer.py +5 -5
  62. geobox/version.py +272 -272
  63. geobox/view.py +981 -981
  64. geobox/workflow.py +338 -339
  65. {geobox-2.0.1.dist-info → geobox-2.2.0.dist-info}/METADATA +15 -1
  66. geobox-2.2.0.dist-info/RECORD +72 -0
  67. geobox-2.0.1.dist-info/RECORD +0 -68
  68. {geobox-2.0.1.dist-info → geobox-2.2.0.dist-info}/WHEEL +0 -0
  69. {geobox-2.0.1.dist-info → geobox-2.2.0.dist-info}/licenses/LICENSE +0 -0
  70. {geobox-2.0.1.dist-info → geobox-2.2.0.dist-info}/top_level.txt +0 -0
geobox/raster.py CHANGED
@@ -1,863 +1,907 @@
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 Base
9
- from .utils import clean_data, join_url_params
10
- from .task import Task
11
-
12
- if TYPE_CHECKING:
13
- from . import GeoboxClient
14
- from .user import User
15
- from .aio import AsyncGeoboxClient
16
- from .aio.raster import Raster as AsyncRaster
17
-
18
- class Raster(Base):
19
-
20
- BASE_ENDPOINT: str = 'rasters/'
21
-
22
- def __init__(self,
23
- api: 'GeoboxClient',
24
- uuid: str,
25
- data: Optional[Dict] = {}):
26
- """
27
- Constructs all the necessary attributes for the Raster object.
28
-
29
- Args:
30
- api (GeoboxClient): The API instance.
31
- uuid (str): The UUID of the raster.
32
- data (Dict, optional): The raster data.
33
- """
34
- super().__init__(api, uuid=uuid, data=data)
35
-
36
-
37
- @classmethod
38
- def get_rasters(cls, api: 'GeoboxClient', **kwargs) -> Union[List['Raster'], int]:
39
- """
40
- Get all rasters.
41
-
42
- Args:
43
- api (GeoboxClient): The API instance.
44
-
45
- Keyword Args:
46
- terrain (bool): whether to get terrain rasters.
47
- q (str): query filter based on OGC CQL standard. e.g. "field1 LIKE '%GIS%' AND created_at > '2021-01-01'"
48
- 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.
49
- search_fields (str): comma separated list of fields for searching
50
- 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.
51
- return_count (bool): whether to return the total count of rasters. default is False.
52
- skip (int): number of rasters to skip. minimum is 0.
53
- limit (int): number of rasters to return. minimum is 1.
54
- user_id (int): user id to show the rasters of the user. privileges required.
55
- shared (bool): whether to return shared rasters. default is False.
56
-
57
- Returns:
58
- List[Raster] | int: A list of Raster objects or the total count of rasters.
59
-
60
- Example:
61
- >>> from geobox import GeoboxClient
62
- >>> from geobox.raster import Raster
63
- >>> client = GeoboxClient()
64
- >>> rasters = Raster.get_rasters(client, terrain=True, q="name LIKE '%GIS%'")
65
- or
66
- >>> rasters = client.get_rasters(terrain=True, q="name LIKE '%GIS%'")
67
- """
68
- params = {
69
- 'terrain': kwargs.get('terrain', None),
70
- 'f': 'json',
71
- 'q': kwargs.get('q', None),
72
- 'search': kwargs.get('search', None),
73
- 'search_fields': kwargs.get('search_fields', None),
74
- 'order_by': kwargs.get('order_by', None),
75
- 'return_count': kwargs.get('return_count', False),
76
- 'skip': kwargs.get('skip', 0),
77
- 'limit': kwargs.get('limit', 100),
78
- 'user_id': kwargs.get('user_id', None),
79
- 'shared': kwargs.get('shared', False)
80
- }
81
- return super()._get_list(api, cls.BASE_ENDPOINT, params, factory_func=lambda api, item: Raster(api, item['uuid'], item))
82
-
83
-
84
-
85
- @classmethod
86
- def get_rasters_by_ids(cls, api: 'GeoboxClient', ids: List[str], user_id: int = None) -> List['Raster']:
87
- """
88
- Get rasters by their IDs.
89
-
90
- Args:
91
- api (GeoboxClient): The API instance.
92
- ids (List[str]): The IDs of the rasters.
93
- user_id (int, optional): specific user. privileges required.
94
-
95
- Returns:
96
- List['Raster']: A list of Raster objects.
97
-
98
- Example:
99
- >>> from geobox import GeoboxClient
100
- >>> from geobox.raster import Raster
101
- >>> client = GeoboxClient()
102
- >>> rasters = Raster.get_rasters_by_ids(client, ids=['123', '456'])
103
- or
104
- >>> rasters = client.get_rasters_by_ids(ids=['123', '456'])
105
- """
106
- params = {
107
- 'ids': ids,
108
- 'user_id': user_id,
109
- }
110
- endpoint = urljoin(cls.BASE_ENDPOINT, 'get-rasters/')
111
-
112
- return super()._get_list_by_ids(api, endpoint, params, factory_func=lambda api, item: Raster(api, item['uuid'], item))
113
-
114
-
115
- @classmethod
116
- def get_raster(cls, api: 'GeoboxClient', uuid: str, user_id: int = None) -> 'Raster':
117
- """
118
- Get a raster by its UUID.
119
-
120
- Args:
121
- api (GeoboxClient): The API instance.
122
- uuid (str): The UUID of the raster.
123
- user_id (int, optional): specific user. privileges required.
124
-
125
- Returns:
126
- Raster: A Raster object.
127
-
128
- Example:
129
- >>> from geobox import GeoboxClient
130
- >>> from geobox.raster import Raster
131
- >>> client = GeoboxClient()
132
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
133
- or
134
- >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
135
- """
136
- params = {
137
- 'f': 'json',
138
- 'user_id': user_id
139
- }
140
- return super()._get_detail(api, cls.BASE_ENDPOINT, uuid, params, factory_func=lambda api, item: Raster(api, item['uuid'], item))
141
-
142
-
143
- @classmethod
144
- def get_raster_by_name(cls, api: 'GeoboxClient', name: str, user_id: int = None) -> Union['Raster', None]:
145
- """
146
- Get a raster by name
147
-
148
- Args:
149
- api (GeoboxClient): The GeoboxClient instance for making requests.
150
- name (str): the name of the raster to get
151
- user_id (int, optional): specific user. privileges required.
152
-
153
- Returns:
154
- Raster | None: returns the raster if a raster matches the given name, else None
155
-
156
- Example:
157
- >>> from geobox import GeoboxClient
158
- >>> from geobox.raster import Raster
159
- >>> client = GeoboxClient()
160
- >>> raster = Raster.get_raster_by_name(client, name='test')
161
- or
162
- >>> raster = client.get_raster_by_name(name='test')
163
- """
164
- rasters = cls.get_rasters(api, q=f"name = '{name}'", user_id=user_id)
165
- if rasters and rasters[0].name == name:
166
- return rasters[0]
167
- else:
168
- return None
169
-
170
-
171
- def update(self, **kwargs) -> None:
172
- """
173
- Update the raster.
174
-
175
- Keyword Args:
176
- name (str): The name of the raster.
177
- display_name (str): The display name of the raster.
178
- description (str): The description of the raster.
179
-
180
- Returns:
181
- None
182
-
183
- Example:
184
- >>> from geobox import GeoboxClient
185
- >>> from geobox.raster import Raster
186
- >>> client = GeoboxClient()
187
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
188
- >>> raster.update(name="new_name")
189
- """
190
- params = {
191
- 'name': kwargs.get('name'),
192
- 'display_name': kwargs.get('display_name'),
193
- 'description': kwargs.get('description')
194
- }
195
- return super()._update(self.endpoint, params)
196
-
197
-
198
- def delete(self) -> None:
199
- """
200
- Delete the raster.
201
-
202
- Returns:
203
- None
204
-
205
- Example:
206
- >>> from geobox import GeoboxClient
207
- >>> from geobox.raster import Raster
208
- >>> client = GeoboxClient()
209
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
210
- >>> raster.delete()
211
- """
212
- super().delete(self.endpoint)
213
-
214
-
215
- @property
216
- def thumbnail(self) -> str:
217
- """
218
- Get the thumbnail of the raster.
219
-
220
- Returns:
221
- str: The url of the thumbnail.
222
-
223
- Example:
224
- >>> from geobox import GeoboxClient
225
- >>> from geobox.raster import Raster
226
- >>> client = GeoboxClient()
227
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
228
- >>> raster.thumbnail
229
- """
230
- return super().thumbnail(format='')
231
-
232
-
233
- @property
234
- def info(self) -> Dict:
235
- """
236
- Get the info of the raster.
237
-
238
- Returns:
239
- Dict: The info of the raster.
240
-
241
- Example:
242
- >>> from geobox import GeoboxClient
243
- >>> from geobox.raster import Raster
244
- >>> client = GeoboxClient()
245
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
246
- >>> raster.info
247
- """
248
- endpoint = urljoin(self.endpoint, 'info/')
249
- return self.api.get(endpoint)
250
-
251
-
252
- def get_statistics(self, indexes: str = None) -> Dict:
253
- """
254
- Get the statistics of the raster.
255
-
256
- Args:
257
- indexes (str): list of comma separated band indexes. e.g. 1, 2, 3
258
-
259
- Returns:
260
- Dict: The statistics of the raster.
261
-
262
- Example:
263
- >>> from geobox import GeoboxClient
264
- >>> from geobox.raster import Raster
265
- >>> client = GeoboxClient()
266
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
267
- >>> raster.get_statistics(indexes='1, 2, 3')
268
- """
269
- params = clean_data({
270
- 'indexes': indexes,
271
- })
272
- query_string = urlencode(params)
273
- endpoint = urljoin(self.endpoint, f'statistics/?{query_string}')
274
- return self.api.get(endpoint)
275
-
276
-
277
- def get_point(self, lat: float, lng: float) -> Dict:
278
- """
279
- Get the point of the raster.
280
-
281
- Args:
282
- lat (float): The latitude of the point. minimum is -90, maximum is 90.
283
- lng (float): The longitude of the point. minimum is -180, maximum is 180.
284
-
285
- Returns:
286
- Dict: The point of the raster.
287
-
288
- Example:
289
- >>> from geobox import GeoboxClient
290
- >>> from geobox.raster import Raster
291
- >>> client = GeoboxClient()
292
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
293
- >>> raster.get_point(lat=60, lng=50)
294
- """
295
- if lat < -90 or lat > 90:
296
- raise ValueError("lat must be between -90 and 90")
297
- if lng < -180 or lng > 180:
298
- raise ValueError("lng must be between -180 and 180")
299
-
300
- params = clean_data({
301
- 'lat': lat,
302
- 'lng': lng,
303
- })
304
- query_string = urlencode(params)
305
- endpoint = urljoin(self.endpoint, f'point?{query_string}')
306
- return self.api.get(endpoint)
307
-
308
-
309
- def _get_save_path(self, save_path: str = None) -> str:
310
- """
311
- Get the path where the file should be saved.
312
-
313
- Args:
314
- save_path (str, optional): The path to save the file.
315
-
316
- Returns:
317
- str: The path where the file is saved.
318
-
319
- Raises:
320
- ValueError: If save_path does not end with a '/'.
321
- """
322
- # If save_path is provided, check if it ends with a '/'
323
- if save_path and save_path.endswith('/'):
324
- return f'{save_path}'
325
-
326
- if save_path and not save_path.endswith('/'):
327
- raise ValueError("save_path must end with a '/'")
328
-
329
- return os.getcwd()
330
-
331
-
332
- def _get_file_name(self, response: requests.Response) -> str:
333
- """
334
- Get the file name from the response.
335
-
336
- Args:
337
- response (requests.Response): The response of the request.
338
-
339
- Returns:
340
- str: The file name
341
- """
342
- if 'Content-Disposition' in response.headers and 'filename=' in response.headers['Content-Disposition']:
343
- file_name = response.headers['Content-Disposition'].split('filename=')[-1].strip().strip('"')
344
-
345
- else:
346
- content_type = response.headers.get("Content-Type", "")
347
- file_name = f'{self.name}.{mimetypes.guess_extension(content_type.split(";")[0])}'
348
-
349
- return file_name
350
-
351
-
352
- def _create_progress_bar(self) -> 'tqdm':
353
- """Creates a progress bar for the task."""
354
- try:
355
- from tqdm.auto import tqdm
356
- except ImportError:
357
- from .api import logger
358
- logger.warning("[tqdm] extra is required to show the progress bar. install with: pip insatll geobox[tqdm]")
359
- return None
360
-
361
- return tqdm(unit="B",
362
- total=int(self.size),
363
- file=sys.stdout,
364
- dynamic_ncols=True,
365
- desc="Downloading",
366
- unit_scale=True,
367
- unit_divisor=1024,
368
- ascii=True
369
- )
370
-
371
-
372
- def download(self, save_path: str = None, progress_bar: bool = True) -> str:
373
- """
374
- Download the raster.
375
-
376
- Args:
377
- save_path (str, optional): Path where the file should be saved.
378
- If not provided, it saves to the current working directory
379
- using the original filename and appropriate extension.
380
- progress_bar (bool, optional): Whether to show a progress bar. default: True
381
-
382
- Returns:
383
- str: The path to save the raster.
384
-
385
- Raises:
386
- ValueError: If file_uuid is not set
387
- OSError: If there are issues with file operations
388
-
389
- Example:
390
- >>> from geobox import GeoboxClient
391
- >>> from geobox.raster import Raster
392
- >>> client = GeoboxClient()
393
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
394
- >>> raster.download(save_path="path/to/save/")
395
- """
396
- if not self.uuid:
397
- raise ValueError("Raster UUID is required to download the raster file")
398
-
399
- save_path = self._get_save_path(save_path)
400
- os.makedirs(os.path.dirname(save_path), exist_ok=True)
401
-
402
- with self.api.get(f"{self.endpoint}download/", stream=True) as response:
403
- file_name = self._get_file_name(response)
404
- full_path = f"{save_path}/{file_name}"
405
- with open(full_path, 'wb') as f:
406
- pbar = self._create_progress_bar() if progress_bar else None
407
- for chunk in response.iter_content(chunk_size=8192):
408
- f.write(chunk)
409
- if pbar:
410
- pbar.update(len(chunk))
411
- pbar.refresh()
412
- if pbar:
413
- pbar.close()
414
-
415
- return os.path.abspath(full_path)
416
-
417
-
418
- def get_content_file(self, save_path: str = None, progress_bar: bool = True) -> str:
419
- """
420
- Get Raster Content URL
421
-
422
- Args:
423
- save_path (str, optional): Path where the file should be saved.
424
- If not provided, it saves to the current working directory
425
- using the original filename and appropriate extension.
426
- progress_bar (bool, optional): Whether to show a progress bar. default: True
427
-
428
- Returns:
429
- str: The path to save the raster.
430
-
431
- Raises:
432
- ValueError: If uuid is not set
433
- OSError: If there are issues with file operations
434
-
435
- Examples:
436
- >>> from geobox import GeoboxClient
437
- >>> from geobox.raster import Raster
438
- >>> client = GeoboxClient()
439
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
440
- >>> raster_tiff = raste.get_content_file()
441
- """
442
- if not self.uuid:
443
- raise ValueError("Raster UUID is required to download the raster content")
444
-
445
- save_path = self._get_save_path(save_path)
446
- os.makedirs(os.path.dirname(save_path), exist_ok=True)
447
-
448
- with self.api.get(f"{self.endpoint}content/", stream=True) as response:
449
- file_name = self._get_file_name(response)
450
- full_path = f"{save_path}/{file_name}"
451
- with open(full_path, 'wb') as f:
452
- pbar = self._create_progress_bar() if progress_bar else None
453
- for chunk in response.iter_content(chunk_size=8192):
454
- f.write(chunk)
455
- if pbar:
456
- pbar.update(len(chunk))
457
- pbar.refresh()
458
- if pbar:
459
- pbar.close()
460
-
461
- return os.path.abspath(full_path)
462
-
463
-
464
- def get_render_png_url(self, x: int, y: int, z: int, **kwargs) -> str:
465
- """
466
- Get the PNG URL of the raster.
467
-
468
- Args:
469
- x (int): The x coordinate of the tile.
470
- y (int): The y coordinate of the tile.
471
- z (int): The zoom level of the tile.
472
-
473
- Keyword Args:
474
- indexes (str, optional): list of comma separated band indexes to be rendered. e.g. 1, 2, 3
475
- nodata (int, optional)
476
- expression (str, optional): band math expression. e.g. b1*b2+b3
477
- rescale (List, optional): comma (',') separated Min,Max range. Can set multiple time for multiple bands.
478
- color_formula (str, optional): Color formula. e.g. gamma R 0.5
479
- colormap_name (str, optional)
480
- colormap (str, optional): JSON encoded custom Colormap. e.g. {"0": "#ff0000", "1": "#00ff00"} or [[[0, 100], "#ff0000"], [[100, 200], "#00ff00"]]
481
-
482
- Returns:
483
- str: The PNG Render URL of the raster.
484
-
485
- Example:
486
- >>> from geobox import GeoboxClient
487
- >>> from geobox.raster import Raster
488
- >>> client = GeoboxClient()
489
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
490
- >>> raster.get_tile_render_url(x=10, y=20, z=1)
491
- """
492
- params = clean_data({
493
- 'indexes': kwargs.get('indexes'),
494
- 'nodata': kwargs.get('nodata'),
495
- 'expression': kwargs.get('expression'),
496
- 'rescale': kwargs.get('rescale'),
497
- 'color_formula': kwargs.get('color_formula'),
498
- 'colormap_name': kwargs.get('colormap_name'),
499
- 'colormap': kwargs.get('colormap')
500
- })
501
- query_string = urlencode(params)
502
- endpoint = f'{self.api.base_url}{self.endpoint}render/{z}/{x}/{y}.png'
503
- if query_string:
504
- endpoint = f'{endpoint}?{query_string}'
505
-
506
- if not self.api.access_token and self.api.apikey:
507
- endpoint = join_url_params(endpoint, {"apikey": self.api.apikey})
508
-
509
- return endpoint
510
-
511
-
512
- def get_tile_pbf_url(self, x: int = '{x}', y: int = '{y}', z: int = '{z}', indexes: str = None) -> str:
513
- """
514
- Get the URL of the tile.
515
-
516
- Args:
517
- x (int, optional): The x coordinate of the tile.
518
- y (int, optional): The y coordinate of the tile.
519
- z (int, optional): The zoom level of the tile.
520
- indexes (str, optional): list of comma separated band indexes to be rendered. e.g. 1, 2, 3
521
-
522
- Returns:
523
- str: The URL of the tile.
524
-
525
- Example:
526
- >>> from geobox import GeoboxClient
527
- >>> from geobox.raster import Raster
528
- >>> client = GeoboxClient()
529
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
530
- >>> raster.get_tile_pbf_url(x=10, y=20, z=1)
531
- """
532
- params = clean_data({
533
- 'indexes': indexes
534
- })
535
- query_string = urlencode(params)
536
- endpoint = urljoin(self.api.base_url, f'{self.endpoint}tiles/{z}/{x}/{y}.pbf')
537
- endpoint = urljoin(endpoint, f'?{query_string}')
538
-
539
- if not self.api.access_token and self.api.apikey:
540
- endpoint = join_url_params(endpoint, {"apikey": self.api.apikey})
541
-
542
- return endpoint
543
-
544
-
545
- def get_tile_png_url(self, x: int = 'x', y: int = 'y', z: int = 'z') -> str:
546
- """
547
- Get the URL of the tile.
548
-
549
- Args:
550
- x (int, optional): The x coordinate of the tile.
551
- y (int, optional): The y coordinate of the tile.
552
- z (int, optional): The zoom level of the tile.
553
-
554
- Returns:
555
- str: The URL of the tile.
556
-
557
- Example:
558
- >>> from geobox import GeoboxClient
559
- >>> from geobox.raster import Raster
560
- >>> client = GeoboxClient()
561
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
562
- >>> raster.get_tile_png_url(x=10, y=20, z=1)
563
- """
564
- endpoint = f'{self.api.base_url}{self.endpoint}tiles/{z}/{x}/{y}.png'
565
-
566
- if not self.api.access_token and self.api.apikey:
567
- endpoint = f'{endpoint}?apikey={self.api.apikey}'
568
-
569
- return endpoint
570
-
571
-
572
- def get_tile_json(self) -> Dict:
573
- """
574
- Get the tile JSON of the raster.
575
-
576
- Returns:
577
- Dict: The tile JSON of the raster.
578
-
579
- Example:
580
- >>> from geobox import GeoboxClient
581
- >>> from geobox.raster import Raster
582
- >>> client = GeoboxClient()
583
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
584
- >>> raster.get_tile_json()
585
- """
586
- endpoint = urljoin(self.endpoint, 'tilejson.json')
587
- return self.api.get(endpoint)
588
-
589
-
590
- def wmts(self, scale: int = None) -> str:
591
- """
592
- Get the WMTS URL
593
-
594
- Args:
595
- scale (int, optional): The scale of the raster. values are: 1, 2
596
-
597
- Returns:
598
- str: the raster WMTS URL
599
-
600
- Example:
601
- >>> from geobox import GeoboxClient
602
- >>> from geobox.raster import Raster
603
- >>> client = GeoboxClient()
604
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
605
- >>> raster.wmts(scale=1)
606
- """
607
- endpoint = urljoin(self.api.base_url, f'{self.endpoint}wmts/')
608
- if scale:
609
- endpoint = f"{endpoint}?scale={scale}"
610
-
611
- if not self.api.access_token and self.api.apikey:
612
- endpoint = join_url_params(endpoint, {"apikey": self.api.apikey})
613
-
614
- return endpoint
615
-
616
-
617
- @property
618
- def settings(self) -> Dict:
619
- """
620
- Get the settings of the raster.
621
-
622
- Returns:
623
- Dict: The settings of the raster.
624
-
625
- Example:
626
- >>> from geobox import GeoboxClient
627
- >>> from geobox.raster import Raster
628
- >>> client = GeoboxClient()
629
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
630
- >>> raster.settings
631
- """
632
- return super()._get_settings(self.endpoint)
633
-
634
-
635
- def update_settings(self, settings: Dict) -> Dict:
636
- """
637
- Update the settings
638
-
639
- settings (Dict): settings dictionary
640
-
641
- Returns:
642
- Dict: updated settings
643
-
644
- Example:
645
- >>> from geobox import GeoboxClient
646
- >>> client = GeoboxClient()
647
- >>> raster1 = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
648
- >>> raster2 = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
649
- >>> raster1.update_settings(raster2.settings)
650
- """
651
- return super()._set_settings(self.endpoint, settings)
652
-
653
-
654
- def set_settings(self, **kwargs) -> None:
655
- """
656
- Set the settings of the raster.
657
-
658
- Keyword Args:
659
- nodata (int): The nodata value of the raster.
660
- indexes (list[int]): The indexes of the raster.
661
- rescale (list[int]): The rescale of the raster.
662
- colormap_name (str): The colormap name of the raster.
663
- color_formula (str): The color formula of the raster.
664
- expression (str): The expression of the raster.
665
- exaggeraion (int): The exaggeraion of the raster.
666
- min_zoom (int): The min zoom of the raster.
667
- max_zoom (int): The max zoom of the raster.
668
- use_cache (bool): Whether to use cache of the raster.
669
- cache_until_zoom (int): The cache until zoom of the raster.
670
-
671
-
672
- Example:
673
- >>> from geobox import GeoboxClient
674
- >>> from geobox.raster import Raster
675
- >>> client = GeoboxClient()
676
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
677
- >>> raster.set_settings(nodata=0,
678
- ... indexes=[1],
679
- ... rescale=[[0, 10000]],
680
- ... colormap_name='gist_rainbow',
681
- ... color_formula='Gamma R 0.5',
682
- ... expression='b1 * 2',
683
- ... exaggeraion=10,
684
- ... min_zoom=0,
685
- ... max_zoom=22,
686
- ... use_cache=True,
687
- ... cache_until_zoom=17)
688
- """
689
- visual_settings = {
690
- 'nodata', 'indexes', 'rescale', 'colormap_name',
691
- 'color_formula', 'expression', 'exaggeraion'
692
- }
693
- tile_settings = {
694
- 'min_zoom', 'max_zoom', 'use_cache', 'cache_until_zoom'
695
- }
696
-
697
- settings = self.settings
698
-
699
- for key, value in kwargs.items():
700
- if key in visual_settings:
701
- settings['visual_settings'][key] = value
702
- elif key in tile_settings:
703
- settings['tile_settings'][key] = value
704
-
705
-
706
- return super()._set_settings(self.endpoint, settings)
707
-
708
-
709
- def share(self, users: List['User']) -> None:
710
- """
711
- Shares the raster with specified users.
712
-
713
- Args:
714
- users (List[User]): The list of user objects to share the raster with.
715
-
716
- Returns:
717
- None
718
-
719
- Example:
720
- >>> from geobox import GeoboxClient
721
- >>> from geobox.raster import Raster
722
- >>> client = GeoboxClient()
723
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
724
- >>> users = client.search_users(search="John")
725
- >>> raster.share(users=users)
726
- """
727
- super()._share(self.endpoint, users)
728
-
729
-
730
- def unshare(self, users: List['User']) -> None:
731
- """
732
- Unshares the raster with specified users.
733
-
734
- Args:
735
- users (List[User]): The list of user objects to unshare the raster with.
736
-
737
- Returns:
738
- None
739
-
740
- Example:
741
- >>> from geobox import GeoboxClient
742
- >>> from geobox.raster import Raster
743
- >>> client = GeoboxClient()
744
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
745
- >>> users = client.search_users(search="John")
746
- >>> raster.unshare(users=users)
747
- """
748
- super()._unshare(self.endpoint, users)
749
-
750
-
751
- def get_shared_users(self, search: str = None, skip: int = 0, limit: int = 10) -> List['User']:
752
- """
753
- Retrieves the list of users the raster is shared with.
754
-
755
- Args:
756
- search (str, optional): The search query.
757
- skip (int, optional): The number of users to skip.
758
- limit (int, optional): The maximum number of users to retrieve.
759
-
760
- Returns:
761
- List[User]: The list of shared users.
762
-
763
- Example:
764
- >>> from geobox import GeoboxClient
765
- >>> from geobox.raster import Raster
766
- >>> client = GeoboxClient()
767
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
768
- >>> raster.get_shared_users(search='John', skip=0, limit=10)
769
- """
770
- params = {
771
- 'search': search,
772
- 'skip': skip,
773
- 'limit': limit
774
- }
775
- return super()._get_shared_users(self.endpoint, params)
776
-
777
-
778
- def seed_cache(self, from_zoom: int = None, to_zoom: int = None, extent: List[int] = None, workers: int = 1) -> List['Task']:
779
- """
780
- Seed the cache of the raster.
781
-
782
- Args:
783
- from_zoom (int, optional): The from zoom of the raster.
784
- to_zoom (int, optional): The to zoom of the raster.
785
- extent (List[int], optional): The extent of the raster.
786
- workers (int, optional): The number of workers to use. default is 1.
787
-
788
- Returns:
789
- Task: The task of the seed cache.
790
-
791
- Example:
792
- >>> from geobox import GeoboxClient
793
- >>> from geobox.raster import Raster
794
- >>> client = GeoboxClient()
795
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
796
- >>> task = raster.seed_cache(from_zoom=0, to_zoom=22, extent=[0, 0, 100, 100], workers=1)
797
- """
798
- data = {
799
- 'from_zoom': from_zoom,
800
- 'to_zoom': to_zoom,
801
- 'extent': extent,
802
- 'workers': workers
803
- }
804
- return super()._seed_cache(self.endpoint, data)
805
-
806
-
807
- def clear_cache(self) -> None:
808
- """
809
- Clear the cache of the raster.
810
-
811
- Returns:
812
- None
813
-
814
- Example:
815
- >>> from geobox import GeoboxClient
816
- >>> from geobox.raster import Raster
817
- >>> client = GeoboxClient()
818
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
819
- >>> raster.clear_cache()
820
- """
821
- super()._clear_cache(self.endpoint)
822
-
823
-
824
- @property
825
- def cache_size(self) -> int:
826
- """
827
- Get the size of the cache of the raster.
828
-
829
- Returns:
830
- int: The size of the cache of the raster.
831
-
832
- Example:
833
- >>> from geobox import GeoboxClient
834
- >>> from geobox.raster import Raster
835
- >>> client = GeoboxClient()
836
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
837
- >>> raster.cache_size
838
- """
839
- return super()._cache_size(self.endpoint)
840
-
841
-
842
- def to_async(self, async_client: 'AsyncGeoboxClient') -> 'AsyncRaster':
843
- """
844
- Switch to async version of the raster instance to have access to the async methods
845
-
846
- Args:
847
- async_client (AsyncGeoboxClient): The async version of the GeoboxClient instance for making requests.
848
-
849
- Returns:
850
- geobox.aio.raster.Raster: the async instance of the raster.
851
-
852
- Example:
853
- >>> from geobox import Geoboxclient
854
- >>> from geobox.aio import AsyncGeoboxClient
855
- >>> from geobox.raster import Raster
856
- >>> client = GeoboxClient()
857
- >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
858
- >>> async with AsyncGeoboxClient() as async_client:
859
- >>> async_raster = raster.to_async(async_client)
860
- """
861
- from .aio.raster import Raster as AsyncRaster
862
-
863
- return AsyncRaster(api=async_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 geobox.field import Field
9
+
10
+ from .base import Base
11
+ from .utils import clean_data, join_url_params
12
+ from .task import Task
13
+ from .vectorlayer import VectorLayer
14
+ from .view import VectorLayerView
15
+ from .enums import PolygonizeConnectivity, AnalysisResampleMethod, SlopeUnit, AnalysisAlgorithm, AnalysisDataType, RangeBound, DistanceUnit
16
+
17
+ if TYPE_CHECKING:
18
+ from . import GeoboxClient
19
+ from .user import User
20
+ from .aio import AsyncGeoboxClient
21
+ from .aio.raster import AsyncRaster
22
+
23
+ class Raster(Base):
24
+
25
+ BASE_ENDPOINT: str = 'rasters/'
26
+
27
+ def __init__(self,
28
+ api: 'GeoboxClient',
29
+ uuid: str,
30
+ data: Optional[Dict] = {}):
31
+ """
32
+ Constructs all the necessary attributes for the Raster object.
33
+
34
+ Args:
35
+ api (GeoboxClient): The API instance.
36
+ uuid (str): The UUID of the raster.
37
+ data (Dict, optional): The raster data.
38
+ """
39
+ super().__init__(api, uuid=uuid, data=data)
40
+
41
+
42
+ @classmethod
43
+ def get_rasters(cls, api: 'GeoboxClient', **kwargs) -> Union[List['Raster'], int]:
44
+ """
45
+ Get all rasters.
46
+
47
+ Args:
48
+ api (GeoboxClient): The API instance.
49
+
50
+ Keyword Args:
51
+ terrain (bool): whether to get terrain rasters.
52
+ q (str): query filter based on OGC CQL standard. e.g. "field1 LIKE '%GIS%' AND created_at > '2021-01-01'"
53
+ 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.
54
+ search_fields (str): comma separated list of fields for searching
55
+ 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.
56
+ return_count (bool): whether to return the total count of rasters. default is False.
57
+ skip (int): number of rasters to skip. minimum is 0.
58
+ limit (int): number of rasters to return. minimum is 1.
59
+ user_id (int): user id to show the rasters of the user. privileges required.
60
+ shared (bool): whether to return shared rasters. default is False.
61
+
62
+ Returns:
63
+ List[Raster] | int: A list of Raster objects or the total count of rasters.
64
+
65
+ Example:
66
+ >>> from geobox import GeoboxClient
67
+ >>> from geobox.raster import Raster
68
+ >>> client = GeoboxClient()
69
+ >>> rasters = Raster.get_rasters(client, terrain=True, q="name LIKE '%GIS%'")
70
+ or
71
+ >>> rasters = client.get_rasters(terrain=True, q="name LIKE '%GIS%'")
72
+ """
73
+ params = {
74
+ 'terrain': kwargs.get('terrain', None),
75
+ 'f': 'json',
76
+ 'q': kwargs.get('q', None),
77
+ 'search': kwargs.get('search', None),
78
+ 'search_fields': kwargs.get('search_fields', None),
79
+ 'order_by': kwargs.get('order_by', None),
80
+ 'return_count': kwargs.get('return_count', False),
81
+ 'skip': kwargs.get('skip', 0),
82
+ 'limit': kwargs.get('limit', 100),
83
+ 'user_id': kwargs.get('user_id', None),
84
+ 'shared': kwargs.get('shared', False)
85
+ }
86
+ return super()._get_list(api, cls.BASE_ENDPOINT, params, factory_func=lambda api, item: Raster(api, item['uuid'], item))
87
+
88
+
89
+
90
+ @classmethod
91
+ def get_rasters_by_ids(cls, api: 'GeoboxClient', ids: List[str], user_id: int = None) -> List['Raster']:
92
+ """
93
+ Get rasters by their IDs.
94
+
95
+ Args:
96
+ api (GeoboxClient): The API instance.
97
+ ids (List[str]): The IDs of the rasters.
98
+ user_id (int, optional): specific user. privileges required.
99
+
100
+ Returns:
101
+ List['Raster']: A list of Raster objects.
102
+
103
+ Example:
104
+ >>> from geobox import GeoboxClient
105
+ >>> from geobox.raster import Raster
106
+ >>> client = GeoboxClient()
107
+ >>> rasters = Raster.get_rasters_by_ids(client, ids=['123', '456'])
108
+ or
109
+ >>> rasters = client.get_rasters_by_ids(ids=['123', '456'])
110
+ """
111
+ params = {
112
+ 'ids': ids,
113
+ 'user_id': user_id,
114
+ }
115
+ endpoint = urljoin(cls.BASE_ENDPOINT, 'get-rasters/')
116
+
117
+ return super()._get_list_by_ids(api, endpoint, params, factory_func=lambda api, item: Raster(api, item['uuid'], item))
118
+
119
+
120
+ @classmethod
121
+ def get_raster(cls, api: 'GeoboxClient', uuid: str, user_id: int = None) -> 'Raster':
122
+ """
123
+ Get a raster by its UUID.
124
+
125
+ Args:
126
+ api (GeoboxClient): The API instance.
127
+ uuid (str): The UUID of the raster.
128
+ user_id (int, optional): specific user. privileges required.
129
+
130
+ Returns:
131
+ Raster: A Raster object.
132
+
133
+ Example:
134
+ >>> from geobox import GeoboxClient
135
+ >>> from geobox.raster import Raster
136
+ >>> client = GeoboxClient()
137
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
138
+ or
139
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
140
+ """
141
+ params = {
142
+ 'f': 'json',
143
+ 'user_id': user_id
144
+ }
145
+ return super()._get_detail(api, cls.BASE_ENDPOINT, uuid, params, factory_func=lambda api, item: Raster(api, item['uuid'], item))
146
+
147
+
148
+ @classmethod
149
+ def get_raster_by_name(cls, api: 'GeoboxClient', name: str, user_id: int = None) -> Union['Raster', None]:
150
+ """
151
+ Get a raster by name
152
+
153
+ Args:
154
+ api (GeoboxClient): The GeoboxClient instance for making requests.
155
+ name (str): the name of the raster to get
156
+ user_id (int, optional): specific user. privileges required.
157
+
158
+ Returns:
159
+ Raster | None: returns the raster if a raster matches the given name, else None
160
+
161
+ Example:
162
+ >>> from geobox import GeoboxClient
163
+ >>> from geobox.raster import Raster
164
+ >>> client = GeoboxClient()
165
+ >>> raster = Raster.get_raster_by_name(client, name='test')
166
+ or
167
+ >>> raster = client.get_raster_by_name(name='test')
168
+ """
169
+ rasters = cls.get_rasters(api, q=f"name = '{name}'", user_id=user_id)
170
+ if rasters and rasters[0].name == name:
171
+ return rasters[0]
172
+ else:
173
+ return None
174
+
175
+
176
+ def update(self, **kwargs) -> None:
177
+ """
178
+ Update the raster.
179
+
180
+ Keyword Args:
181
+ name (str): The name of the raster.
182
+ display_name (str): The display name of the raster.
183
+ description (str): The description of the raster.
184
+
185
+ Returns:
186
+ None
187
+
188
+ Example:
189
+ >>> from geobox import GeoboxClient
190
+ >>> from geobox.raster import Raster
191
+ >>> client = GeoboxClient()
192
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
193
+ >>> raster.update(name="new_name")
194
+ """
195
+ params = {
196
+ 'name': kwargs.get('name'),
197
+ 'display_name': kwargs.get('display_name'),
198
+ 'description': kwargs.get('description')
199
+ }
200
+ return super()._update(self.endpoint, params)
201
+
202
+
203
+ def delete(self) -> None:
204
+ """
205
+ Delete the raster.
206
+
207
+ Returns:
208
+ None
209
+
210
+ Example:
211
+ >>> from geobox import GeoboxClient
212
+ >>> from geobox.raster import Raster
213
+ >>> client = GeoboxClient()
214
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
215
+ >>> raster.delete()
216
+ """
217
+ super()._delete(self.endpoint)
218
+
219
+
220
+ @property
221
+ def thumbnail(self) -> str:
222
+ """
223
+ Get the thumbnail of the raster.
224
+
225
+ Returns:
226
+ str: The url of the thumbnail.
227
+
228
+ Example:
229
+ >>> from geobox import GeoboxClient
230
+ >>> from geobox.raster import Raster
231
+ >>> client = GeoboxClient()
232
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
233
+ >>> raster.thumbnail
234
+ """
235
+ return super()._thumbnail(format='')
236
+
237
+
238
+ @property
239
+ def info(self) -> Dict:
240
+ """
241
+ Get the info of the raster.
242
+
243
+ Returns:
244
+ Dict: The info of the raster.
245
+
246
+ Example:
247
+ >>> from geobox import GeoboxClient
248
+ >>> from geobox.raster import Raster
249
+ >>> client = GeoboxClient()
250
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
251
+ >>> raster.info
252
+ """
253
+ endpoint = urljoin(self.endpoint, 'info/')
254
+ return self.api.get(endpoint)
255
+
256
+
257
+ def get_statistics(self, indexes: str = None) -> Dict:
258
+ """
259
+ Get the statistics of the raster.
260
+
261
+ Args:
262
+ indexes (str): list of comma separated band indexes. e.g. 1, 2, 3
263
+
264
+ Returns:
265
+ Dict: The statistics of the raster.
266
+
267
+ Example:
268
+ >>> from geobox import GeoboxClient
269
+ >>> from geobox.raster import Raster
270
+ >>> client = GeoboxClient()
271
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
272
+ >>> raster.get_statistics(indexes='1, 2, 3')
273
+ """
274
+ params = clean_data({
275
+ 'indexes': indexes,
276
+ })
277
+ query_string = urlencode(params)
278
+ endpoint = urljoin(self.endpoint, f'statistics/?{query_string}')
279
+ return self.api.get(endpoint)
280
+
281
+
282
+ def get_point(self, lat: float, lng: float) -> Dict:
283
+ """
284
+ Get the point of the raster.
285
+
286
+ Args:
287
+ lat (float): The latitude of the point. minimum is -90, maximum is 90.
288
+ lng (float): The longitude of the point. minimum is -180, maximum is 180.
289
+
290
+ Returns:
291
+ Dict: The point of the raster.
292
+
293
+ Example:
294
+ >>> from geobox import GeoboxClient
295
+ >>> from geobox.raster import Raster
296
+ >>> client = GeoboxClient()
297
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
298
+ >>> raster.get_point(lat=60, lng=50)
299
+ """
300
+ if lat < -90 or lat > 90:
301
+ raise ValueError("lat must be between -90 and 90")
302
+ if lng < -180 or lng > 180:
303
+ raise ValueError("lng must be between -180 and 180")
304
+
305
+ params = clean_data({
306
+ 'lat': lat,
307
+ 'lng': lng,
308
+ })
309
+ query_string = urlencode(params)
310
+ endpoint = urljoin(self.endpoint, f'point?{query_string}')
311
+ return self.api.get(endpoint)
312
+
313
+
314
+ def _get_save_path(self, save_path: str = None) -> str:
315
+ """
316
+ Get the path where the file should be saved.
317
+
318
+ Args:
319
+ save_path (str, optional): The path to save the file.
320
+
321
+ Returns:
322
+ str: The path where the file is saved.
323
+
324
+ Raises:
325
+ ValueError: If save_path does not end with a '/'.
326
+ """
327
+ # If save_path is provided, check if it ends with a '/'
328
+ if save_path and save_path.endswith('/'):
329
+ return f'{save_path}'
330
+
331
+ if save_path and not save_path.endswith('/'):
332
+ raise ValueError("save_path must end with a '/'")
333
+
334
+ return os.getcwd()
335
+
336
+
337
+ def _get_file_name(self, response: requests.Response) -> str:
338
+ """
339
+ Get the file name from the response.
340
+
341
+ Args:
342
+ response (requests.Response): The response of the request.
343
+
344
+ Returns:
345
+ str: The file name
346
+ """
347
+ if 'Content-Disposition' in response.headers and 'filename=' in response.headers['Content-Disposition']:
348
+ file_name = response.headers['Content-Disposition'].split('filename=')[-1].strip().strip('"')
349
+
350
+ else:
351
+ content_type = response.headers.get("Content-Type", "")
352
+ file_name = f'{self.name}.{mimetypes.guess_extension(content_type.split(";")[0])}'
353
+
354
+ return file_name
355
+
356
+
357
+ def _create_progress_bar(self) -> 'tqdm':
358
+ """Creates a progress bar for the task."""
359
+ try:
360
+ from tqdm.auto import tqdm
361
+ except ImportError:
362
+ from .api import logger
363
+ logger.warning("[tqdm] extra is required to show the progress bar. install with: pip insatll geobox[tqdm]")
364
+ return None
365
+
366
+ return tqdm(unit="B",
367
+ total=int(self.size),
368
+ file=sys.stdout,
369
+ dynamic_ncols=True,
370
+ desc="Downloading",
371
+ unit_scale=True,
372
+ unit_divisor=1024,
373
+ ascii=True
374
+ )
375
+
376
+
377
+ def download(self, save_path: str = None, progress_bar: bool = True) -> str:
378
+ """
379
+ Download the raster.
380
+
381
+ Args:
382
+ save_path (str, optional): Path where the file should be saved.
383
+ If not provided, it saves to the current working directory
384
+ using the original filename and appropriate extension.
385
+ progress_bar (bool, optional): Whether to show a progress bar. default: True
386
+
387
+ Returns:
388
+ str: The path to save the raster.
389
+
390
+ Raises:
391
+ ValueError: If file_uuid is not set
392
+ OSError: If there are issues with file operations
393
+
394
+ Example:
395
+ >>> from geobox import GeoboxClient
396
+ >>> from geobox.raster import Raster
397
+ >>> client = GeoboxClient()
398
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
399
+ >>> raster.download(save_path="path/to/save/")
400
+ """
401
+ if not self.uuid:
402
+ raise ValueError("Raster UUID is required to download the raster file")
403
+
404
+ save_path = self._get_save_path(save_path)
405
+ os.makedirs(os.path.dirname(save_path), exist_ok=True)
406
+
407
+ with self.api.get(f"{self.endpoint}download/", stream=True) as response:
408
+ file_name = self._get_file_name(response)
409
+ full_path = f"{save_path}/{file_name}"
410
+ with open(full_path, 'wb') as f:
411
+ pbar = self._create_progress_bar() if progress_bar else None
412
+ for chunk in response.iter_content(chunk_size=8192):
413
+ f.write(chunk)
414
+ if pbar:
415
+ pbar.update(len(chunk))
416
+ pbar.refresh()
417
+ if pbar:
418
+ pbar.close()
419
+
420
+ return os.path.abspath(full_path)
421
+
422
+
423
+ def get_content_file(self, save_path: str = None, progress_bar: bool = True) -> str:
424
+ """
425
+ Get Raster Content URL
426
+
427
+ Args:
428
+ save_path (str, optional): Path where the file should be saved.
429
+ If not provided, it saves to the current working directory
430
+ using the original filename and appropriate extension.
431
+ progress_bar (bool, optional): Whether to show a progress bar. default: True
432
+
433
+ Returns:
434
+ str: The path to save the raster.
435
+
436
+ Raises:
437
+ ValueError: If uuid is not set
438
+ OSError: If there are issues with file operations
439
+
440
+ Examples:
441
+ >>> from geobox import GeoboxClient
442
+ >>> from geobox.raster import Raster
443
+ >>> client = GeoboxClient()
444
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
445
+ >>> raster_tiff = raste.get_content_file()
446
+ """
447
+ if not self.uuid:
448
+ raise ValueError("Raster UUID is required to download the raster content")
449
+
450
+ save_path = self._get_save_path(save_path)
451
+ os.makedirs(os.path.dirname(save_path), exist_ok=True)
452
+
453
+ with self.api.get(f"{self.endpoint}content/", stream=True) as response:
454
+ file_name = self._get_file_name(response)
455
+ full_path = f"{save_path}/{file_name}"
456
+ with open(full_path, 'wb') as f:
457
+ pbar = self._create_progress_bar() if progress_bar else None
458
+ for chunk in response.iter_content(chunk_size=8192):
459
+ f.write(chunk)
460
+ if pbar:
461
+ pbar.update(len(chunk))
462
+ pbar.refresh()
463
+ if pbar:
464
+ pbar.close()
465
+
466
+ return os.path.abspath(full_path)
467
+
468
+
469
+ def get_render_png_url(self, x: int, y: int, z: int, **kwargs) -> str:
470
+ """
471
+ Get the PNG URL of the raster.
472
+
473
+ Args:
474
+ x (int): The x coordinate of the tile.
475
+ y (int): The y coordinate of the tile.
476
+ z (int): The zoom level of the tile.
477
+
478
+ Keyword Args:
479
+ indexes (str, optional): list of comma separated band indexes to be rendered. e.g. 1, 2, 3
480
+ nodata (int, optional)
481
+ expression (str, optional): band math expression. e.g. b1*b2+b3
482
+ rescale (List, optional): comma (',') separated Min,Max range. Can set multiple time for multiple bands.
483
+ color_formula (str, optional): Color formula. e.g. gamma R 0.5
484
+ colormap_name (str, optional)
485
+ colormap (str, optional): JSON encoded custom Colormap. e.g. {"0": "#ff0000", "1": "#00ff00"} or [[[0, 100], "#ff0000"], [[100, 200], "#00ff00"]]
486
+
487
+ Returns:
488
+ str: The PNG Render URL of the raster.
489
+
490
+ Example:
491
+ >>> from geobox import GeoboxClient
492
+ >>> from geobox.raster import Raster
493
+ >>> client = GeoboxClient()
494
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
495
+ >>> raster.get_tile_render_url(x=10, y=20, z=1)
496
+ """
497
+ params = clean_data({
498
+ 'indexes': kwargs.get('indexes'),
499
+ 'nodata': kwargs.get('nodata'),
500
+ 'expression': kwargs.get('expression'),
501
+ 'rescale': kwargs.get('rescale'),
502
+ 'color_formula': kwargs.get('color_formula'),
503
+ 'colormap_name': kwargs.get('colormap_name'),
504
+ 'colormap': kwargs.get('colormap')
505
+ })
506
+ query_string = urlencode(params)
507
+ endpoint = f'{self.api.base_url}{self.endpoint}render/{z}/{x}/{y}.png'
508
+ if query_string:
509
+ endpoint = f'{endpoint}?{query_string}'
510
+
511
+ if not self.api.access_token and self.api.apikey:
512
+ endpoint = join_url_params(endpoint, {"apikey": self.api.apikey})
513
+
514
+ return endpoint
515
+
516
+
517
+ def get_tile_pbf_url(self, x: int = '{x}', y: int = '{y}', z: int = '{z}', indexes: str = None) -> str:
518
+ """
519
+ Get the URL of the tile.
520
+
521
+ Args:
522
+ x (int, optional): The x coordinate of the tile.
523
+ y (int, optional): The y coordinate of the tile.
524
+ z (int, optional): The zoom level of the tile.
525
+ indexes (str, optional): list of comma separated band indexes to be rendered. e.g. 1, 2, 3
526
+
527
+ Returns:
528
+ str: The URL of the tile.
529
+
530
+ Example:
531
+ >>> from geobox import GeoboxClient
532
+ >>> from geobox.raster import Raster
533
+ >>> client = GeoboxClient()
534
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
535
+ >>> raster.get_tile_pbf_url(x=10, y=20, z=1)
536
+ """
537
+ params = clean_data({
538
+ 'indexes': indexes
539
+ })
540
+ query_string = urlencode(params)
541
+ endpoint = urljoin(self.api.base_url, f'{self.endpoint}tiles/{z}/{x}/{y}.pbf')
542
+ endpoint = urljoin(endpoint, f'?{query_string}')
543
+
544
+ if not self.api.access_token and self.api.apikey:
545
+ endpoint = join_url_params(endpoint, {"apikey": self.api.apikey})
546
+
547
+ return endpoint
548
+
549
+
550
+ def get_tile_png_url(self, x: int = 'x', y: int = 'y', z: int = 'z') -> str:
551
+ """
552
+ Get the URL of the tile.
553
+
554
+ Args:
555
+ x (int, optional): The x coordinate of the tile.
556
+ y (int, optional): The y coordinate of the tile.
557
+ z (int, optional): The zoom level of the tile.
558
+
559
+ Returns:
560
+ str: The URL of the tile.
561
+
562
+ Example:
563
+ >>> from geobox import GeoboxClient
564
+ >>> from geobox.raster import Raster
565
+ >>> client = GeoboxClient()
566
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
567
+ >>> raster.get_tile_png_url(x=10, y=20, z=1)
568
+ """
569
+ endpoint = f'{self.api.base_url}{self.endpoint}tiles/{z}/{x}/{y}.png'
570
+
571
+ if not self.api.access_token and self.api.apikey:
572
+ endpoint = f'{endpoint}?apikey={self.api.apikey}'
573
+
574
+ return endpoint
575
+
576
+
577
+ def get_tile_json(self) -> Dict:
578
+ """
579
+ Get the tile JSON of the raster.
580
+
581
+ Returns:
582
+ Dict: The tile JSON of the raster.
583
+
584
+ Example:
585
+ >>> from geobox import GeoboxClient
586
+ >>> from geobox.raster import Raster
587
+ >>> client = GeoboxClient()
588
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
589
+ >>> raster.get_tile_json()
590
+ """
591
+ endpoint = urljoin(self.endpoint, 'tilejson.json')
592
+ return self.api.get(endpoint)
593
+
594
+
595
+ def wmts(self, scale: int = None) -> str:
596
+ """
597
+ Get the WMTS URL
598
+
599
+ Args:
600
+ scale (int, optional): The scale of the raster. values are: 1, 2
601
+
602
+ Returns:
603
+ str: the raster WMTS URL
604
+
605
+ Example:
606
+ >>> from geobox import GeoboxClient
607
+ >>> from geobox.raster import Raster
608
+ >>> client = GeoboxClient()
609
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
610
+ >>> raster.wmts(scale=1)
611
+ """
612
+ endpoint = urljoin(self.api.base_url, f'{self.endpoint}wmts/')
613
+ if scale:
614
+ endpoint = f"{endpoint}?scale={scale}"
615
+
616
+ if not self.api.access_token and self.api.apikey:
617
+ endpoint = join_url_params(endpoint, {"apikey": self.api.apikey})
618
+
619
+ return endpoint
620
+
621
+
622
+ @property
623
+ def settings(self) -> Dict:
624
+ """
625
+ Get the settings of the raster.
626
+
627
+ Returns:
628
+ Dict: The settings of the raster.
629
+
630
+ Example:
631
+ >>> from geobox import GeoboxClient
632
+ >>> from geobox.raster import Raster
633
+ >>> client = GeoboxClient()
634
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
635
+ >>> raster.settings
636
+ """
637
+ return super()._get_settings(self.endpoint)
638
+
639
+
640
+ def update_settings(self, settings: Dict) -> Dict:
641
+ """
642
+ Update the settings
643
+
644
+ settings (Dict): settings dictionary
645
+
646
+ Returns:
647
+ Dict: updated settings
648
+
649
+ Example:
650
+ >>> from geobox import GeoboxClient
651
+ >>> client = GeoboxClient()
652
+ >>> raster1 = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
653
+ >>> raster2 = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
654
+ >>> raster1.update_settings(raster2.settings)
655
+ """
656
+ return super()._set_settings(self.endpoint, settings)
657
+
658
+
659
+ def set_settings(self, **kwargs) -> None:
660
+ """
661
+ Set the settings of the raster.
662
+
663
+ Keyword Args:
664
+ nodata (int): The nodata value of the raster.
665
+ indexes (list[int]): The indexes of the raster.
666
+ rescale (list[int]): The rescale of the raster.
667
+ colormap_name (str): The colormap name of the raster.
668
+ color_formula (str): The color formula of the raster.
669
+ expression (str): The expression of the raster.
670
+ exaggeraion (int): The exaggeraion of the raster.
671
+ min_zoom (int): The min zoom of the raster.
672
+ max_zoom (int): The max zoom of the raster.
673
+ use_cache (bool): Whether to use cache of the raster.
674
+ cache_until_zoom (int): The cache until zoom of the raster.
675
+
676
+
677
+ Example:
678
+ >>> from geobox import GeoboxClient
679
+ >>> from geobox.raster import Raster
680
+ >>> client = GeoboxClient()
681
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
682
+ >>> raster.set_settings(nodata=0,
683
+ ... indexes=[1],
684
+ ... rescale=[[0, 10000]],
685
+ ... colormap_name='gist_rainbow',
686
+ ... color_formula='Gamma R 0.5',
687
+ ... expression='b1 * 2',
688
+ ... exaggeraion=10,
689
+ ... min_zoom=0,
690
+ ... max_zoom=22,
691
+ ... use_cache=True,
692
+ ... cache_until_zoom=17)
693
+ """
694
+ visual_settings = {
695
+ 'nodata', 'indexes', 'rescale', 'colormap_name',
696
+ 'color_formula', 'expression', 'exaggeraion'
697
+ }
698
+ tile_settings = {
699
+ 'min_zoom', 'max_zoom', 'use_cache', 'cache_until_zoom'
700
+ }
701
+
702
+ settings = self.settings
703
+
704
+ for key, value in kwargs.items():
705
+ if key in visual_settings:
706
+ settings['visual_settings'][key] = value
707
+ elif key in tile_settings:
708
+ settings['tile_settings'][key] = value
709
+
710
+
711
+ return super()._set_settings(self.endpoint, settings)
712
+
713
+
714
+ def share(self, users: List['User']) -> None:
715
+ """
716
+ Shares the raster with specified users.
717
+
718
+ Args:
719
+ users (List[User]): The list of user objects to share the raster with.
720
+
721
+ Returns:
722
+ None
723
+
724
+ Example:
725
+ >>> from geobox import GeoboxClient
726
+ >>> from geobox.raster import Raster
727
+ >>> client = GeoboxClient()
728
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
729
+ >>> users = client.search_users(search="John")
730
+ >>> raster.share(users=users)
731
+ """
732
+ super()._share(self.endpoint, users)
733
+
734
+
735
+ def unshare(self, users: List['User']) -> None:
736
+ """
737
+ Unshares the raster with specified users.
738
+
739
+ Args:
740
+ users (List[User]): The list of user objects to unshare the raster with.
741
+
742
+ Returns:
743
+ None
744
+
745
+ Example:
746
+ >>> from geobox import GeoboxClient
747
+ >>> from geobox.raster import Raster
748
+ >>> client = GeoboxClient()
749
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
750
+ >>> users = client.search_users(search="John")
751
+ >>> raster.unshare(users=users)
752
+ """
753
+ super()._unshare(self.endpoint, users)
754
+
755
+
756
+ def get_shared_users(self, search: str = None, skip: int = 0, limit: int = 10) -> List['User']:
757
+ """
758
+ Retrieves the list of users the raster is shared with.
759
+
760
+ Args:
761
+ search (str, optional): The search query.
762
+ skip (int, optional): The number of users to skip.
763
+ limit (int, optional): The maximum number of users to retrieve.
764
+
765
+ Returns:
766
+ List[User]: The list of shared users.
767
+
768
+ Example:
769
+ >>> from geobox import GeoboxClient
770
+ >>> from geobox.raster import Raster
771
+ >>> client = GeoboxClient()
772
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
773
+ >>> raster.get_shared_users(search='John', skip=0, limit=10)
774
+ """
775
+ params = {
776
+ 'search': search,
777
+ 'skip': skip,
778
+ 'limit': limit
779
+ }
780
+ return super()._get_shared_users(self.endpoint, params)
781
+
782
+
783
+ def seed_cache(self, from_zoom: int = None, to_zoom: int = None, extent: List[int] = None, workers: int = 1) -> List['Task']:
784
+ """
785
+ Seed the cache of the raster.
786
+
787
+ Args:
788
+ from_zoom (int, optional): The from zoom of the raster.
789
+ to_zoom (int, optional): The to zoom of the raster.
790
+ extent (List[int], optional): The extent of the raster.
791
+ workers (int, optional): The number of workers to use. default is 1.
792
+
793
+ Returns:
794
+ Task: The task of the seed cache.
795
+
796
+ Example:
797
+ >>> from geobox import GeoboxClient
798
+ >>> from geobox.raster import Raster
799
+ >>> client = GeoboxClient()
800
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
801
+ >>> task = raster.seed_cache(from_zoom=0, to_zoom=22, extent=[0, 0, 100, 100], workers=1)
802
+ """
803
+ data = {
804
+ 'from_zoom': from_zoom,
805
+ 'to_zoom': to_zoom,
806
+ 'extent': extent,
807
+ 'workers': workers
808
+ }
809
+ return super()._seed_cache(self.endpoint, data)
810
+
811
+
812
+ def clear_cache(self) -> None:
813
+ """
814
+ Clear the cache of the raster.
815
+
816
+ Returns:
817
+ None
818
+
819
+ Example:
820
+ >>> from geobox import GeoboxClient
821
+ >>> from geobox.raster import Raster
822
+ >>> client = GeoboxClient()
823
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
824
+ >>> raster.clear_cache()
825
+ """
826
+ super()._clear_cache(self.endpoint)
827
+
828
+
829
+ @property
830
+ def cache_size(self) -> int:
831
+ """
832
+ Get the size of the cache of the raster.
833
+
834
+ Returns:
835
+ int: The size of the cache of the raster.
836
+
837
+ Example:
838
+ >>> from geobox import GeoboxClient
839
+ >>> from geobox.raster import Raster
840
+ >>> client = GeoboxClient()
841
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
842
+ >>> raster.cache_size
843
+ """
844
+ return super()._cache_size(self.endpoint)
845
+
846
+
847
+ def to_async(self, async_client: 'AsyncGeoboxClient') -> 'AsyncRaster':
848
+ """
849
+ Switch to async version of the raster instance to have access to the async methods
850
+
851
+ Args:
852
+ async_client (AsyncGeoboxClient): The async version of the GeoboxClient instance for making requests.
853
+
854
+ Returns:
855
+ AsyncRaster: the async instance of the raster.
856
+
857
+ Example:
858
+ >>> from geobox import Geoboxclient
859
+ >>> from geobox.aio import AsyncGeoboxClient
860
+ >>> from geobox.raster import Raster
861
+ >>> client = GeoboxClient()
862
+ >>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
863
+ >>> async with AsyncGeoboxClient() as async_client:
864
+ >>> async_raster = raster.to_async(async_client)
865
+ """
866
+ from .aio.raster import AsyncRaster
867
+
868
+ return AsyncRaster(api=async_client, uuid=self.uuid, data=self.data)
869
+
870
+
871
+ def profile(self,
872
+ polyline: List,
873
+ number_of_samples: int = 100,
874
+ output_epsg: Optional[int] = None,
875
+ include_distance: bool = True,
876
+ treat_nodata_as_null: bool = True) -> Dict:
877
+ """
878
+ Create a profile form a raster along a path
879
+
880
+ Args:
881
+ polyline (List): Path coordinates as [x, y] pairs. Use raster CRS unless `output_epsg` is provided.
882
+ number_of_samples (int, optional): Number of samples along the path. default: 100.
883
+ output_epsg (int, optional): EPSG code for output coordinates. If None, use raster CRS.
884
+ include_distance (bool, optional): Include cumulative distance for each sample. default: True.
885
+ treat_nodata_as_null (bool, optional): Treat NoData pixels as null values. default: True.
886
+
887
+ Returns:
888
+ Dict: the profile result as geojson
889
+
890
+ Example:
891
+ >>> from geobox import GeoboxClient
892
+ >>> client = GeoboxClient()
893
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
894
+ >>> task = raster.profile(polyline=[[0, 0], [10, 10]], number_of_samples=200)
895
+ """
896
+ endpoint = f"{self.endpoint}profile/"
897
+
898
+ data = clean_data({
899
+ 'polyline': polyline,
900
+ 'number_of_samples': number_of_samples,
901
+ 'output_epsg': output_epsg,
902
+ 'include_distance': include_distance,
903
+ 'treat_nodata_as_null': treat_nodata_as_null
904
+ })
905
+
906
+ response = self.api.post(endpoint=endpoint, payload=data)
907
+ return response