earthengine-api 1.5.13rc0__py3-none-any.whl → 1.7.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of earthengine-api might be problematic. Click here for more details.
- {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/METADATA +3 -3
- earthengine_api-1.7.4.dist-info/RECORD +109 -0
- {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/WHEEL +1 -1
- ee/__init__.py +29 -28
- ee/_arg_types.py +7 -6
- ee/_cloud_api_utils.py +95 -78
- ee/_helpers.py +17 -13
- ee/_state.py +105 -0
- ee/_utils.py +2 -1
- ee/apifunction.py +21 -19
- ee/apitestcase.py +33 -38
- ee/batch.py +87 -77
- ee/blob.py +10 -12
- ee/classifier.py +57 -59
- ee/cli/commands.py +178 -114
- ee/cli/eecli.py +1 -1
- ee/cli/utils.py +61 -42
- ee/clusterer.py +39 -41
- ee/collection.py +64 -54
- ee/computedobject.py +19 -16
- ee/confusionmatrix.py +9 -9
- ee/customfunction.py +13 -12
- ee/data.py +220 -322
- ee/daterange.py +10 -10
- ee/deprecation.py +21 -13
- ee/deserializer.py +25 -20
- ee/dictionary.py +11 -11
- ee/ee_array.py +22 -20
- ee/ee_date.py +23 -23
- ee/ee_list.py +15 -16
- ee/ee_number.py +11 -21
- ee/ee_string.py +24 -32
- ee/ee_types.py +4 -4
- ee/element.py +15 -15
- ee/encodable.py +7 -4
- ee/errormargin.py +4 -4
- ee/feature.py +68 -71
- ee/featurecollection.py +41 -40
- ee/filter.py +90 -92
- ee/function.py +8 -8
- ee/geometry.py +95 -93
- ee/image.py +238 -236
- ee/image_converter.py +4 -4
- ee/imagecollection.py +30 -27
- ee/join.py +13 -15
- ee/kernel.py +55 -57
- ee/mapclient.py +9 -9
- ee/model.py +29 -31
- ee/oauth.py +76 -63
- ee/pixeltype.py +6 -6
- ee/projection.py +5 -4
- ee/reducer.py +41 -41
- ee/serializer.py +14 -14
- ee/table_converter.py +7 -6
- ee/terrain.py +7 -9
- ee/tests/_cloud_api_utils_test.py +21 -6
- ee/tests/_helpers_test.py +57 -4
- ee/tests/_state_test.py +49 -0
- ee/tests/algorithms.json +85 -2
- ee/tests/apifunction_test.py +5 -5
- ee/tests/batch_test.py +135 -57
- ee/tests/blob_test.py +5 -5
- ee/tests/classifier_test.py +3 -3
- ee/tests/clusterer_test.py +3 -3
- ee/tests/collection_test.py +48 -13
- ee/tests/confusionmatrix_test.py +3 -3
- ee/tests/data_test.py +484 -55
- ee/tests/daterange_test.py +4 -4
- ee/tests/deprecation_test.py +6 -4
- ee/tests/deserializer_test.py +64 -5
- ee/tests/dictionary_test.py +12 -12
- ee/tests/ee_array_test.py +3 -3
- ee/tests/ee_date_test.py +4 -4
- ee/tests/ee_list_test.py +3 -3
- ee/tests/ee_number_test.py +75 -30
- ee/tests/ee_string_test.py +11 -3
- ee/tests/ee_test.py +40 -22
- ee/tests/element_test.py +2 -2
- ee/tests/errormargin_test.py +1 -1
- ee/tests/feature_test.py +10 -10
- ee/tests/featurecollection_test.py +3 -3
- ee/tests/filter_test.py +4 -4
- ee/tests/function_test.py +5 -5
- ee/tests/geometry_point_test.py +3 -3
- ee/tests/geometry_test.py +93 -52
- ee/tests/image_converter_test.py +1 -3
- ee/tests/image_test.py +3 -3
- ee/tests/imagecollection_test.py +3 -3
- ee/tests/join_test.py +3 -3
- ee/tests/kernel_test.py +7 -3
- ee/tests/model_test.py +17 -5
- ee/tests/oauth_test.py +189 -7
- ee/tests/pixeltype_test.py +6 -7
- ee/tests/projection_test.py +5 -6
- ee/tests/reducer_test.py +16 -3
- ee/tests/serializer_test.py +39 -12
- ee/tests/table_converter_test.py +51 -7
- ee/tests/terrain_test.py +11 -3
- earthengine_api-1.5.13rc0.dist-info/RECORD +0 -107
- {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/entry_points.txt +0 -0
- {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/licenses/LICENSE +0 -0
- {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/top_level.txt +0 -0
ee/reducer.py
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
"""A wrapper for Reducers."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
from typing import Optional
|
|
5
|
-
|
|
6
4
|
from ee import _arg_types
|
|
7
5
|
from ee import apifunction
|
|
8
6
|
from ee import computedobject
|
|
@@ -100,11 +98,11 @@ class Reducer(computedobject.ComputedObject):
|
|
|
100
98
|
@staticmethod
|
|
101
99
|
def autoHistogram(
|
|
102
100
|
# pylint: disable=invalid-name
|
|
103
|
-
maxBuckets:
|
|
104
|
-
minBucketWidth:
|
|
105
|
-
maxRaw:
|
|
101
|
+
maxBuckets: _arg_types.Integer | None = None,
|
|
102
|
+
minBucketWidth: _arg_types.Number | None = None,
|
|
103
|
+
maxRaw: _arg_types.Integer | None = None,
|
|
106
104
|
# pylint: enable=invalid-name
|
|
107
|
-
cumulative:
|
|
105
|
+
cumulative: _arg_types.Bool | None = None,
|
|
108
106
|
) -> Reducer:
|
|
109
107
|
"""Returns an ee.Reducer that computes a histogram of the inputs.
|
|
110
108
|
|
|
@@ -201,8 +199,8 @@ class Reducer(computedobject.ComputedObject):
|
|
|
201
199
|
self,
|
|
202
200
|
reducer2: _arg_types.Reducer,
|
|
203
201
|
# pylint: disable=invalid-name
|
|
204
|
-
outputPrefix:
|
|
205
|
-
sharedInputs:
|
|
202
|
+
outputPrefix: _arg_types.String | None = None,
|
|
203
|
+
sharedInputs: _arg_types.Bool | None = None,
|
|
206
204
|
# pylint: enable=invalid-name
|
|
207
205
|
) -> Reducer:
|
|
208
206
|
"""Returns a Reducer that runs two reducers in parallel.
|
|
@@ -289,7 +287,7 @@ class Reducer(computedobject.ComputedObject):
|
|
|
289
287
|
|
|
290
288
|
return apifunction.ApiFunction.call_('Reducer.covariance')
|
|
291
289
|
|
|
292
|
-
def disaggregate(self, axis:
|
|
290
|
+
def disaggregate(self, axis: _arg_types.Integer | None = None) -> Reducer:
|
|
293
291
|
"""Returns a Reducer that separates aggregate inputs.
|
|
294
292
|
|
|
295
293
|
Separates aggregate inputs (Arrays, Lists, or Dictionaries) into individual
|
|
@@ -363,7 +361,7 @@ class Reducer(computedobject.ComputedObject):
|
|
|
363
361
|
min: _arg_types.Number, # pylint: disable=redefined-builtin
|
|
364
362
|
max: _arg_types.Number, # pylint: disable=redefined-builtin
|
|
365
363
|
steps: _arg_types.Integer,
|
|
366
|
-
cumulative:
|
|
364
|
+
cumulative: _arg_types.Bool | None = None,
|
|
367
365
|
) -> Reducer:
|
|
368
366
|
"""Returns a fixed histogram reducer.
|
|
369
367
|
|
|
@@ -443,9 +441,9 @@ class Reducer(computedobject.ComputedObject):
|
|
|
443
441
|
@staticmethod
|
|
444
442
|
def geometricMedian(
|
|
445
443
|
numX: _arg_types.Integer, # pylint: disable=invalid-name
|
|
446
|
-
eta:
|
|
444
|
+
eta: _arg_types.Number | None = None,
|
|
447
445
|
# pylint: disable-next=invalid-name
|
|
448
|
-
initialStepSize:
|
|
446
|
+
initialStepSize: _arg_types.Number | None = None,
|
|
449
447
|
) -> Reducer:
|
|
450
448
|
"""Returns a reducer that computes the geometric median across the inputs.
|
|
451
449
|
|
|
@@ -467,8 +465,10 @@ class Reducer(computedobject.ComputedObject):
|
|
|
467
465
|
|
|
468
466
|
def group(
|
|
469
467
|
self,
|
|
470
|
-
|
|
471
|
-
|
|
468
|
+
# pylint: disable=invalid-name
|
|
469
|
+
groupField: _arg_types.Integer | None = None,
|
|
470
|
+
groupName: _arg_types.String | None = None,
|
|
471
|
+
# pylint: enable=invalid-name
|
|
472
472
|
) -> Reducer:
|
|
473
473
|
"""Returns a reducer groups reducer records by the value of a given input.
|
|
474
474
|
|
|
@@ -487,9 +487,9 @@ class Reducer(computedobject.ComputedObject):
|
|
|
487
487
|
@staticmethod
|
|
488
488
|
def histogram(
|
|
489
489
|
# pylint: disable=invalid-name
|
|
490
|
-
maxBuckets:
|
|
491
|
-
minBucketWidth:
|
|
492
|
-
maxRaw:
|
|
490
|
+
maxBuckets: _arg_types.Integer | None = None,
|
|
491
|
+
minBucketWidth: _arg_types.Number | None = None,
|
|
492
|
+
maxRaw: _arg_types.Integer | None = None,
|
|
493
493
|
# pylint: enable=invalid-name
|
|
494
494
|
) -> Reducer:
|
|
495
495
|
"""Returns a reducer that will compute a histogram of the inputs.
|
|
@@ -512,9 +512,9 @@ class Reducer(computedobject.ComputedObject):
|
|
|
512
512
|
# pylint: disable=invalid-name
|
|
513
513
|
minPercentile: _arg_types.Number,
|
|
514
514
|
maxPercentile: _arg_types.Number,
|
|
515
|
-
maxBuckets:
|
|
516
|
-
minBucketWidth:
|
|
517
|
-
maxRaw:
|
|
515
|
+
maxBuckets: _arg_types.Integer | None = None,
|
|
516
|
+
minBucketWidth: _arg_types.Number | None = None,
|
|
517
|
+
maxRaw: _arg_types.Integer | None = None,
|
|
518
518
|
# pylint: enable=invalid-name
|
|
519
519
|
) -> Reducer:
|
|
520
520
|
"""Returns an interval mean reducer.
|
|
@@ -547,7 +547,7 @@ class Reducer(computedobject.ComputedObject):
|
|
|
547
547
|
@staticmethod
|
|
548
548
|
def kendallsCorrelation(
|
|
549
549
|
# pylint: disable-next=invalid-name
|
|
550
|
-
numInputs:
|
|
550
|
+
numInputs: _arg_types.Integer | None = None,
|
|
551
551
|
) -> Reducer:
|
|
552
552
|
"""Returns a reducer that computes the Kendall's Tau-b rank correlation.
|
|
553
553
|
|
|
@@ -609,7 +609,7 @@ class Reducer(computedobject.ComputedObject):
|
|
|
609
609
|
@staticmethod
|
|
610
610
|
def linearRegression(
|
|
611
611
|
numX: _arg_types.Integer, # pylint: disable=invalid-name
|
|
612
|
-
numY:
|
|
612
|
+
numY: _arg_types.Integer | None = None, # pylint: disable=invalid-name
|
|
613
613
|
) -> Reducer:
|
|
614
614
|
"""Returns a linear regression reducer.
|
|
615
615
|
|
|
@@ -635,7 +635,7 @@ class Reducer(computedobject.ComputedObject):
|
|
|
635
635
|
@staticmethod
|
|
636
636
|
def max(
|
|
637
637
|
# pylint: disable-next=invalid-name
|
|
638
|
-
numInputs:
|
|
638
|
+
numInputs: _arg_types.Integer | None = None,
|
|
639
639
|
) -> Reducer:
|
|
640
640
|
"""Returns a reducer that outputs the maximum value of its (first) input.
|
|
641
641
|
|
|
@@ -660,9 +660,9 @@ class Reducer(computedobject.ComputedObject):
|
|
|
660
660
|
@staticmethod
|
|
661
661
|
def median(
|
|
662
662
|
# pylint: disable=invalid-name
|
|
663
|
-
maxBuckets:
|
|
664
|
-
minBucketWidth:
|
|
665
|
-
maxRaw:
|
|
663
|
+
maxBuckets: _arg_types.Integer | None = None,
|
|
664
|
+
minBucketWidth: _arg_types.Number | None = None,
|
|
665
|
+
maxRaw: _arg_types.Integer | None = None,
|
|
666
666
|
# pylint: enable=invalid-name
|
|
667
667
|
) -> Reducer:
|
|
668
668
|
"""Returns a reducer that will compute the median of the inputs.
|
|
@@ -687,7 +687,7 @@ class Reducer(computedobject.ComputedObject):
|
|
|
687
687
|
@staticmethod
|
|
688
688
|
def min(
|
|
689
689
|
# pylint: disable-next=invalid-name
|
|
690
|
-
numInputs:
|
|
690
|
+
numInputs: _arg_types.Integer | None = None,
|
|
691
691
|
) -> Reducer:
|
|
692
692
|
"""Returns a reducer that outputs the minimum value of its first input.
|
|
693
693
|
|
|
@@ -712,9 +712,9 @@ class Reducer(computedobject.ComputedObject):
|
|
|
712
712
|
@staticmethod
|
|
713
713
|
def mode(
|
|
714
714
|
# pylint: disable=invalid-name
|
|
715
|
-
maxBuckets:
|
|
716
|
-
minBucketWidth:
|
|
717
|
-
maxRaw:
|
|
715
|
+
maxBuckets: _arg_types.Integer | None = None,
|
|
716
|
+
minBucketWidth: _arg_types.Number | None = None,
|
|
717
|
+
maxRaw: _arg_types.Integer | None = None,
|
|
718
718
|
# pylint: enable=invalid-name
|
|
719
719
|
) -> Reducer:
|
|
720
720
|
"""Returns a reducer that will compute the mode of the inputs.
|
|
@@ -760,10 +760,10 @@ class Reducer(computedobject.ComputedObject):
|
|
|
760
760
|
def percentile(
|
|
761
761
|
percentiles: _arg_types.List,
|
|
762
762
|
# pylint: disable=invalid-name
|
|
763
|
-
outputNames:
|
|
764
|
-
maxBuckets:
|
|
765
|
-
minBucketWidth:
|
|
766
|
-
maxRaw:
|
|
763
|
+
outputNames: _arg_types.List | None = None,
|
|
764
|
+
maxBuckets: _arg_types.Integer | None = None,
|
|
765
|
+
minBucketWidth: _arg_types.Number | None = None,
|
|
766
|
+
maxRaw: _arg_types.Integer | None = None,
|
|
767
767
|
# pylint: enable=invalid-name
|
|
768
768
|
) -> Reducer:
|
|
769
769
|
"""Returns a reducer that will compute the specified percentiles.
|
|
@@ -818,8 +818,8 @@ class Reducer(computedobject.ComputedObject):
|
|
|
818
818
|
@staticmethod
|
|
819
819
|
def ridgeRegression(
|
|
820
820
|
numX: _arg_types.Integer, # pylint: disable=invalid-name
|
|
821
|
-
numY:
|
|
822
|
-
lambda_:
|
|
821
|
+
numY: _arg_types.Integer | None = None, # pylint: disable=invalid-name
|
|
822
|
+
lambda_: _arg_types.Number | None = None,
|
|
823
823
|
**kwargs,
|
|
824
824
|
) -> Reducer:
|
|
825
825
|
# pylint: disable=g-doc-args
|
|
@@ -862,8 +862,8 @@ class Reducer(computedobject.ComputedObject):
|
|
|
862
862
|
@staticmethod
|
|
863
863
|
def robustLinearRegression(
|
|
864
864
|
numX: _arg_types.Integer, # pylint: disable=invalid-name
|
|
865
|
-
numY:
|
|
866
|
-
beta:
|
|
865
|
+
numY: _arg_types.Integer | None = None, # pylint: disable=invalid-name
|
|
866
|
+
beta: _arg_types.Number | None = None,
|
|
867
867
|
) -> Reducer:
|
|
868
868
|
"""Returns a robust linear regression reducer.
|
|
869
869
|
|
|
@@ -979,7 +979,7 @@ class Reducer(computedobject.ComputedObject):
|
|
|
979
979
|
def toCollection(
|
|
980
980
|
# pylint: disable=invalid-name
|
|
981
981
|
propertyNames: _arg_types.List,
|
|
982
|
-
numOptional:
|
|
982
|
+
numOptional: _arg_types.Integer | None = None,
|
|
983
983
|
# pylint: enable=invalid-name
|
|
984
984
|
) -> Reducer:
|
|
985
985
|
"""Returns a reducer that collects its inputs into a FeatureCollection.
|
|
@@ -998,8 +998,8 @@ class Reducer(computedobject.ComputedObject):
|
|
|
998
998
|
@staticmethod
|
|
999
999
|
def toList(
|
|
1000
1000
|
# pylint: disable=invalid-name
|
|
1001
|
-
tupleSize:
|
|
1002
|
-
numOptional:
|
|
1001
|
+
tupleSize: _arg_types.Integer | None = None,
|
|
1002
|
+
numOptional: _arg_types.Integer | None = None,
|
|
1003
1003
|
# pylint: enable=invalid-name
|
|
1004
1004
|
) -> Reducer:
|
|
1005
1005
|
"""Returns a reducer that collects its inputs into a list.
|
ee/serializer.py
CHANGED
|
@@ -4,7 +4,7 @@ import collections
|
|
|
4
4
|
import datetime
|
|
5
5
|
import hashlib
|
|
6
6
|
import json
|
|
7
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
8
8
|
|
|
9
9
|
from ee import _cloud_api_utils
|
|
10
10
|
from ee import _utils
|
|
@@ -32,23 +32,23 @@ def DatetimeToMicroseconds(date: datetime.datetime) -> int:
|
|
|
32
32
|
|
|
33
33
|
class Serializer:
|
|
34
34
|
"""A serializer for EE object trees."""
|
|
35
|
-
unbound_name:
|
|
35
|
+
unbound_name: str | None
|
|
36
36
|
|
|
37
37
|
# Whether the encoding should factor out shared subtrees.
|
|
38
38
|
_is_compound: bool
|
|
39
39
|
_for_cloud_api: bool
|
|
40
40
|
# A list of shared subtrees as [name, value] pairs.
|
|
41
|
-
_scope:
|
|
41
|
+
_scope: list[str]
|
|
42
42
|
# A lookup table from object hash to subtree names as stored in self._scope
|
|
43
|
-
_encoded:
|
|
43
|
+
_encoded: dict[Any, Any]
|
|
44
44
|
# A lookup table from object ID as retrieved by id() to md5 hash values.
|
|
45
|
-
_hashcache:
|
|
45
|
+
_hashcache: dict[Any, Any]
|
|
46
46
|
|
|
47
47
|
def __init__(
|
|
48
48
|
self,
|
|
49
49
|
is_compound: bool = True,
|
|
50
50
|
for_cloud_api: bool = False,
|
|
51
|
-
unbound_name:
|
|
51
|
+
unbound_name: str | None = None,
|
|
52
52
|
):
|
|
53
53
|
"""Constructs a serializer.
|
|
54
54
|
|
|
@@ -160,8 +160,8 @@ class Serializer:
|
|
|
160
160
|
'type':
|
|
161
161
|
'Dictionary',
|
|
162
162
|
'value':
|
|
163
|
-
|
|
164
|
-
for key, value in obj.items()
|
|
163
|
+
{key: self._encode_value(value)
|
|
164
|
+
for key, value in obj.items()}
|
|
165
165
|
}
|
|
166
166
|
else:
|
|
167
167
|
raise ee_exception.EEException('Cannot encode object: %s' % obj)
|
|
@@ -280,7 +280,7 @@ def encode(
|
|
|
280
280
|
obj: Any,
|
|
281
281
|
is_compound: bool = True,
|
|
282
282
|
for_cloud_api: bool = True,
|
|
283
|
-
unbound_name:
|
|
283
|
+
unbound_name: str | None = None,
|
|
284
284
|
) -> Any:
|
|
285
285
|
"""Serialize an object to a JSON-compatible structure for API calls.
|
|
286
286
|
|
|
@@ -358,7 +358,7 @@ class _ExpressionOptimizer:
|
|
|
358
358
|
- Collapse dicts and arrays of constants to constant dicts/arrays.
|
|
359
359
|
"""
|
|
360
360
|
|
|
361
|
-
def __init__(self, result: Any, values:
|
|
361
|
+
def __init__(self, result: Any, values: Any | None = None):
|
|
362
362
|
"""Builds an ExpressionOptimizer.
|
|
363
363
|
|
|
364
364
|
Args:
|
|
@@ -380,12 +380,12 @@ class _ExpressionOptimizer:
|
|
|
380
380
|
def _is_compound(self) -> bool:
|
|
381
381
|
return self._values is not None
|
|
382
382
|
|
|
383
|
-
def _find_single_uses(self) ->
|
|
383
|
+
def _find_single_uses(self) -> set[Any]:
|
|
384
384
|
"""Finds the names of all named values that are referred to only once."""
|
|
385
385
|
reference_counts = collections.defaultdict(int)
|
|
386
386
|
reference_counts[self._result] += 1
|
|
387
387
|
|
|
388
|
-
def _contained_reference(value: Any) ->
|
|
388
|
+
def _contained_reference(value: Any) -> Any | None:
|
|
389
389
|
"""Gets a contained reference from a ValueNode, if there is one."""
|
|
390
390
|
if 'functionDefinitionValue' in value:
|
|
391
391
|
return value['functionDefinitionValue']['body']
|
|
@@ -403,8 +403,8 @@ class _ExpressionOptimizer:
|
|
|
403
403
|
reference_counts[reference] += 1
|
|
404
404
|
|
|
405
405
|
self._visit_all_values_in_expression(increment_reference_count)
|
|
406
|
-
return
|
|
407
|
-
if count == 1
|
|
406
|
+
return {reference for reference, count in reference_counts.items()
|
|
407
|
+
if count == 1}
|
|
408
408
|
|
|
409
409
|
def optimize(self) -> Any:
|
|
410
410
|
"""Optimises the expression, returning the optimised form."""
|
ee/table_converter.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Converters used in the table data fetching methods."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from collections.abc import Iterator
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class TableConverter:
|
|
@@ -24,7 +25,7 @@ class PandasConverter(TableConverter):
|
|
|
24
25
|
|
|
25
26
|
def _convert_to_records(
|
|
26
27
|
self, features: Iterator[Any]
|
|
27
|
-
) -> Iterator[
|
|
28
|
+
) -> Iterator[dict[str, Any]]:
|
|
28
29
|
for feature in features:
|
|
29
30
|
yield {
|
|
30
31
|
'geo': feature.get('geometry'),
|
|
@@ -46,20 +47,20 @@ class GeoPandasConverter(TableConverter):
|
|
|
46
47
|
self._materialize_features(features)
|
|
47
48
|
)
|
|
48
49
|
|
|
49
|
-
def _materialize_features(self, features: Iterator[Any]) ->
|
|
50
|
+
def _materialize_features(self, features: Iterator[Any]) -> list[Any]:
|
|
50
51
|
"""Materializes the features, making several requests if necessary."""
|
|
51
52
|
return list(features)
|
|
52
53
|
|
|
53
54
|
|
|
54
|
-
_TABLE_DATA_CONVERTERS:
|
|
55
|
+
_TABLE_DATA_CONVERTERS: dict[str, type[TableConverter]] = {
|
|
55
56
|
'PANDAS_DATAFRAME': PandasConverter,
|
|
56
57
|
'GEOPANDAS_GEODATAFRAME': GeoPandasConverter,
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
|
|
60
61
|
def from_file_format(
|
|
61
|
-
file_format:
|
|
62
|
-
) ->
|
|
62
|
+
file_format: str | TableConverter
|
|
63
|
+
) -> TableConverter | None:
|
|
63
64
|
if isinstance(file_format, TableConverter):
|
|
64
65
|
return file_format
|
|
65
66
|
if file_format in _TABLE_DATA_CONVERTERS:
|
ee/terrain.py
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
"""A namespace for Terrain."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
from typing import Optional
|
|
5
|
-
|
|
6
4
|
from ee import _arg_types
|
|
7
5
|
from ee import apifunction
|
|
8
6
|
from ee import image as ee_image
|
|
@@ -15,7 +13,7 @@ class Terrain:
|
|
|
15
13
|
|
|
16
14
|
def __init__(self):
|
|
17
15
|
raise RuntimeError(
|
|
18
|
-
self.__name__
|
|
16
|
+
self.__class__.__name__
|
|
19
17
|
+ ' should not be used as an object. Only direct usage of Terrain'
|
|
20
18
|
' static methods is allowed. For example, use this: '
|
|
21
19
|
' `ee.Terrain.aspect(...)`'
|
|
@@ -54,8 +52,8 @@ class Terrain:
|
|
|
54
52
|
@staticmethod
|
|
55
53
|
def fillMinima(
|
|
56
54
|
image: _arg_types.Image,
|
|
57
|
-
borderValue:
|
|
58
|
-
neighborhood:
|
|
55
|
+
borderValue: _arg_types.Integer | None = None,
|
|
56
|
+
neighborhood: _arg_types.Integer | None = None,
|
|
59
57
|
) -> ee_image.Image:
|
|
60
58
|
"""Returns an ee.Image with local minima filled.
|
|
61
59
|
|
|
@@ -76,8 +74,8 @@ class Terrain:
|
|
|
76
74
|
image: _arg_types.Image,
|
|
77
75
|
azimuth: _arg_types.Number,
|
|
78
76
|
zenith: _arg_types.Number,
|
|
79
|
-
neighborhoodSize:
|
|
80
|
-
hysteresis:
|
|
77
|
+
neighborhoodSize: _arg_types.Integer | None = None,
|
|
78
|
+
hysteresis: _arg_types.Bool | None = None,
|
|
81
79
|
) -> ee_image.Image:
|
|
82
80
|
"""Returns an ee.Image with the hill shadow.
|
|
83
81
|
|
|
@@ -109,8 +107,8 @@ class Terrain:
|
|
|
109
107
|
@staticmethod
|
|
110
108
|
def hillshade(
|
|
111
109
|
input: _arg_types.Image,
|
|
112
|
-
azimuth:
|
|
113
|
-
elevation:
|
|
110
|
+
azimuth: _arg_types.Number | None = None,
|
|
111
|
+
elevation: _arg_types.Number | None = None,
|
|
114
112
|
) -> ee_image.Image:
|
|
115
113
|
"""Returns an ee.Image with a simple hillshade from a DEM.
|
|
116
114
|
|
|
@@ -16,10 +16,6 @@ from ee import ee_exception
|
|
|
16
16
|
|
|
17
17
|
class CloudApiUtilsTest(unittest.TestCase):
|
|
18
18
|
|
|
19
|
-
def setUp(self):
|
|
20
|
-
super().setUp()
|
|
21
|
-
_cloud_api_utils.set_cloud_api_user_project('earthengine-legacy')
|
|
22
|
-
|
|
23
19
|
def test_build_cloud_resource(self):
|
|
24
20
|
base = 'https://earthengine.basetest'
|
|
25
21
|
path = '$discovery/rest?version=v1&prettyPrint=false'
|
|
@@ -171,7 +167,10 @@ class CloudApiUtilsTest(unittest.TestCase):
|
|
|
171
167
|
def test_convert_task_id_to_operation_name(self):
|
|
172
168
|
self.assertEqual(
|
|
173
169
|
'projects/earthengine-legacy/operations/taskId',
|
|
174
|
-
_cloud_api_utils.convert_task_id_to_operation_name(
|
|
170
|
+
_cloud_api_utils.convert_task_id_to_operation_name(
|
|
171
|
+
'earthengine-legacy', 'taskId'
|
|
172
|
+
),
|
|
173
|
+
)
|
|
175
174
|
|
|
176
175
|
def test_encode_number_as_cloud_value(self):
|
|
177
176
|
self.assertEqual({
|
|
@@ -414,7 +413,7 @@ class CloudApiUtilsTest(unittest.TestCase):
|
|
|
414
413
|
self.assertEqual({'width': 123, 'height': 123},
|
|
415
414
|
_cloud_api_utils.convert_to_grid_dimensions(123))
|
|
416
415
|
self.assertEqual({'width': 123, 'height': 123},
|
|
417
|
-
_cloud_api_utils.convert_to_grid_dimensions(
|
|
416
|
+
_cloud_api_utils.convert_to_grid_dimensions(123))
|
|
418
417
|
self.assertEqual({'width': 123, 'height': 234},
|
|
419
418
|
_cloud_api_utils.convert_to_grid_dimensions((123, 234)))
|
|
420
419
|
|
|
@@ -551,6 +550,22 @@ class CloudApiUtilsTest(unittest.TestCase):
|
|
|
551
550
|
expected,
|
|
552
551
|
_cloud_api_utils.convert_sources_to_one_platform_sources(old_sources))
|
|
553
552
|
|
|
553
|
+
def test_convert_to_operation_state(self):
|
|
554
|
+
def convert(state):
|
|
555
|
+
return _cloud_api_utils.convert_to_operation_state(state)
|
|
556
|
+
|
|
557
|
+
self.assertEqual('CANCELLED', convert('CANCELLED'))
|
|
558
|
+
self.assertEqual('CANCELLING', convert('CANCEL_REQUESTED'))
|
|
559
|
+
self.assertEqual('CANCELLING', convert('CANCELLING'))
|
|
560
|
+
self.assertEqual('FAILED', convert('FAILED'))
|
|
561
|
+
self.assertEqual('PENDING', convert('PENDING'))
|
|
562
|
+
self.assertEqual('PENDING', convert('READY'))
|
|
563
|
+
self.assertEqual('RUNNING', convert('RUNNING'))
|
|
564
|
+
self.assertEqual('SUCCEEDED', convert('COMPLETED'))
|
|
565
|
+
self.assertEqual('SUCCEEDED', convert('SUCCEEDED'))
|
|
566
|
+
self.assertEqual('UNKNOWN', convert('random_string'))
|
|
567
|
+
self.assertEqual('UNKNOWN', convert('UNKNOWN'))
|
|
568
|
+
|
|
554
569
|
|
|
555
570
|
if __name__ == '__main__':
|
|
556
571
|
unittest.main()
|
ee/tests/_helpers_test.py
CHANGED
|
@@ -7,14 +7,67 @@ name since that is the name we want to ensure works.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import io
|
|
10
|
+
import json
|
|
10
11
|
import unittest
|
|
12
|
+
from unittest import mock
|
|
11
13
|
|
|
12
14
|
import unittest
|
|
13
15
|
import ee
|
|
16
|
+
from ee import _helpers
|
|
14
17
|
from ee import apifunction
|
|
15
18
|
from ee import apitestcase
|
|
16
19
|
from ee import computedobject
|
|
17
20
|
from ee import ee_exception
|
|
21
|
+
from ee import oauth
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ServiceAccountCredentialsTest(unittest.TestCase):
|
|
25
|
+
|
|
26
|
+
def test_no_args(self):
|
|
27
|
+
with self.assertRaisesRegex(ValueError, 'At least one of'):
|
|
28
|
+
ee.ServiceAccountCredentials()
|
|
29
|
+
|
|
30
|
+
@mock.patch('google.oauth2.service_account.Credentials')
|
|
31
|
+
def test_json_file(self, mock_credentials):
|
|
32
|
+
ee.ServiceAccountCredentials(key_file='foo.json')
|
|
33
|
+
mock_credentials.from_service_account_file.assert_called_with(
|
|
34
|
+
'foo.json', scopes=oauth.SCOPES
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
@mock.patch('google.oauth2.service_account.Credentials')
|
|
38
|
+
def test_json_key_data(self, mock_credentials):
|
|
39
|
+
key_data = {'client_email': 'foo@bar.com'}
|
|
40
|
+
ee.ServiceAccountCredentials(key_data=json.dumps(key_data))
|
|
41
|
+
mock_credentials.from_service_account_info.assert_called_with(
|
|
42
|
+
key_data, scopes=oauth.SCOPES
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
@mock.patch('google.auth.crypt.RSASigner')
|
|
46
|
+
@mock.patch('google.oauth2.service_account.Credentials')
|
|
47
|
+
def test_pem_key_data(self, mock_credentials, mock_signer):
|
|
48
|
+
ee.ServiceAccountCredentials(email='foo@bar.com', key_data='pem_key_data')
|
|
49
|
+
mock_signer.from_string.assert_called_with('pem_key_data')
|
|
50
|
+
self.assertEqual(
|
|
51
|
+
mock_credentials.call_args[0][1], 'foo@bar.com'
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
@mock.patch('google.auth.crypt.RSASigner')
|
|
55
|
+
@mock.patch('google.oauth2.service_account.Credentials')
|
|
56
|
+
def test_pem_file(self, mock_credentials, mock_signer):
|
|
57
|
+
with mock.patch.object(
|
|
58
|
+
_helpers, 'open', mock.mock_open(read_data='pem_key_data')
|
|
59
|
+
):
|
|
60
|
+
ee.ServiceAccountCredentials(email='foo@bar.com', key_file='foo.pem')
|
|
61
|
+
mock_signer.from_string.assert_called_with('pem_key_data')
|
|
62
|
+
self.assertEqual(
|
|
63
|
+
mock_credentials.call_args[0][1], 'foo@bar.com'
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def test_bad_json_key_data(self):
|
|
67
|
+
# This causes a different failure based on where the test is run.
|
|
68
|
+
message = r'Could not deserialize key data|No key could be detected'
|
|
69
|
+
with self.assertRaisesRegex(ValueError, message):
|
|
70
|
+
ee.ServiceAccountCredentials(key_data='not json')
|
|
18
71
|
|
|
19
72
|
|
|
20
73
|
class ProfilingTest(apitestcase.ApiTestCase):
|
|
@@ -25,23 +78,23 @@ class ProfilingTest(apitestcase.ApiTestCase):
|
|
|
25
78
|
is_get_profiles = isinstance(
|
|
26
79
|
value, computedobject.ComputedObject
|
|
27
80
|
) and value.func == apifunction.ApiFunction.lookup('Profile.getProfiles')
|
|
28
|
-
return 'hooked
|
|
81
|
+
return f'hooked={hooked} getProfiles={is_get_profiles}'
|
|
29
82
|
|
|
30
|
-
def
|
|
83
|
+
def test_profile_printing(self):
|
|
31
84
|
ee.data.computeValue = self.MockValue
|
|
32
85
|
out = io.StringIO()
|
|
33
86
|
with ee.profilePrinting(destination=out):
|
|
34
87
|
self.assertEqual('hooked=True getProfiles=False', ee.Number(1).getInfo())
|
|
35
88
|
self.assertEqual('hooked=False getProfiles=True', out.getvalue())
|
|
36
89
|
|
|
37
|
-
def
|
|
90
|
+
def test_profile_printing_default_smoke(self):
|
|
38
91
|
# This will print to sys.stderr, so we can't make any assertions about the
|
|
39
92
|
# output. But we can check that it doesn't fail.
|
|
40
93
|
ee.data.computeValue = self.MockValue
|
|
41
94
|
with ee.profilePrinting():
|
|
42
95
|
self.assertEqual('hooked=True getProfiles=False', ee.Number(1).getInfo())
|
|
43
96
|
|
|
44
|
-
def
|
|
97
|
+
def test_profile_printing_error_getting_profiles(self):
|
|
45
98
|
ee.data.computeValue = self.MockValue
|
|
46
99
|
mock = unittest.mock.Mock()
|
|
47
100
|
mock.call.side_effect = ee_exception.EEException('test')
|
ee/tests/_state_test.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Tests for ee._state."""
|
|
3
|
+
|
|
4
|
+
from absl.testing import parameterized
|
|
5
|
+
|
|
6
|
+
import unittest
|
|
7
|
+
from ee import _state
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class StateTest(parameterized.TestCase):
|
|
11
|
+
|
|
12
|
+
def setUp(self):
|
|
13
|
+
super().setUp()
|
|
14
|
+
_state.reset_state()
|
|
15
|
+
|
|
16
|
+
@parameterized.named_parameters(
|
|
17
|
+
('global_mode', False),
|
|
18
|
+
)
|
|
19
|
+
def test_get_state(self, use_context_mode: bool):
|
|
20
|
+
state = _state.get_state()
|
|
21
|
+
|
|
22
|
+
self.assertIsInstance(state, _state.EEState)
|
|
23
|
+
self.assertEqual(state, _state.EEState())
|
|
24
|
+
|
|
25
|
+
@parameterized.named_parameters(
|
|
26
|
+
('global_mode', False),
|
|
27
|
+
)
|
|
28
|
+
def test_update_state(self, use_context_mode: bool):
|
|
29
|
+
state = _state.get_state()
|
|
30
|
+
|
|
31
|
+
# Modify the state and verify the global state has been updated.
|
|
32
|
+
state.cloud_api_user_project = 'my-project'
|
|
33
|
+
|
|
34
|
+
self.assertEqual(_state.get_state().cloud_api_user_project, 'my-project')
|
|
35
|
+
|
|
36
|
+
@parameterized.named_parameters(
|
|
37
|
+
('global_mode', False),
|
|
38
|
+
)
|
|
39
|
+
def test_reset_state(self, use_context_mode: bool):
|
|
40
|
+
state = _state.get_state()
|
|
41
|
+
state.cloud_api_user_project = 'my-project'
|
|
42
|
+
|
|
43
|
+
_state.reset_state()
|
|
44
|
+
|
|
45
|
+
self.assertEqual(_state.get_state(), _state.EEState())
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if __name__ == '__main__':
|
|
49
|
+
unittest.main()
|