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/raster.py CHANGED
@@ -1,863 +1,1270 @@
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 Raster as 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
+ geobox.aio.raster.Raster: 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 Raster as AsyncRaster
867
+
868
+ return AsyncRaster(api=async_client, uuid=self.uuid, data=self.data)
869
+
870
+
871
+ def polygonize(self,
872
+ output_layer_name: str,
873
+ band_index: int = 1,
874
+ value_field: Optional[str] = None,
875
+ mask_nodata: bool = False,
876
+ connectivity: PolygonizeConnectivity = PolygonizeConnectivity.connected_4,
877
+ keep_values: Optional[str] = None,
878
+ layer_name: Optional[str] = None,
879
+ user_id: Optional[int] = None) -> 'Task':
880
+ """
881
+ Convert a raster to vector polygons
882
+
883
+ vectorizes a raster (polygonize) to a vector dataset (*.gpkg). Only users with Publisher role or higher can perform this operation
884
+
885
+ Args:
886
+ api (GeoboxClient): The GeoboxClient instance for making requests
887
+ raster (Raster): Raster instance
888
+ output_layer_name (str): Name for the output vector layer.
889
+ band_index (int, optional): Raster band to polygonize. default: 1
890
+ value_field (str, optional): Name of attribute field storing the pixel value. default: None
891
+ mask_nodata (bool, optional): If True, NoData pixels are excluded using the band mask. default: False
892
+ connectivity (PolygonizeConnectivity, optional): 4 or 8 connectivity for region grouping. default: PolygonizeConnectivity.connected_4
893
+ keep_values (str, optional): JSON array of values to keep (e.g., '[1,2,3]'). default: None
894
+ layer_name (str, optional): Output layer name. default: None
895
+ user_id (int, optional): specific user. priviledges required!
896
+
897
+ Returns:
898
+ Task: task instance of the process
899
+
900
+ Example:
901
+ >>> from geobox import GeoboxClient
902
+ >>> client = GeoboxClient()
903
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
904
+ >>> raster.polygonize(output_layer_name='test')
905
+ """
906
+ from .analysis import Analysis
907
+ return Analysis.polygonize(self.api,
908
+ self,
909
+ output_layer_name=output_layer_name,
910
+ band_index=band_index,
911
+ value_field=value_field,
912
+ mask_nodata=mask_nodata,
913
+ connectivity=connectivity,
914
+ keep_values=keep_values,
915
+ layer_name=layer_name,
916
+ user_id=user_id)
917
+
918
+
919
+ def clip(self,
920
+ layer: Union[VectorLayer, VectorLayerView],
921
+ output_raster_name: str,
922
+ where: Optional[str] = None,
923
+ dst_nodata: int = -9999,
924
+ crop: bool = True,
925
+ resample: AnalysisResampleMethod = AnalysisResampleMethod.near,
926
+ user_id: Optional[int] = None) -> 'Task':
927
+ """
928
+ Clip a raster using a vector layer as a mask
929
+
930
+ clips a raster dataset using a vector layer as the clipping boundary. Only users with Publisher role or higher can perform this operation
931
+
932
+ Args:
933
+ api (GeoboxClient): The GeoboxClient instance for making requests
934
+ raster (Raster): Raster instance
935
+ layer (VectorLayer | VectorLayerView): VectorLayer or VectorLayerView instance
936
+ output_raster_name (str): Name for the output raster dataset
937
+ where (str, optional): Optional attribute filter, e.g. 'VEG=forest'.
938
+ dst_nodata (int, optional): Output NoData value. default: -9999
939
+ crop (bool, optional): True=shrink extent to polygon(s); False=keep full extent but mask outside. default: True
940
+ resample (AnalysisResampleMethod, optional): Resampling method: 'near', 'bilinear', 'cubic', 'lanczos', etc. default: AnalysisResampleMethod.near
941
+ user_id (int, optional): specific user. priviledges required!
942
+
943
+ Returns:
944
+ Task: task instance of the process
945
+
946
+ Example:
947
+ >>> from geobox import GeoboxClient
948
+ >>> client = GeoboxClient()
949
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
950
+ >>> vector = client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
951
+ >>> raster.clip(layer=vector, output_raster_name='test')
952
+ """
953
+ from .analysis import Analysis
954
+ return Analysis.clip(self.api,
955
+ self,
956
+ layer=layer,
957
+ output_raster_name=output_raster_name,
958
+ where=where,
959
+ dst_nodata=dst_nodata,
960
+ crop=crop,
961
+ resample=resample,
962
+ user_id=user_id)
963
+
964
+
965
+ def slope(self,
966
+ output_raster_name: str,
967
+ slope_units: SlopeUnit = SlopeUnit.degree,
968
+ algorithm: AnalysisAlgorithm = AnalysisAlgorithm.Horn,
969
+ scale: int = 1,
970
+ compute_edges: bool = True,
971
+ nodata_out: int = -9999,
972
+ user_id: Optional[int] = None) -> 'Task':
973
+ """
974
+ Calculate slope from a DEM raster.
975
+
976
+ This endpoint creates a slope raster from a Digital Elevation Model (DEM). Only users with Publisher role or higher can perform this operation.
977
+
978
+ Args:
979
+ output_raster_name (str): Name for the output raster dataset.
980
+ slope_units (SlopeUnit, optional): Slope units: 'degree' or 'percent'. default: SlopeUnit.degree
981
+ algorithm (AnalysisAlgorithm, optional): Algorithm: 'Horn' or 'ZevenbergenThorne'. default: AnalysisAlgorithm.Horn
982
+ scale (int, optional): Ratio of vertical units to horizontal units. default: 1
983
+ compute_edges (bool, optional): Whether to compute edges. default: True
984
+ nodata (int, optional): NoData value for the output raster. default = -9999
985
+ user_id (int, optional): specific user. priviledges required!
986
+
987
+ Returns:
988
+ Task: task instance of the process
989
+
990
+ Example:
991
+ >>> from geobox import GeoboxClient
992
+ >>> client = GeoboxClient()
993
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
994
+ >>> raster.slope(output_raster_name='test')
995
+ """
996
+ from .analysis import Analysis
997
+ return Analysis.slope(self.api,
998
+ self,
999
+ slope_units=slope_units,
1000
+ output_raster_name=output_raster_name,
1001
+ algorithm=algorithm,
1002
+ scale=scale,
1003
+ compute_edges=compute_edges,
1004
+ nodata_out=nodata_out,
1005
+ user_id=user_id)
1006
+
1007
+
1008
+ def aspect(self,
1009
+ output_raster_name: str,
1010
+ algorithm: AnalysisAlgorithm = AnalysisAlgorithm.Horn,
1011
+ trigonometric: bool = False,
1012
+ zero_for_flat: bool = True,
1013
+ compute_edges: bool = True,
1014
+ nodata_out: int = -9999,
1015
+ user_id: Optional[int] = None) -> 'Task':
1016
+ """
1017
+ Calculate aspect from a DEM raster.
1018
+
1019
+ it creates an aspect raster (degrees 0–360) from a Digital Elevation Model (DEM).
1020
+ Only users with Publisher role or higher can perform this operation.
1021
+
1022
+ Args:
1023
+ api (GeoboxClient): The GeoboxClient instance for making requests
1024
+ raster (Raster): DEM Raster instance
1025
+ output_raster_name (str): Name for the output raster dataset.
1026
+ algorithm (AnalysisAlgorithm, optional): Algorithm: 'Horn' or 'ZevenbergenThorne'. default: AnalysisAlgorithm.Horn
1027
+ trigonometric (bool, optional): False: azimuth (0°=N, 90°=E, clockwise); True: 0°=E, counter-clockwise. default: False
1028
+ zero_for_flat (bool, optional): Set flats (slope==0) to 0 instead of NoData. default: True
1029
+ compute_edges (bool, optional): Whether to compute edges. default: True
1030
+ nodata (int, optional): NoData value for the output raster. default = -9999
1031
+ user_id (int, optional): specific user. priviledges required!
1032
+
1033
+ Returns:
1034
+ Task: task instance of the process
1035
+
1036
+ Example:
1037
+ >>> from geobox import GeoboxClient
1038
+ >>> client = GeoboxClient()
1039
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
1040
+ >>> raster.aspect(output_raster_name='test')
1041
+ """
1042
+ from .analysis import Analysis
1043
+ return Analysis.aspect(self.api,
1044
+ self,
1045
+ output_raster_name=output_raster_name,
1046
+ algorithm=algorithm,
1047
+ trigonometric=trigonometric,
1048
+ zero_for_flat=zero_for_flat,
1049
+ compute_edges=compute_edges,
1050
+ nodata_out=nodata_out,
1051
+ user_id=user_id)
1052
+
1053
+
1054
+ def reclassify(self,
1055
+ output_raster_name: str,
1056
+ rules: str,
1057
+ default_value: Optional[int] = None,
1058
+ nodata_in: int = -9999,
1059
+ nodata_out: int = -9999,
1060
+ out_dtype: AnalysisDataType = AnalysisDataType.int16,
1061
+ inclusive: RangeBound = RangeBound.left,
1062
+ user_id: Optional[int] = None) -> 'Task':
1063
+ """
1064
+ Reclassify a raster using value mapping or class breaks.
1065
+
1066
+ This endpoint reclassifies raster values according to specified rules.
1067
+ Only users with Publisher role or higher can perform this operation.
1068
+
1069
+ Args:
1070
+ output_raster_name (str): Name for the output reclassified raster dataset.
1071
+ rules (str): JSON string containing reclassification rules.
1072
+ For mode='exact', it should be a dict {old_value: new_value}.
1073
+ For mode='range', it should be a list of (low, high, new_value).
1074
+ Example for mode='exact': '{"1": 10, "2": 20, "3": 30}'.
1075
+ Example for mode='range': '[[0, 10, 1], [10, 20, 2], [20, 30, 3]]'.
1076
+ the method would detect the mode type based on the rules input.
1077
+ default_value (str, optional): Value to assign when a pixel matches no rule.
1078
+ nodata_in (int, optional): NoData of input. If None, tries to get from the input raster.
1079
+ nodata_out (int, optional): NoData value to set on output band.
1080
+ out_dtype (AnalysisDataType, optional): Output data type. default: AnalysisDataType.int16
1081
+ inclusive (RangeBound, optional): Range bound semantics for mode='range': 'left', 'right', 'both', 'neither'. default: RangeBound.left
1082
+ user_id (int, optional): specific user. priviledges required!
1083
+
1084
+ Returns:
1085
+ Task: task instance of the process
1086
+
1087
+ Example:
1088
+ >>> from geobox import GeoboxClient
1089
+ >>> client = GeoboxClient()
1090
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
1091
+ >>> raster.reclassify(output_raster_name='test', rules='{"1": 10, "2": 20, "3": 30}')
1092
+ """
1093
+ from .analysis import Analysis
1094
+ return Analysis.reclassify(self.api,
1095
+ self,
1096
+ output_raster_name=output_raster_name,
1097
+ rules=rules,
1098
+ default_value=default_value,
1099
+ nodata_in=nodata_in,
1100
+ nodata_out=nodata_out,
1101
+ out_dtype=out_dtype,
1102
+ inclusive=inclusive,
1103
+ user_id=user_id)
1104
+
1105
+
1106
+ def resample(self,
1107
+ output_raster_name: str,
1108
+ out_res: Optional[str] = None,
1109
+ scale_factor: Optional[str] = None,
1110
+ match_raster_uuid: Optional[str] = None,
1111
+ resample_method: AnalysisResampleMethod = AnalysisResampleMethod.near,
1112
+ dst_nodata: int = -9999,
1113
+ user_id: Optional[int] = None) -> 'Task':
1114
+ """
1115
+ Resample a raster to a different resolution.
1116
+
1117
+ it resamples a raster using GDAL Warp.
1118
+ Exactly one of out_res, scale_factor, or match_raster_uuid must be provided.
1119
+ Only users with Publisher role or higher can perform this operation.
1120
+
1121
+ Args:
1122
+ output_raster_name (str): Name for the output reclassified raster dataset.
1123
+ out_res (str, optional): Output resolution as 'x_res,y_res' (e.g., '10,10').
1124
+ scale_factor (int, optional): Scale factor (e.g., 2.0 for 2x finer resolution).
1125
+ match_raster_uuid (str, optional): UUID of reference raster to match resolution/extent.
1126
+ resample_method (AnalysisResampleMethod, optional): Resampling method: 'near', 'bilinear', 'cubic', 'lanczos', etc.
1127
+ dst_nodata (int, optional): Output NoData value.
1128
+ user_id (int, optional): specific user. priviledges required!
1129
+
1130
+ Returns:
1131
+ Task: task instance of the process
1132
+
1133
+ Example:
1134
+ >>> from geobox import GeoboxClient
1135
+ >>> client = GeoboxClient()
1136
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
1137
+ >>> raster.resample(output_raster_name='test')
1138
+ """
1139
+ from .analysis import Analysis
1140
+ return Analysis.resample(self.api,
1141
+ self,
1142
+ output_raster_name=output_raster_name,
1143
+ out_res=out_res,
1144
+ scale_factor=scale_factor,
1145
+ match_raster_uuid=match_raster_uuid,
1146
+ resample_method=resample_method,
1147
+ dst_nodata=dst_nodata,
1148
+ user_id=user_id)
1149
+
1150
+
1151
+ def fill_nodata(self,
1152
+ output_raster_name: str,
1153
+ band: Union[int, str] = 1,
1154
+ nodata: Optional[int] = None,
1155
+ max_search_dist: Optional[int] = None,
1156
+ smoothing_iterations: Optional[int] = None,
1157
+ mask_raster_uuid: Optional[str] = None,
1158
+ user_id: Optional[int] = None) -> 'Task':
1159
+ """
1160
+ Fill NoData regions in a raster using GDAL's FillNodata algorithm.
1161
+
1162
+ it fills gaps (NoData regions) in a raster by interpolating values from surrounding valid pixels.
1163
+ This is commonly used for data cleaning and gap filling in remote sensing and elevation data.
1164
+ Only users with Publisher role or higher can perform this operation.
1165
+
1166
+ Args:
1167
+ output_raster_name (str): Name for the output filled raster dataset.
1168
+ band (int | str): 1-based band index to process or 'all' to process all bands. default: 1
1169
+ nodata (int, optional): NoData value to use. If None, uses the band's existing NoData.
1170
+ max_search_dist (int, optoinal): Maximum distance in pixels to search for valid data.
1171
+ smoothing_iterations (int, optional): Number of smoothing iterations to apply.
1172
+ mask_raster_uuid (str, optional): Optional UUID of a mask raster (0=masked, >0=valid).
1173
+ user_id (int, optional): specific user. priviledges required!
1174
+
1175
+ Returns:
1176
+ Task: task instance of the process
1177
+
1178
+ Example:
1179
+ >>> from geobox import GeoboxClient
1180
+ >>> client = GeoboxClient()
1181
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
1182
+ >>> task = raster.fill_nodata(output_raster_name='test')
1183
+ """
1184
+ from .analysis import Analysis
1185
+ return Analysis.fill_nodata(self.api,
1186
+ self,
1187
+ output_raster_name=output_raster_name,
1188
+ band=band,
1189
+ nodata=nodata,
1190
+ max_search_dist=max_search_dist,
1191
+ smoothing_iterations=smoothing_iterations,
1192
+ mask_raster_uuid=mask_raster_uuid,
1193
+ user_id=user_id)
1194
+
1195
+
1196
+ def proximity(self,
1197
+ output_raster_name: str,
1198
+ dist_units: DistanceUnit = DistanceUnit.GEO,
1199
+ burn_value: int = 1,
1200
+ nodata: int = -9999,
1201
+ user_id: Optional[int] = None) -> 'Task':
1202
+ """
1203
+ Create a proximity (distance) raster from a raster layer.
1204
+
1205
+ it creates a raster showing the distance from each pixel to the nearest pixel in the input raster layer.
1206
+ Only users with Publisher role or higher can perform this operation.
1207
+
1208
+ Args:
1209
+ output_raster_name (str): Name for the output proximity raster dataset.
1210
+ dist_units (DistanceUnit, optional): Distance units: 'GEO' for georeferenced units, 'PIXEL' for pixels. default: DistanceUnit.GEO
1211
+ burn_value (int, optional): Value treated as targets (distance 0). default: 1
1212
+ nodata (int, optional): NoData value to use in the output raster. default: -9999
1213
+ user_id (int, optional): specific user. priviledges required!
1214
+
1215
+ Returns:
1216
+ Task: task instance of the process
1217
+
1218
+ Example:
1219
+ >>> from geobox import GeoboxClient
1220
+ >>> client = GeoboxClient()
1221
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
1222
+ >>> task = raster.proximity(output_raster_name='test')
1223
+ """
1224
+ from .analysis import Analysis
1225
+ return Analysis.proximity(self.api,
1226
+ self,
1227
+ output_raster_name=output_raster_name,
1228
+ dist_units=dist_units,
1229
+ burn_value=burn_value,
1230
+ nodata=nodata,
1231
+ user_id=user_id)
1232
+
1233
+
1234
+ def profile(self,
1235
+ polyline: List,
1236
+ number_of_samples: int = 100,
1237
+ output_epsg: Optional[int] = None,
1238
+ include_distance: bool = True,
1239
+ treat_nodata_as_null: bool = True) -> Dict:
1240
+ """
1241
+ Create a profile form a raster along a path
1242
+
1243
+ Args:
1244
+ polyline (List): Path coordinates as [x, y] pairs. Use raster CRS unless `output_epsg` is provided.
1245
+ number_of_samples (int, optional): Number of samples along the path. default: 100.
1246
+ output_epsg (int, optional): EPSG code for output coordinates. If None, use raster CRS.
1247
+ include_distance (bool, optional): Include cumulative distance for each sample. default: True.
1248
+ treat_nodata_as_null (bool, optional): Treat NoData pixels as null values. default: True.
1249
+
1250
+ Returns:
1251
+ Dict: the profile result as geojson
1252
+
1253
+ Example:
1254
+ >>> from geobox import GeoboxClient
1255
+ >>> client = GeoboxClient()
1256
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
1257
+ >>> task = raster.profile(polyline=[[0, 0], [10, 10]], number_of_samples=200)
1258
+ """
1259
+ endpoint = f"{self.endpoint}profile/"
1260
+
1261
+ data = clean_data({
1262
+ 'polyline': polyline,
1263
+ 'number_of_samples': number_of_samples,
1264
+ 'output_epsg': output_epsg,
1265
+ 'include_distance': include_distance,
1266
+ 'treat_nodata_as_null': treat_nodata_as_null
1267
+ })
1268
+
1269
+ response = self.api.post(endpoint=endpoint, payload=data)
1270
+ return response