geobox 1.2.1__py3-none-any.whl → 1.2.3__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/api.py CHANGED
@@ -31,7 +31,7 @@ from .basemap import Basemap
31
31
  from .attachment import Attachment, AttachmentResourceType
32
32
  from .apikey import ApiKey
33
33
  from .log import Log
34
- from .usage import Usage
34
+ from .usage import Usage, UsageScale, UsageParam
35
35
 
36
36
  logger = logging.getLogger(__name__)
37
37
 
@@ -500,6 +500,7 @@ class GeoboxClient:
500
500
  display_name: str = None,
501
501
  description: str = None,
502
502
  has_z: bool = False,
503
+ temporary: bool = False,
503
504
  fields: List = None) -> 'VectorLayer':
504
505
  """
505
506
  Create a new vector layer.
@@ -510,6 +511,7 @@ class GeoboxClient:
510
511
  display_name (str, optional): A human-readable name for the layer. default is None.
511
512
  description (str, optional): A description of the layer. default is None.
512
513
  has_z (bool, optional): Whether the layer includes Z coordinates. default is False.
514
+ temporary (bool, optional): Whether to create a temporary layer. temporary layers will be deleted after 24 hours. default is False.
513
515
  fields (List, optional): List of field definitions for the layer. default is None.
514
516
 
515
517
  Returns:
@@ -528,7 +530,7 @@ class GeoboxClient:
528
530
  ... has_z=False,
529
531
  ... fields=[{"name": "my_field", "datatype": "FieldTypeString"}])
530
532
  """
531
- return VectorLayer.create_vector(self, name=name, layer_type=layer_type, display_name=display_name, description=description, has_z=has_z, fields=fields)
533
+ return VectorLayer.create_vector(self, name=name, layer_type=layer_type, display_name=display_name, description=description, has_z=has_z, temporary=temporary, fields=fields)
532
534
 
533
535
 
534
536
  def get_vector_by_name(self, name: str, user_id: int = None) -> Union['VectorLayer', None]:
@@ -760,7 +762,7 @@ class GeoboxClient:
760
762
  return VectorLayerView.get_view(self, uuid)
761
763
 
762
764
 
763
- def get_view_by_name(self, name: str, user_id: int = None) -> Union['View', None]:
765
+ def get_view_by_name(self, name: str, user_id: int = None) -> Union['VectorLayerView', None]:
764
766
  """
765
767
  Get a view by name
766
768
 
@@ -769,7 +771,7 @@ class GeoboxClient:
769
771
  user_id (int, optional): specific user. privileges required.
770
772
 
771
773
  Returns:
772
- View | None: returns the view if a view matches the given name, else None
774
+ VectorLayerView | None: returns the view if a view matches the given name, else None
773
775
 
774
776
  Example:
775
777
  >>> from geobox import GeoboxClient
geobox/enums.py CHANGED
@@ -134,20 +134,11 @@ class LayerType(Enum):
134
134
  Point = "Point"
135
135
  """A single point in 2D space"""
136
136
 
137
- LineString = "LineString"
138
- """A sequence of points forming a line"""
139
-
140
- Polygon = "Polygon"
141
- """A closed shape defined by a sequence of points"""
142
-
143
137
  MultiPoint = "MultiPoint"
144
138
  """A collection of points"""
145
139
 
146
- MultiLineString = "MultiLineString"
147
- """A collection of lines"""
148
-
149
- MultiPolygon = "MultiPolygon"
150
- """A collection of polygons"""
140
+ Polygon = "Polygon"
141
+ """A closed shape defined by a sequence of points"""
151
142
 
152
143
  Polyline = "Polyline"
153
144
  """A polyline geometry"""
@@ -163,25 +154,22 @@ class FeatureType(Enum):
163
154
  """
164
155
  Point = "Point"
165
156
  """A single point in 2D space"""
157
+
158
+ MultiPoint = "MultiPoint"
159
+ """A collection of points"""
166
160
 
167
161
  LineString = "LineString"
168
162
  """A sequence of points forming a line"""
163
+
164
+ MultiLineString = "MultiLineString"
165
+ """A collection of lines"""
169
166
 
170
167
  Polygon = "Polygon"
171
168
  """A closed shape defined by a sequence of points"""
172
169
 
173
- MultiPoint = "MultiPoint"
174
- """A collection of points"""
175
-
176
- MultiLineString = "MultiLineString"
177
- """A collection of lines"""
178
-
179
170
  MultiPolygon = "MultiPolygon"
180
171
  """A collection of polygons"""
181
172
 
182
- Polyline = "Polyline"
183
- """A polyline geometry"""
184
-
185
173
 
186
174
  # Task Enums
187
175
  class TaskStatus(Enum):
geobox/field.py CHANGED
@@ -258,7 +258,7 @@ class Field(Base):
258
258
  Get the statistic of the field.
259
259
 
260
260
  Args:
261
- func (str): The function to apply to the field.
261
+ func (str): The function to apply to the field. values are: min, max, avg
262
262
 
263
263
  Returns:
264
264
  Dict: The response data.
@@ -269,7 +269,7 @@ class Field(Base):
269
269
  >>> client = GeoboxClient()
270
270
  >>> layer = client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
271
271
  >>> field = layer.get_field(name='test')
272
- >>> field.get_field_statistic()
272
+ >>> field.get_field_statistic(func='avg')
273
273
  """
274
274
  endpoint = urljoin(self.endpoint, f'stats/?func_type={func}')
275
275
  return self.layer.api.get(endpoint)
geobox/file.py CHANGED
@@ -2,13 +2,16 @@ from urllib.parse import urljoin
2
2
  from typing import Optional, Dict, List, Union, TYPE_CHECKING
3
3
  import os
4
4
  import mimetypes
5
+ from geobox.exception import ValidationError
5
6
  import requests
6
7
  import sys
8
+ from pathlib import Path
7
9
 
8
10
  from .base import Base
9
11
  from .enums import FileFormat, PublishFileType, InputGeomType, FileType
10
12
  from .utils import clean_data
11
13
  from .task import Task
14
+ from .feature import Feature
12
15
 
13
16
  if TYPE_CHECKING:
14
17
  from . import GeoboxClient
@@ -221,9 +224,9 @@ class File(Base):
221
224
  >>> from geobox import GeoboxClient
222
225
  >>> from geobox.file import File
223
226
  >>> client = GeoboxClient()
224
- >>> files = File.get_file_by_name(client, name='test')
227
+ >>> files = File.get_files_by_name(client, name='test')
225
228
  or
226
- >>> files = client.get_file_by_name(name='test')
229
+ >>> files = client.get_files_by_name(name='test')
227
230
  """
228
231
  return cls.get_files(api, q=f"name = '{name}'", user_id=user_id)
229
232
 
@@ -248,44 +251,34 @@ class File(Base):
248
251
  >>> client = GeoboxClient()
249
252
  >>> file_path = File.get_file(client, uuid="12345678-1234-5678-1234-567812345678")
250
253
  """
251
- # Get the original filename from data or use uuid
252
- if self.name:
253
- filename = f"{self.name.split('.')[0]}" if len(self.name.split('.')) > 1 else f'{self.name}'
254
- else:
255
- filename = f'{self.uuid}'
256
-
257
254
  # If save_path is provided, check if it ends with a '/'
258
255
  if save_path and save_path.endswith('/'):
259
- return f'{save_path}{filename}'
256
+ return f'{save_path}'
260
257
 
261
258
  if save_path and not save_path.endswith('/'):
262
259
  raise ValueError("save_path must end with a '/'")
263
260
 
264
- return os.path.join(os.getcwd(), filename)
261
+ return os.getcwd()
265
262
 
266
263
 
267
- def _get_file_ext(self, response: requests.Response) -> str:
264
+ def _get_file_name(self, response: requests.Response) -> str:
268
265
  """
269
- Get the file extension of the response.
266
+ Get the file name from the response.
270
267
 
271
268
  Args:
272
269
  response (requests.Response): The response of the request.
273
270
 
274
271
  Returns:
275
- str: The file extension of the response.
272
+ str: The file name
276
273
  """
277
- ext = ""
278
- if 'Content-Disposition' in response.headers:
279
- content_disposition = response.headers['Content-Disposition']
280
- if 'filename=' in content_disposition:
281
- filename = content_disposition.split('filename=')[-1].strip().strip('"')
282
- ext = f".{filename.split('.')[-1]}"
274
+ if 'Content-Disposition' in response.headers and 'filename=' in response.headers['Content-Disposition']:
275
+ file_name = response.headers['Content-Disposition'].split('filename=')[-1].strip().strip('"')
283
276
 
284
- else:
285
- content_type = response.headers.get("Content-Type", "")
286
- ext = mimetypes.guess_extension(content_type.split(";")[0])
277
+ else:
278
+ content_type = response.headers.get("Content-Type", "")
279
+ file_name = f'{self.name}.{mimetypes.guess_extension(content_type.split(";")[0])}'
287
280
 
288
- return ext
281
+ return file_name
289
282
 
290
283
 
291
284
  def _create_progress_bar(self) -> 'tqdm':
@@ -338,20 +331,20 @@ class File(Base):
338
331
  save_path = self._get_save_path(save_path)
339
332
  os.makedirs(os.path.dirname(save_path), exist_ok=True)
340
333
 
341
- with self.api.get(f"{self.endpoint}download/", stream=True) as response, \
342
- open(save_path, 'wb') as f:
343
- pbar = self._create_progress_bar() if progress_bar else None
344
- for chunk in response.iter_content(chunk_size=8192):
345
- f.write(chunk)
334
+ with self.api.get(f"{self.endpoint}download/", stream=True) as response:
335
+ file_name = self._get_file_name(response)
336
+ full_path = f"{save_path}/{file_name}"
337
+ with open(full_path, 'wb') as f:
338
+ pbar = self._create_progress_bar() if progress_bar else None
339
+ for chunk in response.iter_content(chunk_size=8192):
340
+ f.write(chunk)
341
+ if pbar:
342
+ pbar.update(len(chunk))
343
+ pbar.refresh()
346
344
  if pbar:
347
- pbar.update(len(chunk))
348
- pbar.refresh()
349
- if pbar:
350
- pbar.close()
345
+ pbar.close()
351
346
 
352
- final_path = os.path.abspath(save_path) + self._get_file_ext(response)
353
- os.rename(os.path.abspath(save_path), os.path.abspath(final_path))
354
- return os.path.abspath(final_path)
347
+ return os.path.abspath(full_path)
355
348
 
356
349
 
357
350
  def delete(self) -> None:
@@ -378,8 +371,8 @@ class File(Base):
378
371
  input_layer: str = None,
379
372
  input_dataset: str = None,
380
373
  user_id: int = None,
381
- input_srid: int = None,
382
- file_encoding: str = "utf-8",
374
+ input_srid: int = Feature.BASE_SRID,
375
+ file_encoding: str = "UTF-8",
383
376
  replace_domain_codes_by_values: bool = False,
384
377
  report_errors: bool = True,
385
378
  as_terrain: bool = False) -> 'Task':
@@ -393,7 +386,7 @@ class File(Base):
393
386
  input_layer (str, optional): The name of the input layer.
394
387
  input_dataset (str, optional): The name of the input dataset.
395
388
  user_id (int, optional): Specific user. privileges required.
396
- input_srid (int, optional): The SRID of the layer.
389
+ input_srid (int, optional): The SRID of the layer. default is: 3857
397
390
  file_encoding (str, optional): The encoding of the file. default is "utf-8".
398
391
  replace_domain_codes_by_values (bool, optional): Whether to replace domain codes by values. default is False.
399
392
  report_errors (bool, optional): Whether to report errors. default is True.
@@ -404,6 +397,7 @@ class File(Base):
404
397
 
405
398
  Raises:
406
399
  ValueError: If the publish_as is not a valid PublishFileType.
400
+ ValidationError: if the zipped file doesn't have any layers to publish.
407
401
 
408
402
  Example:
409
403
  >>> from geobox import GeoboxClient
@@ -416,30 +410,41 @@ class File(Base):
416
410
  ... input_layer='layer1',
417
411
  ... input_dataset='dataset1',
418
412
  ... input_srid=4326,
419
- ... file_encoding='utf-8')
413
+ ... file_encoding='UTF-8')
420
414
  """
421
415
  if not publish_as:
422
- if self.file_type.value in ['GeoJSON', 'GPKG', 'DXF', 'GPX', 'Shapefile', 'KML', 'CSV', 'FileGDB']:
416
+ # checks the file format or file first layer format to dynamically set the publish_as
417
+ if self.file_type.value in ['GeoJSON', 'GPKG', 'DXF', 'GPX', 'Shapefile', 'KML', 'CSV', 'FileGDB'] or \
418
+ (self.file_type.value in ['Complex'] and self.layers and \
419
+ FileType(self.layers[0]['format']).value in ['GeoJSON', 'GPKG', 'DXF', 'GPX', 'Shapefile', 'KML', 'CSV', 'FileGDB']):
423
420
  publish_as = PublishFileType.VECTOR
424
- elif self.file_type.value in ['GeoTIFF']:
421
+
422
+ elif self.file_type.value in ['GeoTIFF'] or \
423
+ (self.file_type.value in ['Complex'] and self.layers and \
424
+ FileType(self.layers[0]['format']).value in ['GeoTIFF']):
425
425
  publish_as = PublishFileType.RASTER
426
- elif self.file_type.value in ['GLB']:
426
+
427
+ elif self.file_type.value in ['GLB'] or \
428
+ (self.file_type.value in ['Complex'] and self.layers and \
429
+ FileType(self.layers[0]['format']).value in ['GLB']):
427
430
  publish_as = PublishFileType.MODEL3D
428
431
 
432
+ else:
433
+ raise ValidationError('Unknown format')
434
+
429
435
  data = clean_data({
430
436
  "publish_as": publish_as.value if isinstance(publish_as, PublishFileType) else publish_as,
431
437
  "layer_name": name,
432
- "input_layer": input_layer,
438
+ "input_layer": self.layers[0]['layer'] if not input_layer and self.layers else input_layer,
433
439
  "input_geom_type": input_geom_type.value if isinstance(input_geom_type, InputGeomType) else input_geom_type,
434
440
  "replace_domain_codes_by_values": replace_domain_codes_by_values,
435
- "input_dataset": self.name if not input_dataset else input_dataset,
441
+ "input_dataset": self.layers[0]['dataset'] if not input_layer and self.layers else input_dataset,
436
442
  "user_id": user_id,
437
443
  "input_srid": input_srid,
438
444
  "file_encoding": file_encoding,
439
445
  "report_errors": report_errors,
440
446
  "as_terrain": as_terrain
441
447
  })
442
-
443
448
  endpoint = urljoin(self.endpoint, 'publish/')
444
449
  response = self.api.post(endpoint, data, is_json=False)
445
450
  task = Task.get_task(self.api, response.get('task_id'))
geobox/map.py CHANGED
@@ -621,31 +621,32 @@ class Map(Base):
621
621
  grid_settings = {'enable_grid', 'grid_unit', 'grid_width', 'grid_height', 'grid_minzoom', 'grid_maxzoom'}
622
622
  view_settings = {'bearing', 'pitch', 'center', 'zoom'}
623
623
 
624
+ settings = self.settings
625
+
624
626
  for key, value in kwargs.items():
625
627
  if key in general_settings:
626
- self.settings['general_settings'][key] = value
628
+ settings['general_settings'][key] = value
627
629
  elif key in edit_settings:
628
- self.settings['edit_settings'][key] = value
630
+ settings['edit_settings'][key] = value
629
631
  elif key in snap_settings:
630
- self.settings['snap_settings'][key] = value
632
+ settings['snap_settings'][key] = value
631
633
  elif key == 'controls':
632
- self.settings['controls'] = value
634
+ settings['controls'] = value
633
635
  elif key in search_settings:
634
- self.settings['search_settings'][key] = value
636
+ settings['search_settings'][key] = value
635
637
  elif key in marker_settings:
636
- self.settings['marker_settings'][key] = value
638
+ settings['marker_settings'][key] = value
637
639
  elif key in terrain_settings:
638
- self.settings['terrain_settings'][key] = value
640
+ settings['terrain_settings'][key] = value
639
641
  elif key in grid_settings:
640
- self.settings['grid_settings'][key] = value
642
+ settings['grid_settings'][key] = value
641
643
  elif key in view_settings:
642
- self.settings['view_settings'][key] = value
644
+ settings['view_settings'][key] = value
643
645
  elif key == 'toc_settings':
644
- self.settings['toc_settings'] = value
646
+ settings['toc_settings'] = value
647
+
648
+ return super()._set_settings(self.endpoint, settings)
645
649
 
646
- endpoint = urljoin(self.endpoint, 'settings/')
647
- self.api.put(endpoint, self.settings)
648
-
649
650
 
650
651
  def get_markers(self) -> Dict:
651
652
  """
geobox/raster.py CHANGED
@@ -608,10 +608,7 @@ class Raster(Base):
608
608
  'min_zoom', 'max_zoom', 'use_cache', 'cache_until_zoom'
609
609
  }
610
610
 
611
- settings = {
612
- 'visual_settings': {},
613
- 'tile_settings': {}
614
- }
611
+ settings = self.settings
615
612
 
616
613
  for key, value in kwargs.items():
617
614
  if key in visual_settings:
@@ -620,7 +617,7 @@ class Raster(Base):
620
617
  settings['tile_settings'][key] = value
621
618
 
622
619
 
623
- super()._set_settings(self.endpoint, settings)
620
+ return super()._set_settings(self.endpoint, settings)
624
621
 
625
622
 
626
623
  def share(self, users: List['User']) -> None:
geobox/task.py CHANGED
@@ -10,6 +10,7 @@ if TYPE_CHECKING:
10
10
  from .vectorlayer import VectorLayer
11
11
  from .raster import Raster
12
12
  from .model3d import Model
13
+ from .file import File
13
14
 
14
15
  class Task(Base):
15
16
  """
@@ -45,9 +46,9 @@ class Task(Base):
45
46
 
46
47
 
47
48
  @property
48
- def publised_asset(self) -> Union['VectorLayer', 'Raster', 'Model', None]:
49
+ def output_asset(self) -> Union['VectorLayer', 'Raster', 'Model', 'File', None]:
49
50
  """
50
- Published asset property
51
+ output asset property
51
52
 
52
53
  Returns:
53
54
  VectorLayer | Raster | Model | None: if task type is publish, it returns the published layer
@@ -57,14 +58,20 @@ class Task(Base):
57
58
  >>> from geobox.task import Task
58
59
  >>> client = GeoboxClient()
59
60
  >>> task = Task.get_task(client, uuid="12345678-1234-5678-1234-567812345678")
60
- >>> task.publised_asset
61
+ >>> task.output_asset
61
62
  """
62
- if self.name == 'import:publish-file-to-raster-dataset':
63
- return self.api.get_raster(uuid=self.data['result']['raster_uuid'])
64
- elif self.name == 'import:publish-file-to-vector-layer':
63
+ if self.data.get('result', {}).get('layer_uuid'):
65
64
  return self.api.get_vector(uuid=self.data['result']['layer_uuid'])
66
- elif self.name == 'import:publish-file-to-3d-model':
65
+
66
+ elif self.data.get('result', {}).get('raster_uuid'):
67
+ return self.api.get_raster(uuid=self.data['result']['raster_uuid'])
68
+
69
+ elif self.data.get('result', {}).get('model_uuid'):
67
70
  return self.api.get_model(uuid=self.data['result']['model_uuid'])
71
+
72
+ elif self.data.get('result', {}).get('file_uuid'):
73
+ return self.api.get_file(uuid=self.data['result']['file_uuid'])
74
+
68
75
  else:
69
76
  return None
70
77
 
geobox/vectorlayer.py CHANGED
@@ -16,6 +16,7 @@ if TYPE_CHECKING:
16
16
  from .api import GeoboxClient
17
17
  from .view import VectorLayerView
18
18
  from .user import User
19
+ from .file import File
19
20
 
20
21
  class VectorLayer(Base):
21
22
  """
@@ -226,6 +227,7 @@ class VectorLayer(Base):
226
227
  display_name: str = None,
227
228
  description: str = None,
228
229
  has_z: bool = False,
230
+ temporary: bool = False,
229
231
  fields: List = None) -> 'VectorLayer':
230
232
  """
231
233
  Create a new vector layer.
@@ -237,6 +239,7 @@ class VectorLayer(Base):
237
239
  display_name (str, optional): A human-readable name for the layer. default is None.
238
240
  description (str, optional): A description of the layer. default is None.
239
241
  has_z (bool, optional): Whether the layer includes Z coordinates. default is False.
242
+ temporary (bool, optional): Whether to create a temporary layer. temporary layers will be deleted after 24 hours. default is False.
240
243
  fields (List, optional): List of field definitions for the layer. default is None.
241
244
 
242
245
  Returns:
@@ -255,6 +258,7 @@ class VectorLayer(Base):
255
258
  ... display_name="My Layer",
256
259
  ... description="This is a description of my layer",
257
260
  ... has_z=False,
261
+ ... temporary=True,
258
262
  ... fields=[{"name": "my_field", "datatype": "FieldTypeString"}])
259
263
  or
260
264
  >>> layer = client.create_vector(name="my_layer",
@@ -262,6 +266,7 @@ class VectorLayer(Base):
262
266
  ... display_name="My Layer",
263
267
  ... description="This is a description of my layer",
264
268
  ... has_z=False,
269
+ ... temporary=True,
265
270
  ... fields=[{"name": "my_field", "datatype": "FieldTypeString"}])
266
271
  """
267
272
  data = {
@@ -270,6 +275,7 @@ class VectorLayer(Base):
270
275
  "display_name": display_name,
271
276
  "description": description,
272
277
  "has_z": has_z,
278
+ "temporary": temporary,
273
279
  "fields": fields
274
280
  }
275
281
  return super()._create(api=api,
@@ -332,7 +338,8 @@ class VectorLayer(Base):
332
338
  Make the layer permanent.
333
339
  """
334
340
  endpoint = urljoin(self.endpoint, 'makePermanent/')
335
- self.api.post(endpoint, is_json=False)
341
+ response = self.api.post(endpoint, is_json=False)
342
+ self._update_properties(response)
336
343
 
337
344
 
338
345
  def share(self, users: List['User']) -> None:
@@ -470,41 +477,56 @@ class VectorLayer(Base):
470
477
  factory_func=lambda api, item: Field(layer=self, data_type=FieldType(item['datatype']), field_id=item['id'], data=item))
471
478
 
472
479
 
473
- def get_field(self, field_id: str = None, name: str = None) -> 'Field':
480
+ def get_field(self, field_id: int) -> 'Field':
474
481
  """
475
482
  Get a specific field by its ID.
476
483
 
477
484
  Args:
478
- field_id (str, optional): The ID of the field to retrieve.
479
- name (str, optional): The name of the field to retrieve.
485
+ field_id (int, optional): The ID of the field to retrieve.
480
486
 
481
487
  Returns:
482
488
  Field: The requested field instance.
483
489
 
484
490
  Raises:
485
- ValueError: Either id or name must be provided.
486
- NotFoundError: If the field with the specified ID or name is not found.
491
+ NotFoundError: If the field with the specified ID is not found.
487
492
 
488
493
  Example:
489
494
  >>> from geobox import GeoboxClient
490
495
  >>> from geobox.vectorlayer import VectorLayer
491
496
  >>> client = GeoboxClient()
492
497
  >>> layer = VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
493
- >>> field = layer.get_field(field_id="1")
498
+ >>> field = layer.get_field(field_id=1)
499
+ """
500
+ field = next((f for f in self.get_fields() if f.id == field_id), None)
501
+ if not field:
502
+ raise NotFoundError(f'Field with ID {field_id} not found in layer {self.name}')
503
+
504
+ return field
505
+
506
+
507
+ def get_field_by_name(self, name: str) -> 'Field':
494
508
  """
495
- if not (field_id or name):
496
- raise ValueError('Either "field_id" or "name" must be provided')
509
+ Get a specific field by its name.
497
510
 
498
- if field_id:
499
- field = next((f for f in self.get_fields() if str(f.id) == str(field_id)), None)
500
- elif name:
501
- field = next((f for f in self.get_fields() if f.name == name), None)
511
+ Args:
512
+ name (str, optional): The name of the field to retrieve.
502
513
 
514
+ Returns:
515
+ Field: The requested field instance.
516
+
517
+ Raises:
518
+ NotFoundError: If the field with the specified name is not found.
519
+
520
+ Example:
521
+ >>> from geobox import GeoboxClient
522
+ >>> from geobox.vectorlayer import VectorLayer
523
+ >>> client = GeoboxClient()
524
+ >>> layer = VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
525
+ >>> field = layer.get_field_by_name(name='test')
526
+ """
527
+ field = next((f for f in self.get_fields() if f.name == name), None)
503
528
  if not field:
504
- if field_id:
505
- raise NotFoundError(f'Field with ID {field_id} not found in layer {self.name}')
506
- elif name:
507
- raise NotFoundError(f'Field with name "{name}" not found in layer {self.name}')
529
+ raise NotFoundError(f"Field with name '{name}' not found in layer {self.name}")
508
530
 
509
531
  return field
510
532
 
@@ -542,7 +564,7 @@ class VectorLayer(Base):
542
564
  bbox_srid: int = None,
543
565
  feature_ids: List = None,
544
566
  run_async: bool = True,
545
- user_id: int = None) -> 'Task':
567
+ user_id: int = None) -> Union['Task', Dict]:
546
568
  """
547
569
  Calculate values for a field based on an expression.
548
570
 
@@ -557,7 +579,7 @@ class VectorLayer(Base):
557
579
  user_id (int, optional): Specific user. privileges required.
558
580
 
559
581
  Returns:
560
- Task: The task instance of the calculation operation.
582
+ Task | Dict: The task instance of the calculation operation or the api response if the run_async=False.
561
583
 
562
584
  Example:
563
585
  >>> from geobox import GeoboxClient
@@ -585,8 +607,11 @@ class VectorLayer(Base):
585
607
 
586
608
  endpoint = urljoin(self.endpoint, 'calculateField/')
587
609
  response = self.api.post(endpoint, data, is_json=False)
588
- task = Task.get_task(self.api, response.get('task_uuid'))
589
- return task
610
+ if run_async:
611
+ task = Task.get_task(self.api, response.get('task_id'))
612
+ return task
613
+
614
+ return response
590
615
 
591
616
 
592
617
  def get_features(self, **kwargs) -> Union[List['Feature'], int]:
@@ -713,21 +738,21 @@ class VectorLayer(Base):
713
738
  return Feature.create_feature(self, geojson)
714
739
 
715
740
 
716
- def delete_features(self, q: str = None, bbox: List[float] = None, bbox_srid: int = None, feature_ids: List[int] = None,
717
- run_async: bool = True, user_id: int = None) -> 'Task':
741
+ def delete_features(self, q: str = None, bbox: str = None, bbox_srid: int = None, feature_ids: List[int] = None,
742
+ run_async: bool = True, user_id: int = None) -> Union['Task', Dict]:
718
743
  """
719
744
  Delete features from the layer based on specified criteria.
720
745
 
721
746
  Args:
722
- q (Optional[str]): Query to filter features to delete.
723
- bbox (Optional[List[float]]): Bounding box to filter features.
724
- bbox_srid (Optional[int]): Spatial reference ID for the bounding box.
725
- feature_ids (Optional[List[int]]): List of specific feature IDs to delete.
726
- run_async (Optional[bool]): Whether to run the deletion asynchronously. default is True.
727
- user_id (Optional[int]): Specific user. privileges required.
747
+ q (str, optional): Query to filter features to delete.
748
+ bbox (str, optional): Comma seprated bbox.
749
+ bbox_srid (int, optional): Spatial reference ID for the bounding box.
750
+ feature_ids (str, optional): Comma seprated feature ids.
751
+ run_async (bool, optional): Whether to run the deletion asynchronously. default is True.
752
+ user_id (int, optional): Specific user. privileges required.
728
753
 
729
754
  Returns:
730
- Task: The task instance of the deletion operation.
755
+ Task | Dict: The task instance of the deletion operation or the api response if run_async=False.
731
756
 
732
757
  Raises:
733
758
  ValidationError: If the deletion parameters are invalid.
@@ -738,9 +763,9 @@ class VectorLayer(Base):
738
763
  >>> client = GeoboxClient()
739
764
  >>> layer = VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
740
765
  >>> layer.delete_features(q="name like 'my_layer'",
741
- ... bbox=[10, 20, 30, 40],
766
+ ... bbox='10, 20, 30, 40',
742
767
  ... bbox_srid=3857,
743
- ... feature_ids=[1, 2, 3],
768
+ ... feature_ids='1, 2, 3',
744
769
  ... run_async=True)
745
770
  """
746
771
  data = clean_data({
@@ -754,19 +779,29 @@ class VectorLayer(Base):
754
779
 
755
780
  endpoint = urljoin(self.endpoint, 'deleteFeatures/')
756
781
  response = self.api.post(endpoint, data, is_json=False)
757
- task = Task.get_task(self.api, response.get('task_id'))
758
- return task
782
+ if run_async:
783
+ task = Task.get_task(self.api, response.get('task_id'))
784
+ return task
759
785
 
786
+ return response
760
787
 
761
- def import_features(self, file_uuid: str, input_geom_type: 'InputGeomType', input_layer_name: str = None, input_dataset: str = None,
762
- user_id: int = None, input_srid: int = None, file_encoding: str = "utf-8",
763
- replace_domain_codes_by_values: bool = False, report_errors: bool = True) -> 'Task':
788
+
789
+ def import_features(self,
790
+ file: 'File',
791
+ input_geom_type: 'InputGeomType' = None,
792
+ input_layer_name: str = None,
793
+ input_dataset: str = None,
794
+ user_id: int = None,
795
+ input_srid: int = None,
796
+ file_encoding: str = "utf-8",
797
+ replace_domain_codes_by_values: bool = False,
798
+ report_errors: bool = True) -> 'Task':
764
799
  """
765
800
  Import features from a file into the layer.
766
801
 
767
802
  Args:
768
- file_uuid (str): UUID of the uploaded file to import.
769
- input_geom_type (InputGeomType): Type of geometry in the input file.
803
+ file (File): file object to import.
804
+ input_geom_type (InputGeomType, optional): Type of geometry in the input file.
770
805
  input_layer_name (str, optional): Name of the layer in the input file.
771
806
  input_dataset (str, optional): Name of the dataset in the input file.
772
807
  user_id (int, optional): Specific user. privileges required.
@@ -786,7 +821,8 @@ class VectorLayer(Base):
786
821
  >>> from geobox.vectorlayer import VectorLayer
787
822
  >>> client = GeoboxClient()
788
823
  >>> layer = VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
789
- >>> task = layer.import_features(file_uuid="12345678-1234-5678-1234-567812345678",
824
+ >>> file = client.get_file(uuid="12345678-1234-5678-1234-567812345678")
825
+ >>> task = layer.import_features(file=file,
790
826
  ... input_geom_type=InputGeomType.POINT,
791
827
  ... input_layer_name="my_layer",
792
828
  ... input_dataset="my_dataset",
@@ -796,7 +832,7 @@ class VectorLayer(Base):
796
832
  ... report_errors=True)
797
833
  """
798
834
  data = clean_data({
799
- "file_uuid": file_uuid,
835
+ "file_uuid": file.uuid,
800
836
  "input_layer": input_layer_name,
801
837
  "input_geom_type": input_geom_type.value if isinstance(input_geom_type, InputGeomType) else input_geom_type,
802
838
  "replace_domain_codes_by_values": replace_domain_codes_by_values,
@@ -813,9 +849,17 @@ class VectorLayer(Base):
813
849
  return task
814
850
 
815
851
 
816
- def export_features(self, out_filename: str, out_format: 'FileOutputFormat', replace_domain_codes_by_values: bool = False,
817
- run_async: bool = True, bbox: List[float] = None, out_srid: int = None, zipped: bool = True,
818
- feature_ids: List[int] = None, bbox_srid: int = None, q: str = None, fields: List[str] = None) -> 'Task':
852
+ def export_features(self,
853
+ out_filename: str,
854
+ out_format: 'FileOutputFormat',
855
+ replace_domain_codes_by_values: bool = False,
856
+ run_async: bool = True,
857
+ bbox: List[float] = None,
858
+ out_srid: int = None,
859
+ zipped: bool = True,
860
+ feature_ids: List[int] = None,
861
+ bbox_srid: int = None,
862
+ q: str = None, fields: List[str] = None) -> Union['Task', Dict]:
819
863
  """
820
864
  Export features from the layer to a file.
821
865
 
@@ -833,7 +877,7 @@ class VectorLayer(Base):
833
877
  fields (List[str], optional): List of fields to include in the export.
834
878
 
835
879
  Returns:
836
- Task: The task instance of the export operation.
880
+ Task | Dict: The task instance of the export operation or the api response if run_async=False.
837
881
 
838
882
  Raises:
839
883
  ValidationError: If the export parameters are invalid.
@@ -868,8 +912,11 @@ class VectorLayer(Base):
868
912
 
869
913
  endpoint = urljoin(self.endpoint, 'export/')
870
914
  response = self.api.post(endpoint, data, is_json=False)
871
- task = Task.get_task(self.api, response.get('task_id'))
872
- return task
915
+ if run_async:
916
+ task = Task.get_task(self.api, response.get('task_id'))
917
+ return task
918
+
919
+ return response
873
920
 
874
921
 
875
922
  def create_view(self, name: str, display_name: str = None, description: str = None,
@@ -1031,11 +1078,7 @@ class VectorLayer(Base):
1031
1078
  edit_settings = {'editable', 'edit_geometry', 'editable_attributes', 'allow_insert', 'allow_delete'}
1032
1079
  tile_settings = {'min_zoom', 'max_zoom', 'max_features', 'filter_features', 'fields', 'use_cache', 'cache_until_zoom'}
1033
1080
 
1034
- settings = {
1035
- 'general_settings': {},
1036
- 'edit_settings': {},
1037
- 'tile_settings': {}
1038
- }
1081
+ settings = self.settings
1039
1082
 
1040
1083
  for key, value in kwargs.items():
1041
1084
  if key in general_settings:
geobox/view.py CHANGED
@@ -9,6 +9,7 @@ if TYPE_CHECKING:
9
9
  from .user import User
10
10
  from .enums import InputGeomType, FieldType
11
11
  from .task import Task
12
+ from .file import File
12
13
 
13
14
 
14
15
  class VectorLayerView(VectorLayer):
@@ -201,7 +202,7 @@ class VectorLayerView(VectorLayer):
201
202
 
202
203
 
203
204
  @classmethod
204
- def get_view_by_name(cls, api: 'GeoboxClient', name: str, user_id: int = None) -> Union['View', None]:
205
+ def get_view_by_name(cls, api: 'GeoboxClient', name: str, user_id: int = None) -> Union['VectorLayerView', None]:
205
206
  """
206
207
  Get a view by name
207
208
 
@@ -211,7 +212,7 @@ class VectorLayerView(VectorLayer):
211
212
  user_id (int, optional): specific user. privileges required.
212
213
 
213
214
  Returns:
214
- View | None: returns the view if a view matches the given name, else None
215
+ VectorLayerView | None: returns the view if a view matches the given name, else None
215
216
 
216
217
  Example:
217
218
  >>> from geobox import GeoboxClient
@@ -361,13 +362,12 @@ class VectorLayerView(VectorLayer):
361
362
  return super().get_fields()
362
363
 
363
364
 
364
- def get_field(self, field_id: str = None, name: str = None) -> 'Field':
365
+ def get_field(self, field_id: int) -> 'Field':
365
366
  """
366
367
  Get a specific field by its ID or name.
367
368
 
368
369
  Args:
369
- field_id (str): The ID of the field to retrieve.
370
- name (str): The name of the field to retrieve.
370
+ field_id (int): The ID of the field to retrieve.
371
371
 
372
372
  Returns:
373
373
  Field: The requested field instance.
@@ -381,9 +381,32 @@ class VectorLayerView(VectorLayer):
381
381
  >>> from geobox.view import VectorLayerView
382
382
  >>> client = GeoboxClient()
383
383
  >>> view = VectorLayerView.get_view(client, uuid="12345678-1234-5678-1234-567812345678")
384
- >>> field = view.get_field(id="1")
384
+ >>> field = view.get_field(id=1)
385
385
  """
386
- return super().get_field(field_id, name)
386
+ return super().get_field(field_id)
387
+
388
+
389
+ def get_field_by_name(self, name: str) -> 'Field':
390
+ """
391
+ Get a specific field by its name.
392
+
393
+ Args:
394
+ name (str, optional): The name of the field to retrieve.
395
+
396
+ Returns:
397
+ Field: The requested field instance.
398
+
399
+ Raises:
400
+ NotFoundError: If the field with the specified name is not found.
401
+
402
+ Example:
403
+ >>> from geobox import GeoboxClient
404
+ >>> from geobox.view import VectorLayerView
405
+ >>> client = GeoboxClient()
406
+ >>> view = VectorLayerView.get_view(client, uuid="12345678-1234-5678-1234-567812345678")
407
+ >>> field = view.get_field_by_name(name='test')
408
+ """
409
+ return super().get_field_by_name(name)
387
410
 
388
411
 
389
412
  def add_field(self, name: str, data_type: 'FieldType', data: Dict = {}) -> 'Field':
@@ -414,7 +437,7 @@ class VectorLayerView(VectorLayer):
414
437
 
415
438
  def calculate_field(self, target_field: str, expression: str, q: str = None, bbox: List[float] = None,
416
439
  bbox_srid: int = None, feature_ids: List = None, run_async: bool = True,
417
- user_id: int = None) -> 'Task':
440
+ user_id: int = None) -> Union['Task', Dict]:
418
441
  """
419
442
  Calculate values for a field based on an expression.
420
443
 
@@ -429,7 +452,7 @@ class VectorLayerView(VectorLayer):
429
452
  user_id (Optional[int]): ID of the user running the calculation.
430
453
 
431
454
  Returns:
432
- Task: The task instance of the calculation operation.
455
+ Task | Dict: The task instance of the calculation operation or the api response if the run_async=False.
433
456
 
434
457
  Raises:
435
458
  ValidationError: If the calculation parameters are invalid.
@@ -541,7 +564,7 @@ class VectorLayerView(VectorLayer):
541
564
 
542
565
 
543
566
  def delete_features(self, q: str = None, bbox: List[float] = None, bbox_srid: int = None, feature_ids: List[int] = None,
544
- run_async: bool = True, user_id: int = None) -> 'Task':
567
+ run_async: bool = True, user_id: int = None) -> Union['Task', Dict]:
545
568
  """
546
569
  Delete features from the layer based on specified criteria.
547
570
 
@@ -554,7 +577,7 @@ class VectorLayerView(VectorLayer):
554
577
  user_id (Optional[int]): ID of the user performing the deletion.
555
578
 
556
579
  Returns:
557
- Task: The task instance of the deletion operation.
580
+ Task | Dict: The task instance of the deletion operation or the api response if run_async=False.
558
581
 
559
582
  Raises:
560
583
  ValidationError: If the deletion parameters are invalid.
@@ -573,15 +596,22 @@ class VectorLayerView(VectorLayer):
573
596
  return super().delete_features(q, bbox, bbox_srid, feature_ids, run_async, user_id)
574
597
 
575
598
 
576
- def import_features(self, file_uuid: str, input_geom_type: 'InputGeomType', input_layer_name: str = None, input_dataset: str = None,
577
- user_id: int = None, input_srid: int = None, file_encoding: str = "utf-8",
578
- replace_domain_codes_by_values: bool = False, report_errors: bool = True) -> 'Task':
599
+ def import_features(self,
600
+ file: 'File',
601
+ input_geom_type: 'InputGeomType' = None,
602
+ input_layer_name: str = None,
603
+ input_dataset: str = None,
604
+ user_id: int = None,
605
+ input_srid: int = None,
606
+ file_encoding: str = "utf-8",
607
+ replace_domain_codes_by_values: bool = False,
608
+ report_errors: bool = True) -> 'Task':
579
609
  """
580
610
  Import features from a file into the layer.
581
611
 
582
612
  Args:
583
- file_uuid (str): UUID of the uploaded file to import.
584
- input_geom_type (InputGeomType): Type of geometry in the input file.
613
+ file (File): file object to import.
614
+ input_geom_type (InputGeomType, optional): Type of geometry in the input file.
585
615
  input_layer_name (str, optional): Name of the layer in the input file.
586
616
  input_dataset (str, optional): Name of the dataset in the input file.
587
617
  user_id (int, optional): Specific user.privileges requied.
@@ -601,7 +631,8 @@ class VectorLayerView(VectorLayer):
601
631
  >>> from geobox.view import VectorLayerView
602
632
  >>> client = GeoboxClient()
603
633
  >>> view = VectorLayerView.get_view(client, uuid="12345678-1234-5678-1234-567812345678")
604
- >>> task = view.import_features(file_uuid="12345678-1234-5678-1234-567812345678",
634
+ >>> file = client.get_file(uuid="12345678-1234-5678-1234-567812345678")
635
+ >>> task = view.import_features(file=file,
605
636
  ... input_geom_type=InputGeomType.POINT,
606
637
  ... input_layer="my_layer",
607
638
  ... input_dataset="my_dataset",
@@ -610,13 +641,13 @@ class VectorLayerView(VectorLayer):
610
641
  ... replace_domain_codes_by_values=False,
611
642
  ... report_errors=True)
612
643
  """
613
- return super().import_features(file_uuid, input_geom_type, input_layer_name, input_dataset, user_id,
644
+ return super().import_features(file, input_geom_type, input_layer_name, input_dataset, user_id,
614
645
  input_srid, file_encoding, replace_domain_codes_by_values, report_errors)
615
646
 
616
647
 
617
648
  def export_features(self, out_filename: str, out_format: 'FileOutputFormat', replace_domain_codes_by_values: bool = False,
618
649
  run_async: bool = True, bbox: List[float] = None, out_srid: int = None, zipped: bool = True,
619
- feature_ids: List[int] = None, bbox_srid: int = None, q: str = None, fields: List[str] = None) -> 'Task':
650
+ feature_ids: List[int] = None, bbox_srid: int = None, q: str = None, fields: List[str] = None) -> Union['Task', Dict]:
620
651
  """
621
652
  Export features from the layer to a file.
622
653
 
@@ -634,7 +665,7 @@ class VectorLayerView(VectorLayer):
634
665
  fields (List[str], optional): List of fields to include in the export.
635
666
 
636
667
  Returns:
637
- Task: The task instance of the export operation.
668
+ Task | Dict: The task instance of the export operation or the api response if run_async=False.
638
669
 
639
670
  Raises:
640
671
  ValidationError: If the export parameters are invalid.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: geobox
3
- Version: 1.2.1
3
+ Version: 1.2.3
4
4
  Summary: SDK for Geobox's APIs
5
5
  Author-email: Hamid Heydari <heydari.h62@gmail.com>
6
6
  License: MIT
@@ -1,37 +1,37 @@
1
1
  geobox/__init__.py,sha256=iRkJAFIjMXiAaLTVXvZcW5HM7ttyipGdxFZ15NCM8qU,1895
2
- geobox/api.py,sha256=Sq9iKYltgdR96cA4w5_5jMraNqAUpgSzMriO9PbZFNA,100538
2
+ geobox/api.py,sha256=v-_Vay_xIXZRG3-0ahiOWxVT9QkrIyuOcd2KYFyVK04,100795
3
3
  geobox/apikey.py,sha256=FBio1PtGovAUaTE0HW9IJ55UuFdR_1ICk_FQhHu5dAE,7427
4
4
  geobox/attachment.py,sha256=XbGwfWWuFAMimj4tsjKBvlSLP-rhpNACCtxgRmUvcdY,11631
5
5
  geobox/base.py,sha256=p0UVZo9CINw0mW9o0nNR_VNCk7V1r-FrLQ_NH39WARE,12299
6
6
  geobox/basemap.py,sha256=fDWwkMf-F2NTE1tVLijoxQku55825b6gj8nk69TMOII,4386
7
7
  geobox/dashboard.py,sha256=MYyT3YJEGPCbTXHcYoZmn14rFOaut1J3idEA8bCdFgI,11762
8
- geobox/enums.py,sha256=jGs8GzyCzQMJ5go9owDKMiAwuWjDCpKPaNmQg2eiCaQ,6613
8
+ geobox/enums.py,sha256=1ScREE4WFbbIbY1K1jz62PnXUHKav-GiDQmDYZwUj2c,6316
9
9
  geobox/exception.py,sha256=jvpnv0M2Ck1FpxHTL_aKYWxGvLnCQ3d9vOrMIktjw1U,1507
10
10
  geobox/feature.py,sha256=3Kbc1LjIkBt1YqRAry84BrR7qxx9BexvBB3YQ8D9mGk,18504
11
- geobox/field.py,sha256=2VxjeYgRwnDxHYpAsK0ASOCz8V0JmfTA3GqHDv3rfgQ,10526
12
- geobox/file.py,sha256=NqN9EoSGMiTev5vo_1QKciofcyoXonLoZz7B8hH_igk,19352
11
+ geobox/field.py,sha256=p9eitUpnsiGj6GUhjSomk6HWlsNJSbE-Oh_4VcjaqVo,10562
12
+ geobox/file.py,sha256=Ula8r8pZaCmifsnUHDbaJujw53SLxUiCiBzVS358o88,19967
13
13
  geobox/log.py,sha256=ZTmVErhyAszf7M5YFxT5mqXNNDGPnRwXPeGLDS1G6PE,3582
14
- geobox/map.py,sha256=98P1gbB5U_eu71Hu6EQ0kL_ponbPAnoOCitjEa35q4I,31307
14
+ geobox/map.py,sha256=RjuhbOVQN-OBwjBtO46AosB_Ta348EA1nExBdnxH3nw,31252
15
15
  geobox/model3d.py,sha256=qRYCx-q9LpGhdu5oz3av0uUoiDimuk4BvXXwMW5bo9Q,12061
16
16
  geobox/mosaic.py,sha256=RmMyvzt6C2Fd8a0nJX5gUOGJnO3MrCWgwmYfhJ9d_Jw,23025
17
17
  geobox/plan.py,sha256=_ZV9F6loG92uQZGJl_9T08Kg85g3hnODmpccSkTYVdw,11193
18
18
  geobox/query.py,sha256=1guO9O9DAEb8oj_iMiW3PhEDgMVMAN9ccEp19dnwAWE,24209
19
- geobox/raster.py,sha256=9Qnv9CQvgVxuNx1khDPQhHZEcopXofIPYBnRrx40FVk,26495
19
+ geobox/raster.py,sha256=DkvajG2jWDlGFbS2ywv4XCVOyfR82ouUMT3tdIE1PsU,26437
20
20
  geobox/route.py,sha256=cKZTTHBHM8woWbhHIpCvrRX_doJcTkGktouee0z7oG0,2723
21
21
  geobox/scene.py,sha256=Sz2tiyJk-bXYjktfcp1d2gGrW3gt2T4D1FAMoRHOCng,11369
22
22
  geobox/settings.py,sha256=rGRdM18Vo7xnjfZXPLRMbKeoVC_lZmzkNRPS0SQv05o,5660
23
- geobox/task.py,sha256=lACZ-ix3BAAU1-pNnbHC2noAtt79swrkmkCczEO7D2A,13364
23
+ geobox/task.py,sha256=eLMgbhcYTADooWqKDPh6Jlh5S5oqxMMKoYIm0YPVZvg,13519
24
24
  geobox/tile3d.py,sha256=MHDoj2OIUf3VMQtq7ysrayh8njAPSgwgPJCm4ySEB7A,10472
25
25
  geobox/tileset.py,sha256=F_oEV0TERMc2seLcYNu-i_TF4MNSdXPUWcxBCSR8aFw,22242
26
26
  geobox/usage.py,sha256=_54xL-n-2Bg9wGpoOBKnh85WqdpMWEtqxRJVaFlVWe0,7908
27
27
  geobox/user.py,sha256=ScuuAhxUD4FcGg4fDMZjhDgJYNmV6apFDkrJNqATjvk,14486
28
28
  geobox/utils.py,sha256=jKhEs6CWTSlJZDS5qw9O2KgkLnTL9GKvFyKJpp8n1fA,1116
29
- geobox/vectorlayer.py,sha256=xnYsJei-bpKgM_EJlRbZ-bAIHdmvfU-VZ2t-NEEJCfc,49420
29
+ geobox/vectorlayer.py,sha256=_EV-Ht7B9v_tS8fOYXzsbrHASopoAoz5RNdvisW4BWc,50938
30
30
  geobox/version.py,sha256=0GLPhxCeEb2bAkdpPJWtXPXc1KP6kQ_TOMwLAL0ldo0,9374
31
- geobox/view.py,sha256=fRYlzNu4eGl6Zx9gPom47BkVE8DfWLj0bNlW2-u4TOU,37390
31
+ geobox/view.py,sha256=or4n8SQ8tVu8I4BNLqxJTAUXL2ZrhvJ1xDdslhu60_A,38541
32
32
  geobox/workflow.py,sha256=6hKnSw4G0_ZlgmUb0g3fxT-UVsFbiYpF2FbEO5fpQv0,11606
33
- geobox-1.2.1.dist-info/licenses/LICENSE,sha256=AvFB7W94sJYKLDhBxLRshL3upexCOG8HQY_1JibB96w,1063
34
- geobox-1.2.1.dist-info/METADATA,sha256=toqzfxiUTgzmI9Vn5BAIN6v1D_4HGsT-naLcNP4F-28,2556
35
- geobox-1.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
- geobox-1.2.1.dist-info/top_level.txt,sha256=ppXH8Bu2mlB-pLQ6lsoWEm2Gr6wZx1uzkhetsYA5ins,7
37
- geobox-1.2.1.dist-info/RECORD,,
33
+ geobox-1.2.3.dist-info/licenses/LICENSE,sha256=AvFB7W94sJYKLDhBxLRshL3upexCOG8HQY_1JibB96w,1063
34
+ geobox-1.2.3.dist-info/METADATA,sha256=7u1F3M5PmC_7ubwqIj1NiGAw3-JQ7SgcT7jQEizagxk,2556
35
+ geobox-1.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
+ geobox-1.2.3.dist-info/top_level.txt,sha256=ppXH8Bu2mlB-pLQ6lsoWEm2Gr6wZx1uzkhetsYA5ins,7
37
+ geobox-1.2.3.dist-info/RECORD,,
File without changes