geobox 2.1.0__py3-none-any.whl → 2.2.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 (70) hide show
  1. geobox/__init__.py +61 -63
  2. geobox/aio/__init__.py +61 -63
  3. geobox/aio/api.py +491 -574
  4. geobox/aio/apikey.py +263 -263
  5. geobox/aio/attachment.py +341 -339
  6. geobox/aio/base.py +261 -262
  7. geobox/aio/basemap.py +196 -196
  8. geobox/aio/dashboard.py +340 -342
  9. geobox/aio/feature.py +35 -35
  10. geobox/aio/field.py +315 -321
  11. geobox/aio/file.py +72 -72
  12. geobox/aio/layout.py +340 -341
  13. geobox/aio/log.py +23 -23
  14. geobox/aio/map.py +1033 -1034
  15. geobox/aio/model3d.py +415 -415
  16. geobox/aio/mosaic.py +696 -696
  17. geobox/aio/plan.py +314 -314
  18. geobox/aio/query.py +693 -693
  19. geobox/aio/raster.py +88 -454
  20. geobox/aio/{analysis.py → raster_analysis.py} +153 -170
  21. geobox/aio/route.py +4 -4
  22. geobox/aio/scene.py +340 -342
  23. geobox/aio/settings.py +18 -18
  24. geobox/aio/task.py +404 -402
  25. geobox/aio/tile3d.py +337 -339
  26. geobox/aio/tileset.py +102 -103
  27. geobox/aio/usage.py +52 -51
  28. geobox/aio/user.py +506 -507
  29. geobox/aio/vector_tool.py +1968 -0
  30. geobox/aio/vectorlayer.py +316 -414
  31. geobox/aio/version.py +272 -273
  32. geobox/aio/view.py +1019 -983
  33. geobox/aio/workflow.py +340 -341
  34. geobox/api.py +14 -98
  35. geobox/apikey.py +262 -262
  36. geobox/attachment.py +336 -337
  37. geobox/base.py +384 -384
  38. geobox/basemap.py +194 -194
  39. geobox/dashboard.py +339 -341
  40. geobox/enums.py +31 -1
  41. geobox/feature.py +31 -10
  42. geobox/field.py +320 -320
  43. geobox/file.py +4 -4
  44. geobox/layout.py +339 -340
  45. geobox/log.py +4 -4
  46. geobox/map.py +1031 -1032
  47. geobox/model3d.py +410 -410
  48. geobox/mosaic.py +696 -696
  49. geobox/plan.py +313 -313
  50. geobox/query.py +691 -691
  51. geobox/raster.py +5 -368
  52. geobox/{analysis.py → raster_analysis.py} +108 -128
  53. geobox/scene.py +341 -342
  54. geobox/settings.py +194 -194
  55. geobox/task.py +399 -400
  56. geobox/tile3d.py +337 -338
  57. geobox/tileset.py +4 -4
  58. geobox/usage.py +3 -3
  59. geobox/user.py +503 -503
  60. geobox/vector_tool.py +1968 -0
  61. geobox/vectorlayer.py +5 -110
  62. geobox/version.py +272 -272
  63. geobox/view.py +981 -981
  64. geobox/workflow.py +338 -339
  65. {geobox-2.1.0.dist-info → geobox-2.2.1.dist-info}/METADATA +15 -1
  66. geobox-2.2.1.dist-info/RECORD +72 -0
  67. geobox-2.1.0.dist-info/RECORD +0 -70
  68. {geobox-2.1.0.dist-info → geobox-2.2.1.dist-info}/WHEEL +0 -0
  69. {geobox-2.1.0.dist-info → geobox-2.2.1.dist-info}/licenses/LICENSE +0 -0
  70. {geobox-2.1.0.dist-info → geobox-2.2.1.dist-info}/top_level.txt +0 -0
geobox/attachment.py CHANGED
@@ -1,338 +1,337 @@
1
- from typing import List, Dict, Optional, TYPE_CHECKING, Union
2
- from urllib.parse import urljoin
3
-
4
- from .base import Base
5
- from .utils import clean_data
6
- from .map import Map
7
- from .vectorlayer import VectorLayer
8
- from .view import VectorLayerView
9
- from .file import File
10
-
11
- if TYPE_CHECKING:
12
- from . import GeoboxClient
13
- from .feature import Feature
14
- from .aio import AsyncGeoboxClient
15
- from .aio.attachment import Attachment as AsyncAtachment
16
-
17
-
18
- class Attachment(Base):
19
-
20
- BASE_ENDPOINT = 'attachments/'
21
-
22
- def __init__(self,
23
- api: 'GeoboxClient',
24
- attachment_id: str,
25
- data: Optional[Dict] = {}):
26
- """
27
- Initialize an Attachment instance.
28
-
29
- Args:
30
- api (GeoboxClient): The GeoboxClient instance for making requests.
31
- attachment_id (str): The id for the attachment.
32
- data (Dict, optional): The data of the attachment.
33
- """
34
- super().__init__(api, data=data)
35
- self.attachment_id = attachment_id
36
- self.endpoint = f"{self.BASE_ENDPOINT}{str(self.attachment_id)}/"
37
-
38
-
39
- def __repr__(self) -> str:
40
- """
41
- Return a string representation of the attachment.
42
-
43
- Returns:
44
- str: The string representation of the attachment.
45
- """
46
- return f"Attachment(id={self.attachment_id}, name={self.name})"
47
-
48
-
49
- @property
50
- def file(self) -> 'File':
51
- """
52
- Attachment file property
53
-
54
- Returns:
55
- File: the file object
56
- """
57
- return File(self.api, self.data['file'].get('uuid'), self.data['file'])
58
-
59
-
60
- @classmethod
61
- def get_attachments(cls, api: 'GeoboxClient', resource: Union['Map', 'VectorLayer', 'VectorLayerView'], **kwargs) -> Union[List['Attachment'], int]:
62
- """
63
- Get list of attachments with optional filtering and pagination.
64
-
65
- Args:
66
- api (GeoboxClient): The GeoboxClient instance for making requests.
67
- resource (Map | VectorLayer | VectorLayerView): options are: Map, Vector, View objects
68
-
69
- Keyword Args:
70
- element_id (str): the id of the element with attachment.
71
- search (str): search term for keyword-based searching among all textual fields.
72
- 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.
73
- skip (int): Number of items to skip. default is 0.
74
- limit (int): Number of items to return. default is 10.
75
- return_count (bool): Whether to return total count. default is False.
76
-
77
- Returns:
78
- List[Attachment] | int: A list of attachments instances or the total number of attachments.
79
-
80
- Raises:
81
- TypeError: if the resource type is not supported
82
-
83
- Example:
84
- >>> from geobox import GeoboxClient
85
- >>> from geobox.attachment import Attachment
86
- >>> client = GeoboxClient()
87
- >>> map = client.get_maps()[0]
88
- >>> attachments = Attachment.get_attachments(client, resource=map, q="name LIKE '%My attachment%'")
89
- or
90
- >>> attachments = client.get_attachments(resource=map, q="name LIKE '%My attachment%'")
91
- """
92
- if type(resource) == VectorLayer:
93
- resource_type = 'vector'
94
-
95
- elif type(resource) == VectorLayerView:
96
- resource_type = 'view'
97
-
98
- elif type(resource) == Map:
99
- resource_type = 'map'
100
-
101
- else:
102
- raise TypeError('resource must be a vectorlayer or view or map object')
103
-
104
-
105
- params = {
106
- 'resource_type': resource_type,
107
- 'resource_uuid': resource.uuid,
108
- 'element_id': kwargs.get('element_id'),
109
- 'search': kwargs.get('search'),
110
- 'order_by': kwargs.get('order_by'),
111
- 'skip': kwargs.get('skip', 0),
112
- 'limit': kwargs.get('limit', 10),
113
- 'return_count': kwargs.get('return_count')
114
- }
115
- return super()._get_list(api, cls.BASE_ENDPOINT, params, factory_func=lambda api, item: Attachment(api, item['id'], item))
116
-
117
-
118
- @classmethod
119
- def create_attachment(cls,
120
- api: 'GeoboxClient',
121
- name: str,
122
- loc_x: int,
123
- loc_y: int,
124
- resource: Union['Map', 'VectorLayer', 'VectorLayerView'],
125
- file: 'File',
126
- feature: 'Feature' = None,
127
- display_name: str = None,
128
- description: str = None, ) -> 'Attachment':
129
- """
130
- Create a new Attachment.
131
-
132
- Args:
133
- api (GeoboxClient): The GeoboxClient instance for making requests.
134
- name (str): The name of the scene.
135
- loc_x (int): x parameter of the attachment location.
136
- loc_y (int): y parameter of the attachment location.
137
- resource (Map | VectorLayer | VectorLayerView): the resource object.
138
- file (File): the file object.
139
- feature (Feature, optional): the feature object.
140
- display_name (str, optional): The display name of the scene.
141
- description (str, optional): The description of the scene.
142
-
143
- Returns:
144
- Attachment: The newly created Attachment instance.
145
-
146
- Raises:
147
- ValidationError: If the Attachment data is invalid.
148
-
149
- Example:
150
- >>> from geobox import GeoboxClient
151
- >>> from geobox.attachment import Attachment
152
- >>> client = GeoboxClient()
153
- >>> layer = client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
154
- >>> feature = layer.get_feature(feature_id=1)
155
- >>> file = client.get_file(uuid="12345678-1234-5678-1234-567812345678")
156
- >>> attachment = Attachment.create_attachment(client,
157
- ... name="my_attachment",
158
- ... loc_x=30,
159
- ... loc_y=50,
160
- ... resource=layer,
161
- ... file=file,
162
- ... feature=feature,
163
- ... display_name="My Attachment",
164
- ... description="Attachment Description")
165
- or
166
- >>> attachment = client.create_attachment(name="my_attachment",
167
- ... loc_x=30,
168
- ... loc_y=50,
169
- ... resource=layer,
170
- ... file=file,
171
- ... feature=feature,
172
- ... display_name="My Attachment",
173
- ... description="Attachment Description")
174
- """
175
- if isinstance(resource, VectorLayer):
176
- resource_type = 'vector'
177
-
178
- if isinstance(resource, VectorLayerView):
179
- resource_type = 'view'
180
-
181
- if isinstance(resource, Map):
182
- resource_type = 'map'
183
-
184
- data = {
185
- "name": name,
186
- "display_name": display_name,
187
- "description": description,
188
- "loc_x": loc_x,
189
- "loc_y": loc_y,
190
- "resource_type": resource_type,
191
- "resource_uuid": resource.uuid,
192
- "element_id": feature.id if feature else None,
193
- "file_id": file.id
194
- }
195
- return super()._create(api, cls.BASE_ENDPOINT, data, factory_func=lambda api, item: Attachment(api, item['id'], item))
196
-
197
-
198
- @classmethod
199
- def update_attachment(cls, api: 'GeoboxClient', attachment_id: int, **kwargs) -> Dict:
200
- """
201
- Update the attachment.
202
-
203
- Args:
204
- api (GeoboxClient): The GeoboxClient instance for making requests.
205
- attachment_id (int): the attachment id.
206
-
207
- Keyword Args:
208
- name (str): The name of the attachment.
209
- display_name (str): The display name of the attachment.
210
- description (str): The description of the attachment.
211
- loc_x (int): x parameter of the attachment location.
212
- loc_y (int): y parameter of the attachment location.
213
-
214
- Returns:
215
- Dict: The updated attachment data.
216
-
217
- Raises:
218
- ValidationError: If the attachment data is invalid.
219
-
220
- Example:
221
- >>> from geobox import GeoboxClient
222
- >>> from geobox.attachment import Attachment
223
- >>> client = GeoboxClient()
224
- >>> Attachment.update_attachment(client, attachment_id=1, display_name="New Display Name")
225
- or
226
- >>> client.update_attachment(attachment_id=1, display_name="New Display Name")
227
- """
228
- data = clean_data({
229
- "name": kwargs.get('name'),
230
- "display_name": kwargs.get('display_name'),
231
- "description": kwargs.get('description'),
232
- "loc_x": kwargs.get('loc_x'),
233
- "loc_y": kwargs.get('loc_y')
234
- })
235
- endpoint = urljoin(cls.BASE_ENDPOINT, str(attachment_id))
236
- response = api.put(endpoint, data)
237
- return response
238
-
239
-
240
- def update(self, **kwargs) -> Dict:
241
- """
242
- Update the attachment.
243
-
244
- Keyword Args:
245
- name (str): The name of the attachment.
246
- display_name (str): The display name of the attachment.
247
- description (str): The description of the attachment.
248
- loc_x (int): x parameter of the attachment location.
249
- loc_y (int): y parameter of the attachment location.
250
-
251
- Returns:
252
- Dict: The updated attachment data.
253
-
254
- Raises:
255
- ValidationError: If the attachment data is invalid.
256
-
257
- Example:
258
- >>> from geobox import GeoboxClient
259
- >>> from geobox.attachment import Attachment
260
- >>> client = GeoboxClient()
261
- >>> attachment = Attachment.get_attachments(client)[0]
262
- >>> attachment.update(display_name="New Display Name")
263
- """
264
- data = clean_data({
265
- "name": kwargs.get('name'),
266
- "display_name": kwargs.get('display_name'),
267
- "description": kwargs.get('description'),
268
- "loc_x": kwargs.get('loc_x'),
269
- "loc_y": kwargs.get('loc_y')
270
- })
271
- response = self.api.put(self.endpoint, data)
272
- self._update_properties(response)
273
- return response
274
-
275
-
276
- def delete(self) -> None:
277
- """
278
- Delete the scene.
279
-
280
- Returns:
281
- None
282
-
283
- Raises:
284
- ApiRequestError: If the API request fails.
285
- ValidationError: If the scene data is invalid.
286
-
287
- Example:
288
- >>> from geobox import GeoboxClient
289
- >>> from geobox.attachment import Attachment
290
- >>> client = GeoboxClient()
291
- >>> attachment = Attachment.get_attachments(client)[0]
292
- >>> attachment.delete()
293
- """
294
- super().delete(self.endpoint)
295
- self.attachment_id = None
296
-
297
-
298
- @property
299
- def thumbnail(self) -> str:
300
- """
301
- Get the thumbnail URL of the attachment.
302
-
303
- Returns:
304
- str: The thumbnail of the scene.
305
-
306
- Example:
307
- >>> from geobox import GeoboxClient
308
- >>> from geobox.attachment import Attachment
309
- >>> client = GeoboxClient()
310
- >>> attachment = Attachment.get_attachment(client, uuid="12345678-1234-5678-1234-567812345678")
311
- >>> attachment.thumbnail
312
- 'https://example.com/thumbnail.png'
313
- """
314
- return super().thumbnail(format='')
315
-
316
-
317
- def to_async(self, async_client: 'AsyncGeoboxClient') -> 'AsyncAtachment':
318
- """
319
- Switch to async version of the attachment instance to have access to the async methods
320
-
321
- Args:
322
- async_client (AsyncGeoboxClient): The async version of the GeoboxClient instance for making requests.
323
-
324
- Returns:
325
- geobox.aio.attachment.Atachment: the async instance of the attachment.
326
-
327
- Example:
328
- >>> from geobox import Geoboxclient
329
- >>> from geobox.aio import AsyncGeoboxClient
330
- >>> from geobox.attachment import Attachment
331
- >>> client = GeoboxClient()
332
- >>> attachment = Attachment.get_attachment(client, uuid="12345678-1234-5678-1234-567812345678")
333
- >>> async with AsyncGeoboxClient() as async_client:
334
- >>> async_attachment = attachment.to_async(async_client)
335
- """
336
- from .aio.attachment import Attachment as AsyncAttachment
337
-
1
+ from typing import List, Dict, Optional, TYPE_CHECKING, Union
2
+ from urllib.parse import urljoin
3
+
4
+ from .base import Base
5
+ from .utils import clean_data
6
+ from .map import Map
7
+ from .vectorlayer import VectorLayer
8
+ from .view import VectorLayerView
9
+ from .file import File
10
+
11
+ if TYPE_CHECKING:
12
+ from . import GeoboxClient
13
+ from .feature import Feature
14
+ from .aio import AsyncGeoboxClient
15
+ from .aio.attachment import AsyncAttachment
16
+
17
+
18
+ class Attachment(Base):
19
+
20
+ BASE_ENDPOINT = 'attachments/'
21
+
22
+ def __init__(self,
23
+ api: 'GeoboxClient',
24
+ attachment_id: str,
25
+ data: Optional[Dict] = {}):
26
+ """
27
+ Initialize an Attachment instance.
28
+
29
+ Args:
30
+ api (GeoboxClient): The GeoboxClient instance for making requests.
31
+ attachment_id (str): The id for the attachment.
32
+ data (Dict, optional): The data of the attachment.
33
+ """
34
+ super().__init__(api, data=data)
35
+ self.attachment_id = attachment_id
36
+ self.endpoint = f"{self.BASE_ENDPOINT}{str(self.attachment_id)}/"
37
+
38
+
39
+ def __repr__(self) -> str:
40
+ """
41
+ Return a string representation of the attachment.
42
+
43
+ Returns:
44
+ str: The string representation of the attachment.
45
+ """
46
+ return f"Attachment(id={self.attachment_id}, name={self.name})"
47
+
48
+
49
+ @property
50
+ def file(self) -> 'File':
51
+ """
52
+ Attachment file property
53
+
54
+ Returns:
55
+ File: the file object
56
+ """
57
+ return File(self.api, self.data['file'].get('uuid'), self.data['file'])
58
+
59
+
60
+ @classmethod
61
+ def get_attachments(cls, api: 'GeoboxClient', resource: Union['Map', 'VectorLayer', 'VectorLayerView'], **kwargs) -> Union[List['Attachment'], int]:
62
+ """
63
+ Get list of attachments with optional filtering and pagination.
64
+
65
+ Args:
66
+ api (GeoboxClient): The GeoboxClient instance for making requests.
67
+ resource (Map | VectorLayer | VectorLayerView): options are: Map, Vector, View objects
68
+
69
+ Keyword Args:
70
+ element_id (str): the id of the element with attachment.
71
+ search (str): search term for keyword-based searching among all textual fields.
72
+ 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.
73
+ skip (int): Number of items to skip. default is 0.
74
+ limit (int): Number of items to return. default is 10.
75
+ return_count (bool): Whether to return total count. default is False.
76
+
77
+ Returns:
78
+ List[Attachment] | int: A list of attachments instances or the total number of attachments.
79
+
80
+ Raises:
81
+ TypeError: if the resource type is not supported
82
+
83
+ Example:
84
+ >>> from geobox import GeoboxClient
85
+ >>> from geobox.attachment import Attachment
86
+ >>> client = GeoboxClient()
87
+ >>> map = client.get_maps()[0]
88
+ >>> attachments = Attachment.get_attachments(client, resource=map, q="name LIKE '%My attachment%'")
89
+ or
90
+ >>> attachments = client.get_attachments(resource=map, q="name LIKE '%My attachment%'")
91
+ """
92
+ if type(resource) == VectorLayer:
93
+ resource_type = 'vector'
94
+
95
+ elif type(resource) == VectorLayerView:
96
+ resource_type = 'view'
97
+
98
+ elif type(resource) == Map:
99
+ resource_type = 'map'
100
+
101
+ else:
102
+ raise TypeError('resource must be a vectorlayer or view or map object')
103
+
104
+
105
+ params = {
106
+ 'resource_type': resource_type,
107
+ 'resource_uuid': resource.uuid,
108
+ 'element_id': kwargs.get('element_id'),
109
+ 'search': kwargs.get('search'),
110
+ 'order_by': kwargs.get('order_by'),
111
+ 'skip': kwargs.get('skip', 0),
112
+ 'limit': kwargs.get('limit', 10),
113
+ 'return_count': kwargs.get('return_count')
114
+ }
115
+ return super()._get_list(api, cls.BASE_ENDPOINT, params, factory_func=lambda api, item: Attachment(api, item['id'], item))
116
+
117
+
118
+ @classmethod
119
+ def create_attachment(cls,
120
+ api: 'GeoboxClient',
121
+ name: str,
122
+ loc_x: int,
123
+ loc_y: int,
124
+ resource: Union['Map', 'VectorLayer', 'VectorLayerView'],
125
+ file: 'File',
126
+ feature: 'Feature' = None,
127
+ display_name: str = None,
128
+ description: str = None, ) -> 'Attachment':
129
+ """
130
+ Create a new Attachment.
131
+
132
+ Args:
133
+ api (GeoboxClient): The GeoboxClient instance for making requests.
134
+ name (str): The name of the scene.
135
+ loc_x (int): x parameter of the attachment location.
136
+ loc_y (int): y parameter of the attachment location.
137
+ resource (Map | VectorLayer | VectorLayerView): the resource object.
138
+ file (File): the file object.
139
+ feature (Feature, optional): the feature object.
140
+ display_name (str, optional): The display name of the scene.
141
+ description (str, optional): The description of the scene.
142
+
143
+ Returns:
144
+ Attachment: The newly created Attachment instance.
145
+
146
+ Raises:
147
+ ValidationError: If the Attachment data is invalid.
148
+
149
+ Example:
150
+ >>> from geobox import GeoboxClient
151
+ >>> from geobox.attachment import Attachment
152
+ >>> client = GeoboxClient()
153
+ >>> layer = client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
154
+ >>> feature = layer.get_feature(feature_id=1)
155
+ >>> file = client.get_file(uuid="12345678-1234-5678-1234-567812345678")
156
+ >>> attachment = Attachment.create_attachment(client,
157
+ ... name="my_attachment",
158
+ ... loc_x=30,
159
+ ... loc_y=50,
160
+ ... resource=layer,
161
+ ... file=file,
162
+ ... feature=feature,
163
+ ... display_name="My Attachment",
164
+ ... description="Attachment Description")
165
+ or
166
+ >>> attachment = client.create_attachment(name="my_attachment",
167
+ ... loc_x=30,
168
+ ... loc_y=50,
169
+ ... resource=layer,
170
+ ... file=file,
171
+ ... feature=feature,
172
+ ... display_name="My Attachment",
173
+ ... description="Attachment Description")
174
+ """
175
+ if isinstance(resource, VectorLayer):
176
+ resource_type = 'vector'
177
+
178
+ if isinstance(resource, VectorLayerView):
179
+ resource_type = 'view'
180
+
181
+ if isinstance(resource, Map):
182
+ resource_type = 'map'
183
+
184
+ data = {
185
+ "name": name,
186
+ "display_name": display_name,
187
+ "description": description,
188
+ "loc_x": loc_x,
189
+ "loc_y": loc_y,
190
+ "resource_type": resource_type,
191
+ "resource_uuid": resource.uuid,
192
+ "element_id": feature.id if feature else None,
193
+ "file_id": file.id
194
+ }
195
+ return super()._create(api, cls.BASE_ENDPOINT, data, factory_func=lambda api, item: Attachment(api, item['id'], item))
196
+
197
+
198
+ @classmethod
199
+ def update_attachment(cls, api: 'GeoboxClient', attachment_id: int, **kwargs) -> Dict:
200
+ """
201
+ Update the attachment.
202
+
203
+ Args:
204
+ api (GeoboxClient): The GeoboxClient instance for making requests.
205
+ attachment_id (int): the attachment id.
206
+
207
+ Keyword Args:
208
+ name (str): The name of the attachment.
209
+ display_name (str): The display name of the attachment.
210
+ description (str): The description of the attachment.
211
+ loc_x (int): x parameter of the attachment location.
212
+ loc_y (int): y parameter of the attachment location.
213
+
214
+ Returns:
215
+ Dict: The updated attachment data.
216
+
217
+ Raises:
218
+ ValidationError: If the attachment data is invalid.
219
+
220
+ Example:
221
+ >>> from geobox import GeoboxClient
222
+ >>> from geobox.attachment import Attachment
223
+ >>> client = GeoboxClient()
224
+ >>> Attachment.update_attachment(client, attachment_id=1, display_name="New Display Name")
225
+ or
226
+ >>> client.update_attachment(attachment_id=1, display_name="New Display Name")
227
+ """
228
+ data = clean_data({
229
+ "name": kwargs.get('name'),
230
+ "display_name": kwargs.get('display_name'),
231
+ "description": kwargs.get('description'),
232
+ "loc_x": kwargs.get('loc_x'),
233
+ "loc_y": kwargs.get('loc_y')
234
+ })
235
+ endpoint = urljoin(cls.BASE_ENDPOINT, str(attachment_id))
236
+ response = api.put(endpoint, data)
237
+ return response
238
+
239
+
240
+ def update(self, **kwargs) -> Dict:
241
+ """
242
+ Update the attachment.
243
+
244
+ Keyword Args:
245
+ name (str): The name of the attachment.
246
+ display_name (str): The display name of the attachment.
247
+ description (str): The description of the attachment.
248
+ loc_x (int): x parameter of the attachment location.
249
+ loc_y (int): y parameter of the attachment location.
250
+
251
+ Returns:
252
+ Dict: The updated attachment data.
253
+
254
+ Raises:
255
+ ValidationError: If the attachment data is invalid.
256
+
257
+ Example:
258
+ >>> from geobox import GeoboxClient
259
+ >>> from geobox.attachment import Attachment
260
+ >>> client = GeoboxClient()
261
+ >>> attachment = Attachment.get_attachments(client)[0]
262
+ >>> attachment.update(display_name="New Display Name")
263
+ """
264
+ data = clean_data({
265
+ "name": kwargs.get('name'),
266
+ "display_name": kwargs.get('display_name'),
267
+ "description": kwargs.get('description'),
268
+ "loc_x": kwargs.get('loc_x'),
269
+ "loc_y": kwargs.get('loc_y')
270
+ })
271
+ response = self.api.put(self.endpoint, data)
272
+ self._update_properties(response)
273
+ return response
274
+
275
+
276
+ def delete(self) -> None:
277
+ """
278
+ Delete the scene.
279
+
280
+ Returns:
281
+ None
282
+
283
+ Raises:
284
+ ApiRequestError: If the API request fails.
285
+ ValidationError: If the scene data is invalid.
286
+
287
+ Example:
288
+ >>> from geobox import GeoboxClient
289
+ >>> from geobox.attachment import Attachment
290
+ >>> client = GeoboxClient()
291
+ >>> attachment = Attachment.get_attachments(client)[0]
292
+ >>> attachment.delete()
293
+ """
294
+ super()._delete(self.endpoint)
295
+ self.attachment_id = None
296
+
297
+
298
+ @property
299
+ def thumbnail(self) -> str:
300
+ """
301
+ Get the thumbnail URL of the attachment.
302
+
303
+ Returns:
304
+ str: The thumbnail of the scene.
305
+
306
+ Example:
307
+ >>> from geobox import GeoboxClient
308
+ >>> from geobox.attachment import Attachment
309
+ >>> client = GeoboxClient()
310
+ >>> attachment = Attachment.get_attachment(client, uuid="12345678-1234-5678-1234-567812345678")
311
+ >>> attachment.thumbnail
312
+ """
313
+ return super()._thumbnail(format='')
314
+
315
+
316
+ def to_async(self, async_client: 'AsyncGeoboxClient') -> 'AsyncAttachment':
317
+ """
318
+ Switch to async version of the attachment instance to have access to the async methods
319
+
320
+ Args:
321
+ async_client (AsyncGeoboxClient): The async version of the GeoboxClient instance for making requests.
322
+
323
+ Returns:
324
+ AsyncAttachment: the async instance of the attachment.
325
+
326
+ Example:
327
+ >>> from geobox import Geoboxclient
328
+ >>> from geobox.aio import AsyncGeoboxClient
329
+ >>> from geobox.attachment import Attachment
330
+ >>> client = GeoboxClient()
331
+ >>> attachment = Attachment.get_attachment(client, uuid="12345678-1234-5678-1234-567812345678")
332
+ >>> async with AsyncGeoboxClient() as async_client:
333
+ >>> async_attachment = attachment.to_async(async_client)
334
+ """
335
+ from .aio.attachment import AsyncAttachment
336
+
338
337
  return AsyncAttachment(api=async_client, attachment_id=self.attachment_id, data=self.data)