geobox 1.3.1__tar.gz → 1.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {geobox-1.3.1 → geobox-1.3.3}/PKG-INFO +1 -1
- {geobox-1.3.1 → geobox-1.3.3}/geobox/api.py +6 -11
- {geobox-1.3.1 → geobox-1.3.3}/geobox/enums.py +0 -6
- {geobox-1.3.1 → geobox-1.3.3}/geobox/query.py +6 -8
- {geobox-1.3.1 → geobox-1.3.3}/geobox/raster.py +8 -2
- {geobox-1.3.1 → geobox-1.3.3}/geobox/tileset.py +66 -30
- {geobox-1.3.1 → geobox-1.3.3}/geobox/user.py +4 -7
- {geobox-1.3.1 → geobox-1.3.3}/geobox/utils.py +7 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/vectorlayer.py +30 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox.egg-info/PKG-INFO +1 -1
- {geobox-1.3.1 → geobox-1.3.3}/pyproject.toml +1 -1
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_tileset.py +43 -18
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_user.py +4 -3
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_vectorlayer.py +11 -0
- {geobox-1.3.1 → geobox-1.3.3}/LICENSE +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/README.md +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/__init__.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/apikey.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/attachment.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/base.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/basemap.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/dashboard.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/exception.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/feature.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/field.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/file.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/log.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/map.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/model3d.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/mosaic.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/plan.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/route.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/scene.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/settings.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/task.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/tile3d.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/usage.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/version.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/view.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox/workflow.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox.egg-info/SOURCES.txt +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox.egg-info/dependency_links.txt +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox.egg-info/requires.txt +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/geobox.egg-info/top_level.txt +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/setup.cfg +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_api.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_apikey.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_attachment.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_basemap.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_dashboard.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_feature.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_field.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_file.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_log.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_map.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_model3d.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_mosaic.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_plan.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_query.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_raster.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_route.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_scene.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_settings.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_task.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_tile3d.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_usage.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_version.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_view.py +0 -0
- {geobox-1.3.1 → geobox-1.3.3}/tests/test_workflow.py +0 -0
|
@@ -781,16 +781,14 @@ class GeoboxClient:
|
|
|
781
781
|
return VectorLayerView.get_view_by_name(self, name, user_id)
|
|
782
782
|
|
|
783
783
|
|
|
784
|
-
def create_tileset(self, name: str, layers: List[
|
|
784
|
+
def create_tileset(self, name: str, layers: List[Union['VectorLayer', 'VectorLayerView']], display_name: str = None, description: str = None,
|
|
785
785
|
min_zoom: int = None, max_zoom: int = None, user_id: int = None) -> 'Tileset':
|
|
786
786
|
"""
|
|
787
787
|
Create a new tileset.
|
|
788
788
|
|
|
789
789
|
Args:
|
|
790
790
|
name (str): The name of the tileset.
|
|
791
|
-
layers (List[
|
|
792
|
-
- layer_type: The type of the layer. valid values are "vector" and "view".
|
|
793
|
-
- layer_uuid: The uuid of the layer.
|
|
791
|
+
layers (List['VectorLayer' | 'VectorLayerView']): list of vectorlayer and view objects to add to tileset.
|
|
794
792
|
display_name (str, optional): The display name of the tileset.
|
|
795
793
|
description (str, optional): The description of the tileset.
|
|
796
794
|
min_zoom (int, optional): The minimum zoom level of the tileset.
|
|
@@ -802,19 +800,16 @@ class GeoboxClient:
|
|
|
802
800
|
|
|
803
801
|
Example:
|
|
804
802
|
>>> from geobox import GeoboxClient
|
|
803
|
+
>>> from geobox.tileset import Tileset
|
|
805
804
|
>>> client = GeoboxClient()
|
|
806
|
-
>>>
|
|
807
|
-
|
|
808
|
-
... "layer_type": "vector",
|
|
809
|
-
... "layer_uuid": "12345678-1234-5678-1234-567812345678"
|
|
810
|
-
... }
|
|
811
|
-
... ]
|
|
805
|
+
>>> layer = client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
|
|
806
|
+
>>> view = client.get_view(uuid="12345678-1234-5678-1234-567812345678")
|
|
812
807
|
>>> tileset = client.create_tileset(name="your_tileset_name",
|
|
813
808
|
... display_name="Your Tileset",
|
|
814
809
|
... description="Your description",
|
|
815
810
|
... min_zoom=0,
|
|
816
811
|
... max_zoom=14,
|
|
817
|
-
... layers=
|
|
812
|
+
... layers=[layer, view])
|
|
818
813
|
"""
|
|
819
814
|
return Tileset.create_tileset(api=self,
|
|
820
815
|
name=name,
|
|
@@ -582,7 +582,7 @@ class Query(Base):
|
|
|
582
582
|
super()._unshare(self.endpoint, users)
|
|
583
583
|
|
|
584
584
|
|
|
585
|
-
def get_shared_users(self, search: str, skip: int = 0, limit: int = 10) -> List['User']:
|
|
585
|
+
def get_shared_users(self, search: str = None, skip: int = 0, limit: int = 10) -> List['User']:
|
|
586
586
|
"""
|
|
587
587
|
Retrieves the list of users the query is shared with.
|
|
588
588
|
|
|
@@ -624,18 +624,16 @@ class Query(Base):
|
|
|
624
624
|
return endpoint
|
|
625
625
|
|
|
626
626
|
|
|
627
|
-
def save_as_layer(self, layer_name: str, layer_type: 'QueryGeometryType') ->
|
|
627
|
+
def save_as_layer(self, layer_name: str, layer_type: 'QueryGeometryType' = None) -> Task:
|
|
628
628
|
"""
|
|
629
629
|
Saves the query as a new layer.
|
|
630
630
|
|
|
631
631
|
Args:
|
|
632
|
-
sql (str): The SQL statement for the query.
|
|
633
|
-
params (list): The parameters for the SQL statement.
|
|
634
632
|
layer_name (str): The name of the new layer.
|
|
635
|
-
layer_type (QueryGeometryType): The type of the new layer.
|
|
633
|
+
layer_type (QueryGeometryType, optional): The type of the new layer.
|
|
636
634
|
|
|
637
635
|
Returns:
|
|
638
|
-
|
|
636
|
+
Task: The response task object.
|
|
639
637
|
|
|
640
638
|
Raises:
|
|
641
639
|
PermissionError: If the query is a read-only system query.
|
|
@@ -645,7 +643,7 @@ class Query(Base):
|
|
|
645
643
|
>>> from geobox.query import Query
|
|
646
644
|
>>> client = GeoboxClient()
|
|
647
645
|
>>> query = Query.get_query(client, uuid="12345678-1234-5678-1234-567812345678")
|
|
648
|
-
>>> query.save_as_layer(layer_name='test'
|
|
646
|
+
>>> query.save_as_layer(layer_name='test')
|
|
649
647
|
"""
|
|
650
648
|
self._check_access()
|
|
651
649
|
|
|
@@ -659,7 +657,7 @@ class Query(Base):
|
|
|
659
657
|
"sql": self.sql,
|
|
660
658
|
"params": params,
|
|
661
659
|
"layer_name": layer_name,
|
|
662
|
-
"layer_type": layer_type.value
|
|
660
|
+
"layer_type": layer_type.value if layer_type else None
|
|
663
661
|
})
|
|
664
662
|
|
|
665
663
|
endpoint = urljoin(self.BASE_ENDPOINT, 'saveAsLayer/')
|
|
@@ -510,13 +510,13 @@ class Raster(Base):
|
|
|
510
510
|
'colormap_name': kwargs.get('colormap_name'),
|
|
511
511
|
'colormap': kwargs.get('colormap')
|
|
512
512
|
})
|
|
513
|
-
query_string = urlencode(
|
|
513
|
+
query_string = urlencode(params)
|
|
514
514
|
endpoint = urljoin(self.api.base_url, f'{self.endpoint}render/{z}/{x}/{y}.png')
|
|
515
515
|
endpoint = urljoin(endpoint, f'?{query_string}')
|
|
516
516
|
return endpoint
|
|
517
517
|
|
|
518
518
|
|
|
519
|
-
def get_tile_pbf_url(self, x: int, y: int, z: int) -> str:
|
|
519
|
+
def get_tile_pbf_url(self, x: int, y: int, z: int, indexes: str = None) -> str:
|
|
520
520
|
"""
|
|
521
521
|
Get the URL of the tile.
|
|
522
522
|
|
|
@@ -524,6 +524,7 @@ class Raster(Base):
|
|
|
524
524
|
x (int): The x coordinate of the tile.
|
|
525
525
|
y (int): The y coordinate of the tile.
|
|
526
526
|
z (int): The zoom level of the tile.
|
|
527
|
+
indexes (str, optional): list of comma separated band indexes to be rendered. e.g. 1, 2, 3
|
|
527
528
|
|
|
528
529
|
Returns:
|
|
529
530
|
str: The URL of the tile.
|
|
@@ -535,7 +536,12 @@ class Raster(Base):
|
|
|
535
536
|
>>> raster = Raster.get_raster(client, uuid="12345678-1234-5678-1234-567812345678")
|
|
536
537
|
>>> raster.get_tile_pbf_url(x=10, y=20, z=1)
|
|
537
538
|
"""
|
|
539
|
+
params = clean_data({
|
|
540
|
+
'indexes': indexes
|
|
541
|
+
})
|
|
542
|
+
query_string = urlencode(params)
|
|
538
543
|
endpoint = urljoin(self.api.base_url, f'{self.endpoint}tiles/{z}/{x}/{y}.pbf')
|
|
544
|
+
endpoint = urljoin(endpoint, f'?{query_string}')
|
|
539
545
|
return endpoint
|
|
540
546
|
|
|
541
547
|
|
|
@@ -3,8 +3,8 @@ from typing import Dict, List, Optional, Union, TYPE_CHECKING
|
|
|
3
3
|
|
|
4
4
|
from .base import Base
|
|
5
5
|
from .vectorlayer import VectorLayer
|
|
6
|
+
from .view import VectorLayerView
|
|
6
7
|
from .task import Task
|
|
7
|
-
from .enums import TilesetLayerType
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
from . import GeoboxClient
|
|
@@ -35,7 +35,7 @@ class Tileset(Base):
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
@classmethod
|
|
38
|
-
def create_tileset(cls, api: 'GeoboxClient', name: str, layers: List[
|
|
38
|
+
def create_tileset(cls, api: 'GeoboxClient', name: str, layers: List[Union['VectorLayer', 'VectorLayerView']], display_name: str = None, description: str = None,
|
|
39
39
|
min_zoom: int = None, max_zoom: int = None, user_id: int = None) -> 'Tileset':
|
|
40
40
|
"""
|
|
41
41
|
Create a new tileset.
|
|
@@ -43,9 +43,7 @@ class Tileset(Base):
|
|
|
43
43
|
Args:
|
|
44
44
|
api (GeoboxClient): The GeoboxClient instance for making requests.
|
|
45
45
|
name (str): The name of the tileset.
|
|
46
|
-
layers (List[
|
|
47
|
-
- layer_type: The type of the layer. valid values are "vector" and "view".
|
|
48
|
-
- layer_uuid: The uuid of the layer.
|
|
46
|
+
layers (List['VectorLayer' | 'VectorLayerView']): list of vectorlayer and view objects to add to tileset.
|
|
49
47
|
display_name (str, optional): The display name of the tileset.
|
|
50
48
|
description (str, optional): The description of the tileset.
|
|
51
49
|
min_zoom (int, optional): The minimum zoom level of the tileset.
|
|
@@ -59,34 +57,46 @@ class Tileset(Base):
|
|
|
59
57
|
>>> from geobox import GeoboxClient
|
|
60
58
|
>>> from geobox.tileset import Tileset
|
|
61
59
|
>>> client = GeoboxClient()
|
|
62
|
-
>>>
|
|
63
|
-
|
|
64
|
-
... "layer_type": "vector",
|
|
65
|
-
... "layer_uuid": "12345678-1234-5678-1234-567812345678"
|
|
66
|
-
... }
|
|
67
|
-
... ]
|
|
60
|
+
>>> layer = client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
|
|
61
|
+
>>> view = client.get_view(uuid="12345678-1234-5678-1234-567812345678")
|
|
68
62
|
>>> tileset = Tileset.create_tileset(client,
|
|
69
63
|
... name="your_tileset_name",
|
|
70
64
|
... display_name="Your Tileset",
|
|
71
65
|
... description="Your description",
|
|
72
66
|
... min_zoom=0,
|
|
73
67
|
... max_zoom=14,
|
|
74
|
-
... layers=
|
|
68
|
+
... layers=[layer, view])
|
|
75
69
|
or
|
|
76
70
|
>>> tileset = client.create_tileset(name="your_tileset_name",
|
|
77
71
|
... display_name="Your Tileset",
|
|
78
72
|
... description="Your description",
|
|
79
73
|
... min_zoom=0,
|
|
80
74
|
... max_zoom=14,
|
|
81
|
-
... layers=
|
|
75
|
+
... layers=[layer, view])
|
|
82
76
|
"""
|
|
77
|
+
formatted_layers = []
|
|
78
|
+
for item in layers:
|
|
79
|
+
if type(item) == VectorLayer:
|
|
80
|
+
item_type = 'vector'
|
|
81
|
+
|
|
82
|
+
elif type(item) == VectorLayerView:
|
|
83
|
+
item_type = 'view'
|
|
84
|
+
|
|
85
|
+
else:
|
|
86
|
+
continue
|
|
87
|
+
|
|
88
|
+
formatted_layers.append({
|
|
89
|
+
'layer_type': item_type,
|
|
90
|
+
'layer_uuid': item.uuid
|
|
91
|
+
})
|
|
92
|
+
|
|
83
93
|
data = {
|
|
84
94
|
"name": name,
|
|
85
95
|
"display_name": display_name,
|
|
86
96
|
"description": description,
|
|
87
97
|
"min_zoom": min_zoom,
|
|
88
98
|
"max_zoom": max_zoom,
|
|
89
|
-
"layers":
|
|
99
|
+
"layers": formatted_layers,
|
|
90
100
|
"user_id": user_id
|
|
91
101
|
}
|
|
92
102
|
return super()._create(api, cls.BASE_ENDPOINT, data, factory_func=lambda api, item: Tileset(api, item['uuid'], item))
|
|
@@ -283,7 +293,7 @@ class Tileset(Base):
|
|
|
283
293
|
super().delete(urljoin(self.BASE_ENDPOINT, f'{self.uuid}/'))
|
|
284
294
|
|
|
285
295
|
|
|
286
|
-
def
|
|
296
|
+
def get_layers(self, **kwargs) -> List['VectorLayer']:
|
|
287
297
|
"""
|
|
288
298
|
Retrieves the layers of the tileset with optional parameters.
|
|
289
299
|
|
|
@@ -307,13 +317,14 @@ class Tileset(Base):
|
|
|
307
317
|
Example:
|
|
308
318
|
|
|
309
319
|
Returns:
|
|
310
|
-
List: A list of VectorLayer instances.
|
|
320
|
+
List: A list of VectorLayer or VectorLayerView instances.
|
|
311
321
|
|
|
312
322
|
Example:
|
|
313
323
|
>>> from geobox import GeoboxClient
|
|
314
324
|
>>> from geobox.tileset import Tileset
|
|
315
325
|
>>> client = GeoboxClient()
|
|
316
|
-
>>>
|
|
326
|
+
>>> tileset = Tileset.get_tileset(client, uuid="12345678-1234-5678-1234-567812345678")
|
|
327
|
+
>>> layers = tileset.get_layers()
|
|
317
328
|
"""
|
|
318
329
|
params = {
|
|
319
330
|
'f': 'json',
|
|
@@ -328,57 +339,82 @@ class Tileset(Base):
|
|
|
328
339
|
'shared': kwargs.get('shared', False)
|
|
329
340
|
}
|
|
330
341
|
endpoint = urljoin(self.BASE_ENDPOINT, f'{self.uuid}/layers/')
|
|
331
|
-
return super()._get_list(self.api, endpoint, params, factory_func=lambda api, item: VectorLayer(api, item['uuid'], item['layer_type'], item)
|
|
342
|
+
return super()._get_list(self.api, endpoint, params, factory_func=lambda api, item: VectorLayer(api, item['uuid'], item['layer_type'], item) if not item['is_view'] else \
|
|
343
|
+
VectorLayerView(api, item['uuid'], item['layer_type'], item) )
|
|
332
344
|
|
|
333
345
|
|
|
334
|
-
def add_layer(self,
|
|
346
|
+
def add_layer(self, layer: Union['VectorLayer', 'VectorLayerView']) -> None:
|
|
335
347
|
"""
|
|
336
348
|
Adds a layer to the tileset.
|
|
337
349
|
|
|
338
350
|
Args:
|
|
339
|
-
|
|
340
|
-
layer_uuid (str): The UUID of the layer.
|
|
351
|
+
layer (VectorLayer | VectorLayerView): the layer object to add to the tileset
|
|
341
352
|
|
|
342
353
|
Returns:
|
|
343
354
|
None
|
|
344
355
|
|
|
356
|
+
Raises:
|
|
357
|
+
ValueError: if the layer input is not a 'VectorLayer' or 'VetorLayerview' object
|
|
358
|
+
|
|
345
359
|
Example:
|
|
346
360
|
>>> from geobox import GeoboxClient
|
|
347
361
|
>>> from geobox.tileset import Tileset
|
|
348
362
|
>>> client = GeoboxClient()
|
|
349
363
|
>>> tileset = Tileset.get_tileset(client, uuid="12345678-1234-5678-1234-567812345678")
|
|
350
|
-
>>>
|
|
364
|
+
>>> layer = client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
|
|
365
|
+
>>> tileset.add_layer(layer)
|
|
351
366
|
"""
|
|
367
|
+
if type(layer) == VectorLayer:
|
|
368
|
+
layer_type = 'vector'
|
|
369
|
+
|
|
370
|
+
elif type(layer) == VectorLayerView:
|
|
371
|
+
layer_type = 'view'
|
|
372
|
+
|
|
373
|
+
else:
|
|
374
|
+
raise ValueError("layer input must be either 'VectorLayer' or 'VetorLayerview' object")
|
|
375
|
+
|
|
352
376
|
data = {
|
|
353
|
-
"layer_type": layer_type
|
|
354
|
-
"layer_uuid":
|
|
377
|
+
"layer_type": layer_type,
|
|
378
|
+
"layer_uuid": layer.uuid
|
|
355
379
|
}
|
|
356
380
|
|
|
357
381
|
endpoint = urljoin(self.endpoint, 'layers/')
|
|
358
382
|
return self.api.post(endpoint, data, is_json=False)
|
|
359
383
|
|
|
360
384
|
|
|
361
|
-
def delete_layer(self,
|
|
385
|
+
def delete_layer(self, layer: Union['VectorLayer', 'VectorLayerView']) -> None:
|
|
362
386
|
"""
|
|
363
387
|
Deletes a layer from the tileset.
|
|
364
388
|
|
|
365
389
|
Args:
|
|
366
|
-
|
|
367
|
-
layer_uuid (str): The UUID of the layer.
|
|
390
|
+
layer (VectorLayer | VectorLayerView): the layer object to delete from the tileset
|
|
368
391
|
|
|
369
392
|
Returns:
|
|
370
393
|
None
|
|
371
394
|
|
|
395
|
+
Raises:
|
|
396
|
+
ValueError: if the layer input is not a 'VectorLayer' or 'VetorLayerview' object
|
|
397
|
+
|
|
372
398
|
Example:
|
|
373
399
|
>>> from geobox import GeoboxClient
|
|
374
400
|
>>> from geobox.tileset import Tileset
|
|
375
401
|
>>> client = GeoboxClient()
|
|
376
402
|
>>> tileset = Tileset.get_tileset(client, uuid="12345678-1234-5678-1234-567812345678")
|
|
377
|
-
>>>
|
|
403
|
+
>>> layer = client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
|
|
404
|
+
>>> tileset.delete_layer(layer)
|
|
378
405
|
"""
|
|
406
|
+
if type(layer) == VectorLayer:
|
|
407
|
+
layer_type = 'vector'
|
|
408
|
+
|
|
409
|
+
elif type(layer) == VectorLayerView:
|
|
410
|
+
layer_type = 'view'
|
|
411
|
+
|
|
412
|
+
else:
|
|
413
|
+
raise ValueError("layer input must be either 'VectorLayer' or 'VetorLayerview' object")
|
|
414
|
+
|
|
379
415
|
data = {
|
|
380
|
-
"layer_type": layer_type
|
|
381
|
-
"layer_uuid":
|
|
416
|
+
"layer_type": layer_type,
|
|
417
|
+
"layer_uuid": layer.uuid
|
|
382
418
|
}
|
|
383
419
|
|
|
384
420
|
endpoint = urljoin(self.endpoint, 'layers/')
|
|
@@ -2,7 +2,7 @@ from typing import List, Any, TYPE_CHECKING, Union, Dict
|
|
|
2
2
|
from urllib.parse import urlencode, urljoin
|
|
3
3
|
|
|
4
4
|
from .base import Base
|
|
5
|
-
from .utils import clean_data
|
|
5
|
+
from .utils import clean_data, xor_encode
|
|
6
6
|
from .enums import UserRole, UserStatus
|
|
7
7
|
from .plan import Plan
|
|
8
8
|
|
|
@@ -113,11 +113,9 @@ class User(Base):
|
|
|
113
113
|
'return_count': kwargs.get('return_count', False),
|
|
114
114
|
'skip': kwargs.get('skip', 0),
|
|
115
115
|
'limit': kwargs.get('limit', 10),
|
|
116
|
-
'user_id': kwargs.get('user_id')
|
|
117
|
-
'shared': kwargs.get('shared', False)
|
|
116
|
+
'user_id': kwargs.get('user_id')
|
|
118
117
|
}
|
|
119
118
|
return super()._get_list(api, cls.BASE_ENDPOINT, params, factory_func=lambda api, item: User(api, item['id'], item))
|
|
120
|
-
|
|
121
119
|
|
|
122
120
|
|
|
123
121
|
@classmethod
|
|
@@ -174,7 +172,7 @@ class User(Base):
|
|
|
174
172
|
data = {
|
|
175
173
|
"username": username,
|
|
176
174
|
"email": email,
|
|
177
|
-
"password": password,
|
|
175
|
+
"password": xor_encode(password),
|
|
178
176
|
"role": role.value,
|
|
179
177
|
"first_name": first_name,
|
|
180
178
|
"last_name": last_name,
|
|
@@ -184,7 +182,6 @@ class User(Base):
|
|
|
184
182
|
return super()._create(api, cls.BASE_ENDPOINT, data, factory_func=lambda api, item: User(api, item['id'], item))
|
|
185
183
|
|
|
186
184
|
|
|
187
|
-
|
|
188
185
|
@classmethod
|
|
189
186
|
def search_users(cls, api: 'GeoboxClient', search: str = None, skip: int = 0, limit: int = 10) -> List['User']:
|
|
190
187
|
"""
|
|
@@ -377,7 +374,7 @@ class User(Base):
|
|
|
377
374
|
>>> user.change_password(new_password='user_new_password')
|
|
378
375
|
"""
|
|
379
376
|
data = clean_data({
|
|
380
|
-
"new_password": new_password
|
|
377
|
+
"new_password": xor_encode(new_password)
|
|
381
378
|
})
|
|
382
379
|
endpoint = urljoin(self.endpoint, 'change-password')
|
|
383
380
|
self.api.post(endpoint, data, is_json=False)
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
from urllib.parse import urlparse, parse_qs, urlencode, urlunparse
|
|
2
|
+
import base64
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def xor_encode(s, key=42):
|
|
6
|
+
xor_str = ''.join(chr(ord(c) ^ key) for c in s)
|
|
7
|
+
encoded_bytes = base64.b64encode(xor_str.encode('utf-8'))
|
|
8
|
+
return encoded_bytes.decode('utf-8')
|
|
2
9
|
|
|
3
10
|
|
|
4
11
|
def clean_data(data: dict) -> dict:
|
|
@@ -439,6 +439,36 @@ class VectorLayer(Base):
|
|
|
439
439
|
return super()._create(self.api, endpoint, data, factory_func=lambda api, item: VectorLayerVersion(api, item['uuid'], item))
|
|
440
440
|
|
|
441
441
|
|
|
442
|
+
def get_versions(self, **kwargs) -> List['VectorLayerVersion']:
|
|
443
|
+
"""
|
|
444
|
+
Get list of versions of the current vector layer object with optional filtering and pagination.
|
|
445
|
+
|
|
446
|
+
Keyword Args:
|
|
447
|
+
q (str): query filter based on OGC CQL standard. e.g. "field1 LIKE '%GIS%' AND created_at > '2021-01-01'"
|
|
448
|
+
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.
|
|
449
|
+
search_fields (str): comma separated list of fields for searching.
|
|
450
|
+
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.
|
|
451
|
+
return_count (bool): Whether to return total count. default is False.
|
|
452
|
+
skip (int): Number of items to skip. default is 0.
|
|
453
|
+
limit (int): Number of items to return. default is 10.
|
|
454
|
+
user_id (int): Specific user. privileges required.
|
|
455
|
+
shared (bool): Whether to return shared versions. default is False.
|
|
456
|
+
|
|
457
|
+
Returns:
|
|
458
|
+
List[VectorLayerVersion] | int: A list of vector layer version instances or the total number of versions.
|
|
459
|
+
|
|
460
|
+
Example:
|
|
461
|
+
>>> from geobox import GeoboxClient
|
|
462
|
+
>>> from geobox.version import VectorLayerVersion
|
|
463
|
+
>>> client = GeoboxClient()
|
|
464
|
+
>>> layer = client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
|
|
465
|
+
>>> versions = layer.get_versions()
|
|
466
|
+
or
|
|
467
|
+
>>> versions = layer.get_versions()
|
|
468
|
+
"""
|
|
469
|
+
return VectorLayerVersion.get_versions(self.api, layer_id=self.id, **kwargs)
|
|
470
|
+
|
|
471
|
+
|
|
442
472
|
@property
|
|
443
473
|
def wfs(self) -> str:
|
|
444
474
|
"""
|
|
@@ -2,10 +2,12 @@ import pytest
|
|
|
2
2
|
from urllib.parse import urljoin
|
|
3
3
|
from unittest.mock import patch
|
|
4
4
|
|
|
5
|
-
from geobox.tileset import Tileset
|
|
5
|
+
from geobox.tileset import Tileset
|
|
6
6
|
from geobox.enums import LayerType
|
|
7
7
|
from geobox.user import User
|
|
8
8
|
from geobox.task import Task
|
|
9
|
+
from geobox.vectorlayer import VectorLayer
|
|
10
|
+
from geobox.view import VectorLayerView
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
def test_init(api, mock_tileset_data):
|
|
@@ -35,15 +37,11 @@ def test_getattr(api, mock_tileset_data):
|
|
|
35
37
|
_ = tileset.nonexistent_attribute
|
|
36
38
|
|
|
37
39
|
|
|
38
|
-
def test_create_tileset(api, mock_tileset_data):
|
|
40
|
+
def test_create_tileset(api, mock_tileset_data, mock_vector_data, mock_view_data):
|
|
39
41
|
"""Test creating a tileset"""
|
|
40
42
|
api.post.return_value = mock_tileset_data
|
|
41
|
-
layers = [
|
|
42
|
-
|
|
43
|
-
"layer_type": "vector",
|
|
44
|
-
"layer_uuid": "your_layer_uuid"
|
|
45
|
-
}
|
|
46
|
-
]
|
|
43
|
+
layers = [VectorLayer(api, uuid=mock_vector_data['uuid'], layer_type=LayerType.MultiPoint, data=mock_vector_data),
|
|
44
|
+
VectorLayerView(api, uuid=mock_view_data['uuid'], layer_type=LayerType.MultiPoint, data=mock_view_data)]
|
|
47
45
|
tileset = Tileset.create_tileset(api,
|
|
48
46
|
mock_tileset_data['name'],
|
|
49
47
|
layers=layers,
|
|
@@ -54,6 +52,13 @@ def test_create_tileset(api, mock_tileset_data):
|
|
|
54
52
|
assert isinstance(tileset, Tileset)
|
|
55
53
|
assert tileset.uuid == mock_tileset_data['uuid']
|
|
56
54
|
assert tileset.data == mock_tileset_data
|
|
55
|
+
api.post.assert_called_once_with('tilesets/', {'name': mock_tileset_data['name'],
|
|
56
|
+
'min_zoom': mock_tileset_data['min_zoom'],
|
|
57
|
+
'max_zoom': mock_tileset_data['max_zoom'],
|
|
58
|
+
'layers': [{'layer_type': 'vector',
|
|
59
|
+
'layer_uuid': mock_vector_data['uuid']},
|
|
60
|
+
{'layer_type': 'view',
|
|
61
|
+
'layer_uuid': mock_view_data['uuid']}]})
|
|
57
62
|
|
|
58
63
|
|
|
59
64
|
def test_get_tilesets(api, mock_tileset_data):
|
|
@@ -133,43 +138,63 @@ def test_delete(api, mock_tileset_data):
|
|
|
133
138
|
api.delete.assert_called_once_with(endpoint)
|
|
134
139
|
|
|
135
140
|
|
|
136
|
-
def
|
|
141
|
+
def test_get_layers(api, mock_tileset_data, mock_vector_data, mock_view_data):
|
|
137
142
|
"""Test getting tileset layers."""
|
|
138
143
|
mock_layers = [
|
|
139
144
|
mock_vector_data,
|
|
140
|
-
|
|
145
|
+
mock_view_data
|
|
141
146
|
]
|
|
142
147
|
with patch.object(api, 'get', return_value=mock_layers):
|
|
143
148
|
tileset = Tileset(api, uuid=mock_tileset_data['uuid'], data=mock_tileset_data)
|
|
144
|
-
layers = tileset.
|
|
149
|
+
layers = tileset.get_layers()
|
|
145
150
|
assert len(layers) == 2
|
|
146
|
-
assert all(layer.layer_type == LayerType.Polygon for layer in layers)
|
|
147
151
|
assert layers[0].name == mock_vector_data['name']
|
|
148
|
-
assert layers[
|
|
152
|
+
assert type(layers[0]) == VectorLayer
|
|
153
|
+
assert layers[1].name == mock_view_data['name']
|
|
154
|
+
assert type(layers[1]) == VectorLayerView
|
|
155
|
+
|
|
149
156
|
# return count
|
|
150
157
|
with patch.object(api, 'get', return_value=10):
|
|
151
158
|
tileset = Tileset(api, uuid=mock_tileset_data['uuid'], data=mock_tileset_data)
|
|
152
|
-
layer_count = tileset.
|
|
159
|
+
layer_count = tileset.get_layers(return_count=True)
|
|
153
160
|
assert layer_count == 10
|
|
154
161
|
api.get.assert_called_once_with(f'{tileset.endpoint}layers/?f=json&return_count=True&skip=0&limit=10&shared=False')
|
|
155
162
|
|
|
156
163
|
|
|
157
|
-
def test_add_layer(api, mock_tileset_data, mock_vector_data):
|
|
164
|
+
def test_add_layer(api, mock_tileset_data, mock_vector_data, mock_view_data):
|
|
158
165
|
"""Test adding layer to tileset."""
|
|
159
166
|
with patch.object(api, 'post'):
|
|
160
167
|
tileset = Tileset(api, uuid=mock_tileset_data['uuid'], data=mock_tileset_data)
|
|
161
|
-
|
|
168
|
+
layer = VectorLayer(api, uuid=mock_vector_data['uuid'], layer_type=LayerType.MultiPoint, data=mock_vector_data)
|
|
169
|
+
view = VectorLayerView(api, uuid=mock_view_data['uuid'], layer_type=LayerType.MultiPoint, data=mock_view_data)
|
|
170
|
+
tileset.add_layer(layer=layer)
|
|
162
171
|
endpoint = urljoin(tileset.endpoint, 'layers/')
|
|
163
172
|
api.post.assert_called_once_with(endpoint, {'layer_type': 'vector', 'layer_uuid': mock_vector_data['uuid']}, is_json=False)
|
|
173
|
+
api.post.reset_mock()
|
|
174
|
+
tileset.add_layer(layer=view)
|
|
175
|
+
api.post.assert_called_once_with(endpoint, {'layer_type': 'view', 'layer_uuid': mock_view_data['uuid']}, is_json=False)
|
|
176
|
+
|
|
177
|
+
# usupported input
|
|
178
|
+
with pytest.raises(ValueError):
|
|
179
|
+
tileset.add_layer(layer='')
|
|
164
180
|
|
|
165
181
|
|
|
166
|
-
def test_delete_layer(api, mock_tileset_data, mock_vector_data):
|
|
182
|
+
def test_delete_layer(api, mock_tileset_data, mock_vector_data, mock_view_data):
|
|
167
183
|
"""Test deleting layer from tileset."""
|
|
168
184
|
with patch.object(api, 'delete'):
|
|
169
185
|
tileset = Tileset(api, uuid=mock_tileset_data['uuid'], data=mock_tileset_data)
|
|
170
|
-
|
|
186
|
+
layer = VectorLayer(api, uuid=mock_vector_data['uuid'], layer_type=LayerType.MultiPoint, data=mock_vector_data)
|
|
187
|
+
view = VectorLayerView(api, uuid=mock_view_data['uuid'], layer_type=LayerType.MultiPoint, data=mock_view_data)
|
|
188
|
+
tileset.delete_layer(layer=layer)
|
|
171
189
|
endpoint = urljoin(tileset.endpoint, 'layers/')
|
|
172
190
|
api.delete.assert_called_once_with(endpoint, {'layer_type': 'vector', 'layer_uuid': mock_vector_data['uuid']}, is_json=False)
|
|
191
|
+
api.delete.reset_mock()
|
|
192
|
+
tileset.delete_layer(layer=view)
|
|
193
|
+
api.delete.assert_called_once_with(endpoint, {'layer_type': 'view', 'layer_uuid': mock_view_data['uuid']}, is_json=False)
|
|
194
|
+
|
|
195
|
+
# usupported input
|
|
196
|
+
with pytest.raises(ValueError):
|
|
197
|
+
tileset.delete_layer(layer='')
|
|
173
198
|
|
|
174
199
|
|
|
175
200
|
def test_share(api, mock_tileset_data, mock_user_data):
|
|
@@ -5,6 +5,7 @@ from urllib.parse import urljoin, urlencode
|
|
|
5
5
|
from geobox.user import User
|
|
6
6
|
from geobox.enums import UserRole, UserStatus
|
|
7
7
|
from geobox.plan import Plan
|
|
8
|
+
from geobox.utils import xor_encode
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def test_init(api, mock_user_data):
|
|
@@ -38,7 +39,7 @@ def test_get_users(api, mock_admin_user_data):
|
|
|
38
39
|
result = User.get_users(api, search='test')
|
|
39
40
|
assert isinstance(result, list)
|
|
40
41
|
assert isinstance(result[0], User)
|
|
41
|
-
api.get.assert_called_once_with('users/?f=json&search=test&return_count=False&skip=0&limit=10
|
|
42
|
+
api.get.assert_called_once_with('users/?f=json&search=test&return_count=False&skip=0&limit=10')
|
|
42
43
|
|
|
43
44
|
def test_create_user(api, mock_admin_user_data):
|
|
44
45
|
api.post.return_value = mock_admin_user_data
|
|
@@ -57,7 +58,7 @@ def test_create_user(api, mock_admin_user_data):
|
|
|
57
58
|
expected_data = {
|
|
58
59
|
"username": mock_admin_user_data['username'],
|
|
59
60
|
"email": mock_admin_user_data['email'],
|
|
60
|
-
"password": 'password',
|
|
61
|
+
"password": xor_encode('password'),
|
|
61
62
|
"role": UserRole.ACCOUNT_ADMIN.value,
|
|
62
63
|
"first_name": mock_admin_user_data['first_name'],
|
|
63
64
|
"last_name": mock_admin_user_data['last_name'],
|
|
@@ -126,7 +127,7 @@ def test_change_password(api, mock_admin_user_data):
|
|
|
126
127
|
with patch.object(api, 'post', return_value={"message": "success"}):
|
|
127
128
|
user.change_password('new_password')
|
|
128
129
|
expected_endpoint = urljoin(user.endpoint, 'change-password')
|
|
129
|
-
api.post.assert_called_once_with(expected_endpoint, {'new_password': 'new_password'}, is_json=False)
|
|
130
|
+
api.post.assert_called_once_with(expected_endpoint, {'new_password': xor_encode('new_password')}, is_json=False)
|
|
130
131
|
|
|
131
132
|
|
|
132
133
|
def test_renew_plan(api, mock_admin_user_data):
|
|
@@ -253,6 +253,17 @@ def test_create_version(api, mock_vector_data, layer_type, mock_version_data):
|
|
|
253
253
|
api.post.assert_called_once_with(f'{layer.endpoint}versions', {'name': 'design_version', 'display_name': 'Design Version', 'description': 'This layer represents design version.'})
|
|
254
254
|
|
|
255
255
|
|
|
256
|
+
@pytest.mark.parametrize("layer_type", [type for type in LayerType])
|
|
257
|
+
def test_get_versions(api, mock_vector_data, layer_type, mock_version_data):
|
|
258
|
+
layer = VectorLayer(api, uuid=mock_vector_data['uuid'], data=mock_vector_data, layer_type=layer_type)
|
|
259
|
+
api.get.return_value = [mock_version_data]
|
|
260
|
+
versions = layer.get_versions()
|
|
261
|
+
assert len(versions) == 1
|
|
262
|
+
assert versions[0].uuid == mock_version_data['uuid']
|
|
263
|
+
assert versions[0].data == mock_version_data
|
|
264
|
+
api.get.assert_called_once_with(f'vectorLayerVersions/?layer_id={layer.id}&f=json&return_count=False&skip=0&limit=10&shared=False')
|
|
265
|
+
|
|
266
|
+
|
|
256
267
|
@pytest.mark.parametrize("layer_type", [type for type in LayerType])
|
|
257
268
|
def test_wfs_access_token(api, mock_vector_data, layer_type):
|
|
258
269
|
layer = VectorLayer(api, uuid=mock_vector_data['uuid'], data=mock_vector_data, layer_type=layer_type)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|