geobox 2.2.1__tar.gz → 2.2.2__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.
Files changed (107) hide show
  1. {geobox-2.2.1 → geobox-2.2.2}/PKG-INFO +1 -1
  2. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/feature.py +20 -7
  3. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/task.py +4 -1
  4. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/vectorlayer.py +3 -2
  5. {geobox-2.2.1 → geobox-2.2.2}/geobox/feature.py +21 -7
  6. {geobox-2.2.1 → geobox-2.2.2}/geobox/task.py +4 -1
  7. {geobox-2.2.1 → geobox-2.2.2}/geobox/vectorlayer.py +3 -2
  8. {geobox-2.2.1 → geobox-2.2.2}/geobox.egg-info/PKG-INFO +1 -1
  9. {geobox-2.2.1 → geobox-2.2.2}/pyproject.toml +1 -1
  10. {geobox-2.2.1 → geobox-2.2.2}/tests/test_feature.py +56 -3
  11. {geobox-2.2.1 → geobox-2.2.2}/LICENSE +0 -0
  12. {geobox-2.2.1 → geobox-2.2.2}/README.md +0 -0
  13. {geobox-2.2.1 → geobox-2.2.2}/geobox/__init__.py +0 -0
  14. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/__init__.py +0 -0
  15. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/api.py +0 -0
  16. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/apikey.py +0 -0
  17. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/attachment.py +0 -0
  18. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/base.py +0 -0
  19. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/basemap.py +0 -0
  20. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/dashboard.py +0 -0
  21. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/field.py +0 -0
  22. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/file.py +0 -0
  23. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/layout.py +0 -0
  24. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/log.py +0 -0
  25. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/map.py +0 -0
  26. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/model3d.py +0 -0
  27. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/mosaic.py +0 -0
  28. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/plan.py +0 -0
  29. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/query.py +0 -0
  30. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/raster.py +0 -0
  31. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/raster_analysis.py +0 -0
  32. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/route.py +0 -0
  33. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/scene.py +0 -0
  34. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/settings.py +0 -0
  35. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/tile3d.py +0 -0
  36. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/tileset.py +0 -0
  37. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/usage.py +0 -0
  38. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/user.py +0 -0
  39. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/vector_tool.py +0 -0
  40. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/version.py +0 -0
  41. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/view.py +0 -0
  42. {geobox-2.2.1 → geobox-2.2.2}/geobox/aio/workflow.py +0 -0
  43. {geobox-2.2.1 → geobox-2.2.2}/geobox/api.py +0 -0
  44. {geobox-2.2.1 → geobox-2.2.2}/geobox/apikey.py +0 -0
  45. {geobox-2.2.1 → geobox-2.2.2}/geobox/attachment.py +0 -0
  46. {geobox-2.2.1 → geobox-2.2.2}/geobox/base.py +0 -0
  47. {geobox-2.2.1 → geobox-2.2.2}/geobox/basemap.py +0 -0
  48. {geobox-2.2.1 → geobox-2.2.2}/geobox/dashboard.py +0 -0
  49. {geobox-2.2.1 → geobox-2.2.2}/geobox/enums.py +0 -0
  50. {geobox-2.2.1 → geobox-2.2.2}/geobox/exception.py +0 -0
  51. {geobox-2.2.1 → geobox-2.2.2}/geobox/field.py +0 -0
  52. {geobox-2.2.1 → geobox-2.2.2}/geobox/file.py +0 -0
  53. {geobox-2.2.1 → geobox-2.2.2}/geobox/layout.py +0 -0
  54. {geobox-2.2.1 → geobox-2.2.2}/geobox/log.py +0 -0
  55. {geobox-2.2.1 → geobox-2.2.2}/geobox/map.py +0 -0
  56. {geobox-2.2.1 → geobox-2.2.2}/geobox/model3d.py +0 -0
  57. {geobox-2.2.1 → geobox-2.2.2}/geobox/mosaic.py +0 -0
  58. {geobox-2.2.1 → geobox-2.2.2}/geobox/plan.py +0 -0
  59. {geobox-2.2.1 → geobox-2.2.2}/geobox/query.py +0 -0
  60. {geobox-2.2.1 → geobox-2.2.2}/geobox/raster.py +0 -0
  61. {geobox-2.2.1 → geobox-2.2.2}/geobox/raster_analysis.py +0 -0
  62. {geobox-2.2.1 → geobox-2.2.2}/geobox/route.py +0 -0
  63. {geobox-2.2.1 → geobox-2.2.2}/geobox/scene.py +0 -0
  64. {geobox-2.2.1 → geobox-2.2.2}/geobox/settings.py +0 -0
  65. {geobox-2.2.1 → geobox-2.2.2}/geobox/tile3d.py +0 -0
  66. {geobox-2.2.1 → geobox-2.2.2}/geobox/tileset.py +0 -0
  67. {geobox-2.2.1 → geobox-2.2.2}/geobox/usage.py +0 -0
  68. {geobox-2.2.1 → geobox-2.2.2}/geobox/user.py +0 -0
  69. {geobox-2.2.1 → geobox-2.2.2}/geobox/utils.py +0 -0
  70. {geobox-2.2.1 → geobox-2.2.2}/geobox/vector_tool.py +0 -0
  71. {geobox-2.2.1 → geobox-2.2.2}/geobox/version.py +0 -0
  72. {geobox-2.2.1 → geobox-2.2.2}/geobox/view.py +0 -0
  73. {geobox-2.2.1 → geobox-2.2.2}/geobox/workflow.py +0 -0
  74. {geobox-2.2.1 → geobox-2.2.2}/geobox.egg-info/SOURCES.txt +0 -0
  75. {geobox-2.2.1 → geobox-2.2.2}/geobox.egg-info/dependency_links.txt +0 -0
  76. {geobox-2.2.1 → geobox-2.2.2}/geobox.egg-info/requires.txt +0 -0
  77. {geobox-2.2.1 → geobox-2.2.2}/geobox.egg-info/top_level.txt +0 -0
  78. {geobox-2.2.1 → geobox-2.2.2}/setup.cfg +0 -0
  79. {geobox-2.2.1 → geobox-2.2.2}/tests/test_api.py +0 -0
  80. {geobox-2.2.1 → geobox-2.2.2}/tests/test_apikey.py +0 -0
  81. {geobox-2.2.1 → geobox-2.2.2}/tests/test_attachment.py +0 -0
  82. {geobox-2.2.1 → geobox-2.2.2}/tests/test_basemap.py +0 -0
  83. {geobox-2.2.1 → geobox-2.2.2}/tests/test_dashboard.py +0 -0
  84. {geobox-2.2.1 → geobox-2.2.2}/tests/test_field.py +0 -0
  85. {geobox-2.2.1 → geobox-2.2.2}/tests/test_file.py +0 -0
  86. {geobox-2.2.1 → geobox-2.2.2}/tests/test_layout.py +0 -0
  87. {geobox-2.2.1 → geobox-2.2.2}/tests/test_log.py +0 -0
  88. {geobox-2.2.1 → geobox-2.2.2}/tests/test_map.py +0 -0
  89. {geobox-2.2.1 → geobox-2.2.2}/tests/test_model3d.py +0 -0
  90. {geobox-2.2.1 → geobox-2.2.2}/tests/test_mosaic.py +0 -0
  91. {geobox-2.2.1 → geobox-2.2.2}/tests/test_plan.py +0 -0
  92. {geobox-2.2.1 → geobox-2.2.2}/tests/test_query.py +0 -0
  93. {geobox-2.2.1 → geobox-2.2.2}/tests/test_raster.py +0 -0
  94. {geobox-2.2.1 → geobox-2.2.2}/tests/test_raster_analysis.py +0 -0
  95. {geobox-2.2.1 → geobox-2.2.2}/tests/test_route.py +0 -0
  96. {geobox-2.2.1 → geobox-2.2.2}/tests/test_scene.py +0 -0
  97. {geobox-2.2.1 → geobox-2.2.2}/tests/test_settings.py +0 -0
  98. {geobox-2.2.1 → geobox-2.2.2}/tests/test_task.py +0 -0
  99. {geobox-2.2.1 → geobox-2.2.2}/tests/test_tile3d.py +0 -0
  100. {geobox-2.2.1 → geobox-2.2.2}/tests/test_tileset.py +0 -0
  101. {geobox-2.2.1 → geobox-2.2.2}/tests/test_usage.py +0 -0
  102. {geobox-2.2.1 → geobox-2.2.2}/tests/test_user.py +0 -0
  103. {geobox-2.2.1 → geobox-2.2.2}/tests/test_vector_tool.py +0 -0
  104. {geobox-2.2.1 → geobox-2.2.2}/tests/test_vectorlayer.py +0 -0
  105. {geobox-2.2.1 → geobox-2.2.2}/tests/test_version.py +0 -0
  106. {geobox-2.2.1 → geobox-2.2.2}/tests/test_view.py +0 -0
  107. {geobox-2.2.1 → geobox-2.2.2}/tests/test_workflow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: geobox
3
- Version: 2.2.1
3
+ Version: 2.2.2
4
4
  Summary: SDK for Geobox's APIs
5
5
  Author-email: Hamid Heydari <heydari.h62@gmail.com>
6
6
  License: MIT
@@ -29,7 +29,11 @@ class AsyncFeature(AsyncBase):
29
29
  super().__init__(api=layer.api)
30
30
  self.layer = layer
31
31
  self._srid = srid
32
- self.data = data or {}
32
+ self.data = data or {
33
+ "type": "Feature",
34
+ "geometry": {},
35
+ "properties": {}
36
+ }
33
37
  self.original_geometry = self.data.get('geometry')
34
38
  self.endpoint = urljoin(layer.endpoint, f'features/{self.data.get("id")}/') if self.data.get('id') else None
35
39
 
@@ -277,13 +281,14 @@ class AsyncFeature(AsyncBase):
277
281
 
278
282
 
279
283
  @classmethod
280
- async def create_feature(cls, layer: 'VectorLayer', geojson: Dict) -> 'AsyncFeature':
284
+ async def create_feature(cls, layer: 'VectorLayer', geojson: Dict, srid: int = 3857) -> 'AsyncFeature':
281
285
  """
282
286
  [async] Create a new feature in the vector layer.
283
287
 
284
288
  Args:
285
289
  layer (VectorLayer): The vector layer to create the feature in
286
290
  geojson (Dict): The GeoJSON data for the feature
291
+ srid (int, optional): the feature srid. default: 3857
287
292
 
288
293
  Returns:
289
294
  AsyncFeature: The created feature instance
@@ -299,8 +304,19 @@ class AsyncFeature(AsyncBase):
299
304
  ... }
300
305
  >>> feature = await Feature.create_feature(layer, geojson)
301
306
  """
307
+ feature = AsyncFeature(layer, srid=srid, data=geojson)
308
+ data = feature.data.copy()
309
+ srid = feature.srid
310
+
311
+ if feature.srid != feature.BASE_SRID:
312
+ feature = feature.transform(feature.BASE_SRID)
313
+
302
314
  endpoint = urljoin(layer.endpoint, 'features/')
303
- return await cls._create(layer.api, endpoint, geojson, factory_func=lambda api, item: AsyncFeature(layer, data=item))
315
+
316
+ feature = await cls._create(layer.api, endpoint, feature.data, factory_func=lambda api, item: AsyncFeature(layer, data=item))
317
+ feature.data['geometry'] = data['geometry']
318
+ feature._srid = srid
319
+ return feature
304
320
 
305
321
 
306
322
  @classmethod
@@ -360,10 +376,7 @@ class AsyncFeature(AsyncBase):
360
376
  "Install it with: pip install geobox[geometry]"
361
377
  )
362
378
 
363
- if not self.data:
364
- return None
365
-
366
- elif not self.data.get('geometry'):
379
+ if not self.data.get('geometry'):
367
380
  raise ValueError("Geometry is not present in the feature data")
368
381
 
369
382
  elif not isinstance(self.data['geometry'], dict):
@@ -2,6 +2,7 @@ import time
2
2
  import asyncio
3
3
  from urllib.parse import urljoin
4
4
  from typing import Optional, Dict, List, Optional, Union, TYPE_CHECKING
5
+ import logging
5
6
 
6
7
  from .base import AsyncBase
7
8
  from ..enums import TaskStatus
@@ -16,6 +17,8 @@ if TYPE_CHECKING:
16
17
  from ..api import GeoboxClient
17
18
  from ..task import Task
18
19
 
20
+ logger = logging.getLogger(__name__)
21
+
19
22
 
20
23
  class AsyncTask(AsyncBase):
21
24
 
@@ -233,7 +236,7 @@ class AsyncTask(AsyncBase):
233
236
  return await self._wait(timeout, interval, progress_bar)
234
237
  except Exception as e:
235
238
  last_exception = e
236
- print(f"[Retry {attempt}/{retry}] Task wait failed: {e}")
239
+ logger.warning(f"[Retry {attempt}/{retry}] Task wait failed: {e}")
237
240
  time.sleep(interval)
238
241
  raise last_exception
239
242
 
@@ -755,12 +755,13 @@ class AsyncVectorLayer(AsyncBase):
755
755
  return feature
756
756
 
757
757
 
758
- async def create_feature(self, geojson: Dict)-> 'AsyncFeature':
758
+ async def create_feature(self, geojson: Dict, srid: int = AsyncFeature.BASE_SRID)-> 'AsyncFeature':
759
759
  """
760
760
  [async] Create a new feature in the layer.
761
761
 
762
762
  Args:
763
763
  geojson (Dict): The feature data including properties and geometry.
764
+ srid (int, optional): the feature srid. default: 3857
764
765
 
765
766
  Returns:
766
767
  Feature: The newly created feature instance.
@@ -780,7 +781,7 @@ class AsyncVectorLayer(AsyncBase):
780
781
  ... }
781
782
  >>> feature = await layer.create_feature(geojson=geojson)
782
783
  """
783
- return await AsyncFeature.create_feature(self, geojson)
784
+ return await AsyncFeature.create_feature(self, geojson, srid=srid)
784
785
 
785
786
 
786
787
  async def delete_features(self,
@@ -34,7 +34,7 @@ class Feature(Base):
34
34
  ... "type": "Feature",
35
35
  ... "geometry": {
36
36
  ... "type": "Point",
37
- ... "coordinates": [58.4, 23.6]
37
+ ... "coordinates": [10.0, 10.0]
38
38
  ... },
39
39
  ... "properties": {
40
40
  ... "name": "Test feature"
@@ -46,7 +46,11 @@ class Feature(Base):
46
46
  super().__init__(api=layer.api)
47
47
  self.layer = layer
48
48
  self._srid = srid
49
- self.data = data or {}
49
+ self.data = data or {
50
+ "type": "Feature",
51
+ "geometry": {},
52
+ "properties": {}
53
+ }
50
54
  self.original_geometry = self.data.get('geometry')
51
55
  self.endpoint = urljoin(layer.endpoint, f'features/{self.data.get("id")}/') if self.data.get('id') else None
52
56
 
@@ -295,13 +299,14 @@ class Feature(Base):
295
299
 
296
300
 
297
301
  @classmethod
298
- def create_feature(cls, layer: 'VectorLayer', geojson: Dict) -> 'Feature':
302
+ def create_feature(cls, layer: 'VectorLayer', geojson: Dict, srid: int = 3857) -> 'Feature':
299
303
  """
300
304
  Create a new feature in the vector layer.
301
305
 
302
306
  Args:
303
307
  layer (VectorLayer): The vector layer to create the feature in
304
308
  geojson (Dict): The GeoJSON data for the feature
309
+ srid (int, optional): the feature srid. default: 3857
305
310
 
306
311
  Returns:
307
312
  Feature: The created feature instance
@@ -318,8 +323,19 @@ class Feature(Base):
318
323
  ... }
319
324
  >>> feature = Feature.create_feature(layer, geojson)
320
325
  """
326
+ feature = Feature(layer, srid=srid, data=geojson)
327
+ data = feature.data.copy()
328
+ srid = feature.srid
329
+
330
+ if feature.srid != feature.BASE_SRID:
331
+ feature = feature.transform(feature.BASE_SRID)
332
+
321
333
  endpoint = urljoin(layer.endpoint, 'features/')
322
- return cls._create(layer.api, endpoint, geojson, factory_func=lambda api, item: Feature(layer, data=item))
334
+
335
+ feature = cls._create(layer.api, endpoint, feature.data, factory_func=lambda api, item: Feature(layer, data=item))
336
+ feature.data['geometry'] = data['geometry']
337
+ feature._srid = srid
338
+ return feature
323
339
 
324
340
 
325
341
  @classmethod
@@ -380,10 +396,8 @@ class Feature(Base):
380
396
  "Install it with: pip install geobox[geometry]"
381
397
  )
382
398
 
383
- if not self.data:
384
- return None
385
399
 
386
- elif not self.data.get('geometry'):
400
+ if not self.data.get('geometry'):
387
401
  raise ValueError("Geometry is not present in the feature data")
388
402
 
389
403
  elif not isinstance(self.data['geometry'], dict):
@@ -1,6 +1,7 @@
1
1
  from urllib.parse import urljoin, urlencode
2
2
  from typing import Optional, Dict, List, Optional, Union, TYPE_CHECKING
3
3
  import time
4
+ import logging
4
5
 
5
6
  from .base import Base
6
7
  from .enums import TaskStatus
@@ -15,6 +16,8 @@ if TYPE_CHECKING:
15
16
  from .aio import AsyncGeoboxClient
16
17
  from .aio.task import AsyncTask
17
18
 
19
+ logger = logging.getLogger(__name__)
20
+
18
21
 
19
22
  class Task(Base):
20
23
 
@@ -230,7 +233,7 @@ class Task(Base):
230
233
  return self._wait(timeout, interval, progress_bar)
231
234
  except Exception as e:
232
235
  last_exception = e
233
- print(f"[Retry {attempt}/{retry}] Task wait failed: {e}")
236
+ logger.warning(f"[Retry {attempt}/{retry}] Task wait failed: {e}")
234
237
  time.sleep(interval)
235
238
  raise last_exception
236
239
 
@@ -756,12 +756,13 @@ class VectorLayer(Base):
756
756
  return feature
757
757
 
758
758
 
759
- def create_feature(self, geojson: Dict)-> 'Feature':
759
+ def create_feature(self, geojson: Dict, srid: int = Feature.BASE_SRID)-> 'Feature':
760
760
  """
761
761
  Create a new feature in the layer.
762
762
 
763
763
  Args:
764
764
  geojson (Dict): The feature data including properties and geometry.
765
+ srid (int, optional): the feature srid. default: 3857
765
766
 
766
767
  Returns:
767
768
  Feature: The newly created feature instance.
@@ -781,7 +782,7 @@ class VectorLayer(Base):
781
782
  ... }
782
783
  >>> feature = layer.create_feature(geojson=geojson)
783
784
  """
784
- return Feature.create_feature(self, geojson)
785
+ return Feature.create_feature(self, geojson, srid=srid)
785
786
 
786
787
 
787
788
  def delete_features(self, q: str = None, bbox: str = None, bbox_srid: int = None, feature_ids: List[int] = None,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: geobox
3
- Version: 2.2.1
3
+ Version: 2.2.2
4
4
  Summary: SDK for Geobox's APIs
5
5
  Author-email: Hamid Heydari <heydari.h62@gmail.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "geobox"
7
- version = "2.2.1"
7
+ version = "2.2.2"
8
8
  description = "SDK for Geobox's APIs"
9
9
  authors = [
10
10
  {name = "Hamid Heydari", email = "heydari.h62@gmail.com"}
@@ -107,6 +107,26 @@ def test_save_srid(api, mock_vector_data, mock_feature_data, feature_type):
107
107
  assert feature.srid == 3857
108
108
  assert feature.coordinates == coords
109
109
 
110
+ # without id
111
+ feature_id = feature_data['id']
112
+ del feature_data['id']
113
+ # Mock response should include the id as the API returns the created feature with id
114
+ response_data = feature_data.copy()
115
+ response_data['id'] = feature_id
116
+ api.post.return_value = response_data
117
+ feature = Feature(layer, srid=3857, data=feature_data)
118
+ feature.transform(4326)
119
+ coords = feature.coordinates
120
+ feature.save()
121
+ assert feature.srid == 4326
122
+ assert feature.coordinates == coords
123
+
124
+ feature = Feature(layer, srid=3857, data=feature_data)
125
+ coords = feature.coordinates
126
+ feature.save()
127
+ assert feature.srid == 3857
128
+ assert feature.coordinates == coords
129
+
110
130
 
111
131
  @pytest.mark.parametrize("feature_type", [type.value for type in FeatureType])
112
132
  def test_delete(api, mock_vector_data, mock_feature_data, feature_type):
@@ -159,7 +179,7 @@ def test_create_feature(api, mock_vector_data, mock_feature_data, feature_type):
159
179
 
160
180
  feature = Feature.create_feature(
161
181
  layer=layer,
162
- geojson=expected_request
182
+ geojson=expected_request,
163
183
  )
164
184
  api.post.assert_called_once_with(f'{layer.endpoint}features/', expected_request)
165
185
  assert isinstance(feature, Feature)
@@ -168,6 +188,39 @@ def test_create_feature(api, mock_vector_data, mock_feature_data, feature_type):
168
188
  assert feature.data == api_response
169
189
 
170
190
 
191
+ @pytest.mark.parametrize("feature_type", [type.value for type in FeatureType])
192
+ def test_create_feature_with_srid(api, mock_vector_data, mock_feature_data, feature_type):
193
+ """Test creating a feature for different geometry types"""
194
+ layer = VectorLayer(api, uuid=mock_vector_data['uuid'], layer_type=LayerType.MultiPoint, data=mock_vector_data)
195
+ feature_data = mock_feature_data[feature_type]
196
+
197
+ expected_request = {
198
+ 'type': 'Feature',
199
+ 'geometry': feature_data['geometry'],
200
+ 'properties': {'name': f'test_{feature_type.lower()}'}
201
+ }
202
+
203
+ api_response = {
204
+ 'type': 'Feature',
205
+ 'geometry': feature_data['geometry'],
206
+ 'properties': {'name': f'test_{feature_type.lower()}'},
207
+ 'id': feature_data['id'],
208
+ 'bbox': feature_data['bbox']
209
+ }
210
+ api.post.return_value = api_response
211
+
212
+ feature = Feature.create_feature(
213
+ layer=layer,
214
+ geojson=expected_request,
215
+ srid=4326
216
+ )
217
+ api.post.assert_called_once_with(f'{layer.endpoint}features/', expected_request)
218
+ assert isinstance(feature, Feature)
219
+ assert feature.layer == layer
220
+ assert feature.srid == 4326
221
+ assert feature.data == api_response
222
+
223
+
171
224
  @pytest.mark.parametrize("feature_type", [type.value for type in FeatureType])
172
225
  def test_get_feature(api, mock_vector_data, mock_feature_data, feature_type):
173
226
  """Test getting a feature for different geometry types"""
@@ -200,8 +253,8 @@ def test_geometry_no_data(api, mock_vector_data):
200
253
  layer = VectorLayer(api, uuid=mock_vector_data['uuid'], layer_type=LayerType('Point'), data=mock_vector_data)
201
254
  feature = Feature(layer, srid=3857, data=None) # No data
202
255
 
203
- result = feature.geometry
204
- assert result is None
256
+ with pytest.raises(ValueError, match="Geometry is not present in the feature data"):
257
+ feature.geometry
205
258
 
206
259
 
207
260
  def test_geometry_no_geometry_field(api, mock_vector_data):
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
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