ee-client 3.1.2__tar.gz → 3.2.0__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 (45) hide show
  1. {ee_client-3.1.2 → ee_client-3.2.0}/PKG-INFO +1 -1
  2. {ee_client-3.1.2 → ee_client-3.2.0}/ee_client.egg-info/PKG-INFO +1 -1
  3. {ee_client-3.1.2 → ee_client-3.2.0}/ee_client.egg-info/SOURCES.txt +1 -0
  4. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/__init__.py +1 -1
  5. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/export/image.py +31 -1
  6. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/interfaces/export.py +9 -2
  7. {ee_client-3.1.2 → ee_client-3.2.0}/pyproject.toml +2 -2
  8. ee_client-3.2.0/tests/test_export_image_pyramiding.py +96 -0
  9. {ee_client-3.1.2 → ee_client-3.2.0}/LICENSE +0 -0
  10. {ee_client-3.1.2 → ee_client-3.2.0}/README.rst +0 -0
  11. {ee_client-3.1.2 → ee_client-3.2.0}/ee_client.egg-info/dependency_links.txt +0 -0
  12. {ee_client-3.1.2 → ee_client-3.2.0}/ee_client.egg-info/requires.txt +0 -0
  13. {ee_client-3.1.2 → ee_client-3.2.0}/ee_client.egg-info/top_level.txt +0 -0
  14. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/cache.py +0 -0
  15. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/client.py +0 -0
  16. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/credential_mixin.py +0 -0
  17. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/data.py +0 -0
  18. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/exceptions.py +0 -0
  19. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/export/__init__.py +0 -0
  20. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/export/table.py +0 -0
  21. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/helpers.py +0 -0
  22. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/interfaces/__init__.py +0 -0
  23. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/interfaces/operations.py +0 -0
  24. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/interfaces/tasks.py +0 -0
  25. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/loopstate.py +0 -0
  26. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/models.py +0 -0
  27. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/providers.py +0 -0
  28. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/sepal_credential_mixin.py +0 -0
  29. {ee_client-3.1.2 → ee_client-3.2.0}/eeclient/tasks.py +0 -0
  30. {ee_client-3.1.2 → ee_client-3.2.0}/setup.cfg +0 -0
  31. {ee_client-3.1.2 → ee_client-3.2.0}/tests/test_agnostic_auth_integration.py +0 -0
  32. {ee_client-3.1.2 → ee_client-3.2.0}/tests/test_cache.py +0 -0
  33. {ee_client-3.1.2 → ee_client-3.2.0}/tests/test_client.py +0 -0
  34. {ee_client-3.1.2 → ee_client-3.2.0}/tests/test_credential_mixin_close.py +0 -0
  35. {ee_client-3.1.2 → ee_client-3.2.0}/tests/test_data.py +0 -0
  36. {ee_client-3.1.2 → ee_client-3.2.0}/tests/test_export_image.py +0 -0
  37. {ee_client-3.1.2 → ee_client-3.2.0}/tests/test_export_table.py +0 -0
  38. {ee_client-3.1.2 → ee_client-3.2.0}/tests/test_factories.py +0 -0
  39. {ee_client-3.1.2 → ee_client-3.2.0}/tests/test_integration_get_assets.py +0 -0
  40. {ee_client-3.1.2 → ee_client-3.2.0}/tests/test_loop_scoping.py +0 -0
  41. {ee_client-3.1.2 → ee_client-3.2.0}/tests/test_models.py +0 -0
  42. {ee_client-3.1.2 → ee_client-3.2.0}/tests/test_providers.py +0 -0
  43. {ee_client-3.1.2 → ee_client-3.2.0}/tests/test_resolution.py +0 -0
  44. {ee_client-3.1.2 → ee_client-3.2.0}/tests/test_tasks.py +0 -0
  45. {ee_client-3.1.2 → ee_client-3.2.0}/tests/test_tls_verify.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ee-client
3
- Version: 3.1.2
3
+ Version: 3.2.0
4
4
  Summary: extends the capabilities of the earthengine-api by providing custom session management and client interactions
5
5
  Author-email: Daniel Guerrero <dfgm2006@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/dfguerrerom/ee-client
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ee-client
3
- Version: 3.1.2
3
+ Version: 3.2.0
4
4
  Summary: extends the capabilities of the earthengine-api by providing custom session management and client interactions
5
5
  Author-email: Daniel Guerrero <dfgm2006@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/dfguerrerom/ee-client
@@ -31,6 +31,7 @@ tests/test_client.py
31
31
  tests/test_credential_mixin_close.py
32
32
  tests/test_data.py
33
33
  tests/test_export_image.py
34
+ tests/test_export_image_pyramiding.py
34
35
  tests/test_export_table.py
35
36
  tests/test_factories.py
36
37
  tests/test_integration_get_assets.py
@@ -1,6 +1,6 @@
1
1
  __title__ = "eeclient"
2
2
  __summary__ = "A client for Google Earth Engine"
3
- __version__ = "3.1.2"
3
+ __version__ = "3.2.0"
4
4
 
5
5
  __author__ = "Daniel Guerrero"
6
6
  __email__ = "dfgm2006@gmail.com"
@@ -1,5 +1,5 @@
1
1
  from enum import Enum
2
- from typing import TYPE_CHECKING, Optional, Union
2
+ from typing import TYPE_CHECKING, Dict, Optional, Union
3
3
  from pydantic import BaseModel, ConfigDict, field_validator, model_validator
4
4
  from pydantic.alias_generators import to_camel
5
5
 
@@ -66,8 +66,34 @@ class DriveOptions(BaseExportModel):
66
66
  # See the api: https://developers.google.com/earth-engine/reference/rest/v1alpha/ImageFileExportOptions
67
67
 
68
68
 
69
+ class PyramidingPolicy(str, Enum):
70
+ """Reducer that builds an asset's overview levels.
71
+
72
+ The server default is ``MEAN``, which averages class codes across every
73
+ overview of a categorical image and renders it wrong below native zoom.
74
+ Such images want ``MODE`` or ``SAMPLE``.
75
+ """
76
+
77
+ MEAN = "MEAN"
78
+ SAMPLE = "SAMPLE"
79
+ MIN = "MIN"
80
+ MAX = "MAX"
81
+ MODE = "MODE"
82
+ MEDIAN = "MEDIAN"
83
+
84
+ @classmethod
85
+ def _missing_(cls, value):
86
+ # ``ee.batch`` upper-cases whatever the caller passes, so callers are
87
+ # used to writing "mode".
88
+ if isinstance(value, str):
89
+ return cls.__members__.get(value.upper())
90
+ return None
91
+
92
+
69
93
  class AssetOptions(BaseExportModel):
70
94
  earth_engine_destination: EarthEngineDestination
95
+ pyramiding_policy: Optional[PyramidingPolicy] = None
96
+ pyramiding_policy_overrides: Optional[Dict[str, PyramidingPolicy]] = None
71
97
 
72
98
 
73
99
  class GridDimensions(BaseModel):
@@ -267,6 +293,8 @@ async def image_to_asset_async(
267
293
  scale: Optional[float] = None,
268
294
  crs: Optional[str] = None,
269
295
  crs_transform: Optional[AffineTransform] = None,
296
+ pyramiding_policy: Optional[PyramidingPolicy] = None,
297
+ pyramiding_policy_overrides: Optional[Dict[str, PyramidingPolicy]] = None,
270
298
  ) -> Task:
271
299
  """Abstracts the export of an image to Earth Engine Asset.
272
300
 
@@ -275,6 +303,8 @@ async def image_to_asset_async(
275
303
  """
276
304
  asset_options = AssetOptions(
277
305
  earth_engine_destination=EarthEngineDestination(name=asset_id),
306
+ pyramiding_policy=pyramiding_policy,
307
+ pyramiding_policy_overrides=pyramiding_policy_overrides,
278
308
  )
279
309
 
280
310
  return await _export_image(
@@ -1,5 +1,10 @@
1
- from typing import Optional, Protocol, Union, Any
2
- from eeclient.export.image import ImageFileFormat, PixelGrid, AffineTransform
1
+ from typing import Any, Dict, Optional, Protocol, Union
2
+ from eeclient.export.image import (
3
+ AffineTransform,
4
+ ImageFileFormat,
5
+ PixelGrid,
6
+ PyramidingPolicy,
7
+ )
3
8
  from eeclient.export.table import TableFileFormat
4
9
 
5
10
 
@@ -18,6 +23,8 @@ class ExportProtocol(Protocol):
18
23
  scale: Optional[float] = ...,
19
24
  crs: Optional[str] = ...,
20
25
  crs_transform: Optional[AffineTransform] = ...,
26
+ pyramiding_policy: Optional[PyramidingPolicy] = ...,
27
+ pyramiding_policy_overrides: Optional[Dict[str, PyramidingPolicy]] = ...,
21
28
  ) -> dict: ...
22
29
 
23
30
  async def image_to_drive_async(
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ee-client"
7
- version = "3.1.2"
7
+ version = "3.2.0"
8
8
  description = "extends the capabilities of the earthengine-api by providing custom session management and client interactions"
9
9
  readme = { file = "README.rst", content-type = "text/x-rst" }
10
10
  authors = [
@@ -66,7 +66,7 @@ branch = true
66
66
  [tool.commitizen]
67
67
  tag_format = "v$major.$minor.$patch$prerelease"
68
68
  update_changelog_on_bump = false
69
- version = "3.1.2"
69
+ version = "3.2.0"
70
70
  version_files = [
71
71
  "pyproject.toml:version",
72
72
  "eeclient/__init__.py:__version__",
@@ -0,0 +1,96 @@
1
+ import json
2
+ from unittest.mock import AsyncMock, MagicMock, patch
3
+
4
+ import pytest
5
+
6
+ from eeclient.export.image import (
7
+ AssetOptions,
8
+ EarthEngineDestination,
9
+ PyramidingPolicy,
10
+ image_to_asset_async,
11
+ )
12
+ from eeclient.interfaces.export import ExportProtocol
13
+
14
+
15
+ def _asset_options(**kwargs):
16
+ return AssetOptions(
17
+ earth_engine_destination=EarthEngineDestination(name="projects/p/assets/x"),
18
+ **kwargs,
19
+ )
20
+
21
+
22
+ def test_asset_options_omit_pyramiding_by_default():
23
+ dumped = _asset_options().model_dump(by_alias=True, exclude_none=True)
24
+ assert "pyramidingPolicy" not in dumped
25
+ assert "pyramidingPolicyOverrides" not in dumped
26
+
27
+
28
+ def test_asset_options_serialize_default_policy_as_camel_case():
29
+ dumped = _asset_options(pyramiding_policy="MODE").model_dump(
30
+ by_alias=True, exclude_none=True
31
+ )
32
+ assert dumped["pyramidingPolicy"] == "MODE"
33
+
34
+
35
+ def test_asset_options_serialize_per_band_overrides():
36
+ dumped = _asset_options(pyramiding_policy_overrides={"B1": "MIN"}).model_dump(
37
+ by_alias=True, exclude_none=True
38
+ )
39
+ assert dumped["pyramidingPolicyOverrides"] == {"B1": "MIN"}
40
+
41
+
42
+ def test_asset_options_accept_lowercase_policy():
43
+ """`ee.batch` upper-cases whatever the caller passes, so callers write "mode"."""
44
+ opts = _asset_options(
45
+ pyramiding_policy="mode", pyramiding_policy_overrides={"B1": "min"}
46
+ )
47
+ assert opts.pyramiding_policy is PyramidingPolicy.MODE
48
+ assert opts.pyramiding_policy_overrides == {"B1": PyramidingPolicy.MIN}
49
+
50
+
51
+ def test_asset_options_reject_unknown_policy():
52
+ with pytest.raises(ValueError):
53
+ _asset_options(pyramiding_policy="average")
54
+
55
+
56
+ def test_asset_options_reject_unknown_override_policy():
57
+ with pytest.raises(ValueError):
58
+ _asset_options(pyramiding_policy_overrides={"B1": "average"})
59
+
60
+
61
+ @pytest.mark.asyncio
62
+ async def test_image_to_asset_async_forwards_pyramiding_to_the_request():
63
+ image = MagicMock(name="image")
64
+ image._apply_crs_and_affine.return_value = (image, {}, False)
65
+ image._apply_selection_and_scale.return_value = (image, {})
66
+
67
+ client = MagicMock()
68
+ client.rest_call = AsyncMock(return_value={})
69
+
70
+ with (
71
+ patch("eeclient.export.image.serializer.encode", return_value={}),
72
+ patch("eeclient.export.image.Task.model_validate", return_value=MagicMock()),
73
+ ):
74
+ await image_to_asset_async(
75
+ client=client,
76
+ image=image,
77
+ asset_id="projects/p/assets/x",
78
+ pyramiding_policy="mode",
79
+ pyramiding_policy_overrides={"B1": "min"},
80
+ )
81
+
82
+ # Asserted through json, because `rest_call` hands `data` to httpx as
83
+ # `json=` -- a policy that stringified as "PyramidingPolicy.MODE" would
84
+ # still compare equal to "MODE" here as a str enum.
85
+ body = json.loads(json.dumps(client.rest_call.await_args.kwargs["data"]))
86
+ asset_options = body["assetExportOptions"]
87
+ assert asset_options["pyramidingPolicy"] == "MODE"
88
+ assert asset_options["pyramidingPolicyOverrides"] == {"B1": "MIN"}
89
+
90
+
91
+ @pytest.mark.parametrize("param", ["pyramiding_policy", "pyramiding_policy_overrides"])
92
+ def test_export_protocol_matches_the_implementation(param):
93
+ """`session.export.*` dispatches through the Protocol, so it must agree."""
94
+ import inspect
95
+
96
+ assert param in inspect.signature(ExportProtocol.image_to_asset_async).parameters
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