geobox 2.2.0__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.0 → geobox-2.2.2}/PKG-INFO +1 -1
  2. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/feature.py +33 -10
  3. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/task.py +4 -1
  4. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/vectorlayer.py +3 -2
  5. {geobox-2.2.0 → geobox-2.2.2}/geobox/feature.py +46 -11
  6. {geobox-2.2.0 → geobox-2.2.2}/geobox/task.py +4 -1
  7. {geobox-2.2.0 → geobox-2.2.2}/geobox/vectorlayer.py +3 -2
  8. {geobox-2.2.0 → geobox-2.2.2}/geobox.egg-info/PKG-INFO +1 -1
  9. {geobox-2.2.0 → geobox-2.2.2}/pyproject.toml +1 -1
  10. {geobox-2.2.0 → geobox-2.2.2}/tests/test_feature.py +56 -3
  11. {geobox-2.2.0 → geobox-2.2.2}/LICENSE +0 -0
  12. {geobox-2.2.0 → geobox-2.2.2}/README.md +0 -0
  13. {geobox-2.2.0 → geobox-2.2.2}/geobox/__init__.py +0 -0
  14. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/__init__.py +0 -0
  15. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/api.py +0 -0
  16. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/apikey.py +0 -0
  17. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/attachment.py +0 -0
  18. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/base.py +0 -0
  19. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/basemap.py +0 -0
  20. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/dashboard.py +0 -0
  21. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/field.py +0 -0
  22. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/file.py +0 -0
  23. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/layout.py +0 -0
  24. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/log.py +0 -0
  25. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/map.py +0 -0
  26. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/model3d.py +0 -0
  27. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/mosaic.py +0 -0
  28. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/plan.py +0 -0
  29. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/query.py +0 -0
  30. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/raster.py +0 -0
  31. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/raster_analysis.py +0 -0
  32. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/route.py +0 -0
  33. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/scene.py +0 -0
  34. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/settings.py +0 -0
  35. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/tile3d.py +0 -0
  36. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/tileset.py +0 -0
  37. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/usage.py +0 -0
  38. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/user.py +0 -0
  39. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/vector_tool.py +0 -0
  40. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/version.py +0 -0
  41. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/view.py +0 -0
  42. {geobox-2.2.0 → geobox-2.2.2}/geobox/aio/workflow.py +0 -0
  43. {geobox-2.2.0 → geobox-2.2.2}/geobox/api.py +0 -0
  44. {geobox-2.2.0 → geobox-2.2.2}/geobox/apikey.py +0 -0
  45. {geobox-2.2.0 → geobox-2.2.2}/geobox/attachment.py +0 -0
  46. {geobox-2.2.0 → geobox-2.2.2}/geobox/base.py +0 -0
  47. {geobox-2.2.0 → geobox-2.2.2}/geobox/basemap.py +0 -0
  48. {geobox-2.2.0 → geobox-2.2.2}/geobox/dashboard.py +0 -0
  49. {geobox-2.2.0 → geobox-2.2.2}/geobox/enums.py +0 -0
  50. {geobox-2.2.0 → geobox-2.2.2}/geobox/exception.py +0 -0
  51. {geobox-2.2.0 → geobox-2.2.2}/geobox/field.py +0 -0
  52. {geobox-2.2.0 → geobox-2.2.2}/geobox/file.py +0 -0
  53. {geobox-2.2.0 → geobox-2.2.2}/geobox/layout.py +0 -0
  54. {geobox-2.2.0 → geobox-2.2.2}/geobox/log.py +0 -0
  55. {geobox-2.2.0 → geobox-2.2.2}/geobox/map.py +0 -0
  56. {geobox-2.2.0 → geobox-2.2.2}/geobox/model3d.py +0 -0
  57. {geobox-2.2.0 → geobox-2.2.2}/geobox/mosaic.py +0 -0
  58. {geobox-2.2.0 → geobox-2.2.2}/geobox/plan.py +0 -0
  59. {geobox-2.2.0 → geobox-2.2.2}/geobox/query.py +0 -0
  60. {geobox-2.2.0 → geobox-2.2.2}/geobox/raster.py +0 -0
  61. {geobox-2.2.0 → geobox-2.2.2}/geobox/raster_analysis.py +0 -0
  62. {geobox-2.2.0 → geobox-2.2.2}/geobox/route.py +0 -0
  63. {geobox-2.2.0 → geobox-2.2.2}/geobox/scene.py +0 -0
  64. {geobox-2.2.0 → geobox-2.2.2}/geobox/settings.py +0 -0
  65. {geobox-2.2.0 → geobox-2.2.2}/geobox/tile3d.py +0 -0
  66. {geobox-2.2.0 → geobox-2.2.2}/geobox/tileset.py +0 -0
  67. {geobox-2.2.0 → geobox-2.2.2}/geobox/usage.py +0 -0
  68. {geobox-2.2.0 → geobox-2.2.2}/geobox/user.py +0 -0
  69. {geobox-2.2.0 → geobox-2.2.2}/geobox/utils.py +0 -0
  70. {geobox-2.2.0 → geobox-2.2.2}/geobox/vector_tool.py +0 -0
  71. {geobox-2.2.0 → geobox-2.2.2}/geobox/version.py +0 -0
  72. {geobox-2.2.0 → geobox-2.2.2}/geobox/view.py +0 -0
  73. {geobox-2.2.0 → geobox-2.2.2}/geobox/workflow.py +0 -0
  74. {geobox-2.2.0 → geobox-2.2.2}/geobox.egg-info/SOURCES.txt +0 -0
  75. {geobox-2.2.0 → geobox-2.2.2}/geobox.egg-info/dependency_links.txt +0 -0
  76. {geobox-2.2.0 → geobox-2.2.2}/geobox.egg-info/requires.txt +0 -0
  77. {geobox-2.2.0 → geobox-2.2.2}/geobox.egg-info/top_level.txt +0 -0
  78. {geobox-2.2.0 → geobox-2.2.2}/setup.cfg +0 -0
  79. {geobox-2.2.0 → geobox-2.2.2}/tests/test_api.py +0 -0
  80. {geobox-2.2.0 → geobox-2.2.2}/tests/test_apikey.py +0 -0
  81. {geobox-2.2.0 → geobox-2.2.2}/tests/test_attachment.py +0 -0
  82. {geobox-2.2.0 → geobox-2.2.2}/tests/test_basemap.py +0 -0
  83. {geobox-2.2.0 → geobox-2.2.2}/tests/test_dashboard.py +0 -0
  84. {geobox-2.2.0 → geobox-2.2.2}/tests/test_field.py +0 -0
  85. {geobox-2.2.0 → geobox-2.2.2}/tests/test_file.py +0 -0
  86. {geobox-2.2.0 → geobox-2.2.2}/tests/test_layout.py +0 -0
  87. {geobox-2.2.0 → geobox-2.2.2}/tests/test_log.py +0 -0
  88. {geobox-2.2.0 → geobox-2.2.2}/tests/test_map.py +0 -0
  89. {geobox-2.2.0 → geobox-2.2.2}/tests/test_model3d.py +0 -0
  90. {geobox-2.2.0 → geobox-2.2.2}/tests/test_mosaic.py +0 -0
  91. {geobox-2.2.0 → geobox-2.2.2}/tests/test_plan.py +0 -0
  92. {geobox-2.2.0 → geobox-2.2.2}/tests/test_query.py +0 -0
  93. {geobox-2.2.0 → geobox-2.2.2}/tests/test_raster.py +0 -0
  94. {geobox-2.2.0 → geobox-2.2.2}/tests/test_raster_analysis.py +0 -0
  95. {geobox-2.2.0 → geobox-2.2.2}/tests/test_route.py +0 -0
  96. {geobox-2.2.0 → geobox-2.2.2}/tests/test_scene.py +0 -0
  97. {geobox-2.2.0 → geobox-2.2.2}/tests/test_settings.py +0 -0
  98. {geobox-2.2.0 → geobox-2.2.2}/tests/test_task.py +0 -0
  99. {geobox-2.2.0 → geobox-2.2.2}/tests/test_tile3d.py +0 -0
  100. {geobox-2.2.0 → geobox-2.2.2}/tests/test_tileset.py +0 -0
  101. {geobox-2.2.0 → geobox-2.2.2}/tests/test_usage.py +0 -0
  102. {geobox-2.2.0 → geobox-2.2.2}/tests/test_user.py +0 -0
  103. {geobox-2.2.0 → geobox-2.2.2}/tests/test_vector_tool.py +0 -0
  104. {geobox-2.2.0 → geobox-2.2.2}/tests/test_vectorlayer.py +0 -0
  105. {geobox-2.2.0 → geobox-2.2.2}/tests/test_version.py +0 -0
  106. {geobox-2.2.0 → geobox-2.2.2}/tests/test_view.py +0 -0
  107. {geobox-2.2.0 → 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.0
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
 
@@ -58,7 +62,8 @@ class AsyncFeature(AsyncBase):
58
62
  Returns:
59
63
  str: A string representation of the Feature object.
60
64
  """
61
- return f"AsyncFeature(id={self.id}, type={self.feature_type})"
65
+ feature_id = getattr(self, "id", "-1")
66
+ return f"AsyncFeature(id={feature_id}, type={self.feature_type})"
62
67
 
63
68
 
64
69
  def __getattr__(self, name: str) -> Any:
@@ -210,18 +215,27 @@ class AsyncFeature(AsyncBase):
210
215
  >>> await feature.save()
211
216
  """
212
217
  data = self.data.copy()
218
+ srid = self.srid
219
+
213
220
  try:
214
221
  if self.id:
215
222
  if self.srid != self.BASE_SRID:
216
- self.data['geometry'] = self.original_geometry
223
+ self = self.transform(self.BASE_SRID)
224
+ self.data['geometry'] = self.original_geometry
217
225
  await self.update(self.data)
218
226
  self.coordinates = data['geometry']['coordinates']
219
227
  except AttributeError:
228
+ if self.srid != self.BASE_SRID:
229
+ self = self.transform(self.BASE_SRID)
220
230
  endpoint = urljoin(self.layer.endpoint, 'features/')
221
- response = await self.layer.api.post(endpoint, data)
231
+ request_data = self.data.copy()
232
+ response = await self.layer.api.post(endpoint, request_data)
222
233
  self.endpoint = urljoin(self.layer.endpoint, f'features/{response["id"]}/')
223
234
  self.data.update(response)
224
235
 
236
+ self.data['geometry'] = data['geometry']
237
+ self._srid = srid
238
+
225
239
 
226
240
  async def delete(self) -> None:
227
241
  """
@@ -267,13 +281,14 @@ class AsyncFeature(AsyncBase):
267
281
 
268
282
 
269
283
  @classmethod
270
- async def create_feature(cls, layer: 'VectorLayer', geojson: Dict) -> 'AsyncFeature':
284
+ async def create_feature(cls, layer: 'VectorLayer', geojson: Dict, srid: int = 3857) -> 'AsyncFeature':
271
285
  """
272
286
  [async] Create a new feature in the vector layer.
273
287
 
274
288
  Args:
275
289
  layer (VectorLayer): The vector layer to create the feature in
276
290
  geojson (Dict): The GeoJSON data for the feature
291
+ srid (int, optional): the feature srid. default: 3857
277
292
 
278
293
  Returns:
279
294
  AsyncFeature: The created feature instance
@@ -289,8 +304,19 @@ class AsyncFeature(AsyncBase):
289
304
  ... }
290
305
  >>> feature = await Feature.create_feature(layer, geojson)
291
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
+
292
314
  endpoint = urljoin(layer.endpoint, 'features/')
293
- 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
294
320
 
295
321
 
296
322
  @classmethod
@@ -350,10 +376,7 @@ class AsyncFeature(AsyncBase):
350
376
  "Install it with: pip install geobox[geometry]"
351
377
  )
352
378
 
353
- if not self.data:
354
- return None
355
-
356
- elif not self.data.get('geometry'):
379
+ if not self.data.get('geometry'):
357
380
  raise ValueError("Geometry is not present in the feature data")
358
381
 
359
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,
@@ -30,13 +30,27 @@ class Feature(Base):
30
30
  >>> from geobox import GeoboxClient, Feature
31
31
  >>> client = GeoboxClient()
32
32
  >>> layer = client.get_vector(uuid="12345678-1234-5678-1234-567812345678")
33
- >>> feature = Feature(layer=layer, srid=4326) # example srid set to 4326
33
+ >>> geojson = {
34
+ ... "type": "Feature",
35
+ ... "geometry": {
36
+ ... "type": "Point",
37
+ ... "coordinates": [10.0, 10.0]
38
+ ... },
39
+ ... "properties": {
40
+ ... "name": "Test feature"
41
+ ... }
42
+ ... }
43
+ >>> feature = Feature(layer=layer, data=geojson, srid=4326) # example srid set to 4326
34
44
  >>> feature.save()
35
45
  """
36
46
  super().__init__(api=layer.api)
37
47
  self.layer = layer
38
48
  self._srid = srid
39
- self.data = data or {}
49
+ self.data = data or {
50
+ "type": "Feature",
51
+ "geometry": {},
52
+ "properties": {}
53
+ }
40
54
  self.original_geometry = self.data.get('geometry')
41
55
  self.endpoint = urljoin(layer.endpoint, f'features/{self.data.get("id")}/') if self.data.get('id') else None
42
56
 
@@ -65,8 +79,9 @@ class Feature(Base):
65
79
  Returns:
66
80
  str: A string representation of the Feature object.
67
81
  """
68
- return f"Feature(id={self.id}, type={self.feature_type})"
69
-
82
+ feature_id = getattr(self, "id", "-1")
83
+ return f"Feature(id={feature_id}, type={self.feature_type})"
84
+
70
85
 
71
86
  def __getattr__(self, name: str) -> Any:
72
87
  """
@@ -217,18 +232,28 @@ class Feature(Base):
217
232
  >>> feature.save()
218
233
  """
219
234
  data = self.data.copy()
235
+ srid = self.srid
236
+
220
237
  try:
221
238
  if self.id:
222
239
  if self.srid != self.BASE_SRID:
223
- self.data['geometry'] = self.original_geometry
240
+ self = self.transform(self.BASE_SRID)
241
+ self.data['geometry'] = self.original_geometry
224
242
  self.update(self.data)
225
243
  self.coordinates = data['geometry']['coordinates']
226
244
  except AttributeError:
245
+ if self.srid != self.BASE_SRID:
246
+ self = self.transform(self.BASE_SRID)
227
247
  endpoint = urljoin(self.layer.endpoint, 'features/')
228
- response = self.layer.api.post(endpoint, data)
248
+ request_data = self.data.copy()
249
+ response = self.layer.api.post(endpoint, request_data)
229
250
  self.endpoint = urljoin(self.layer.endpoint, f'features/{response["id"]}/')
230
251
  self.data.update(response)
231
252
 
253
+ self.data['geometry'] = data['geometry']
254
+ self._srid = srid
255
+
256
+
232
257
 
233
258
  def delete(self) -> None:
234
259
  """
@@ -274,13 +299,14 @@ class Feature(Base):
274
299
 
275
300
 
276
301
  @classmethod
277
- def create_feature(cls, layer: 'VectorLayer', geojson: Dict) -> 'Feature':
302
+ def create_feature(cls, layer: 'VectorLayer', geojson: Dict, srid: int = 3857) -> 'Feature':
278
303
  """
279
304
  Create a new feature in the vector layer.
280
305
 
281
306
  Args:
282
307
  layer (VectorLayer): The vector layer to create the feature in
283
308
  geojson (Dict): The GeoJSON data for the feature
309
+ srid (int, optional): the feature srid. default: 3857
284
310
 
285
311
  Returns:
286
312
  Feature: The created feature instance
@@ -297,8 +323,19 @@ class Feature(Base):
297
323
  ... }
298
324
  >>> feature = Feature.create_feature(layer, geojson)
299
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
+
300
333
  endpoint = urljoin(layer.endpoint, 'features/')
301
- 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
302
339
 
303
340
 
304
341
  @classmethod
@@ -359,10 +396,8 @@ class Feature(Base):
359
396
  "Install it with: pip install geobox[geometry]"
360
397
  )
361
398
 
362
- if not self.data:
363
- return None
364
399
 
365
- elif not self.data.get('geometry'):
400
+ if not self.data.get('geometry'):
366
401
  raise ValueError("Geometry is not present in the feature data")
367
402
 
368
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.0
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.0"
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