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/model3d.py CHANGED
@@ -1,411 +1,411 @@
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 Base
8
- from .exception import ApiRequestError
9
- from .utils import get_save_path
10
-
11
- if TYPE_CHECKING:
12
- from . import GeoboxClient
13
- from .user import User
14
- from .aio import AsyncGeoboxClient
15
- from .aio.model3d import Model as AsyncModel
16
-
17
-
18
- class Model(Base):
19
-
20
- BASE_ENDPOINT = '3dmodels/'
21
-
22
- def __init__(self,
23
- api: 'GeoboxClient',
24
- uuid: str,
25
- data: Optional[Dict] = {}):
26
- """
27
- Initialize a 3D Model instance.
28
-
29
- Args:
30
- api (GeoboxClient): The GeoboxClient 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
- def get_models(cls, api: 'GeoboxClient', **kwargs) -> Union[List['Model'], int]:
39
- """
40
- Get a list of models with optional filtering and pagination.
41
-
42
- Args:
43
- api (GeoboxClient): The GeoboxClient 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 import GeoboxClient
61
- >>> from geobox.model3d import Model
62
- >>> client = GeoboxClient()
63
- >>> models = 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 = 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 super()._get_list(api, cls.BASE_ENDPOINT, params, factory_func=lambda api, item: Model(api, item['uuid'], item))
93
-
94
-
95
- @classmethod
96
- def get_model(cls, api: 'GeoboxClient', uuid: str, user_id: int = None) -> 'Model':
97
- """
98
- Get a model by its UUID.
99
-
100
- Args:
101
- api (GeoboxClient): The GeoboxClient 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 import GeoboxClient
113
- >>> from geobox.model3d import Model
114
- >>> client = GeoboxClient()
115
- >>> model = Model.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
116
- or
117
- >>> model = client.get_model(uuid="12345678-1234-5678-1234-567812345678")
118
- """
119
- params = {
120
- 'f': 'json',
121
- 'user_id': user_id
122
- }
123
- return super()._get_detail(api, cls.BASE_ENDPOINT, uuid, params, factory_func=lambda api, item: Model(api, item['uuid'], item))
124
-
125
-
126
- @classmethod
127
- def get_model_by_name(cls, api: 'GeoboxClient', name: str, user_id: int = None) -> Union['Model', None]:
128
- """
129
- Get a model by name
130
-
131
- Args:
132
- api (GeoboxClient): The GeoboxClient 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 import GeoboxClient
141
- >>> from geobox.model3d import Model
142
- >>> client = GeoboxClient()
143
- >>> model = Model.get_model_by_name(client, name='test')
144
- or
145
- >>> model = client.get_model_by_name(name='test')
146
- """
147
- models = 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
- def update(self, **kwargs) -> Dict:
155
- """
156
- 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 import GeoboxClient
173
- >>> from geobox.model3d import Model
174
- >>> client = GeoboxClient()
175
- >>> model = 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
- >>> 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 super()._update(self.endpoint, data)
205
-
206
-
207
- def delete(self) -> None:
208
- """
209
- Delete the model.
210
-
211
- Returns:
212
- None
213
-
214
- Example:
215
- >>> from geobox import GeoboxClient
216
- >>> from geobox.model3d import Model
217
- >>> client = GeoboxClient()
218
- >>> model = Model.get_model(api=client, uuid="12345678-1234-5678-1234-567812345678")
219
- >>> model.delete()
220
- """
221
- 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
- def download(self, save_path: str = None, progress_bar: bool = True) -> str:
245
- """
246
- 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 import GeoboxClient
260
- >>> client = GeoboxClient()
261
- >>> model = client.get_models()[0]
262
- >>> 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 = 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
- with model.api.session.get(endpoint, stream=True) as response:
277
- if response.status_code != 200:
278
- raise ApiRequestError(f"Failed to get model content: {response.status_code}")
279
-
280
- glb_filename = f"{model.name}.glb"
281
- glb_path = os.path.join(save_path, glb_filename)
282
-
283
- with open(glb_path, "wb") as f:
284
- pbar = model._create_progress_bar() if progress_bar else None
285
- for chunk in response.iter_content(chunk_size=8192):
286
- f.write(chunk)
287
- if pbar:
288
- pbar.update(len(chunk))
289
- pbar.refresh()
290
- if pbar:
291
- pbar.close()
292
-
293
- zip_filename = f"{model.name}.zip"
294
- zip_path = os.path.join(save_path, zip_filename)
295
- with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zipf:
296
- zipf.write(glb_path, arcname=os.path.basename(glb_path))
297
-
298
- os.remove(glb_path)
299
-
300
- return os.path.abspath(zip_path)
301
-
302
-
303
- @property
304
- def thumbnail(self) -> str:
305
- """
306
- Get the thumbnail URL of the model.
307
-
308
- Returns:
309
- str: The thumbnail of the model.
310
-
311
- Example:
312
- >>> from geobox import GeoboxClient
313
- >>> from geobox.model3d import Model
314
- >>> client = GeoboxClient()
315
- >>> model = Model.get_model(api=client, uuid="12345678-1234-5678-1234-567812345678")
316
- >>> model.thumbnail
317
- """
318
- return super().thumbnail()
319
-
320
-
321
- def share(self, users: List['User']) -> None:
322
- """
323
- Shares the model with specified users.
324
-
325
- Args:
326
- users (List[users]): The list of user objects to share the model with.
327
-
328
- Returns:
329
- None
330
-
331
- Example:
332
- >>> from geobox import GeoboxClient
333
- >>> from geobox.model3d import Model
334
- >>> client = GeoboxClient()
335
- >>> model = Model.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
336
- >>> users = client.search_users(search='John')
337
- >>> model.share(users=users)
338
- """
339
- super()._share(self.endpoint, users)
340
-
341
-
342
- def unshare(self, users: List['User']) -> None:
343
- """
344
- Unshares the model with specified users.
345
-
346
- Args:
347
- users (List[User]): The list of user objects to unshare the model with.
348
-
349
- Returns:
350
- None
351
-
352
- Example:
353
- >>> from geobox import GeoboxClient
354
- >>> from geobox.model3d import Model
355
- >>> client = GeoboxClient()
356
- >>> model = Model.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
357
- >>> users = client.search_users(search='John')
358
- >>> model.unshare(users=users)
359
- """
360
- super()._unshare(self.endpoint, users)
361
-
362
-
363
- def get_shared_users(self, search: str = None, skip: int = 0, limit: int = 10) -> List['User']:
364
- """
365
- Retrieves the list of users the model is shared with.
366
-
367
- Args:
368
- search (str, optional): The search query.
369
- skip (int, optional): The number of users to skip.
370
- limit (int, optional): The maximum number of users to retrieve.
371
-
372
- Returns:
373
- List[User]: The list of shared users.
374
-
375
- Example:
376
- >>> from geobox import GeoboxClient
377
- >>> from geobox.model3d import Model
378
- >>> client = GeoboxClient()
379
- >>> model = Model.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
380
- >>> model.get_shared_users(search='John', skip=0, limit=10)
381
- """
382
- params = {
383
- 'search': search,
384
- 'skip': skip,
385
- 'limit': limit
386
- }
387
- return super()._get_shared_users(self.endpoint, params)
388
-
389
-
390
- def to_async(self, async_client: 'AsyncGeoboxClient') -> 'AsyncModel':
391
- """
392
- Switch to async version of the 3d model instance to have access to the async methods
393
-
394
- Args:
395
- async_client (AsyncGeoboxClient): The async version of the GeoboxClient instance for making requests.
396
-
397
- Returns:
398
- geobox.aio.model3d.Model: the async instance of the 3d model.
399
-
400
- Example:
401
- >>> from geobox import Geoboxclient
402
- >>> from geobox.aio import AsyncGeoboxClient
403
- >>> from geobox.model3d import Model
404
- >>> client = GeoboxClient()
405
- >>> model = Model.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
406
- >>> async with AsyncGeoboxClient() as async_client:
407
- >>> async_model = model.to_async(async_client)
408
- """
409
- from .aio.model3d import Model as AsyncModel
410
-
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 Base
8
+ from .exception import ApiRequestError
9
+ from .utils import get_save_path
10
+
11
+ if TYPE_CHECKING:
12
+ from . import GeoboxClient
13
+ from .user import User
14
+ from .aio import AsyncGeoboxClient
15
+ from .aio.model3d import AsyncModel
16
+
17
+
18
+ class Model(Base):
19
+
20
+ BASE_ENDPOINT = '3dmodels/'
21
+
22
+ def __init__(self,
23
+ api: 'GeoboxClient',
24
+ uuid: str,
25
+ data: Optional[Dict] = {}):
26
+ """
27
+ Initialize a 3D Model instance.
28
+
29
+ Args:
30
+ api (GeoboxClient): The GeoboxClient 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
+ def get_models(cls, api: 'GeoboxClient', **kwargs) -> Union[List['Model'], int]:
39
+ """
40
+ Get a list of models with optional filtering and pagination.
41
+
42
+ Args:
43
+ api (GeoboxClient): The GeoboxClient 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 import GeoboxClient
61
+ >>> from geobox.model3d import Model
62
+ >>> client = GeoboxClient()
63
+ >>> models = 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 = 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 super()._get_list(api, cls.BASE_ENDPOINT, params, factory_func=lambda api, item: Model(api, item['uuid'], item))
93
+
94
+
95
+ @classmethod
96
+ def get_model(cls, api: 'GeoboxClient', uuid: str, user_id: int = None) -> 'Model':
97
+ """
98
+ Get a model by its UUID.
99
+
100
+ Args:
101
+ api (GeoboxClient): The GeoboxClient 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 import GeoboxClient
113
+ >>> from geobox.model3d import Model
114
+ >>> client = GeoboxClient()
115
+ >>> model = Model.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
116
+ or
117
+ >>> model = client.get_model(uuid="12345678-1234-5678-1234-567812345678")
118
+ """
119
+ params = {
120
+ 'f': 'json',
121
+ 'user_id': user_id
122
+ }
123
+ return super()._get_detail(api, cls.BASE_ENDPOINT, uuid, params, factory_func=lambda api, item: Model(api, item['uuid'], item))
124
+
125
+
126
+ @classmethod
127
+ def get_model_by_name(cls, api: 'GeoboxClient', name: str, user_id: int = None) -> Union['Model', None]:
128
+ """
129
+ Get a model by name
130
+
131
+ Args:
132
+ api (GeoboxClient): The GeoboxClient 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 import GeoboxClient
141
+ >>> from geobox.model3d import Model
142
+ >>> client = GeoboxClient()
143
+ >>> model = Model.get_model_by_name(client, name='test')
144
+ or
145
+ >>> model = client.get_model_by_name(name='test')
146
+ """
147
+ models = 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
+ def update(self, **kwargs) -> Dict:
155
+ """
156
+ 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 import GeoboxClient
173
+ >>> from geobox.model3d import Model
174
+ >>> client = GeoboxClient()
175
+ >>> model = 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
+ >>> 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 super()._update(self.endpoint, data)
205
+
206
+
207
+ def delete(self) -> None:
208
+ """
209
+ Delete the model.
210
+
211
+ Returns:
212
+ None
213
+
214
+ Example:
215
+ >>> from geobox import GeoboxClient
216
+ >>> from geobox.model3d import Model
217
+ >>> client = GeoboxClient()
218
+ >>> model = Model.get_model(api=client, uuid="12345678-1234-5678-1234-567812345678")
219
+ >>> model.delete()
220
+ """
221
+ 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
+ def download(self, save_path: str = None, progress_bar: bool = True) -> str:
245
+ """
246
+ 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 import GeoboxClient
260
+ >>> client = GeoboxClient()
261
+ >>> model = client.get_models()[0]
262
+ >>> 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 = 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
+ with model.api.session.get(endpoint, stream=True) as response:
277
+ if response.status_code != 200:
278
+ raise ApiRequestError(f"Failed to get model content: {response.status_code}")
279
+
280
+ glb_filename = f"{model.name}.glb"
281
+ glb_path = os.path.join(save_path, glb_filename)
282
+
283
+ with open(glb_path, "wb") as f:
284
+ pbar = model._create_progress_bar() if progress_bar else None
285
+ for chunk in response.iter_content(chunk_size=8192):
286
+ f.write(chunk)
287
+ if pbar:
288
+ pbar.update(len(chunk))
289
+ pbar.refresh()
290
+ if pbar:
291
+ pbar.close()
292
+
293
+ zip_filename = f"{model.name}.zip"
294
+ zip_path = os.path.join(save_path, zip_filename)
295
+ with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zipf:
296
+ zipf.write(glb_path, arcname=os.path.basename(glb_path))
297
+
298
+ os.remove(glb_path)
299
+
300
+ return os.path.abspath(zip_path)
301
+
302
+
303
+ @property
304
+ def thumbnail(self) -> str:
305
+ """
306
+ Get the thumbnail URL of the model.
307
+
308
+ Returns:
309
+ str: The thumbnail of the model.
310
+
311
+ Example:
312
+ >>> from geobox import GeoboxClient
313
+ >>> from geobox.model3d import Model
314
+ >>> client = GeoboxClient()
315
+ >>> model = Model.get_model(api=client, uuid="12345678-1234-5678-1234-567812345678")
316
+ >>> model.thumbnail
317
+ """
318
+ return super()._thumbnail()
319
+
320
+
321
+ def share(self, users: List['User']) -> None:
322
+ """
323
+ Shares the model with specified users.
324
+
325
+ Args:
326
+ users (List[users]): The list of user objects to share the model with.
327
+
328
+ Returns:
329
+ None
330
+
331
+ Example:
332
+ >>> from geobox import GeoboxClient
333
+ >>> from geobox.model3d import Model
334
+ >>> client = GeoboxClient()
335
+ >>> model = Model.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
336
+ >>> users = client.search_users(search='John')
337
+ >>> model.share(users=users)
338
+ """
339
+ super()._share(self.endpoint, users)
340
+
341
+
342
+ def unshare(self, users: List['User']) -> None:
343
+ """
344
+ Unshares the model with specified users.
345
+
346
+ Args:
347
+ users (List[User]): The list of user objects to unshare the model with.
348
+
349
+ Returns:
350
+ None
351
+
352
+ Example:
353
+ >>> from geobox import GeoboxClient
354
+ >>> from geobox.model3d import Model
355
+ >>> client = GeoboxClient()
356
+ >>> model = Model.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
357
+ >>> users = client.search_users(search='John')
358
+ >>> model.unshare(users=users)
359
+ """
360
+ super()._unshare(self.endpoint, users)
361
+
362
+
363
+ def get_shared_users(self, search: str = None, skip: int = 0, limit: int = 10) -> List['User']:
364
+ """
365
+ Retrieves the list of users the model is shared with.
366
+
367
+ Args:
368
+ search (str, optional): The search query.
369
+ skip (int, optional): The number of users to skip.
370
+ limit (int, optional): The maximum number of users to retrieve.
371
+
372
+ Returns:
373
+ List[User]: The list of shared users.
374
+
375
+ Example:
376
+ >>> from geobox import GeoboxClient
377
+ >>> from geobox.model3d import Model
378
+ >>> client = GeoboxClient()
379
+ >>> model = Model.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
380
+ >>> model.get_shared_users(search='John', skip=0, limit=10)
381
+ """
382
+ params = {
383
+ 'search': search,
384
+ 'skip': skip,
385
+ 'limit': limit
386
+ }
387
+ return super()._get_shared_users(self.endpoint, params)
388
+
389
+
390
+ def to_async(self, async_client: 'AsyncGeoboxClient') -> 'AsyncModel':
391
+ """
392
+ Switch to async version of the 3d model instance to have access to the async methods
393
+
394
+ Args:
395
+ async_client (AsyncGeoboxClient): The async version of the GeoboxClient instance for making requests.
396
+
397
+ Returns:
398
+ AsyncModel: the async instance of the 3d model.
399
+
400
+ Example:
401
+ >>> from geobox import Geoboxclient
402
+ >>> from geobox.aio import AsyncGeoboxClient
403
+ >>> from geobox.model3d import Model
404
+ >>> client = GeoboxClient()
405
+ >>> model = Model.get_model(client, uuid="12345678-1234-5678-1234-567812345678")
406
+ >>> async with AsyncGeoboxClient() as async_client:
407
+ >>> async_model = model.to_async(async_client)
408
+ """
409
+ from .aio.model3d import AsyncModel
410
+
411
411
  return AsyncModel(api=async_client, uuid=self.uuid, data=self.data)