geobox 2.0.1__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/analysis.py ADDED
@@ -0,0 +1,757 @@
1
+ from typing import List, Dict, Optional, TYPE_CHECKING, Union
2
+
3
+ from geobox.field import Field
4
+
5
+ from .vectorlayer import VectorLayer
6
+ from .view import VectorLayerView
7
+ from .base import Base
8
+ from .raster import Raster
9
+ from .enums import (AnalysisDataType, PolygonizeConnectivity, AnalysisResampleMethod,
10
+ SlopeUnit, AnalysisAlgorithm, RangeBound, DistanceUnit)
11
+ from .utils import clean_data
12
+
13
+ if TYPE_CHECKING:
14
+ from . import GeoboxClient
15
+ from .task import Task
16
+
17
+ class Analysis(Base):
18
+
19
+ BASE_ENDPOINT = 'analysis/'
20
+
21
+ def __init__(self,
22
+ api: 'GeoboxClient',
23
+ data: Optional[Dict] = {}):
24
+ """
25
+ Initialize a workflow instance.
26
+
27
+ Args:
28
+ api (GeoboxClient): The GeoboxClient instance for making requests.
29
+ uuid (str): The unique identifier for the workflow.
30
+ data (Dict): The response data of the workflow.
31
+ """
32
+ super().__init__(api, data=data)
33
+
34
+
35
+ def __repr__(self) -> str:
36
+ return f"Analysis()"
37
+
38
+ @classmethod
39
+ def rasterize(cls,
40
+ api: 'GeoboxClient',
41
+ layer: Union[VectorLayer, VectorLayerView],
42
+ output_raster_name: str,
43
+ pixel_size: int = 10,
44
+ nodata: Optional[int] = -9999,
45
+ data_type: Optional[AnalysisDataType] = AnalysisDataType.int16,
46
+ burn_value: Optional[int] = 1,
47
+ burn_attribute: Optional[str] = None,
48
+ user_id: Optional[int] = None) -> 'Task':
49
+ """
50
+ Rasterize a vector layer
51
+
52
+ This method converts a vector layer (or view) to a raster dataset using the specified parameters.
53
+ You can control the output raster's name, pixel size, data type, nodata value, and the value to burn (either a constant or from an attribute field).
54
+ Only users with Publisher role or higher can perform this operation.
55
+
56
+ Args:
57
+ api (GeoboxClient): The GeoboxClient instance for making requests
58
+ layer (VectorLayer | VectorLayerView): VectorLayer or VectorLayerView instance
59
+ output_raster_name (str): Name for the output raster dataset
60
+ pixel_size (int, optional): Pixel size for the output raster (must be > 0). default: 10
61
+ nodata (int, optional): NoData value to use in the output raster. default: -9999
62
+ data_type (AnalysisDataType, optional): Data type for the output raster (e.g., int16, float32). default: AnalysisDataType.int16
63
+ burn_value (int, optional): Value to burn into the raster for all features (if burn_attribute is not set). default: 1
64
+ burn_attribute (str, optional): Name of the attribute field to use for burning values into the raster
65
+ user_id (int, optional): specific user. priviledges required!
66
+
67
+ Returns:
68
+ Task: task instance of the process
69
+
70
+ Example:
71
+ >>> from geobox import GeoboxClient
72
+ >>> from geobox.raster_analysis import Analysis
73
+ >>> client = GeoboxClient()
74
+ >>> vector = client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
75
+ >>> task = Analysis.rasterize(api=client, layer=vector, output_raster_name='test')
76
+ or
77
+ >>> task = vector.rasterize(output_raster_name='test')
78
+ """
79
+ if not isinstance(layer, VectorLayer) and not isinstance(layer, VectorLayerView):
80
+ raise ValueError("'layer' input only accepts vector layer and view objects!")
81
+
82
+ endpoint = f'{cls.BASE_ENDPOINT}rasterize/'
83
+
84
+ data = clean_data({
85
+ 'layer_uuid': layer.uuid,
86
+ 'output_raster_name': output_raster_name,
87
+ 'is_view': False if isinstance(layer, VectorLayer) else True,
88
+ 'pixel_size': pixel_size,
89
+ 'nodata': nodata,
90
+ 'data_type': data_type.value,
91
+ 'burn_value': burn_value,
92
+ 'burn_attribute': burn_attribute,
93
+ 'user_id': user_id
94
+ })
95
+
96
+ response = api.post(endpoint=endpoint, payload=data, is_json=False)
97
+ return api.get_task(response['task_id'])
98
+
99
+
100
+ @classmethod
101
+ def polygonize(cls,
102
+ api: 'GeoboxClient',
103
+ raster: 'Raster',
104
+ output_layer_name: str,
105
+ band_index: int = 1,
106
+ value_field: Optional[str] = None,
107
+ mask_nodata: bool = False,
108
+ connectivity: PolygonizeConnectivity = PolygonizeConnectivity.connected_4,
109
+ keep_values: Optional[str] = None,
110
+ layer_name: Optional[str] = None,
111
+ user_id: Optional[int] = None) -> 'Task':
112
+ """
113
+ Convert a raster to vector polygons
114
+
115
+ vectorizes a raster (polygonize) to a vector dataset (*.gpkg). Only users with Publisher role or higher can perform this operation
116
+
117
+ Args:
118
+ api (GeoboxClient): The GeoboxClient instance for making requests
119
+ raster (Raster): Raster instance
120
+ output_layer_name (str): Name for the output vector layer.
121
+ band_index (int, optional): Raster band to polygonize. default: 1
122
+ value_field (str, optional): Name of attribute field storing the pixel value. default: None
123
+ mask_nodata (bool, optional): If True, NoData pixels are excluded using the band mask. default: False
124
+ connectivity (PolygonizeConnectivity, optional): 4 or 8 connectivity for region grouping. default: PolygonizeConnectivity.connected_4
125
+ keep_values (str, optional): JSON array of values to keep (e.g., '[1,2,3]'). default: None
126
+ layer_name (str, optional): Output layer name. default: None
127
+ user_id (int, optional): specific user. priviledges required!
128
+
129
+ Returns:
130
+ Task: task instance of the process
131
+
132
+ Example:
133
+ >>> from geobox import GeoboxClient
134
+ >>> from geobox.raster_analysis import Analysis
135
+ >>> client = GeoboxClient()
136
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
137
+ >>> task = Analysis.polygonize(api=client, raster=raster, output_layer_name='test')
138
+ or
139
+ >>> task = raster.polygonize(output_layer_name='test')
140
+ """
141
+ endpoint = f'{cls.BASE_ENDPOINT}polygonize/'
142
+
143
+ data = clean_data({
144
+ 'raster_uuid': raster.uuid,
145
+ 'output_layer_name': output_layer_name,
146
+ 'band_index': band_index,
147
+ 'value_field': value_field,
148
+ 'mask_nodata': mask_nodata,
149
+ 'connectivity': connectivity.value,
150
+ 'keep_values': keep_values,
151
+ 'layer_name': layer_name,
152
+ 'user_id': user_id
153
+ })
154
+
155
+ response = api.post(endpoint=endpoint, payload=data, is_json=False)
156
+ return api.get_task(response['task_id'])
157
+
158
+
159
+ @classmethod
160
+ def clip(cls,
161
+ api: 'GeoboxClient',
162
+ raster: 'Raster',
163
+ layer: Union[VectorLayer, VectorLayerView],
164
+ output_raster_name: str,
165
+ where: Optional[str] = None,
166
+ dst_nodata: int = -9999,
167
+ crop: bool = True,
168
+ resample: AnalysisResampleMethod = AnalysisResampleMethod.near,
169
+ user_id: Optional[int] = None) -> 'Task':
170
+ """
171
+ Clip a raster using a vector layer as a mask
172
+
173
+ clips a raster dataset using a vector layer as the clipping boundary. Only users with Publisher role or higher can perform this operation
174
+
175
+ Args:
176
+ api (GeoboxClient): The GeoboxClient instance for making requests
177
+ raster (Raster): Raster instance
178
+ layer (VectorLayer | VectorLayerView): VectorLayer or VectorLayerView instance
179
+ output_raster_name (str): Name for the output raster dataset
180
+ where (str, optional): Optional attribute filter, e.g. 'VEG=forest'.
181
+ dst_nodata (int, optional): Output NoData value. default: -9999
182
+ crop (bool, optional): True=shrink extent to polygon(s); False=keep full extent but mask outside. default: True
183
+ resample (CropResample, optional): Resampling method: 'near', 'bilinear', 'cubic', 'lanczos', etc. default: CropResample.near
184
+ user_id (int, optional): specific user. priviledges required!
185
+
186
+ Returns:
187
+ Task: task instance of the process
188
+
189
+ Example:
190
+ >>> from geobox import GeoboxClient
191
+ >>> from geobox.raster_analysis import Analysis
192
+ >>> client = GeoboxClient()
193
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
194
+ >>> vector = client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
195
+ >>> task = Analysis.clip(api=client, raster=raster, layer=vector, output_raster_name='test')
196
+ or
197
+ >>> task = raster.clip(layer=vector, output_raster_name='test')
198
+ """
199
+ if not isinstance(layer, VectorLayer) and not isinstance(layer, VectorLayerView):
200
+ raise ValueError("'layer' input only accepts vector layer and view objects!")
201
+
202
+ endpoint = f'{cls.BASE_ENDPOINT}clip/'
203
+
204
+ data = clean_data({
205
+ 'raster_uuid': raster.uuid,
206
+ 'layer_uuid': layer.uuid,
207
+ 'output_raster_name': output_raster_name,
208
+ 'is_view': False if isinstance(layer, VectorLayer) else True,
209
+ 'where': where,
210
+ 'dst_nodata': dst_nodata,
211
+ 'crop': crop,
212
+ 'resample': resample.value,
213
+ 'user_id': user_id
214
+ })
215
+
216
+ response = api.post(endpoint=endpoint, payload=data, is_json=False)
217
+ return api.get_task(response['task_id'])
218
+
219
+
220
+ @classmethod
221
+ def calculator(cls,
222
+ api: 'GeoboxClient',
223
+ variables: str,
224
+ expr: str,
225
+ output_raster_name: str,
226
+ match_raster_uuid: Optional[str] = None,
227
+ resample: AnalysisResampleMethod = AnalysisResampleMethod.bilinear,
228
+ out_dtype: AnalysisDataType = AnalysisDataType.float32,
229
+ dst_nodata: int = -9999,
230
+ user_id: Optional[int] = None) -> 'Task':
231
+ """
232
+ Perform raster calculator operations on multiple raster datasets.
233
+
234
+ it allows you to perform mathematical operations on one or more raster datasets using NumPy expressions.
235
+ Variables in the expression correspond to raster datasets specified in the variables dictionary.
236
+
237
+ Examples:
238
+ NDVI calculation: variables='{"NIR": "raster_uuid_1", "RED": "raster_uuid_2"}', expr="(NIR-RED)/(NIR+RED)"
239
+ Slope threshold: variables='{"SLOPE": "raster_uuid_1"}', expr="np.where(SLOPE>30,1,0)"
240
+ Multi-band operations: variables='{"IMG": ["raster_uuid_1", 2]}', expr="IMG*2"
241
+
242
+ Args:
243
+ api (GeoboxClient): The GeoboxClient instance for making requests
244
+ variables (str): JSON string mapping variable names to raster specifications. Format: '{"NIR": "raster_uuid_1", "RED": "raster_uuid_2"}' or '{"IMG": ["raster_uuid_1", 2]}' for multi-band operations.
245
+ expr (str): Mathematical expression using NumPy syntax. Use variable names from the variables dict, e.g., '(NIR-RED)/(NIR+RED)' or 'where(SLOPE>30,1,0)' or 'where((dist_to_highway < 1000) & (slope < 10), 1, 0)' .Supported functions: np, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, exp, log, log10, sqrt, abs, floor, ceil, round, minimum, maximum, clip, where, isnan, isfinite, pi, e.
246
+ output_raster_name (str): Name for the output raster dataset.
247
+ match_raster_uuid (str, optional): Optional raster UUID to match the output grid and projection. If not provided, the first variable becomes the reference grid.
248
+ resample (CropResample, optional): Resampling method: 'near', 'bilinear', 'cubic', 'lanczos', etc. default: CropResample.near
249
+ out_dtype (AnalysisDataType, optional): Data type for the output raster (e.g., int16, float32). default: AnalysisDataType.float32
250
+ dst_nodata (int, optional): NoData value for the output raster. default = -9999
251
+ user_id (int, optional): specific user. priviledges required!
252
+
253
+ Returns:
254
+ Task: task instance of the process
255
+
256
+ Example:
257
+ >>> from geobox import GeoboxClient
258
+ >>> from geobox.raster_analysis import Analysis
259
+ >>> client = GeoboxClient()
260
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
261
+ >>> task = Analysis.calculator(api=client, variables={"NIR": "raster_uuid_1", "RED": "raster_uuid_2"},
262
+ ... expr='where(SLOPE>30,1,0)',
263
+ ... output_raster_name='test')
264
+ """
265
+ endpoint = f'{cls.BASE_ENDPOINT}calculator/'
266
+
267
+ data = clean_data({
268
+ 'variables': variables,
269
+ 'expr': expr,
270
+ 'output_raster_name': output_raster_name,
271
+ 'match_raster_uuid': match_raster_uuid,
272
+ 'resample': resample.value,
273
+ 'out_dtype': out_dtype.value,
274
+ 'dst_nodata': dst_nodata,
275
+ 'user_id': user_id
276
+ })
277
+
278
+ response = api.post(endpoint=endpoint, payload=data, is_json=False)
279
+ return api.get_task(response['task_id'])
280
+
281
+
282
+ @classmethod
283
+ def slope(cls,
284
+ api: 'GeoboxClient',
285
+ raster: 'Raster',
286
+ output_raster_name: str,
287
+ slope_units: SlopeUnit = SlopeUnit.degree,
288
+ algorithm: AnalysisAlgorithm = AnalysisAlgorithm.Horn,
289
+ scale: int = 1,
290
+ compute_edges: bool = True,
291
+ nodata_out: int = -9999,
292
+ user_id: Optional[int] = None) -> 'Task':
293
+ """
294
+ Calculate slope from a DEM raster.
295
+
296
+ This endpoint creates a slope raster from a Digital Elevation Model (DEM). Only users with Publisher role or higher can perform this operation.
297
+
298
+ Args:
299
+ api (GeoboxClient): The GeoboxClient instance for making requests
300
+ raster (Raster): DEM Raster instance
301
+ output_raster_name (str): Name for the output raster dataset.
302
+ slope_units (SlopeUnit, optional): Slope units: 'degree' or 'percent'. default: SlopeUnit.degree
303
+ algorithm (AnalysisAlgorithm, optional): Algorithm: 'Horn' or 'ZevenbergenThorne'. default: AnalysisAlgorithm.Horn
304
+ scale (int, optional): Ratio of vertical units to horizontal units. default: 1
305
+ compute_edges (bool, optional): Whether to compute edges. default: True
306
+ nodata (int, optional): NoData value for the output raster. default = -9999
307
+ user_id (int, optional): specific user. priviledges required!
308
+
309
+ Returns:
310
+ Task: task instance of the process
311
+
312
+ Example:
313
+ >>> from geobox import GeoboxClient
314
+ >>> from geobox.raster_analysis import Analysis
315
+ >>> client = GeoboxClient()
316
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
317
+ >>> task = Analysis.slope(api=client, raster=raster, output_raster_name='test')
318
+ or
319
+ >>> task = raster.slope(output_raster_name='test')
320
+ """
321
+ endpoint = f'{cls.BASE_ENDPOINT}slope/'
322
+
323
+ data = clean_data({
324
+ 'raster_uuid': raster.uuid,
325
+ 'output_raster_name': output_raster_name,
326
+ 'slope_units': slope_units.value,
327
+ 'algorithm': algorithm.value,
328
+ 'scale': scale,
329
+ 'compute_edges': compute_edges,
330
+ 'nodata_out': nodata_out,
331
+ 'user_id': user_id
332
+ })
333
+
334
+ response = api.post(endpoint=endpoint, payload=data, is_json=False)
335
+ return api.get_task(response['task_id'])
336
+
337
+
338
+ @classmethod
339
+ def aspect(cls,
340
+ api: 'GeoboxClient',
341
+ raster: 'Raster',
342
+ output_raster_name: str,
343
+ algorithm: AnalysisAlgorithm = AnalysisAlgorithm.Horn,
344
+ trigonometric: bool = False,
345
+ zero_for_flat: bool = True,
346
+ compute_edges: bool = True,
347
+ nodata_out: int = -9999,
348
+ user_id: Optional[int] = None) -> 'Task':
349
+ """
350
+ Calculate aspect from a DEM raster.
351
+
352
+ it creates an aspect raster (degrees 0–360) from a Digital Elevation Model (DEM).
353
+ Only users with Publisher role or higher can perform this operation.
354
+
355
+ Args:
356
+ api (GeoboxClient): The GeoboxClient instance for making requests
357
+ raster (Raster): DEM Raster instance
358
+ output_raster_name (str): Name for the output raster dataset.
359
+ algorithm (AnalysisAlgorithm, optional): Algorithm: 'Horn' or 'ZevenbergenThorne'. default: AnalysisAlgorithm.Horn
360
+ trigonometric (bool, optional): False: azimuth (0°=N, 90°=E, clockwise); True: 0°=E, counter-clockwise. default: False
361
+ zero_for_flat (bool, optional): Set flats (slope==0) to 0 instead of NoData. default: True
362
+ compute_edges (bool, optional): Whether to compute edges. default: True
363
+ nodata (int, optional): NoData value for the output raster. default = -9999
364
+ user_id (int, optional): specific user. priviledges required!
365
+
366
+ Returns:
367
+ Task: task instance of the process
368
+
369
+ Example:
370
+ >>> from geobox import GeoboxClient
371
+ >>> from geobox.raster_analysis import Analysis
372
+ >>> client = GeoboxClient()
373
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
374
+ >>> task = Analysis.aspect(api=client, raster=raster, output_raster_name='test')
375
+ or
376
+ >>> task = raster.aspect(output_raster_name='test')
377
+ """
378
+
379
+ endpoint = f'{cls.BASE_ENDPOINT}aspect/'
380
+
381
+ data = clean_data({
382
+ 'raster_uuid': raster.uuid,
383
+ 'output_raster_name': output_raster_name,
384
+ 'algorithm': algorithm.value,
385
+ 'trigonometric': trigonometric,
386
+ 'zero_for_flat': zero_for_flat,
387
+ 'compute_edges': compute_edges,
388
+ 'nodata_out': nodata_out,
389
+ 'user_id': user_id
390
+ })
391
+
392
+ response = api.post(endpoint=endpoint, payload=data, is_json=False)
393
+ return api.get_task(response['task_id'])
394
+
395
+
396
+ @classmethod
397
+ def reclassify(cls,
398
+ api: 'GeoboxClient',
399
+ raster: 'Raster',
400
+ output_raster_name: str,
401
+ rules: str,
402
+ default_value: Optional[int] = None,
403
+ nodata_in: int = -9999,
404
+ nodata_out: int = -9999,
405
+ out_dtype: AnalysisDataType = AnalysisDataType.int16,
406
+ inclusive: RangeBound = RangeBound.left,
407
+ user_id: Optional[int] = None) -> 'Task':
408
+ """
409
+ Reclassify a raster using value mapping or class breaks.
410
+
411
+ This endpoint reclassifies raster values according to specified rules.
412
+ Only users with Publisher role or higher can perform this operation.
413
+
414
+ Args:
415
+ api (GeoboxClient): The GeoboxClient instance for making requests
416
+ raster (Raster): Raster instance
417
+ output_raster_name (str): Name for the output reclassified raster dataset.
418
+ rules (str): JSON string containing reclassification rules.
419
+ For mode='exact', it should be a dict {old_value: new_value}.
420
+ For mode='range', it should be a list of (low, high, new_value).
421
+ Example for mode='exact': '{"1": 10, "2": 20, "3": 30}'.
422
+ Example for mode='range': '[[0, 10, 1], [10, 20, 2], [20, 30, 3]]'.
423
+ the method would detect the mode type based on the rules input.
424
+ default_value (str, optional): Value to assign when a pixel matches no rule.
425
+ nodata_in (int, optional): NoData of input. If None, tries to get from the input raster.
426
+ nodata_out (int, optional): NoData value to set on output band.
427
+ out_dtype (AnalysisDataType, optional): Output data type. default: AnalysisDataType.int16
428
+ inclusive (RangeBound, optional): Range bound semantics for mode='range': 'left', 'right', 'both', 'neither'. default: RangeBound.left
429
+ user_id (int, optional): specific user. priviledges required!
430
+
431
+ Returns:
432
+ Task: task instance of the process
433
+
434
+ Example:
435
+ >>> from geobox import GeoboxClient
436
+ >>> from geobox.raster_analysis import Analysis
437
+ >>> client = GeoboxClient()
438
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
439
+ >>> task = Analysis.reclassify(api=client, raster=raster, output_raster_name='test', rules='{"1": 10, "2": 20, "3": 30}')
440
+ or
441
+ >>> task = raster.reclassify(output_raster_name='test', rules='{"1": 10, "2": 20, "3": 30}')
442
+ """
443
+ endpoint = f'{cls.BASE_ENDPOINT}reclassify/'
444
+
445
+ data = clean_data({
446
+ 'raster_uuid': raster.uuid,
447
+ 'output_raster_name': output_raster_name,
448
+ 'rules': rules,
449
+ 'mode': 'exact' if isinstance(rules, dict) else 'range' if isinstance(rules, list) else None,
450
+ 'default_value': default_value,
451
+ 'nodata_in': nodata_in,
452
+ 'nodata_out': nodata_out,
453
+ 'out_dtype': out_dtype.value,
454
+ 'inclusive': inclusive.value,
455
+ 'user_id': user_id
456
+ })
457
+
458
+ response = api.post(endpoint=endpoint, payload=data, is_json=False)
459
+ return api.get_task(response['task_id'])
460
+
461
+
462
+ @classmethod
463
+ def resample(cls,
464
+ api: 'GeoboxClient',
465
+ raster: 'Raster',
466
+ output_raster_name: str,
467
+ out_res: Optional[str] = None,
468
+ scale_factor: Optional[str] = None,
469
+ match_raster_uuid: Optional[str] = None,
470
+ resample_method: AnalysisResampleMethod = AnalysisResampleMethod.near,
471
+ dst_nodata: int = -9999,
472
+ user_id: Optional[int] = None) -> 'Task':
473
+ """
474
+ Resample a raster to a different resolution.
475
+
476
+ it resamples a raster using GDAL Warp.
477
+ Exactly one of out_res, scale_factor, or match_raster_uuid must be provided.
478
+ Only users with Publisher role or higher can perform this operation.
479
+
480
+ Args:
481
+ api (GeoboxClient): The GeoboxClient instance for making requests
482
+ raster (Raster): Raster instance
483
+ output_raster_name (str): Name for the output reclassified raster dataset.
484
+ out_res (str, optional): Output resolution as 'x_res,y_res' (e.g., '10,10').
485
+ scale_factor (int, optional): Scale factor (e.g., 2.0 for 2x finer resolution).
486
+ match_raster_uuid (str, optional): UUID of reference raster to match resolution/extent.
487
+ resample_method (AnalysisResampleMethod, optional): Resampling method: 'near', 'bilinear', 'cubic', 'lanczos', etc.
488
+ dst_nodata (int, optional): Output NoData value.
489
+ user_id (int, optional): specific user. priviledges required!
490
+
491
+ Returns:
492
+ Task: task instance of the process
493
+
494
+ Example:
495
+ >>> from geobox import GeoboxClient
496
+ >>> from geobox.raster_analysis import Analysis
497
+ >>> client = GeoboxClient()
498
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
499
+ >>> task = Analysis.resample(api=client, raster=raster, output_raster_name='test', out_res='10,10')
500
+ or
501
+ >>> task = raster.resample(output_raster_name='test', out_res='10,10')
502
+ """
503
+ if sum(x is not None for x in [out_res, scale_factor, match_raster_uuid]) != 1:
504
+ raise ValueError('Exactly one of out_res, scale_factor, or match_raster_uuid must be provided!')
505
+
506
+ endpoint = f'{cls.BASE_ENDPOINT}resample/'
507
+
508
+ data = clean_data({
509
+ 'raster_uuid': raster.uuid,
510
+ 'output_raster_name': output_raster_name,
511
+ 'out_res': out_res,
512
+ 'scale_factor': scale_factor,
513
+ 'match_raster_uuid': match_raster_uuid,
514
+ 'resample_method': resample_method.value,
515
+ 'dst_nodata': dst_nodata,
516
+ 'user_id': user_id
517
+ })
518
+
519
+ response = api.post(endpoint=endpoint, payload=data, is_json=False)
520
+ return api.get_task(response['task_id'])
521
+
522
+
523
+ @classmethod
524
+ def idw_interpolation(cls,
525
+ api: 'GeoboxClient',
526
+ layer: Union[VectorLayer, VectorLayerView],
527
+ output_raster_name: str,
528
+ z_field: Field,
529
+ match_raster_uuid: Optional[str] = None,
530
+ pixel_size: int = 10,
531
+ extent: Optional[str] = None,
532
+ power: float = 2.0,
533
+ smoothing: float = 0.0,
534
+ max_points: int = 16,
535
+ radius: int = 1000,
536
+ nodata: int = -9999,
537
+ out_dtype: AnalysisDataType = AnalysisDataType.float32,
538
+ user_id: Optional[int] = None) -> 'Task':
539
+ """
540
+ Create an IDW (Inverse Distance Weighting) interpolation raster from point data.
541
+
542
+ it creates a raster using IDW interpolation from point data in a vector layer.
543
+ Only users with Publisher role or higher can perform this operation.
544
+
545
+ Args:
546
+ api (GeoboxClient): The GeoboxClient instance for making requests
547
+ layer (VectorLayer | VectorLayerview): layer containing point data
548
+ output_raster_name (str): Name for the output IDW raster dataset.
549
+ z_field (Field): the field containing the values to interpolate.
550
+ match_raster_uuid (str, optional): UUID of reference raster to match resolution/extent.
551
+ pixel_size (int, optional): Pixel size for the output raster. default: 10
552
+ extent (str, optional): Extent as 'minX,minY,maxX,maxY'.
553
+ power (float, optional): Power parameter for IDW. default: 2.0
554
+ smoothing (float, optional): Smoothing parameter for IDW. default: 0.0
555
+ max_points (int, optional): Maximum number of neighbors to use. default: 16
556
+ radius (int, optional): Search radius in map units. default: 1000
557
+ nodata (int, optional): NoData value for the output raster. default: -9999
558
+ out_dtype (AnalysisDataType, optional): Output data type.
559
+ user_id (int, optional): specific user. priviledges required!
560
+
561
+ Returns:
562
+ Task: task instance of the process
563
+
564
+ Example:
565
+ >>> from geobox import GeoboxClient
566
+ >>> from geobox.raster_analysis import Analysis
567
+ >>> client = GeoboxClient()
568
+ >>> vector = client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
569
+ >>> field = vector.get_field_by_name('field_name')
570
+ >>> task = Analysis.idw(api=client, layer=vector, output_raster_name='test', z_field=field)
571
+ or
572
+ >>> task = vector.idw(output_raster_name='test', z_field=field)
573
+ """
574
+ endpoint = f'{cls.BASE_ENDPOINT}idw/'
575
+
576
+ data = clean_data({
577
+ 'layer_uuid': layer.uuid,
578
+ 'output_raster_name': output_raster_name,
579
+ 'z_field': z_field.name,
580
+ 'is_view': False if isinstance(layer, VectorLayer) else True,
581
+ 'match_raster_uuid': match_raster_uuid,
582
+ 'pixel_size': pixel_size,
583
+ 'extent': extent,
584
+ 'power': power,
585
+ 'smoothing': smoothing,
586
+ 'max_points': max_points,
587
+ 'radius': radius,
588
+ 'nodata': nodata,
589
+ 'out_dtype': out_dtype.value,
590
+ 'user_id': user_id
591
+ })
592
+
593
+ response = api.post(endpoint=endpoint, payload=data, is_json=False)
594
+ return api.get_task(response['task_id'])
595
+
596
+
597
+ @classmethod
598
+ def constant(cls,
599
+ api: 'GeoboxClient',
600
+ output_raster_name: str,
601
+ extent: str,
602
+ value : int,
603
+ pixel_size: int = 10,
604
+ dtype: AnalysisDataType = AnalysisDataType.float32,
605
+ nodata: int = -9999,
606
+ align_to: Optional[str] = None,
607
+ user_id: Optional[int] = None) -> 'Task':
608
+ """
609
+ Create a raster filled with a constant value.
610
+
611
+ This endpoint creates a north-up GeoTIFF filled with a constant value.
612
+ Only users with Publisher role or higher can perform this operation.
613
+
614
+ Args:
615
+ api (GeoboxClient): The GeoboxClient instance for making requests
616
+ output_raster_name (str): Name for the output constant raster dataset.
617
+ extent (str): Extent as 'minX,minY,maxX,maxY' (e.g., '0,0,100,100').
618
+ value (int): Constant value to fill the raster with.
619
+ pixel_size (int, optional): Pixel size for the output raster (must be > 0). default: 10
620
+ dtype (AnalysisDataType, optoinal): Output data type. default: AnalysisDataType.float32
621
+ nodata (int, optional): NoData value for the raster. default: -9999
622
+ align_to (str, optional): Grid origin to snap to as 'x0,y0' (e.g., '0,0').
623
+ user_id (int, optional): specific user. priviledges required!
624
+
625
+ Returns:
626
+ Task: task instance of the process
627
+
628
+ Example:
629
+ >>> from geobox import GeoboxClient
630
+ >>> from geobox.raster_analysis import Analysis
631
+ >>> client = GeoboxClient()
632
+ >>> task = Analysis.constant(api=client, output_raster_name='test', extent='0,0,100,100', value=10)
633
+ """
634
+ endpoint = f'{cls.BASE_ENDPOINT}constant/'
635
+
636
+ data = clean_data({
637
+ 'output_raster_name': output_raster_name,
638
+ 'extent': extent,
639
+ 'value': value,
640
+ 'pixel_size': pixel_size,
641
+ 'dtype': dtype.value,
642
+ 'nodata': nodata,
643
+ 'align_to': align_to,
644
+ 'user_id': user_id
645
+ })
646
+
647
+ response = api.post(endpoint=endpoint, payload=data, is_json=False)
648
+ return api.get_task(response['task_id'])
649
+
650
+
651
+ @classmethod
652
+ def fill_nodata(cls,
653
+ api: 'GeoboxClient',
654
+ raster: 'Raster',
655
+ output_raster_name: str,
656
+ band: Union[int, str] = 1,
657
+ nodata: Optional[int] = None,
658
+ max_search_dist: Optional[int] = None,
659
+ smoothing_iterations: Optional[int] = None,
660
+ mask_raster_uuid: Optional[str] = None,
661
+ user_id: Optional[int] = None) -> 'Task':
662
+ """
663
+ Fill NoData regions in a raster using GDAL's FillNodata algorithm.
664
+
665
+ it fills gaps (NoData regions) in a raster by interpolating values from surrounding valid pixels.
666
+ This is commonly used for data cleaning and gap filling in remote sensing and elevation data.
667
+ Only users with Publisher role or higher can perform this operation.
668
+
669
+ Args:
670
+ api (GeoboxClient): The GeoboxClient instance for making requests
671
+ raster (Raster): the input raster to fill NoData regions in
672
+ output_raster_name (str): Name for the output filled raster dataset.
673
+ band (int | str): 1-based band index to process or 'all' to process all bands. default: 1
674
+ nodata (int, optional): NoData value to use. If None, uses the band's existing NoData.
675
+ max_search_dist (int, optoinal): Maximum distance in pixels to search for valid data.
676
+ smoothing_iterations (int, optional): Number of smoothing iterations to apply.
677
+ mask_raster_uuid (str, optional): Optional UUID of a mask raster (0=masked, >0=valid).
678
+ user_id (int, optional): specific user. priviledges required!
679
+
680
+ Returns:
681
+ Task: task instance of the process
682
+
683
+ Example:
684
+ >>> from geobox import GeoboxClient
685
+ >>> from geobox.raster_analysis import Analysis
686
+ >>> client = GeoboxClient()
687
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
688
+ >>> task = Analysis.fill_nodata(api=client, raster=raster, output_raster_name='test')
689
+ or
690
+ >>> task = raster.fill_nodata(output_raster_name='test')
691
+ """
692
+ endpoint = f'{cls.BASE_ENDPOINT}fill/'
693
+
694
+ data = clean_data({
695
+ 'raster_uuid': raster.uuid,
696
+ 'output_raster_name': output_raster_name,
697
+ 'band': band,
698
+ 'nodata': nodata,
699
+ 'max_search_dist': max_search_dist,
700
+ 'smoothing_iterations': smoothing_iterations,
701
+ 'mask_raster_uuid': mask_raster_uuid,
702
+ 'user_id': user_id
703
+ })
704
+
705
+ response = api.post(endpoint=endpoint, payload=data, is_json=False)
706
+ return api.get_task(response['task_id'])
707
+
708
+
709
+ @classmethod
710
+ def proximity(cls,
711
+ api: 'GeoboxClient',
712
+ raster: 'Raster',
713
+ output_raster_name: str,
714
+ dist_units: DistanceUnit = DistanceUnit.GEO,
715
+ burn_value: int = 1,
716
+ nodata: int = -9999,
717
+ user_id: Optional[int] = None) -> 'Task':
718
+ """
719
+ Create a proximity (distance) raster from a raster layer.
720
+
721
+ it creates a raster showing the distance from each pixel to the nearest pixel in the input raster layer.
722
+ Only users with Publisher role or higher can perform this operation.
723
+
724
+ Args:
725
+ api (GeoboxClient): The GeoboxClient instance for making requests
726
+ raster (Raster): the raster layer to create proximity raster from.
727
+ output_raster_name (str): Name for the output proximity raster dataset.
728
+ dist_units (DistanceUnit, optional): Distance units: 'GEO' for georeferenced units, 'PIXEL' for pixels. default: DistanceUnit.GEO
729
+ burn_value (int, optional): Value treated as targets (distance 0). default: 1
730
+ nodata (int, optional): NoData value to use in the output raster. default: -9999
731
+ user_id (int, optional): specific user. priviledges required!
732
+
733
+ Returns:
734
+ Task: task instance of the process
735
+
736
+ Example:
737
+ >>> from geobox import GeoboxClient
738
+ >>> from geobox.raster_analysis import Analysis
739
+ >>> client = GeoboxClient()
740
+ >>> raster = client.get_raster(uuid="12345678-1234-5678-1234-567812345678")
741
+ >>> task = Analysis.proximity(api=client, raster=raster, output_raster_name='test')
742
+ or
743
+ >>> task = raster.proximity(output_raster_name='test')
744
+ """
745
+ endpoint = f'{cls.BASE_ENDPOINT}proximity/'
746
+
747
+ data = clean_data({
748
+ 'raster_uuid': raster.uuid,
749
+ 'output_raster_name': output_raster_name,
750
+ 'dist_units': dist_units.value,
751
+ 'burn_value': burn_value,
752
+ 'nodata': nodata,
753
+ 'user_id': user_id
754
+ })
755
+
756
+ response = api.post(endpoint=endpoint, payload=data, is_json=False)
757
+ return api.get_task(response['task_id'])