geobox 1.4.2__py3-none-any.whl → 2.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. geobox/__init__.py +2 -2
  2. geobox/aio/__init__.py +63 -0
  3. geobox/aio/api.py +2640 -0
  4. geobox/aio/apikey.py +263 -0
  5. geobox/aio/attachment.py +339 -0
  6. geobox/aio/base.py +262 -0
  7. geobox/aio/basemap.py +196 -0
  8. geobox/aio/dashboard.py +342 -0
  9. geobox/aio/feature.py +527 -0
  10. geobox/aio/field.py +321 -0
  11. geobox/aio/file.py +522 -0
  12. geobox/aio/layout.py +341 -0
  13. geobox/aio/log.py +145 -0
  14. geobox/aio/map.py +1034 -0
  15. geobox/aio/model3d.py +415 -0
  16. geobox/aio/mosaic.py +696 -0
  17. geobox/aio/plan.py +315 -0
  18. geobox/aio/query.py +693 -0
  19. geobox/aio/raster.py +869 -0
  20. geobox/aio/route.py +63 -0
  21. geobox/aio/scene.py +342 -0
  22. geobox/aio/settings.py +194 -0
  23. geobox/aio/task.py +402 -0
  24. geobox/aio/tile3d.py +339 -0
  25. geobox/aio/tileset.py +672 -0
  26. geobox/aio/usage.py +243 -0
  27. geobox/aio/user.py +507 -0
  28. geobox/aio/vectorlayer.py +1363 -0
  29. geobox/aio/version.py +273 -0
  30. geobox/aio/view.py +983 -0
  31. geobox/aio/workflow.py +341 -0
  32. geobox/api.py +14 -15
  33. geobox/apikey.py +28 -1
  34. geobox/attachment.py +27 -1
  35. geobox/base.py +4 -4
  36. geobox/basemap.py +30 -1
  37. geobox/dashboard.py +27 -0
  38. geobox/feature.py +33 -13
  39. geobox/field.py +33 -21
  40. geobox/file.py +40 -46
  41. geobox/layout.py +28 -1
  42. geobox/log.py +31 -7
  43. geobox/map.py +34 -2
  44. geobox/model3d.py +31 -37
  45. geobox/mosaic.py +28 -7
  46. geobox/plan.py +29 -3
  47. geobox/query.py +39 -14
  48. geobox/raster.py +26 -13
  49. geobox/scene.py +26 -0
  50. geobox/settings.py +30 -1
  51. geobox/task.py +28 -6
  52. geobox/tile3d.py +27 -1
  53. geobox/tileset.py +26 -5
  54. geobox/usage.py +32 -1
  55. geobox/user.py +62 -6
  56. geobox/utils.py +34 -0
  57. geobox/vectorlayer.py +40 -4
  58. geobox/version.py +25 -1
  59. geobox/view.py +37 -17
  60. geobox/workflow.py +27 -1
  61. {geobox-1.4.2.dist-info → geobox-2.0.1.dist-info}/METADATA +4 -1
  62. geobox-2.0.1.dist-info/RECORD +68 -0
  63. geobox-1.4.2.dist-info/RECORD +0 -38
  64. {geobox-1.4.2.dist-info → geobox-2.0.1.dist-info}/WHEEL +0 -0
  65. {geobox-1.4.2.dist-info → geobox-2.0.1.dist-info}/licenses/LICENSE +0 -0
  66. {geobox-1.4.2.dist-info → geobox-2.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1363 @@
1
+ from urllib.parse import urljoin
2
+ from typing import List, Optional, TYPE_CHECKING, Dict, Union
3
+
4
+ from .base import AsyncBase
5
+ from .field import Field, FieldType
6
+ from .feature import Feature
7
+ from .task import Task
8
+ from .version import VectorLayerVersion
9
+ from ..utils import clean_data
10
+ from ..exception import NotFoundError
11
+ from ..enums import LayerType, InputGeomType, FileOutputFormat
12
+
13
+ if TYPE_CHECKING:
14
+ from .api import AsyncGeoboxClient
15
+ from .view import VectorLayerView
16
+ from .user import User
17
+ from .file import File
18
+ from .attachment import Attachment
19
+ from ..api import GeoboxClient as SyncGeoboxClient
20
+ from ..vectorlayer import VectorLayer as SyncVectorLayer
21
+
22
+
23
+ class VectorLayer(AsyncBase):
24
+ """
25
+ A class representing a vector layer in Geobox.
26
+
27
+ This class provides functionality to create, manage, and manipulate vector layers.
28
+ It supports various operations including CRUD operations on layers, features, and fields,
29
+ as well as advanced operations like importing/exporting features and calculating field values.
30
+ """
31
+ BASE_ENDPOINT = 'vectorLayers/'
32
+
33
+ def __init__(self,
34
+ api: 'AsyncGeoboxClient',
35
+ uuid: str,
36
+ layer_type: LayerType,
37
+ data: Optional[Dict] = {}):
38
+ """
39
+ Initialize a VectorLayer instance.
40
+
41
+ Args:
42
+ api (AsyncGeoboxClient): The AsyncGeoboxClient instance for making requests.
43
+ uuid (str): The unique identifier for the layer.
44
+ layer_type (LayerType): The type of geometry stored in the layer.
45
+ data (Dict, optional): Additional layer metadata and configuration.
46
+ """
47
+ super().__init__(api=api, uuid=uuid, data=data)
48
+ self.layer_type = layer_type if isinstance(layer_type, LayerType) else LayerType(layer_type)
49
+
50
+
51
+ def __repr__(self) -> str:
52
+ """
53
+ Return a string representation of the VectorLayer object.
54
+
55
+ Returns:
56
+ str: A string representation of the VectorLayer object.
57
+ """
58
+ return f"VectorLayer(uuid={self.uuid}, name={self.name}, layer_type={self.layer_type})"
59
+
60
+
61
+ @classmethod
62
+ async def get_vectors(cls, api: 'AsyncGeoboxClient', **kwargs) -> Union[List['VectorLayer'], int]:
63
+ """
64
+ [async] Get a list of vector layers with optional filtering and pagination.
65
+
66
+ Args:
67
+ api (AsyncGeoboxClient): The AsyncGeoboxClient instance for making requests.
68
+
69
+ Keyword Args:
70
+ include_settings (bool): Whether to include layer settings. Default is False.
71
+ temporary (bool): Whether to return temporary layers, default is False
72
+ q (str): Query filter based on OGC CQL standard. e.g. "field1 LIKE '%GIS%' AND created_at > '2021-01-01'"
73
+ 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.
74
+ search_fields (str): Comma separated list of fields for searching
75
+ 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.
76
+ return_count (bool): Whether to return total count. default is False.
77
+ skip (int): Number of layers to skip. default is 0.
78
+ limit (int): Maximum number of layers to return. default is 10.
79
+ user_id (int): Specific user. privileges required.
80
+ shared (bool): Whether to return shared layers. default is False.
81
+
82
+ Returns:
83
+ List[VectorLayer] | int: A list of VectorLayer instances or the layers count if return_count is True.
84
+
85
+ Example:
86
+ >>> from geobox.aio import AsyncGeoboxClient
87
+ >>> from geobox.aio.vectorlayer import VectorLayer
88
+ >>> async with AsyncGeoboxClient() as client:
89
+ >>> layers = await VectorLayer.get_vectors(api=client,
90
+ ... include_settings=True,
91
+ ... skip=0,
92
+ ... limit=100,
93
+ ... return_count=False,
94
+ ... search="my_layer",
95
+ ... search_fields="name, description",
96
+ ... order_by="name",
97
+ ... shared=True)
98
+ or
99
+ >>> layers = await client.get_vectors(include_settings=True,
100
+ ... skip=0,
101
+ ... limit=100,
102
+ ... return_count=False,
103
+ ... search="my_layer",
104
+ ... search_fields="name, description",
105
+ ... order_by="name",
106
+ ... shared=True)
107
+ """
108
+ params = {
109
+ 'f': 'json',
110
+ 'include_settings': kwargs.get('include_settings', False),
111
+ 'temporary': kwargs.get('temporary', False),
112
+ 'q': kwargs.get('q', None),
113
+ 'search': kwargs.get('search', None),
114
+ 'search_fields': kwargs.get('search_fields', None),
115
+ 'order_by': kwargs.get('order_by', None),
116
+ 'return_count': kwargs.get('return_count', False),
117
+ 'skip': kwargs.get('skip', 0),
118
+ 'limit': kwargs.get('limit', 10),
119
+ 'user_id': kwargs.get('user_id', None),
120
+ 'shared': kwargs.get('shared', False)
121
+ }
122
+ return await super()._get_list(api=api,
123
+ endpoint=cls.BASE_ENDPOINT,
124
+ params=params,
125
+ factory_func=lambda api, item: VectorLayer(api, item['uuid'], LayerType(item['layer_type']), item))
126
+
127
+
128
+ @classmethod
129
+ async def get_vector(cls, api: 'AsyncGeoboxClient', uuid: str, user_id: int = None) -> 'VectorLayer':
130
+ """
131
+ [async] Get a specific vector layer by its UUID.
132
+
133
+ Args:
134
+ api (AsyncGeoboxClient): The AsyncGeoboxClient instance for making requests.
135
+ uuid (str): The UUID of the layer to retrieve.
136
+ user_id (int, optional): Specific user. privileges required.
137
+
138
+ Returns:
139
+ VectorLayer: The requested layer instance.
140
+
141
+ Raises:
142
+ NotFoundError: If the layer with the specified UUID is not found.
143
+
144
+ Example:
145
+ >>> from geobox.aio import AsyncGeoboxClient
146
+ >>> from geobox.aio.vectorlayer import VectorLayer
147
+ >>> async with AsyncGeoboxClient() as client:
148
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
149
+ or
150
+ >>> layer = await client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
151
+ """
152
+ params = {
153
+ 'f': 'json',
154
+ 'user_id': user_id
155
+ }
156
+ return await super()._get_detail(api=api,
157
+ endpoint=cls.BASE_ENDPOINT,
158
+ uuid=uuid,
159
+ params=params,
160
+ factory_func=lambda api, item: VectorLayer(api, item['uuid'], LayerType(item['layer_type']), item))
161
+
162
+
163
+ @classmethod
164
+ async def get_vector_by_name(cls, api: 'AsyncGeoboxClient', name: str, user_id: int = None) -> Union['VectorLayer', None]:
165
+ """
166
+ [async] Get a vector layer by name
167
+
168
+ Args:
169
+ api (AsyncGeoboxClient): The AsyncGeoboxClient instance for making requests.
170
+ name (str): the name of the vector to get
171
+ user_id (int, optional): specific user. privileges required.
172
+
173
+ Returns:
174
+ VectorLayer | None: returns the vector if a vector matches the given name, else None
175
+
176
+ Example:
177
+ >>> from geobox.aio import AsyncGeoboxClient
178
+ >>> from geobox.aio.vectorlayer import VectorLayer
179
+ >>> async with AsyncGeoboxClient() as client:
180
+ >>> layer = await VectorLayer.get_vector_by_name(client, name='test')
181
+ or
182
+ >>> layer = await client.get_vector_by_name(name='test')
183
+ """
184
+ layers = await cls.get_vectors(api, q=f"name = '{name}'", user_id=user_id)
185
+ if layers and layers[0].name == name:
186
+ return layers[0]
187
+ else:
188
+ return None
189
+
190
+
191
+ @classmethod
192
+ async def get_vectors_by_ids(cls, api: 'AsyncGeoboxClient', ids: List[int], user_id: int = None, include_settings: bool = False) -> List['VectorLayer']:
193
+ """
194
+ [async] Get vector layers by their IDs.
195
+
196
+ Args:
197
+ api (AsyncGeoboxClient): The AsyncGeoboxClient instance for making requests.
198
+ ids (List[int]): The IDs of the layers to retrieve.
199
+ user_id (int, optional): Specific user. privileges required.
200
+ include_settings (bool, optional): Whether to include the layer settings. default is False.
201
+
202
+ Returns:
203
+ List[VectorLayer]: The list of VectorLayer instances.
204
+
205
+ Example:
206
+ >>> from geobox.aio import AsyncGeoboxClient
207
+ >>> from geobox.aio.vectorlayer import VectorLayer
208
+ >>> async with AsyncGeoboxClient() as client:
209
+ >>> layers = await VectorLayer.get_vectors_by_ids(api=client, ids=[1, 2, 3])
210
+ or
211
+ >>> layers = await client.get_vectors_by_ids(ids=[1, 2, 3])
212
+ """
213
+ params = {
214
+ 'ids': ids,
215
+ 'user_id': user_id,
216
+ 'include_settings': include_settings
217
+ }
218
+ return await super()._get_list_by_ids(api=api,
219
+ endpoint=f'{cls.BASE_ENDPOINT}get-layers/',
220
+ params=params,
221
+ factory_func=lambda api, item: VectorLayer(api, item['uuid'], LayerType(item['layer_type']), item))
222
+
223
+
224
+ @classmethod
225
+ async def create_vector(cls,
226
+ api: 'AsyncGeoboxClient',
227
+ name: str,
228
+ layer_type: LayerType,
229
+ display_name: str = None,
230
+ description: str = None,
231
+ has_z: bool = False,
232
+ temporary: bool = False,
233
+ fields: List = None) -> 'VectorLayer':
234
+ """
235
+ [async] Create a new vector layer.
236
+
237
+ Args:
238
+ api (AsyncGeoboxClient): The AsyncGeoboxClient instance for making requests.
239
+ name (str): The name of the layer.
240
+ layer_type (LayerType): The type of geometry to store.
241
+ display_name (str, optional): A human-readable name for the layer. default is None.
242
+ description (str, optional): A description of the layer. default is None.
243
+ has_z (bool, optional): Whether the layer includes Z coordinates. default is False.
244
+ temporary (bool, optional): Whether to create a temporary layer. temporary layers will be deleted after 24 hours. default is False.
245
+ fields (List, optional): List of field definitions for the layer. default is None.
246
+
247
+ Returns:
248
+ VectorLayer: The newly created layer instance.
249
+
250
+ Raises:
251
+ ValidationError: If the layer data is invalid.
252
+
253
+ Example:
254
+ >>> from geobox.aio import AsyncGeoboxClient
255
+ >>> from geobox.aio.vectorlayer import VectorLayer
256
+ >>> async with AsyncGeoboxClient() as client:
257
+ >>> layer = await VectorLayer.create_vector(api=client,
258
+ ... name="my_layer",
259
+ ... layer_type=LayerType.Point,
260
+ ... display_name="My Layer",
261
+ ... description="This is a description of my layer",
262
+ ... has_z=False,
263
+ ... temporary=True,
264
+ ... fields=[{"name": "my_field", "datatype": "FieldTypeString"}])
265
+ or
266
+ >>> layer = await client.create_vector(name="my_layer",
267
+ ... layer_type=LayerType.Point,
268
+ ... display_name="My Layer",
269
+ ... description="This is a description of my layer",
270
+ ... has_z=False,
271
+ ... temporary=True,
272
+ ... fields=[{"name": "my_field", "datatype": "FieldTypeString"}])
273
+ """
274
+ data = {
275
+ "name": name,
276
+ "layer_type": layer_type.value,
277
+ "display_name": display_name,
278
+ "description": description,
279
+ "has_z": has_z,
280
+ "temporary": temporary,
281
+ "fields": fields
282
+ }
283
+ return await super()._create(api=api,
284
+ endpoint=cls.BASE_ENDPOINT,
285
+ data=data,
286
+ factory_func=lambda api, item: VectorLayer(api, item['uuid'], layer_type, item))
287
+
288
+
289
+ async def update(self, **kwargs) -> Dict:
290
+ """
291
+ [async] Update the layer's properties.
292
+
293
+ Keyword Args:
294
+ name (str): The new name for the layer.
295
+ display_name (str): The new display name for the layer.
296
+ description (str): The new description for the layer.
297
+
298
+ Returns:
299
+ Dict: The updated layer data.
300
+
301
+ Raises:
302
+ ValidationError: If the update data is invalid.
303
+
304
+ Example:
305
+ >>> from geobox.aio import AsyncGeoboxClient
306
+ >>> from geobox.aio.vectorlayer import VectorLayer
307
+ >>> async with AsyncGeoboxClient() as client:
308
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
309
+ >>> await layer.update(name="new_name")
310
+ >>> await layer.update(display_name="new_display_name")
311
+ >>> await layer.update(description="new_description")
312
+ """
313
+ data = {
314
+ "name": kwargs.get('name'),
315
+ "display_name": kwargs.get('display_name'),
316
+ "description": kwargs.get('description')
317
+ }
318
+ return await super()._update(endpoint=self.endpoint, data=data)
319
+
320
+
321
+ async def delete(self) -> None:
322
+ """
323
+ [async] Delete the layer.
324
+
325
+ Returns:
326
+ None
327
+
328
+ Example:
329
+ >>> from geobox.aio import AsyncGeoboxClient
330
+ >>> from geobox.aio.vectorlayer import VectorLayer
331
+ >>> async with AsyncGeoboxClient() as client:
332
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
333
+ >>> await layer.delete()
334
+ """
335
+ await super().delete(endpoint=self.endpoint)
336
+
337
+
338
+ async def make_permanent(self) -> None:
339
+ """
340
+ [async] Make the layer permanent.
341
+
342
+ Returns:
343
+ None
344
+
345
+ Example:
346
+ >>> from geobox.aio import AsyncGeoboxClient
347
+ >>> from geobox.aio.vectorlayer import VectorLayer
348
+ >>> async with AsyncGeoboxClient() as client:
349
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
350
+ >>> await layer.make_permanent()
351
+ """
352
+ endpoint = urljoin(self.endpoint, 'makePermanent/')
353
+ response = await self.api.post(endpoint, is_json=False)
354
+ self._update_properties(response)
355
+
356
+
357
+ async def share(self, users: List['User']) -> None:
358
+ """
359
+ [async] Shares the layer with specified users.
360
+
361
+ Args:
362
+ users (List[User]): The list of user objects to share the layer with.
363
+
364
+ Returns:
365
+ None
366
+
367
+ Example:
368
+ >>> from geobox.aio import AsyncGeoboxClient
369
+ >>> from geobox.aio.vectorlayer import VectorLayer
370
+ >>> async with AsyncGeoboxClient() as client:
371
+ >>> layer = await VectorLayer.get_vector(client, uuid="12345678-1234-5678-1234-567812345678")
372
+ >>> users = await client.search_users(search="John")
373
+ >>> await layer.share(users=users)
374
+ """
375
+ await super()._share(self.endpoint, users)
376
+
377
+
378
+ async def unshare(self, users: List['User']) -> None:
379
+ """
380
+ [async] Unshares the layer with specified users.
381
+
382
+ Args:
383
+ users (List[User]): The list of user objectss to unshare the layer with.
384
+
385
+ Returns:
386
+ None
387
+
388
+ Example:
389
+ >>> from geobox.aio import AsyncGeoboxClient
390
+ >>> from geobox.aio.vectorlayer import VectorLayer
391
+ >>> async with AsyncGeoboxClient() as client:
392
+ >>> layer = await VectorLayer.get_vector(client, uuid="12345678-1234-5678-1234-567812345678")
393
+ >>> users = await client.search_users(search="John")
394
+ >>> await layer.unshare(users=users)
395
+ """
396
+ await super()._unshare(self.endpoint, users)
397
+
398
+
399
+ async def get_shared_users(self, search: str = None, skip: int = 0, limit: int = 10) -> List['User']:
400
+ """
401
+ [async] Retrieves the list of users the layer is shared with.
402
+
403
+ Args:
404
+ search (str, optional): The search query.
405
+ skip (int, optional): The number of users to skip.
406
+ limit (int, optional): The maximum number of users to retrieve.
407
+
408
+ Returns:
409
+ List[User]: The list of shared users.
410
+
411
+ Example:
412
+ >>> from geobox.aio import AsyncGeoboxClient
413
+ >>> from geobox.aio.vectorlayer import VectorLayer
414
+ >>> async with AsyncGeoboxClient() as client:
415
+ >>> layer = await VectorLayer.get_vector(client, uuid="12345678-1234-5678-1234-567812345678")
416
+ >>> await layer.get_shared_users(search='John', skip=0, limit=10)
417
+ """
418
+ params = {
419
+ 'search': search,
420
+ 'skip': skip,
421
+ 'limit': limit
422
+ }
423
+ return await super()._get_shared_users(self.endpoint, params)
424
+
425
+
426
+ async def create_version(self, name: str, display_name: str = None, description: str = None) -> 'VectorLayerVersion':
427
+ """
428
+ [async] Create a version from the layer
429
+
430
+ Args:
431
+ name (str): the name of the version.
432
+ display_name (str, optional): the display name of the version.
433
+ description (str, optional): the description of the version.
434
+
435
+ Returns:
436
+ VectorLayerVersion: the object of the version.
437
+
438
+ Example:
439
+ >>> from geobox.aio import AsyncGeoboxClient
440
+ >>> from geobox.aio.vectorlayer import VectorLayer
441
+ >>> async with AsyncGeoboxClient() as client:
442
+ >>> layer = await client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
443
+ >>> version = await layer.create_version(name="my_version")
444
+ """
445
+ data = {
446
+ "name": name,
447
+ "display_name": display_name,
448
+ "description": description
449
+ }
450
+ endpoint = urljoin(self.endpoint, 'versions')
451
+ return await super()._create(self.api, endpoint, data, factory_func=lambda api, item: VectorLayerVersion(api, item['uuid'], item))
452
+
453
+
454
+ async def get_versions(self, **kwargs) -> List['VectorLayerVersion']:
455
+ """
456
+ [async] Get list of versions of the current vector layer object with optional filtering and pagination.
457
+
458
+ Keyword Args:
459
+ q (str): query filter based on OGC CQL standard. e.g. "field1 LIKE '%GIS%' AND created_at > '2021-01-01'"
460
+ 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.
461
+ search_fields (str): comma separated list of fields for searching.
462
+ 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.
463
+ return_count (bool): Whether to return total count. default is False.
464
+ skip (int): Number of items to skip. default is 0.
465
+ limit (int): Number of items to return. default is 10.
466
+ user_id (int): Specific user. privileges required.
467
+ shared (bool): Whether to return shared versions. default is False.
468
+
469
+ Returns:
470
+ List[VectorLayerVersion] | int: A list of vector layer version instances or the total number of versions.
471
+
472
+ Example:
473
+ >>> from geobox.aio import AsyncGeoboxClient
474
+ >>> from geobox.aio.vectorlayer import VectorLayer
475
+ >>> async with AsyncGeoboxClient() as client:
476
+ >>> layer = await client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
477
+ >>> versions = await layer.get_versions()
478
+ or
479
+ >>> versions = await layer.get_versions()
480
+ """
481
+ return await VectorLayerVersion.get_versions(self.api, layer_id=self.id, **kwargs)
482
+
483
+
484
+ @property
485
+ def wfs(self) -> str:
486
+ """
487
+ Get the WFS endpoint for the layer.
488
+
489
+ Returns:
490
+ str: The WFS endpoint for the layer.
491
+
492
+ Example:
493
+ >>> from geobox.aio import AsyncGeoboxClient
494
+ >>> from geobox.aio.vectorlayer import VectorLayer
495
+ >>> async with AsyncGeoboxClient() as client:
496
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
497
+ >>> layer.wfs
498
+ """
499
+ if self.api.access_token:
500
+ return f'{self.api.base_url}{self.endpoint}wfs/'
501
+ elif self.api.apikey:
502
+ return f'{self.api.base_url}{self.endpoint}apikey:{self.api.apikey}/wfs/'
503
+
504
+
505
+ async def get_fields(self) -> List['Field']:
506
+ """
507
+ [async] Get all fields in the layer.
508
+
509
+ Returns:
510
+ List[Field]: A list of Field instances representing the layer's fields.
511
+
512
+ Example:
513
+ >>> from geobox.aio import AsyncGeoboxClient
514
+ >>> from geobox.aio.vectorlayer import VectorLayer
515
+ >>> async with AsyncGeoboxClient() as client:
516
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
517
+ >>> fields = await layer.get_fields()
518
+ """
519
+ endpoint = urljoin(self.endpoint, 'fields/')
520
+ return await super()._get_list(api=self.api,
521
+ endpoint=endpoint,
522
+ factory_func=lambda api, item: Field(layer=self, data_type=FieldType(item['datatype']), field_id=item['id'], data=item))
523
+
524
+
525
+ async def get_field(self, field_id: int) -> 'Field':
526
+ """
527
+ [async] Get a specific field by its ID.
528
+
529
+ Args:
530
+ field_id (int, optional): The ID of the field to retrieve.
531
+
532
+ Returns:
533
+ Field: The requested field instance.
534
+
535
+ Raises:
536
+ NotFoundError: If the field with the specified ID is not found.
537
+
538
+ Example:
539
+ >>> from geobox.aio import AsyncGeoboxClient
540
+ >>> from geobox.aio.vectorlayer import VectorLayer
541
+ >>> async with AsyncGeoboxClient() as client:
542
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
543
+ >>> field = await layer.get_field(field_id=1)
544
+ """
545
+ field = next((f for f in await self.get_fields() if f.id == field_id), None)
546
+ if not field:
547
+ raise NotFoundError(f'Field with ID {field_id} not found in layer {self.name}')
548
+
549
+ return field
550
+
551
+
552
+ async def get_field_by_name(self, name: str) -> 'Field':
553
+ """
554
+ [async] Get a specific field by its name.
555
+
556
+ Args:
557
+ name (str): The name of the field to retrieve.
558
+
559
+ Returns:
560
+ Field: The requested field instance.
561
+
562
+ Raises:
563
+ NotFoundError: If the field with the specified name is not found.
564
+
565
+ Example:
566
+ >>> from geobox.aio import AsyncGeoboxClient
567
+ >>> from geobox.aio.vectorlayer import VectorLayer
568
+ >>> async with AsyncGeoboxClient() as client:
569
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
570
+ >>> field = await layer.get_field_by_name(name='test')
571
+ """
572
+ field = next((f for f in await self.get_fields() if f.name == name), None)
573
+ if not field:
574
+ raise NotFoundError(f"Field with name '{name}' not found in layer {self.name}")
575
+
576
+ return field
577
+
578
+
579
+ async def add_field(self, name: str, data_type: 'FieldType', data: Dict = {}) -> 'Field':
580
+ """
581
+ [async] Add a new field to the layer.
582
+
583
+ Args:
584
+ name (str): The name of the new field.
585
+ data_type (FieldType): The data type of the new field.
586
+ data (Dict): Additional field properties (display_name, description, etc.).
587
+
588
+ Returns:
589
+ Field: The newly created field instance.
590
+
591
+ Raises:
592
+ ValidationError: If the field data is invalid.
593
+
594
+ Example:
595
+ >>> from geobox.aio import AsyncGeoboxClient
596
+ >>> from geobox.aio.vectorlayer import VectorLayer
597
+ >>> async with AsyncGeoboxClient() as client:
598
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
599
+ >>> field = await layer.add_field(name="new_field", data_type=FieldType.String)
600
+ """
601
+ return await Field.create_field(self.api, layer=self, name=name, data_type=data_type, data=data)
602
+
603
+
604
+ async def calculate_field(self,
605
+ target_field: str,
606
+ expression: str,
607
+ q: str = None,
608
+ bbox: List = None,
609
+ bbox_srid: int = None,
610
+ feature_ids: List = None,
611
+ run_async: bool = True,
612
+ user_id: int = None) -> Union['Task', Dict]:
613
+ """
614
+ [async] Calculate values for a field based on an expression.
615
+
616
+ Args:
617
+ target_field (str): The field to calculate values for.
618
+ expression (str): The expression to use for calculation.
619
+ q (str, optional): Query to filter features. default is None.
620
+ bbox (List, optional): Bounding box to filter features. default is None.
621
+ bbox_srid (int, optional): Spatial reference ID for the bounding box. default is None.
622
+ feature_ids (List, optional): List of specific feature IDs to include. default is None
623
+ run_async (bool, optional): Whether to run the calculation asynchronously. default is True.
624
+ user_id (int, optional): Specific user. privileges required.
625
+
626
+ Returns:
627
+ Task | Dict: The task instance of the calculation operation or the api response if the run_async=False.
628
+
629
+ Example:
630
+ >>> from geobox.aio import AsyncGeoboxClient
631
+ >>> from geobox.aio.vectorlayer import VectorLayer
632
+ >>> async with AsyncGeoboxClient() as client:
633
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
634
+ >>> task = await layer.calculate_field(target_field="target_field",
635
+ ... expression="expression",
636
+ ... q="name like 'my_layer'",
637
+ ... bbox=[10, 20, 30, 40],
638
+ ... bbox_srid=3857,
639
+ ... feature_ids=[1, 2, 3],
640
+ ... run_async=True)
641
+ """
642
+ data = clean_data({
643
+ "target_field": target_field,
644
+ "expression": expression,
645
+ "q": q,
646
+ "bbox": bbox,
647
+ "bbox_srid": bbox_srid,
648
+ "feature_ids": feature_ids,
649
+ "run_async": run_async,
650
+ "user_id": user_id
651
+ })
652
+
653
+ endpoint = urljoin(self.endpoint, 'calculateField/')
654
+ response = await self.api.post(endpoint, data, is_json=False)
655
+ if run_async:
656
+ task = await Task.get_task(self.api, response.get('task_id'))
657
+ return task
658
+
659
+ return response
660
+
661
+
662
+ async def get_features(self, **kwargs) -> Union[List['Feature'], int]:
663
+ """
664
+ [async] Get features from the layer with optional filtering and pagination.
665
+
666
+ Keyword Args:
667
+ quant_factor (int): Quantization factor. This parameter is only used by topojson encoder and is ignored for other formats. Higher quantizaion value means higher geometry precision. default is 1000000.
668
+ skip (int): Number of features to skip. default is 0.
669
+ limit (int): Maximum number of features to return. default is 100.
670
+ user_id (int): Specific user. privileges required.
671
+ 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.
672
+ search_fields (str): comma separated list of fields for searching.
673
+ skip_geometry (bool): Whether to exclude geometry data. default is False.
674
+ return_count (bool): Whether to return total count. default is False.
675
+ feature_ids (list): Comma separated list of feature ids which should be filtered.
676
+ select_fields (str): comma separated field names which should be included to the result. default is "[ALL]".
677
+ skip_fields (str): comma separated field names which should be excluded from the result.
678
+ out_srid (int): srid (epsg code) of result features. e.g. 4326. default is 3857.
679
+ order_by (str): comma separated list of fields for sorting results [field1 A|D, field2 A|D, …]. e.g. name A, length D. NOTE: "A" denotes ascending order and "D" denotes descending order.
680
+ q (str): query filter based on OGC CQL standard. e.g. Name LIKE '%GIS%' AND INTERSECTS(geometry, 'SRID=3857;POLYGON((4901948 2885079, 7049893 2885079, 7049893 4833901, 4901948 4833901, 4901948 2885079))').
681
+ bbox (str): Bounding box to filter features by. e.g. [50.275, 35.1195, 51.4459, 36.0416].
682
+ bbox_srid (int): srid (epsg code) of bbox. e.g. 4326. default is 3857.
683
+
684
+ Returns:
685
+ List[Feature] | int: A list of Feature instances or the features count if return_count is True.
686
+
687
+ Example:
688
+ >>> from geobox.aio import AsyncGeoboxClient
689
+ >>> from geobox.aio.vectorlayer import VectorLayer
690
+ >>> async with AsyncGeoboxClient() as client:
691
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
692
+ >>> features = await layer.get_features(quant_factor=1000000,
693
+ ... skip=0,
694
+ ... limit=100,
695
+ ... skip_geometry=False,
696
+ ... return_count=False,
697
+ ... select_fields="fclass, osm_id",
698
+ ... out_srid=3857,
699
+ ... bbox_srid=3857)
700
+ """
701
+ params = {
702
+ 'f': 'json',
703
+ 'quant_factor': kwargs.get('quant_factor', 1000000),
704
+ 'skip': kwargs.get('skip', 0),
705
+ 'limit': kwargs.get('limit', 100),
706
+ 'user_id': kwargs.get('user_id', None),
707
+ 'search': kwargs.get('search', None),
708
+ 'search_fields': kwargs.get('search_fields', None),
709
+ 'skip_geometry': kwargs.get('skip_geometry', False),
710
+ 'return_count': kwargs.get('return_count', False),
711
+ 'feature_ids': kwargs.get('feature_ids', None),
712
+ 'select_fields': kwargs.get('select_fields', '[ALL]'),
713
+ 'skip_fields': kwargs.get('skip_fields', None),
714
+ 'out_srid': kwargs.get('out_srid', 3857),
715
+ 'order_by': kwargs.get('order_by', None),
716
+ 'q': kwargs.get('q', None),
717
+ 'bbox': kwargs.get('bbox', None),
718
+ 'bbox_srid': kwargs.get('bbox_srid', 3857)
719
+ }
720
+
721
+ return await super()._get_list(api=self.api,
722
+ endpoint=f'{self.endpoint}features/',
723
+ params=params,
724
+ factory_func=lambda api, item, srid: Feature(self, srid, item),
725
+ geojson=True)
726
+
727
+
728
+ async def get_feature(self, feature_id: int, out_srid: int = Feature.BASE_SRID) -> 'Feature':
729
+ """
730
+ [async] Get a specific feature by its ID.
731
+
732
+ Args:
733
+ feature_id (int): The ID of the feature to retrieve.
734
+ out_srid (int, optional): Output spatial reference ID. default is 3857.
735
+
736
+ Returns:
737
+ Feature: The requested feature instance.
738
+
739
+ Raises:
740
+ NotFoundError: If the feature with the specified ID is not found.
741
+
742
+ Example:
743
+ >>> from geobox.aio import AsyncGeoboxClient
744
+ >>> from geobox.aio.vectorlayer import VectorLayer
745
+ >>> async with AsyncGeoboxClient() as client:
746
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
747
+ >>> feature = await layer.get_feature(feature_id=1, out_srid=4326)
748
+ """
749
+ endpoint = f"{self.endpoint}features/{feature_id}"
750
+ response = await self.api.get(endpoint)
751
+ feature = Feature(self, data=response)
752
+ if out_srid != Feature.BASE_SRID:
753
+ feature.transform(out_srid)
754
+
755
+ return feature
756
+
757
+
758
+ async def create_feature(self, geojson: Dict)-> 'Feature':
759
+ """
760
+ [async] Create a new feature in the layer.
761
+
762
+ Args:
763
+ geojson (Dict): The feature data including properties and geometry.
764
+
765
+ Returns:
766
+ Feature: The newly created feature instance.
767
+
768
+ Raises:
769
+ ValidationError: If the feature data is invalid.
770
+
771
+ Example:
772
+ >>> from geobox.aio import AsyncGeoboxClient
773
+ >>> from geobox.aio.vectorlayer import VectorLayer
774
+ >>> async with AsyncGeoboxClient() as client:
775
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
776
+ >>> geojson = {
777
+ ... "type": "Feature",
778
+ ... "geometry": {"type": "Point", "coordinates": [10, 20]},
779
+ ... "properties": {"name": "My Point"}
780
+ ... }
781
+ >>> feature = await layer.create_feature(geojson=geojson)
782
+ """
783
+ return await Feature.create_feature(self, geojson)
784
+
785
+
786
+ async def delete_features(self, q: str = None, bbox: str = None, bbox_srid: int = None, feature_ids: List[int] = None,
787
+ run_async: bool = True, user_id: int = None) -> Union['Task', Dict]:
788
+ """
789
+ [async] Delete features from the layer based on specified criteria.
790
+
791
+ Args:
792
+ q (str, optional): Query to filter features to delete.
793
+ bbox (str, optional): Comma seprated bbox.
794
+ bbox_srid (int, optional): Spatial reference ID for the bounding box.
795
+ feature_ids (str, optional): Comma seprated feature ids.
796
+ run_async (bool, optional): Whether to run the deletion asynchronously. default is True.
797
+ user_id (int, optional): Specific user. privileges required.
798
+
799
+ Returns:
800
+ Task | Dict: The task instance of the deletion operation or the api response if run_async=False.
801
+
802
+ Raises:
803
+ ValidationError: If the deletion parameters are invalid.
804
+
805
+ Example:
806
+ >>> from geobox.aio import AsyncGeoboxClient
807
+ >>> from geobox.aio.vectorlayer import VectorLayer
808
+ >>> async with AsyncGeoboxClient() as client:
809
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
810
+ >>> await layer.delete_features(q="name like 'my_layer'",
811
+ ... bbox='10, 20, 30, 40',
812
+ ... bbox_srid=3857,
813
+ ... feature_ids='1, 2, 3',
814
+ ... run_async=True)
815
+ """
816
+ data = clean_data({
817
+ "q": q,
818
+ "bbox": bbox,
819
+ "bbox_srid": bbox_srid,
820
+ "feature_ids": feature_ids,
821
+ "run_async": run_async,
822
+ "user_id": user_id
823
+ })
824
+
825
+ endpoint = urljoin(self.endpoint, 'deleteFeatures/')
826
+ response = await self.api.post(endpoint, data, is_json=False)
827
+ if run_async:
828
+ task = await Task.get_task(self.api, response.get('task_id'))
829
+ return task
830
+
831
+ return response
832
+
833
+
834
+ async def import_features(self,
835
+ file: 'File',
836
+ input_geom_type: 'InputGeomType' = None,
837
+ input_layer_name: str = None,
838
+ input_dataset: str = None,
839
+ user_id: int = None,
840
+ input_srid: int = None,
841
+ file_encoding: str = "utf-8",
842
+ replace_domain_codes_by_values: bool = False,
843
+ report_errors: bool = True) -> 'Task':
844
+ """
845
+ [async] Import features from a file into the layer.
846
+
847
+ Args:
848
+ file (File): file object to import.
849
+ input_geom_type (InputGeomType, optional): Type of geometry in the input file.
850
+ input_layer_name (str, optional): Name of the layer in the input file.
851
+ input_dataset (str, optional): Name of the dataset in the input file.
852
+ user_id (int, optional): Specific user. privileges required.
853
+ input_srid (int, optional): Spatial reference ID of the input data.
854
+ file_encoding (str, optional): Character encoding of the input file.
855
+ replace_domain_codes_by_values (bool, optional): Whether to replace domain codes with values.
856
+ report_errors (bool, optional): Whether to report import errors.
857
+
858
+ Returns:
859
+ Task: The task instance of the import operation.
860
+
861
+ Raises:
862
+ ValidationError: If the import parameters are invalid.
863
+
864
+ Example:
865
+ >>> from geobox.aio import AsyncGeoboxClient
866
+ >>> from geobox.aio.vectorlayer import VectorLayer
867
+ >>> async with AsyncGeoboxClient() as client:
868
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
869
+ >>> file = await client.get_file(uuid="12345678-1234-5678-1234-567812345678")
870
+ >>> task = await layer.import_features(file=file,
871
+ ... input_geom_type=InputGeomType.POINT,
872
+ ... input_layer_name="my_layer",
873
+ ... input_dataset="my_dataset",
874
+ ... input_srid=3857,
875
+ ... file_encoding="utf-8",
876
+ ... replace_domain_codes_by_values=False,
877
+ ... report_errors=True)
878
+ """
879
+ data = clean_data({
880
+ "file_uuid": file.uuid,
881
+ "input_layer": input_layer_name,
882
+ "input_geom_type": input_geom_type.value if isinstance(input_geom_type, InputGeomType) else input_geom_type,
883
+ "replace_domain_codes_by_values": replace_domain_codes_by_values,
884
+ "input_dataset": input_dataset,
885
+ "user_id": user_id,
886
+ "input_srid": input_srid,
887
+ "file_encoding": file_encoding,
888
+ "report_errors": report_errors
889
+ })
890
+
891
+ endpoint = urljoin(self.endpoint, 'import/')
892
+ response = await self.api.post(endpoint, data, is_json=False)
893
+ task = await Task.get_task(self.api, response.get('task_id'))
894
+ return task
895
+
896
+
897
+ async def export_features(self,
898
+ out_filename: str,
899
+ out_format: 'FileOutputFormat',
900
+ replace_domain_codes_by_values: bool = False,
901
+ run_async: bool = True,
902
+ bbox: List[float] = None,
903
+ out_srid: int = None,
904
+ zipped: bool = True,
905
+ feature_ids: List[int] = None,
906
+ bbox_srid: int = None,
907
+ q: str = None, fields: List[str] = None) -> Union['Task', Dict]:
908
+ """
909
+ [async] Export features from the layer to a file.
910
+
911
+ Args:
912
+ out_filename (str): Name of the output file.
913
+ out_format (FileOutputFormat): Format of the output file (e.g., 'Shapefile', 'GPKG', 'GeoJSON', 'CSV', 'KML', 'DXF').
914
+ replace_domain_codes_by_values (bool, optional): Whether to replace domain codes with values.
915
+ run_async (bool, optional): Whether to run the export asynchronously.
916
+ bbox (List, optional): Bounding box to filter features.
917
+ out_srid (int): Spatial reference ID for the output.
918
+ zipped (bool, optional): Whether to compress the output file.
919
+ feature_ids (List[int], optional): List of specific feature IDs to export.
920
+ bbox_srid (int, optional): Spatial reference ID for the bounding box.
921
+ q (str, optional): Query to filter features.
922
+ fields (List[str], optional): List of fields to include in the export.
923
+
924
+ Returns:
925
+ Task | Dict: The task instance of the export operation or the api response if run_async=False.
926
+
927
+ Raises:
928
+ ValidationError: If the export parameters are invalid.
929
+
930
+ Example:
931
+ >>> from geobox.aio import AsyncGeoboxClient
932
+ >>> from geobox.aio.vectorlayer import VectorLayer
933
+ >>> async with AsyncGeoboxClient() as client:
934
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
935
+ >>> task = await layer.export_features(out_filename="output.shp",
936
+ ... out_format="shp",
937
+ ... replace_domain_codes_by_values=False,
938
+ ... run_async=True,
939
+ ... bbox=[10, 20, 30, 40],
940
+ ... out_srid=3857,
941
+ ... zipped=True,
942
+ ... feature_ids=[1, 2, 3])
943
+ """
944
+ data = clean_data({
945
+ "replace_domain_codes_by_values": replace_domain_codes_by_values,
946
+ "out_format": out_format.value,
947
+ "run_async": run_async,
948
+ "bbox": bbox,
949
+ "out_srid": out_srid,
950
+ "zipped": zipped,
951
+ "feature_ids": feature_ids,
952
+ "bbox_srid": bbox_srid,
953
+ "q": q,
954
+ "out_filename": out_filename,
955
+ "fields": fields
956
+ })
957
+
958
+ endpoint = urljoin(self.endpoint, 'export/')
959
+ response = await self.api.post(endpoint, data, is_json=False)
960
+ if run_async:
961
+ task = await Task.get_task(self.api, response.get('task_id'))
962
+ return task
963
+
964
+ return response
965
+
966
+
967
+ async def create_view(self, name: str, display_name: str = None, description: str = None,
968
+ view_filter: str = None, view_extent: Dict = None, view_cols: str = None) -> 'VectorLayerView':
969
+ """
970
+ [async] Create a view of the vector layer.
971
+
972
+ Args:
973
+ name (str): The name of the view.
974
+ display_name (str, optional): The display name of the view.
975
+ description (str, optional): The description of the view.
976
+ view_filter (str, optional): The filter for the view.
977
+ view_extent (List[float], optional): The extent of the view.
978
+ view_cols (str, optional): The columns to include in the view.
979
+
980
+ Returns:
981
+ VectorLayerView: The created view instance.
982
+
983
+ Raises:
984
+ ValidationError: If the view parameters are invalid.
985
+
986
+ Example:
987
+ >>> from geobox.aio import AsyncGeoboxClient
988
+ >>> from geobox.aio.vectorlayer import VectorLayer
989
+ >>> async with AsyncGeoboxClient() as client:
990
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
991
+ >>> view = await layer.create_view(name="my_view",
992
+ ... display_name="My View",
993
+ ... description="This is a view of my layer",
994
+ ... view_filter="province_name = 'Tehran'",
995
+ ... view_extent=[10, 20, 30, 40],
996
+ ... view_cols="[ALL]")
997
+ """
998
+ from .view import VectorLayerView
999
+
1000
+ data = {
1001
+ "name": name,
1002
+ "display_name": display_name,
1003
+ "description": description,
1004
+ "view_filter": view_filter,
1005
+ "view_extent": view_extent,
1006
+ "view_cols": view_cols
1007
+ }
1008
+
1009
+ return await super()._create(api=self.api,
1010
+ endpoint=f'{self.endpoint}views/',
1011
+ data=data,
1012
+ factory_func=lambda api, item: VectorLayerView(api, item['uuid'], self.layer_type, item))
1013
+
1014
+
1015
+ def get_tile_pbf_url(self, x: int = '{x}', y: int = '{y}', z: int = '{z}') -> str:
1016
+ """
1017
+ Get a vector tile pbf url for the layer.
1018
+
1019
+ Args:
1020
+ x (int, optional): X coordinate of the tile.
1021
+ y (int, optional): Y coordinate of the tile.
1022
+ z (int, optioanl): Zoom level of the tile.
1023
+
1024
+ Returns:
1025
+ str: The vector tile url.
1026
+
1027
+ Example:
1028
+ >>> from geobox.aio import AsyncGeoboxClient
1029
+ >>> from geobox.aio.vectorlayer import VectorLayer
1030
+ >>> async with AsyncGeoboxClient() as client:
1031
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
1032
+ >>> tile = layer.get_tile(x=10, y=20, z=1)
1033
+ """
1034
+ endpoint = f'{self.api.base_url}{self.endpoint}tiles/{z}/{x}/{y}.pbf'
1035
+
1036
+ if not self.api.access_token and self.api.apikey:
1037
+ endpoint = f'{endpoint}?apikey={self.api.apikey}'
1038
+
1039
+ return endpoint
1040
+
1041
+
1042
+ async def get_tile_json(self) -> Dict:
1043
+ """
1044
+ [async] Get the vector tile JSON configuration for the layer.
1045
+
1046
+ Returns:
1047
+ Dict: The vector tile JSON configuration.
1048
+
1049
+ Example:
1050
+ >>> from geobox.aio import AsyncGeoboxClient
1051
+ >>> from geobox.aio.vectorlayer import VectorLayer
1052
+ >>> async with AsyncGeoboxClient() as client:
1053
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
1054
+ >>> tile_json = await layer.get_tile_json()
1055
+ """
1056
+ endpoint = urljoin(self.endpoint, 'tilejson.json')
1057
+ return await self.api.get(endpoint)
1058
+
1059
+
1060
+ @property
1061
+ async def settings(self) -> Dict:
1062
+ """
1063
+ [async] Get the layer's settings.
1064
+
1065
+ Returns:
1066
+ Dict: The layer settings.
1067
+
1068
+ Example:
1069
+ >>> from geobox.aio import AsyncGeoboxClient
1070
+ >>> from geobox.aio.vectorlayer import VectorLayer
1071
+ >>> async with AsyncGeoboxClient() as client:
1072
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
1073
+ >>> setting = await layer.setting
1074
+ """
1075
+ return await super()._get_settings(endpoint=self.endpoint)
1076
+
1077
+
1078
+ async def update_settings(self, settings: Dict) -> Dict:
1079
+ """
1080
+ [async] Update the settings
1081
+
1082
+ settings (Dict): settings dictionary
1083
+
1084
+ Returns:
1085
+ Dict: updated settings
1086
+
1087
+ Example:
1088
+ >>> from geobox.aio import AsyncGeoboxClient
1089
+ >>> from geobox.aio.vectorlayer import VectorLayer
1090
+ >>> async with AsyncGeoboxClient() as client:
1091
+ >>> layer1 = await client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
1092
+ >>> layer2 = await client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
1093
+ >>> await layer1.update_settings(await layer2.settings)
1094
+ """
1095
+ return await super()._set_settings(self.endpoint, settings)
1096
+
1097
+
1098
+ async def set_settings(self, **kwargs) -> Dict:
1099
+ """
1100
+ [async] Set the settings of the Vector Layer.
1101
+
1102
+ Keyword Args:
1103
+ title_field (str): The field to use as the title.
1104
+ domain_display_type (str): The type of domain display.
1105
+ allow_export (bool): Whether to allow export.
1106
+ editable (bool): Whether to allow editing.
1107
+ edit_geometry (bool): Whether to allow editing the geometry.
1108
+ editable_attributes (str): The attributes to allow editing.
1109
+ allow_insert (bool): Whether to allow inserting.
1110
+ allow_delete (bool): Whether to allow deleting.
1111
+ min_zoom (int): The minimum zoom level.
1112
+ max_zoom (int): The maximum zoom level.
1113
+ max_features (int): The maximum number of features.
1114
+ filter_features (bool): Whether to filter features.
1115
+ fields (List[str]): The fields to include in the layer.
1116
+ use_cache (bool): Whether to use caching.
1117
+ cache_until_zoom (int): The zoom level to cache until.
1118
+
1119
+ Returns:
1120
+ Dict: The updated settings.
1121
+
1122
+ Raises:
1123
+ ValidationError: If the settings data is invalid.
1124
+
1125
+ Example:
1126
+ >>> from geobox.aio import AsyncGeoboxClient
1127
+ >>> from geobox.aio.vectorlayer import VectorLayer
1128
+ >>> async with AsyncGeoboxClient() as client:
1129
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
1130
+ >>> await layer.set_settings(title_field="name",
1131
+ ... domain_display_type="Value",
1132
+ ... allow_export=True,
1133
+ ... editable=True,
1134
+ ... edit_geometry=True,
1135
+ ... editable_attributes="[ALL]",
1136
+ ... allow_insert=True,
1137
+ ... allow_delete=True,
1138
+ ... min_zoom=0,
1139
+ ... max_zoom=24,
1140
+ ... max_features=65536,
1141
+ ... filter_features=True,
1142
+ ... fields=["id"],
1143
+ ... use_cache=True,
1144
+ ... cache_until_zoom=17)
1145
+ """
1146
+ general_settings = {'title_field', 'domain_display_type', 'allow_export'}
1147
+ edit_settings = {'editable', 'edit_geometry', 'editable_attributes', 'allow_insert', 'allow_delete'}
1148
+ tile_settings = {'min_zoom', 'max_zoom', 'max_features', 'filter_features', 'fields', 'use_cache', 'cache_until_zoom'}
1149
+
1150
+ settings = await self.settings
1151
+
1152
+ for key, value in kwargs.items():
1153
+ if key in general_settings:
1154
+ settings['general_settings'][key] = value
1155
+ elif key in edit_settings:
1156
+ settings['edit_settings'][key] = value
1157
+ elif key in tile_settings:
1158
+ settings['tile_settings'][key] = value
1159
+
1160
+ return await super()._set_settings(endpoint=self.endpoint, data=settings)
1161
+
1162
+
1163
+ async def seed_cache(self, from_zoom: int = None, to_zoom: int = None, ignore_cache: bool = False, workers: int = 1, user_id: int = None) -> List['Task']:
1164
+ """
1165
+ [async] Seed the cache for the layer.
1166
+
1167
+ Args:
1168
+ from_zoom (int, optional): The zoom level to start caching from.
1169
+ to_zoom (int, optional): The zoom level to stop caching at.
1170
+ ignore_cache (bool, optional): Whether to ignore the cache. default is False.
1171
+ workers (int, optional): The number of workers to use. default is 1.
1172
+ user_id (int, optional): Specific user. privileges required.
1173
+
1174
+ Returns:
1175
+ List[Task]: The task instance of the cache seeding operation.
1176
+
1177
+ Example:
1178
+ >>> from geobox.aio import AsyncGeoboxClient
1179
+ >>> from geobox.aio.vectorlayer import VectorLayer
1180
+ >>> async with AsyncGeoboxClient() as client:
1181
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
1182
+ >>> task = await layer.seed_cache(from_zoom=0, to_zoom=10, ignore_cache=False, workers=1)
1183
+ """
1184
+ data = {
1185
+ 'from_zoom': from_zoom,
1186
+ 'to_zoom': to_zoom,
1187
+ 'ignore_cache': ignore_cache,
1188
+ 'workers': workers,
1189
+ 'user_id': user_id
1190
+ }
1191
+ return await super()._seed_cache(endpoint=self.endpoint, data=data)
1192
+
1193
+
1194
+ async def clear_cache(self) -> None:
1195
+ """
1196
+ [async] Clear the layer's cache.
1197
+
1198
+ Returns:
1199
+ None
1200
+
1201
+ Example:
1202
+ >>> from geobox.aio import AsyncGeoboxClient
1203
+ >>> from geobox.aio.vectorlayer import VectorLayer
1204
+ >>> async with AsyncGeoboxClient() as client:
1205
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
1206
+ >>> await layer.clear_cache()
1207
+ """
1208
+ await super()._clear_cache(endpoint=self.endpoint)
1209
+
1210
+
1211
+ @property
1212
+ async def cache_size(self) -> int:
1213
+ """
1214
+ [async] Get the size of the layer's cache.
1215
+
1216
+ Returns:
1217
+ int: The size of the layer's cache.
1218
+
1219
+ Example:
1220
+ >>> from geobox.aio import AsyncGeoboxClient
1221
+ >>> from geobox.aio.vectorlayer import VectorLayer
1222
+ >>> async with AsyncGeoboxClient() as client:
1223
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
1224
+ >>> await layer.cache_size
1225
+ """
1226
+ return await super()._cache_size(endpoint=self.endpoint)
1227
+
1228
+
1229
+ async def update_stats(self) -> None:
1230
+ """
1231
+ [async] Update the layer's statistics.
1232
+
1233
+ Returns:
1234
+ None
1235
+
1236
+ Example:
1237
+ >>> from geobox.aio import AsyncGeoboxClient
1238
+ >>> from geobox.aio.vectorlayer import VectorLayer
1239
+ >>> async with AsyncGeoboxClient() as client:
1240
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
1241
+ >>> await layer.update_stats()
1242
+ """
1243
+ endpoint = urljoin(self.endpoint, 'updateStats/')
1244
+ return await self.api.post(endpoint)
1245
+
1246
+
1247
+ async def prune_edited_areas(self) -> None:
1248
+ """
1249
+ [async] Prune edited areas. This method eliminates edited areas when there are too many of them. Cache builder uses this edited areas for partial cache generating.
1250
+
1251
+ Returns:
1252
+ None
1253
+
1254
+ Example:
1255
+ >>> from geobox.aio import AsyncGeoboxClient
1256
+ >>> from geobox.aio.vectorlayer import VectorLayer
1257
+ >>> async with AsyncGeoboxClient() as client:
1258
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
1259
+ >>> await layer.prune_edited_areas()
1260
+ """
1261
+ endpoint = urljoin(self.endpoint, 'prune/')
1262
+ return await self.api.post(endpoint)
1263
+
1264
+
1265
+ async def get_attachments(self, **kwargs) -> List['Attachment']:
1266
+ """
1267
+ [async] Get the resouces attachments
1268
+
1269
+ Keyword Args:
1270
+ element_id (str): the id of the element with attachment.
1271
+ search (str): search term for keyword-based searching among all textual fields.
1272
+ 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.
1273
+ skip (int): Number of items to skip. default is 0.
1274
+ limit (int): Number of items to return. default is 10.
1275
+ return_count (bool): Whether to return total count. default is False.
1276
+
1277
+ Returns:
1278
+ List[Attachment] | int: A list of attachments instances or the total number of attachments.
1279
+
1280
+ Raises:
1281
+ TypeError: if the resource type is not supported
1282
+
1283
+ Example:
1284
+ >>> from geobox.aio import AsyncGeoboxClient
1285
+ >>> from geobox.aio.vectorlayer import VectorLayer
1286
+ >>> async with AsyncGeoboxClient() as client:
1287
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
1288
+ >>> await layer.get_attachments()
1289
+ """
1290
+ from .attachment import Attachment
1291
+
1292
+ return await Attachment.get_attachments(self.api, resource=self, **kwargs)
1293
+
1294
+
1295
+ async def create_attachment(self,
1296
+ name: str,
1297
+ loc_x: int,
1298
+ loc_y: int,
1299
+ file: 'File',
1300
+ feature: 'Feature' = None,
1301
+ display_name: str = None,
1302
+ description: str = None) -> 'Attachment':
1303
+ """
1304
+ [async] Create a new Attachment.
1305
+
1306
+ Args:
1307
+ name (str): The name of the scene.
1308
+ loc_x (int): x parameter of the attachment location.
1309
+ loc_y (int): y parameter of the attachment location.
1310
+ file (File): the file object.
1311
+ feature (Feature, optional): the feature object.
1312
+ display_name (str, optional): The display name of the scene.
1313
+ description (str, optional): The description of the scene.
1314
+
1315
+ Returns:
1316
+ Attachment: The newly created Attachment instance.
1317
+
1318
+ Raises:
1319
+ ValidationError: If the Attachment data is invalid.
1320
+
1321
+ Example:
1322
+ >>> from geobox.aio import AsyncGeoboxClient
1323
+ >>> from geobox.aio.vectorlayer import VectorLayer
1324
+ >>> async with AsyncGeoboxClient() as client:
1325
+ >>> layer = await VectorLayer.get_vector(api=client, uuid="12345678-1234-5678-1234-567812345678")
1326
+ >>> file = await client.get_file(uuid="12345678-1234-5678-1234-567812345678")
1327
+ >>> await layer.create_attachment(name='test', loc_x=10, loc_y=10, file=file)
1328
+ """
1329
+ from .attachment import Attachment
1330
+
1331
+ return await Attachment.create_attachment(self.api,
1332
+ name=name,
1333
+ loc_x=loc_x,
1334
+ loc_y=loc_y,
1335
+ resource=self,
1336
+ file=file,
1337
+ feature=feature,
1338
+ display_name=display_name,
1339
+ description=description)
1340
+
1341
+
1342
+ def to_sync(self, sync_client: 'SyncGeoboxClient') -> 'SyncVectorLayer':
1343
+ """
1344
+ Switch to sync version of the vector layer instance to have access to the sync methods
1345
+
1346
+ Args:
1347
+ sync_client (SyncGeoboxClient): The sync version of the GeoboxClient instance for making requests.
1348
+
1349
+ Returns:
1350
+ geobox.vectorlayer.VectorLayer: the sync instance of the vector layer.
1351
+
1352
+ Example:
1353
+ >>> from geobox import Geoboxclient
1354
+ >>> from geobox.aio import AsyncGeoboxClient
1355
+ >>> from geobox.aio.vectorlayer import VectorLayer
1356
+ >>> client = GeoboxClient()
1357
+ >>> async with AsyncGeoboxClient() as async_client:
1358
+ >>> layer = await VectorLayer.get_vector(async_client, uuid="12345678-1234-5678-1234-567812345678")
1359
+ >>> sync_layer = layer.to_sync(client)
1360
+ """
1361
+ from ..vectorlayer import VectorLayer as SyncVectorLayer
1362
+
1363
+ return SyncVectorLayer(api=sync_client, uuid=self.uuid, layer_type=self.layer_type, data=self.data)