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/aio/model3d.py CHANGED
@@ -1,415 +1,415 @@
1
- from typing import Dict, List, Optional, Optional, Union, TYPE_CHECKING
2
- from urllib.parse import urljoin, urlencode
3
- import os
4
- import zipfile
5
- import sys
6
-
7
- from .base import AsyncBase
8
- from ..exception import ApiRequestError
9
- from ..utils import get_save_path
10
-
11
- if TYPE_CHECKING:
12
- from . import AsyncGeoboxClient
13
- from .user import User
14
- from ..api import GeoboxClient as SyncGeoboxClient
15
- from ..model3d import Model as SyncModel
16
-
17
-
18
- class Model(AsyncBase):
19
-
20
- BASE_ENDPOINT = '3dmodels/'
21
-
22
- def __init__(self,
23
- api: 'AsyncGeoboxClient',
24
- uuid: str,
25
- data: Optional[Dict] = {}):
26
- """
27
- Initialize a 3D Model instance.
28
-
29
- Args:
30
- api (AsyncGeoboxClient): The AsyncGeoboxClient instance for making requests.
31
- uuid (str): The unique identifier for the model.
32
- data (Dict, optional): The data of the model.
33
- """
34
- super().__init__(api, uuid=uuid, data=data)
35
-
36
-
37
- @classmethod
38
- async def get_models(cls, api: 'AsyncGeoboxClient', **kwargs) -> Union[List['Model'], int]:
39
- """
40
- [async] Get a list of models with optional filtering and pagination.
41
-
42
- Args:
43
- api (AsyncGeoboxClient): The AsyncGeoboxClient instance for making requests.
44
-
45
- Keyword Args:
46
- q (str): query filter based on OGC CQL standard. e.g. "field1 LIKE '%GIS%' AND created_at > '2021-01-01'".
47
- 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.
48
- search_fields (str): comma separated list of fields for searching.
49
- 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.
50
- return_count (bool): whether to return total count. default is False.
51
- skip (int): number of models to skip. default is 0.
52
- limit (int): maximum number of models to return. default is 10.
53
- user_id (int): specific user. privileges required.
54
- shared (bool): Whether to return shared models. default is False.
55
-
56
- Returns:
57
- List[Model] | int: A list of Model objects or the count number.
58
-
59
- Example:
60
- >>> from geobox.aio import AsyncGeoboxClient
61
- >>> from geobox.aio.model3d import Model
62
- >>> async with AsyncgeoboxClient() as client:
63
- >>> models = await Model.get_models(api=client,
64
- ... search="my_model",
65
- ... search_fields="name, description",
66
- ... order_by="name A",
67
- ... return_count=True,
68
- ... skip=0,
69
- ... limit=10,
70
- ... shared=False)
71
- or
72
- >>> models = await client.get_models(search="my_model",
73
- ... search_fields="name, description",
74
- ... order_by="name A",
75
- ... return_count=True,
76
- ... skip=0,
77
- ... limit=10,
78
- ... shared=False)
79
- """
80
- params = {
81
- 'f': 'json',
82
- 'q': kwargs.get('q'),
83
- 'search': kwargs.get('search'),
84
- 'search_fields': kwargs.get('search_fields'),
85
- 'order_by': kwargs.get('order_by'),
86
- 'return_count': kwargs.get('return_count', False),
87
- 'skip': kwargs.get('skip', 0),
88
- 'limit': kwargs.get('limit', 10),
89
- 'user_id': kwargs.get('user_id'),
90
- 'shared': kwargs.get('shared', False)
91
- }
92
- return await super()._get_list(api, cls.BASE_ENDPOINT, params, factory_func=lambda api, item: Model(api, item['uuid'], item))
93
-
94
-
95
- @classmethod
96
- async def get_model(cls, api: 'AsyncGeoboxClient', uuid: str, user_id: int = None) -> 'Model':
97
- """
98
- [async] Get a model by its UUID.
99
-
100
- Args:
101
- api (AsyncGeoboxClient): The AsyncGeoboxClient instance for making requests.
102
- uuid (str): The UUID of the model to get.
103
- user_id (int, optional): Specific user. privileges required.
104
-
105
- Returns:
106
- Model: The model object.
107
-
108
- Raises:
109
- NotFoundError: If the model with the specified UUID is not found.
110
-
111
- Example:
112
- >>> from geobox.aio import AsyncGeoboxClient
113
- >>> from geobox.aio.model3d import Model
114
- >>> async with AsyncgeoboxClient() as client:
115
- >>> model = await Model.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
116
- or
117
- >>> model = await client.get_model(uuid="12345678-1234-5678-1234-567812345678")
118
- """
119
- params = {
120
- 'f': 'json',
121
- 'user_id': user_id
122
- }
123
- return await super()._get_detail(api, cls.BASE_ENDPOINT, uuid, params, factory_func=lambda api, item: Model(api, item['uuid'], item))
124
-
125
-
126
- @classmethod
127
- async def get_model_by_name(cls, api: 'AsyncGeoboxClient', name: str, user_id: int = None) -> Union['Model', None]:
128
- """
129
- [async] Get a model by name
130
-
131
- Args:
132
- api (AsyncGeoboxClient): The AsyncGeoboxClient instance for making requests.
133
- name (str): the name of the model to get
134
- user_id (int, optional): specific user. privileges required.
135
-
136
- Returns:
137
- Model | None: returns the model if a model matches the given name, else None
138
-
139
- Example:
140
- >>> from geobox.aio import AsyncGeoboxClient
141
- >>> from geobox.aio.model3d import Model
142
- >>> async with AsyncgeoboxClient() as client:
143
- >>> model = await Model.get_model_by_name(client, name='test')
144
- or
145
- >>> model = await client.get_model_by_name(name='test')
146
- """
147
- models = await cls.get_models(api, q=f"name = '{name}'", user_id=user_id)
148
- if models and models[0].name == name:
149
- return models[0]
150
- else:
151
- return None
152
-
153
-
154
- async def update(self, **kwargs) -> Dict:
155
- """
156
- [async] Update the model's properties.
157
-
158
- Keyword Args:
159
- name (str): The new name for the model.
160
- display_name (str): The new display name.
161
- description (str): The new description for the model.
162
- settings (Dict): The new settings for the model.
163
- thumbnail (str): The new thumbnail for the model.
164
-
165
- Returns:
166
- Dict: The updated model data.
167
-
168
- Raises:
169
- ValidationError: If the update data is invalid.
170
-
171
- Example:
172
- >>> from geobox.aio import AsyncGeoboxClient
173
- >>> from geobox.aio.model3d import Model
174
- >>> async with AsyncgeoboxClient() as client:
175
- >>> model = await Model.get_model(api=client, uuid="12345678-1234-5678-1234-567812345678")
176
- >>> settings = {
177
- ... "model_settings": {
178
- ... "scale": 0,
179
- ... "rotation": [
180
- ... 0
181
- ... ],
182
- ... "location": [
183
- ... 0
184
- ... ]
185
- ... },
186
- ... "view_settings": {
187
- ... "center": [
188
- ... 0
189
- ... ],
190
- ... "zoom": 0,
191
- ... "pitch": 0,
192
- ... "bearing": 0
193
- ... }
194
- ... }
195
- >>> await model.update(name="new_name", description="new_description", settings=settings, thumbnail="new_thumbnail")
196
- """
197
- data = {
198
- 'name': kwargs.get('name'),
199
- 'display_name': kwargs.get('display_name'),
200
- 'description': kwargs.get('description'),
201
- 'settings': kwargs.get('settings'),
202
- 'thumbnail': kwargs.get('thumbnail')
203
- }
204
- return await super()._update(self.endpoint, data)
205
-
206
-
207
- async def delete(self) -> None:
208
- """
209
- [async] Delete the model.
210
-
211
- Returns:
212
- None
213
-
214
- Example:
215
- >>> from geobox.aio import AsyncGeoboxClient
216
- >>> from geobox.aio.model3d import Model
217
- >>> async with AsyncgeoboxClient() as client:
218
- >>> model = await Model.get_model(api=client, uuid="12345678-1234-5678-1234-567812345678")
219
- >>> await model.delete()
220
- """
221
- await super().delete(self.endpoint)
222
-
223
-
224
- def _create_progress_bar(self) -> 'tqdm':
225
- """Creates a progress bar for the task."""
226
- try:
227
- from tqdm.auto import tqdm
228
- except ImportError:
229
- from .api import logger
230
- logger.warning("[tqdm] extra is required to show the progress bar. install with: pip insatll geobox[tqdm]")
231
- return None
232
-
233
- return tqdm(unit="B",
234
- total=int(self.size),
235
- file=sys.stdout,
236
- dynamic_ncols=True,
237
- desc="Downloading",
238
- unit_scale=True,
239
- unit_divisor=1024,
240
- ascii=True
241
- )
242
-
243
-
244
- async def download(self, save_path: str = None, progress_bar: bool = True) -> str:
245
- """
246
- [async] Download the 3D model, save it as a .glb file, zip it, and return the zip file path.
247
-
248
- Args:
249
- save_path (str, optional): Directory where the file should be saved.
250
- progress_bar (bool, optional): Whether to show a progress bar. Default: True
251
-
252
- Returns:
253
- str: Path to the .zip file containing the .glb model.
254
-
255
- Raises:
256
- ApiRequestError: If the API request fails.
257
-
258
- Example:
259
- >>> from geobox.aio import AsyncGeoboxClient
260
- >>> async with AsyncgeoboxClient() as client:
261
- >>> model = await client.get_models()[0]
262
- >>> await model.download()
263
- """
264
- if not self.uuid:
265
- raise ValueError("Model UUID is required to download content")
266
-
267
- if self.data.get('obj'):
268
- model = await self.api.get_model(self.obj)
269
- else:
270
- model = self
271
-
272
- save_path = get_save_path(save_path)
273
- os.makedirs(save_path, exist_ok=True)
274
-
275
- endpoint = urljoin(model.api.base_url, f"{model.endpoint}/content/")
276
-
277
- async with model.api.session.session.get(endpoint) as response:
278
- if response.status != 200:
279
- raise ApiRequestError(f"Failed to get model content: {response.status}")
280
-
281
- glb_filename = f"{model.name}.glb"
282
- glb_path = os.path.join(save_path, glb_filename)
283
-
284
- pbar = model._create_progress_bar() if progress_bar else None
285
-
286
- with open(glb_path, "wb") as f:
287
- async for chunk in response.content.iter_chunked(8192):
288
- f.write(chunk)
289
- if pbar:
290
- pbar.update(len(chunk))
291
- pbar.refresh()
292
-
293
- if pbar:
294
- pbar.close()
295
-
296
- zip_filename = f"{model.name}.zip"
297
- zip_path = os.path.join(save_path, zip_filename)
298
-
299
- with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zipf:
300
- zipf.write(glb_path, arcname=os.path.basename(glb_path))
301
-
302
- os.remove(glb_path)
303
-
304
- return os.path.abspath(zip_path)
305
-
306
-
307
- @property
308
- def thumbnail(self) -> str:
309
- """
310
- Get the thumbnail URL of the model.
311
-
312
- Returns:
313
- str: The thumbnail of the model.
314
-
315
- Example:
316
- >>> from geobox.aio import AsyncGeoboxClient
317
- >>> from geobox.aio.model3d import Model
318
- >>> async with AsyncgeoboxClient() as client:
319
- >>> model = await Model.get_model(api=client, uuid="12345678-1234-5678-1234-567812345678")
320
- >>> model.thumbnail
321
- """
322
- return super().thumbnail()
323
-
324
-
325
- async def share(self, users: List['User']) -> None:
326
- """
327
- [async] Shares the model with specified users.
328
-
329
- Args:
330
- users (List[users]): The list of user objects to share the model with.
331
-
332
- Returns:
333
- None
334
-
335
- Example:
336
- >>> from geobox.aio import AsyncGeoboxClient
337
- >>> from geobox.aio.model3d import Model
338
- >>> async with AsyncgeoboxClient() as client:
339
- >>> model = await Model.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
340
- >>> users = await client.search_users(search='John')
341
- >>> await model.share(users=users)
342
- """
343
- await super()._share(self.endpoint, users)
344
-
345
-
346
- async def unshare(self, users: List['User']) -> None:
347
- """
348
- [async] Unshares the model with specified users.
349
-
350
- Args:
351
- users (List[User]): The list of user objects to unshare the model with.
352
-
353
- Returns:
354
- None
355
-
356
- Example:
357
- >>> from geobox.aio import AsyncGeoboxClient
358
- >>> from geobox.aio.model3d import Model
359
- >>> async with AsyncgeoboxClient() as client:
360
- >>> model = await Model.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
361
- >>> users = await client.search_users(search='John')
362
- >>> await model.unshare(users=users)
363
- """
364
- await super()._unshare(self.endpoint, users)
365
-
366
-
367
- async def get_shared_users(self, search: str = None, skip: int = 0, limit: int = 10) -> List['User']:
368
- """
369
- [async] Retrieves the list of users the model is shared with.
370
-
371
- Args:
372
- search (str, optional): The search query.
373
- skip (int, optional): The number of users to skip.
374
- limit (int, optional): The maximum number of users to retrieve.
375
-
376
- Returns:
377
- List[User]: The list of shared users.
378
-
379
- Example:
380
- >>> from geobox.aio import AsyncGeoboxClient
381
- >>> from geobox.aio.model3d import Model
382
- >>> async with AsyncgeoboxClient() as client:
383
- >>> model = await Model.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
384
- >>> await model.get_shared_users(search='John', skip=0, limit=10)
385
- """
386
- params = {
387
- 'search': search,
388
- 'skip': skip,
389
- 'limit': limit
390
- }
391
- return await super()._get_shared_users(self.endpoint, params)
392
-
393
-
394
- def to_sync(self, sync_client: 'SyncGeoboxClient') -> 'SyncModel':
395
- """
396
- Switch to sync version of the 3d model instance to have access to the sync methods
397
-
398
- Args:
399
- sync_client (SyncGeoboxClient): The sync version of the GeoboxClient instance for making requests.
400
-
401
- Returns:
402
- geobox.model3d.Model: the sync instance of the 3d model.
403
-
404
- Example:
405
- >>> from geobox import Geoboxclient
406
- >>> from geobox.aio import AsyncGeoboxClient
407
- >>> from geobox.aio.model3d import Model
408
- >>> client = GeoboxClient()
409
- >>> async with AsyncGeoboxClient() as async_client:
410
- >>> model = await Model.get_model(async_client, uuid="12345678-1234-5678-1234-567812345678")
411
- >>> sync_model = model.to_sync(client)
412
- """
413
- from ..model3d import Model as SyncModel
414
-
415
- return SyncModel(api=sync_client, uuid=self.uuid, data=self.data)
1
+ import os
2
+ import zipfile
3
+ import sys
4
+ from typing import Dict, List, Optional, Optional, Union, TYPE_CHECKING
5
+ from urllib.parse import urljoin
6
+
7
+ from .base import AsyncBase
8
+ from ..exception import ApiRequestError
9
+ from ..utils import get_save_path
10
+
11
+ if TYPE_CHECKING:
12
+ from . import AsyncGeoboxClient
13
+ from .user import AsyncUser
14
+ from ..api import GeoboxClient
15
+ from ..model3d import Model
16
+
17
+
18
+ class AsyncModel(AsyncBase):
19
+
20
+ BASE_ENDPOINT = '3dmodels/'
21
+
22
+ def __init__(self,
23
+ api: 'AsyncGeoboxClient',
24
+ uuid: str,
25
+ data: Optional[Dict] = {}):
26
+ """
27
+ Initialize a 3D Model instance.
28
+
29
+ Args:
30
+ api (AsyncGeoboxClient): The AsyncGeoboxClient instance for making requests.
31
+ uuid (str): The unique identifier for the model.
32
+ data (Dict, optional): The data of the model.
33
+ """
34
+ super().__init__(api, uuid=uuid, data=data)
35
+
36
+
37
+ @classmethod
38
+ async def get_models(cls, api: 'AsyncGeoboxClient', **kwargs) -> Union[List['AsyncModel'], int]:
39
+ """
40
+ [async] Get a list of models with optional filtering and pagination.
41
+
42
+ Args:
43
+ api (AsyncGeoboxClient): The AsyncGeoboxClient instance for making requests.
44
+
45
+ Keyword Args:
46
+ q (str): query filter based on OGC CQL standard. e.g. "field1 LIKE '%GIS%' AND created_at > '2021-01-01'".
47
+ 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.
48
+ search_fields (str): comma separated list of fields for searching.
49
+ 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.
50
+ return_count (bool): whether to return total count. default is False.
51
+ skip (int): number of models to skip. default is 0.
52
+ limit (int): maximum number of models to return. default is 10.
53
+ user_id (int): specific user. privileges required.
54
+ shared (bool): Whether to return shared models. default is False.
55
+
56
+ Returns:
57
+ List[AsyncModel] | int: A list of Model objects or the count number.
58
+
59
+ Example:
60
+ >>> from geobox.aio import AsyncGeoboxClient
61
+ >>> from geobox.aio.model3d import AsyncModel
62
+ >>> async with AsyncgeoboxClient() as client:
63
+ >>> models = await AsyncModel.get_models(api=client,
64
+ ... search="my_model",
65
+ ... search_fields="name, description",
66
+ ... order_by="name A",
67
+ ... return_count=True,
68
+ ... skip=0,
69
+ ... limit=10,
70
+ ... shared=False)
71
+ or
72
+ >>> models = await client.get_models(search="my_model",
73
+ ... search_fields="name, description",
74
+ ... order_by="name A",
75
+ ... return_count=True,
76
+ ... skip=0,
77
+ ... limit=10,
78
+ ... shared=False)
79
+ """
80
+ params = {
81
+ 'f': 'json',
82
+ 'q': kwargs.get('q'),
83
+ 'search': kwargs.get('search'),
84
+ 'search_fields': kwargs.get('search_fields'),
85
+ 'order_by': kwargs.get('order_by'),
86
+ 'return_count': kwargs.get('return_count', False),
87
+ 'skip': kwargs.get('skip', 0),
88
+ 'limit': kwargs.get('limit', 10),
89
+ 'user_id': kwargs.get('user_id'),
90
+ 'shared': kwargs.get('shared', False)
91
+ }
92
+ return await super()._get_list(api, cls.BASE_ENDPOINT, params, factory_func=lambda api, item: AsyncModel(api, item['uuid'], item))
93
+
94
+
95
+ @classmethod
96
+ async def get_model(cls, api: 'AsyncGeoboxClient', uuid: str, user_id: int = None) -> 'AsyncModel':
97
+ """
98
+ [async] Get a model by its UUID.
99
+
100
+ Args:
101
+ api (AsyncGeoboxClient): The AsyncGeoboxClient instance for making requests.
102
+ uuid (str): The UUID of the model to get.
103
+ user_id (int, optional): Specific user. privileges required.
104
+
105
+ Returns:
106
+ AsyncModel: The model object.
107
+
108
+ Raises:
109
+ NotFoundError: If the model with the specified UUID is not found.
110
+
111
+ Example:
112
+ >>> from geobox.aio import AsyncGeoboxClient
113
+ >>> from geobox.aio.model3d import AsyncModel
114
+ >>> async with AsyncgeoboxClient() as client:
115
+ >>> model = await AsyncModel.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
116
+ or
117
+ >>> model = await client.get_model(uuid="12345678-1234-5678-1234-567812345678")
118
+ """
119
+ params = {
120
+ 'f': 'json',
121
+ 'user_id': user_id
122
+ }
123
+ return await super()._get_detail(api, cls.BASE_ENDPOINT, uuid, params, factory_func=lambda api, item: AsyncModel(api, item['uuid'], item))
124
+
125
+
126
+ @classmethod
127
+ async def get_model_by_name(cls, api: 'AsyncGeoboxClient', name: str, user_id: int = None) -> Union['AsyncModel', None]:
128
+ """
129
+ [async] Get a model by name
130
+
131
+ Args:
132
+ api (AsyncGeoboxClient): The AsyncGeoboxClient instance for making requests.
133
+ name (str): the name of the model to get
134
+ user_id (int, optional): specific user. privileges required.
135
+
136
+ Returns:
137
+ AsyncModel | None: returns the model if a model matches the given name, else None
138
+
139
+ Example:
140
+ >>> from geobox.aio import AsyncGeoboxClient
141
+ >>> from geobox.aio.model3d import AsyncModel
142
+ >>> async with AsyncgeoboxClient() as client:
143
+ >>> model = await AsyncModel.get_model_by_name(client, name='test')
144
+ or
145
+ >>> model = await client.get_model_by_name(name='test')
146
+ """
147
+ models = await cls.get_models(api, q=f"name = '{name}'", user_id=user_id)
148
+ if models and models[0].name == name:
149
+ return models[0]
150
+ else:
151
+ return None
152
+
153
+
154
+ async def update(self, **kwargs) -> Dict:
155
+ """
156
+ [async] Update the model's properties.
157
+
158
+ Keyword Args:
159
+ name (str): The new name for the model.
160
+ display_name (str): The new display name.
161
+ description (str): The new description for the model.
162
+ settings (Dict): The new settings for the model.
163
+ thumbnail (str): The new thumbnail for the model.
164
+
165
+ Returns:
166
+ Dict: The updated model data.
167
+
168
+ Raises:
169
+ ValidationError: If the update data is invalid.
170
+
171
+ Example:
172
+ >>> from geobox.aio import AsyncGeoboxClient
173
+ >>> from geobox.aio.model3d import AsyncModel
174
+ >>> async with AsyncgeoboxClient() as client:
175
+ >>> model = await AsyncModel.get_model(api=client, uuid="12345678-1234-5678-1234-567812345678")
176
+ >>> settings = {
177
+ ... "model_settings": {
178
+ ... "scale": 0,
179
+ ... "rotation": [
180
+ ... 0
181
+ ... ],
182
+ ... "location": [
183
+ ... 0
184
+ ... ]
185
+ ... },
186
+ ... "view_settings": {
187
+ ... "center": [
188
+ ... 0
189
+ ... ],
190
+ ... "zoom": 0,
191
+ ... "pitch": 0,
192
+ ... "bearing": 0
193
+ ... }
194
+ ... }
195
+ >>> await model.update(name="new_name", description="new_description", settings=settings, thumbnail="new_thumbnail")
196
+ """
197
+ data = {
198
+ 'name': kwargs.get('name'),
199
+ 'display_name': kwargs.get('display_name'),
200
+ 'description': kwargs.get('description'),
201
+ 'settings': kwargs.get('settings'),
202
+ 'thumbnail': kwargs.get('thumbnail')
203
+ }
204
+ return await super()._update(self.endpoint, data)
205
+
206
+
207
+ async def delete(self) -> None:
208
+ """
209
+ [async] Delete the model.
210
+
211
+ Returns:
212
+ None
213
+
214
+ Example:
215
+ >>> from geobox.aio import AsyncGeoboxClient
216
+ >>> from geobox.aio.model3d import AsyncModel
217
+ >>> async with AsyncgeoboxClient() as client:
218
+ >>> model = await AsyncModel.get_model(api=client, uuid="12345678-1234-5678-1234-567812345678")
219
+ >>> await model.delete()
220
+ """
221
+ await super()._delete(self.endpoint)
222
+
223
+
224
+ def _create_progress_bar(self) -> 'tqdm':
225
+ """Creates a progress bar for the task."""
226
+ try:
227
+ from tqdm.auto import tqdm
228
+ except ImportError:
229
+ from .api import logger
230
+ logger.warning("[tqdm] extra is required to show the progress bar. install with: pip insatll geobox[tqdm]")
231
+ return None
232
+
233
+ return tqdm(unit="B",
234
+ total=int(self.size),
235
+ file=sys.stdout,
236
+ dynamic_ncols=True,
237
+ desc="Downloading",
238
+ unit_scale=True,
239
+ unit_divisor=1024,
240
+ ascii=True
241
+ )
242
+
243
+
244
+ async def download(self, save_path: str = None, progress_bar: bool = True) -> str:
245
+ """
246
+ [async] Download the 3D model, save it as a .glb file, zip it, and return the zip file path.
247
+
248
+ Args:
249
+ save_path (str, optional): Directory where the file should be saved.
250
+ progress_bar (bool, optional): Whether to show a progress bar. Default: True
251
+
252
+ Returns:
253
+ str: Path to the .zip file containing the .glb model.
254
+
255
+ Raises:
256
+ ApiRequestError: If the API request fails.
257
+
258
+ Example:
259
+ >>> from geobox.aio import AsyncGeoboxClient
260
+ >>> async with AsyncgeoboxClient() as client:
261
+ >>> model = await client.get_models()[0]
262
+ >>> await model.download()
263
+ """
264
+ if not self.uuid:
265
+ raise ValueError("Model UUID is required to download content")
266
+
267
+ if self.data.get('obj'):
268
+ model = await self.api.get_model(self.obj)
269
+ else:
270
+ model = self
271
+
272
+ save_path = get_save_path(save_path)
273
+ os.makedirs(save_path, exist_ok=True)
274
+
275
+ endpoint = urljoin(model.api.base_url, f"{model.endpoint}/content/")
276
+
277
+ async with model.api.session.session.get(endpoint) as response:
278
+ if response.status != 200:
279
+ raise ApiRequestError(f"Failed to get model content: {response.status}")
280
+
281
+ glb_filename = f"{model.name}.glb"
282
+ glb_path = os.path.join(save_path, glb_filename)
283
+
284
+ pbar = model._create_progress_bar() if progress_bar else None
285
+
286
+ with open(glb_path, "wb") as f:
287
+ async for chunk in response.content.iter_chunked(8192):
288
+ f.write(chunk)
289
+ if pbar:
290
+ pbar.update(len(chunk))
291
+ pbar.refresh()
292
+
293
+ if pbar:
294
+ pbar.close()
295
+
296
+ zip_filename = f"{model.name}.zip"
297
+ zip_path = os.path.join(save_path, zip_filename)
298
+
299
+ with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zipf:
300
+ zipf.write(glb_path, arcname=os.path.basename(glb_path))
301
+
302
+ os.remove(glb_path)
303
+
304
+ return os.path.abspath(zip_path)
305
+
306
+
307
+ @property
308
+ def thumbnail(self) -> str:
309
+ """
310
+ Get the thumbnail URL of the model.
311
+
312
+ Returns:
313
+ str: The thumbnail of the model.
314
+
315
+ Example:
316
+ >>> from geobox.aio import AsyncGeoboxClient
317
+ >>> from geobox.aio.model3d import AsyncModel
318
+ >>> async with AsyncgeoboxClient() as client:
319
+ >>> model = await AsyncModel.get_model(api=client, uuid="12345678-1234-5678-1234-567812345678")
320
+ >>> model.thumbnail
321
+ """
322
+ return super()._thumbnail()
323
+
324
+
325
+ async def share(self, users: List['AsyncUser']) -> None:
326
+ """
327
+ [async] Shares the model with specified users.
328
+
329
+ Args:
330
+ users (List[AsyncUsers]): The list of user objects to share the model with.
331
+
332
+ Returns:
333
+ None
334
+
335
+ Example:
336
+ >>> from geobox.aio import AsyncGeoboxClient
337
+ >>> from geobox.aio.model3d import AsyncModel
338
+ >>> async with AsyncgeoboxClient() as client:
339
+ >>> model = await AsyncModel.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
340
+ >>> users = await client.search_users(search='John')
341
+ >>> await model.share(users=users)
342
+ """
343
+ await super()._share(self.endpoint, users)
344
+
345
+
346
+ async def unshare(self, users: List['AsyncUser']) -> None:
347
+ """
348
+ [async] Unshares the model with specified users.
349
+
350
+ Args:
351
+ users (List[AsyncUser]): The list of user objects to unshare the model with.
352
+
353
+ Returns:
354
+ None
355
+
356
+ Example:
357
+ >>> from geobox.aio import AsyncGeoboxClient
358
+ >>> from geobox.aio.model3d import AsyncModel
359
+ >>> async with AsyncgeoboxClient() as client:
360
+ >>> model = await AsyncModel.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
361
+ >>> users = await client.search_users(search='John')
362
+ >>> await model.unshare(users=users)
363
+ """
364
+ await super()._unshare(self.endpoint, users)
365
+
366
+
367
+ async def get_shared_users(self, search: str = None, skip: int = 0, limit: int = 10) -> List['AsyncUser']:
368
+ """
369
+ [async] Retrieves the list of users the model is shared with.
370
+
371
+ Args:
372
+ search (str, optional): The search query.
373
+ skip (int, optional): The number of users to skip.
374
+ limit (int, optional): The maximum number of users to retrieve.
375
+
376
+ Returns:
377
+ List[AsyncUser]: The list of shared users.
378
+
379
+ Example:
380
+ >>> from geobox.aio import AsyncGeoboxClient
381
+ >>> from geobox.aio.model3d import AsyncModel
382
+ >>> async with AsyncgeoboxClient() as client:
383
+ >>> model = await AsyncModel.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
384
+ >>> await model.get_shared_users(search='John', skip=0, limit=10)
385
+ """
386
+ params = {
387
+ 'search': search,
388
+ 'skip': skip,
389
+ 'limit': limit
390
+ }
391
+ return await super()._get_shared_users(self.endpoint, params)
392
+
393
+
394
+ def to_sync(self, sync_client: 'GeoboxClient') -> 'Model':
395
+ """
396
+ Switch to sync version of the 3d model instance to have access to the sync methods
397
+
398
+ Args:
399
+ sync_client (GeoboxClient): The sync version of the GeoboxClient instance for making requests.
400
+
401
+ Returns:
402
+ Model: the sync instance of the 3d model.
403
+
404
+ Example:
405
+ >>> from geobox import Geoboxclient
406
+ >>> from geobox.aio import AsyncGeoboxClient
407
+ >>> from geobox.aio.model3d import AsyncModel
408
+ >>> client = GeoboxClient()
409
+ >>> async with AsyncGeoboxClient() as async_client:
410
+ >>> model = await AsyncModel.get_model(async_client, uuid="12345678-1234-5678-1234-567812345678")
411
+ >>> sync_model = model.to_sync(client)
412
+ """
413
+ from ..model3d import Model
414
+
415
+ return Model(api=sync_client, uuid=self.uuid, data=self.data)