earthengine-api 1.7.7rc0__py3-none-any.whl → 1.7.9__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: earthengine-api
3
- Version: 1.7.7rc0
3
+ Version: 1.7.9
4
4
  Summary: Earth Engine Python API
5
5
  Author-email: Google LLC <noreply@google.com>
6
6
  License: Apache-2.0
@@ -1,5 +1,5 @@
1
- earthengine_api-1.7.7rc0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
2
- ee/__init__.py,sha256=rDU5yyDT731tZQhWqtHaNCsiUhA4bNZeuEB37dHZotg,16762
1
+ earthengine_api-1.7.9.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
2
+ ee/__init__.py,sha256=egVqpmJfMrMKvX_gT-jVCpbVrq4erXjuDEDLD-5oRLM,16759
3
3
  ee/_arg_types.py,sha256=nrJrnPFnAS8fzMxAGmG3TbUOi_yFVrnSGW5IP8ATzDQ,2662
4
4
  ee/_cloud_api_utils.py,sha256=5WPzP5bcBIbjA7yYN-Cwmht_HU-XobbTrZvRiOEn0e0,32890
5
5
  ee/_helpers.py,sha256=afDDqoz1WESKE-lzqLtrCbvoQ1yVpfvJIgFcmUjO4L8,4821
@@ -15,7 +15,7 @@ ee/collection.py,sha256=c8OFIX9OVLxTbu8HGYiHOS3Yg6QjnxaO_FAhDhDQpic,32588
15
15
  ee/computedobject.py,sha256=Cdlccd4vw2neeRQlo5Jn3Q5R-VpQcQufNuxOHfpd1FU,9186
16
16
  ee/confusionmatrix.py,sha256=e6vz-FcT6acWxk5pDEK0vLIN1pUjYMh5Gyn1QSGJT3I,4233
17
17
  ee/customfunction.py,sha256=Me_iHDnjkYR33JMttGA7pKJlPCjUk8rO_kgaIhDm27M,7408
18
- ee/data.py,sha256=nuebPtBm3rBZMHGqxakSZmskDGFA52aip-9zOmXwBL0,85374
18
+ ee/data.py,sha256=GdskXveIWk4hFMqd0T0P6qoOUEEP8MO1XE4LX2hc8lU,85503
19
19
  ee/daterange.py,sha256=nrRYkR2M2aVU0ZJyG7yiZStFt-W2TvYVuazoZK_WZqM,4948
20
20
  ee/deprecation.py,sha256=mAFdO7fBjOu9TJd8CJB_M_NsZ6VNSxtSe820QKJx8fE,6243
21
21
  ee/deserializer.py,sha256=TjhQjcBnFpz8Kodoj74eSbvkJ2zLjpTTfUpcNEoQR2o,8357
@@ -23,15 +23,15 @@ ee/dictionary.py,sha256=b0t46vaeVMsFGI2icdGNspmedRtVVxfiIcFLWcmL2v8,10508
23
23
  ee/ee_array.py,sha256=EJmQ-zku1x0e6z44SlVD-F7mafmvRS0DoByKFyeDCuc,36368
24
24
  ee/ee_date.py,sha256=ixmLMrrGjq30DX9QY92HTeUgFZhWdn485cyaJkksh6U,10757
25
25
  ee/ee_exception.py,sha256=uN3X76HuhicU0LTxqRKQu1ZU6L7zw0GuABgsBTT-Ycw,164
26
- ee/ee_list.py,sha256=-jf0-zcqiiNexX_6GLZaRKotfmLabJ2o071Bv3MF6kI,19819
26
+ ee/ee_list.py,sha256=UszZsQoAyJR-ms7trDXaZKf2-Mo2EpNLCf7pfxvCTk8,19860
27
27
  ee/ee_number.py,sha256=nNcgntUyc4h-zYLKcBIlanucxsiKqXC_koN-pzmuBnc,22376
28
28
  ee/ee_string.py,sha256=eg-dR1JAuazNFyOJsbCULQn3BvBpQd5LER8oDfWtXcU,7875
29
- ee/ee_types.py,sha256=wQCE74pDRxz_IAYc6hjSSqJwp5ozOIoZF0cHijTd8Sg,3081
29
+ ee/ee_types.py,sha256=HHMZqgwSQ_zOLgPZ2cN45qCyJvIY7wl4GA3OPEzAM7M,3119
30
30
  ee/element.py,sha256=SdIVljB0Sbut02a61XgsPLiJpA66n34fsYZGMwsS8jQ,6261
31
- ee/encodable.py,sha256=J9QanzbB-_f5LUCQcIhrEV5W9CbRZtxa5gicmSVxLsk,1911
31
+ ee/encodable.py,sha256=bZLsuIPNO5_nOBuwnbhuxTRcjTKqFPgL8ox7uy41w6M,1963
32
32
  ee/errormargin.py,sha256=x8kAgFeUdXmpc_kv8Vx5qv-fHeJvFU9EBFbSOxhNl8Y,3039
33
- ee/feature.py,sha256=WItnwlQp8U3D0E0YkgJNXIAk2JviwloS9c8j2XqbKEM,30292
34
- ee/featurecollection.py,sha256=XbDgzYEEyhim0_y2AOD5Wi-TRIl90OnB1DHtAJXgvg8,14424
33
+ ee/feature.py,sha256=Qp5Dyl0eM6yjmQSH0D-dc9LEeKMCqlNibYJ9jOhHsaE,30326
34
+ ee/featurecollection.py,sha256=HBLIAS0HogRzTEVHaLn-X9ONfdu5qFvN_6Bb54KYQ28,14400
35
35
  ee/filter.py,sha256=GKz3IqeZSrK4X26TjWaw_uOE8KjZgSx1pS1zM07PXfI,35321
36
36
  ee/function.py,sha256=X-Mz0AvF_l809NZXK8RM6fa2h-nP7YU2sUFp6hvIbdU,7049
37
37
  ee/geometry.py,sha256=D__UeT72DDaJFakmyaPnmfHwxNtDxLJW7Vbze0Hh2Y4,59517
@@ -69,30 +69,31 @@ ee/tests/clusterer_test.py,sha256=B4m06wAtBeqvnIhRD2lnNy1UHDB_caleK_CqcrAU8dk,11
69
69
  ee/tests/collection_test.py,sha256=zsBerYrFr390awe-wD8uN9VME_qEsuaEN197hom4Xps,8815
70
70
  ee/tests/computedobject_test.py,sha256=B27rDq9Urpvy0WqpdbKRYbt6AcT1i93HX-es7hrhWVY,4840
71
71
  ee/tests/confusionmatrix_test.py,sha256=46JJh1-91AiYISXWZ6-2lvY5_Njvc8ompO9kmwqlFdg,7437
72
- ee/tests/data_test.py,sha256=8oiSnzGoOtWx3YIld8owWYw1zC517uLkq1Uf2Xn4iCI,52063
73
- ee/tests/daterange_test.py,sha256=a5fpg2lko3kCJzxQPCoAc_vjXkKy2zYcXbeSZKAFovI,8583
72
+ ee/tests/data_test.py,sha256=SBxHQqgZ4kEaWR13MWa-XPBuUr06keImvbp0t308wuY,57306
73
+ ee/tests/daterange_test.py,sha256=7gEhFCWEa2KJW_ZaQMBavuJ0wLtUcVgsD8COF7NOd98,9389
74
74
  ee/tests/deprecation_test.py,sha256=CoVug7J9YdB2utZ-BikyJcjitYPQLz49JL_z89xuQSQ,8484
75
75
  ee/tests/deserializer_test.py,sha256=i_tlyzKgCo1-VpWPAQnTWNcr2klourcMcBbFZJN6Ivw,5050
76
76
  ee/tests/dictionary_test.py,sha256=IRaXMr3GzZFQ5BiHdB35Iio2TckIK67Cnp6KH0h8Aa8,11818
77
77
  ee/tests/ee_array_test.py,sha256=JVXShdbOVOjlfSpNgYc_NVrknQatuPOZ19fG0Ii5yVU,50268
78
78
  ee/tests/ee_date_test.py,sha256=8rLUXfjyiW3LiBOCSneA7ZGgmoFgN5oZr58x5THtKGY,11106
79
- ee/tests/ee_list_test.py,sha256=yd2EWZGdg7pLJhsHSR5AbK58ZhT31GY-n2o1dDT3p9A,21797
79
+ ee/tests/ee_list_test.py,sha256=-EtJE4YoDjPoyk8co8XHnbxHZexZbX90XCqQ-PO2r8M,22838
80
80
  ee/tests/ee_number_test.py,sha256=YuJAWg4V4ELCLdmpEN7f4Wc_6ayOreWKui8ZM5fkjbg,35029
81
81
  ee/tests/ee_string_test.py,sha256=X6XJwa09yJXhg0rIpRb4QFA3eeGIrh2XClwhVByMRBY,9664
82
82
  ee/tests/ee_test.py,sha256=CY5XUfh_5Bx6yIUrHDI1SbVfztIgejB-LGdgCMgT0IA,17835
83
- ee/tests/ee_types_test.py,sha256=oRnqplaTWg47zuYfAYTTVwembCnw8XT20HPNMdAvgNE,921
84
- ee/tests/element_test.py,sha256=Gft1vmSeuhSelRHsdGKHd3S1xucnUQJh8ZlGSZ2JxRw,1968
83
+ ee/tests/ee_types_test.py,sha256=-ZDZxPiAbKHHCBa8_z3sU5CRpnX07EZUIQc0tD-QTE4,2048
84
+ ee/tests/element_test.py,sha256=Fgxmg6wTC2VtoQXkn6rTSA3ifdhKNjtEdgxzdgXpQDs,4638
85
+ ee/tests/encodable_test.py,sha256=tRCtW6HO5-OnjXlxVUEUgoVrEOllvs-Z9pWMXISMYHQ,836
85
86
  ee/tests/errormargin_test.py,sha256=YEBzvBFsD756nicZBcjnPFAXy06jZNKiSSAa2hAzN-M,5061
86
- ee/tests/feature_test.py,sha256=-iq-p9xidPV0WE398j1nWL6Gn4Yvi_CQRetHbHhDooM,22514
87
- ee/tests/featurecollection_test.py,sha256=b3SwieXb8nyRzAlwKK_nSa4xGcy5AynIDZ1A3sV02Es,38440
87
+ ee/tests/feature_test.py,sha256=-9wJgY-eJM53ZKYjRcnfWAZXjkF_u8Tn4I1wc209R0U,23336
88
+ ee/tests/featurecollection_test.py,sha256=CXy240UIRCJNY6nZ8wW-u2O9MqFkUX-ZVba9wTr8OTU,39118
88
89
  ee/tests/filter_test.py,sha256=d-KQ_zI-r7BAMazKqqHMMzNUZdeC46BZiciMfthKEO8,35033
89
- ee/tests/function_test.py,sha256=0hv7H-Q21vdDvqHNH_3NvFj_nPEWM2Fh7CD0wgjXE2M,3131
90
+ ee/tests/function_test.py,sha256=60Yl3K6VvOsObW3jhbhWnb9TzBjdQne63wbZWuyQxbY,3524
90
91
  ee/tests/geometry_point_test.py,sha256=w9MYTwvw2Co9rIjffFB0ezQ_jZz8nxbdgOcyhqTTmjk,15093
91
92
  ee/tests/geometry_test.py,sha256=5cNblTtXg4gg0k1_6go4XL2EldY2StOfD7sQ7Bu0WZU,32424
92
93
  ee/tests/image_converter_test.py,sha256=x29StMrzv6jlATtx3XXC7rEo7Cgrcx0XyVoM-ccVuao,1853
93
- ee/tests/image_test.py,sha256=_SJmee7orPot26vbxKEZcNZ6OsrNrxv3ToInWYLNZLM,150917
94
- ee/tests/imagecollection_test.py,sha256=qosRZXCbhwFuy8qR7DbA-FMl4ktW7Y2cUJHeXLcszpg,38444
95
- ee/tests/join_test.py,sha256=pFILq3qM27rO64WYbBC1A_Gs8_pabRv68X7MU_EM_cw,7630
94
+ ee/tests/image_test.py,sha256=bhu_GLQy0jRJORVNHnpShOHzg-ZptE3XG3r_APiJ4n4,152371
95
+ ee/tests/imagecollection_test.py,sha256=M2WdkCPx-w7wWFGw9q2XHRAXo7rCMAqIZknjhldlPs4,41535
96
+ ee/tests/join_test.py,sha256=uuMW2tvHVoBXuanhcsJ95cQTfRjJTs5gmaPrzAJZrGM,7860
96
97
  ee/tests/kernel_test.py,sha256=-0nZVNSxE1IGs4y60oijISh2Hi1V7NCjrmUnEMDd0nQ,19378
97
98
  ee/tests/model_test.py,sha256=dgnWfqR4R4MIUljMBL1TOcztzA-lWEl7cT5f2Y0k2ck,12483
98
99
  ee/tests/oauth_test.py,sha256=8hldNiR3AYtQkWA4vwwSAO6jDIsh_fww7b_cOI9MJjo,9202
@@ -102,8 +103,8 @@ ee/tests/reducer_test.py,sha256=vqXpgLZ7fPyfZ12srpREgMpfrKYLcFQod3Qn1Niv7VM,3197
102
103
  ee/tests/serializer_test.py,sha256=d6IEW_dt-G900IRfQnZftrwjpc1wJ-ouaQQwaLO0FxI,9834
103
104
  ee/tests/table_converter_test.py,sha256=t4yIfy40R3U17I_2nVtKm_Q2Tae-UifwZLz8qOmoC9A,5091
104
105
  ee/tests/terrain_test.py,sha256=inZ2sy807nDG_HMutzGHaqcTUaLnZQOMdWyf0NrQzV0,4561
105
- earthengine_api-1.7.7rc0.dist-info/METADATA,sha256=QVtJ6bHiuN8SkNyQx0ppDbshaV6WAmwN7NoV2Tf10KY,2198
106
- earthengine_api-1.7.7rc0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
107
- earthengine_api-1.7.7rc0.dist-info/entry_points.txt,sha256=-Ax4SCU-S474r8OD2LIxata6PRmkZoDrppQ4fP_exNc,50
108
- earthengine_api-1.7.7rc0.dist-info/top_level.txt,sha256=go5zOwCgm5lIS3yTR-Vsxp1gNI4qdS-MP5eY-7zMxVY,3
109
- earthengine_api-1.7.7rc0.dist-info/RECORD,,
106
+ earthengine_api-1.7.9.dist-info/METADATA,sha256=lebc7nR6BBje0swYH-S9jec7wr2Tc0skwu1bnyfWwWE,2195
107
+ earthengine_api-1.7.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
108
+ earthengine_api-1.7.9.dist-info/entry_points.txt,sha256=-Ax4SCU-S474r8OD2LIxata6PRmkZoDrppQ4fP_exNc,50
109
+ earthengine_api-1.7.9.dist-info/top_level.txt,sha256=go5zOwCgm5lIS3yTR-Vsxp1gNI4qdS-MP5eY-7zMxVY,3
110
+ earthengine_api-1.7.9.dist-info/RECORD,,
ee/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """The EE Python library."""
2
2
 
3
- __version__ = '1.7.7rc0'
3
+ __version__ = '1.7.9'
4
4
 
5
5
  # Using lowercase function naming to match the JavaScript names.
6
6
  # pylint: disable=g-bad-name
ee/data.py CHANGED
@@ -689,8 +689,10 @@ def getMapId(params: dict[str, Any]) -> dict[str, Any]:
689
689
  )
690
690
  state = _get_state()
691
691
  map_name = result['name']
692
- url_format = '{}/{}/{}/tiles/{{z}}/{{x}}/{{y}}'.format(
693
- state.tile_base_url, _cloud_api_utils.VERSION, map_name)
692
+ version = _cloud_api_utils.VERSION
693
+ url_format = (
694
+ '{state.tile_base_url}/{version}/{map_name)}/tiles/{{z}}/{{x}}/{{y}}'
695
+ )
694
696
  if state.cloud_api_key:
695
697
  url_format += f'?key={state.cloud_api_key}'
696
698
 
@@ -1199,9 +1201,9 @@ def makeThumbUrl(thumbId: dict[str, str]) -> str:
1199
1201
  A URL from which the thumbnail can be obtained.
1200
1202
  """
1201
1203
  state = _get_state()
1202
- url = '{}/{}/{}:getPixels'.format(
1203
- state.tile_base_url, _cloud_api_utils.VERSION, thumbId['thumbid']
1204
- )
1204
+ version = _cloud_api_utils.VERSION
1205
+ thumb_id = thumbId['thumbid']
1206
+ url = f'{state.tile_base_url}/{version}/{thumb_id}:getPixels'
1205
1207
  if state.cloud_api_key:
1206
1208
  url += f'?key={state.cloud_api_key}'
1207
1209
  return url
@@ -1332,9 +1334,10 @@ def makeDownloadUrl(downloadId: dict[str, str]) -> str:
1332
1334
  Returns:
1333
1335
  A URL from which the download can be obtained.
1334
1336
  """
1335
- return '{}/{}/{}:getPixels'.format(
1336
- _get_state().tile_base_url, _cloud_api_utils.VERSION, downloadId['docid']
1337
- )
1337
+ title_base_url = _get_state().tile_base_url
1338
+ version = _cloud_api_utils.VERSION
1339
+ docid = downloadId['docid']
1340
+ return f'{title_base_url}/{version}/{docid}:getPixels'
1338
1341
 
1339
1342
 
1340
1343
  def getTableDownloadId(params: dict[str, Any]) -> dict[str, str]:
@@ -1395,10 +1398,10 @@ def makeTableDownloadUrl(downloadId: dict[str, str]) -> str:
1395
1398
  Returns:
1396
1399
  A Url from which the download can be obtained.
1397
1400
  """
1398
- return '{}/{}/{}:getFeatures'.format(
1399
- _get_state().tile_base_url, _cloud_api_utils.VERSION, downloadId['docid']
1400
- )
1401
-
1401
+ title_base_url = _get_state().tile_base_url
1402
+ version = _cloud_api_utils.VERSION
1403
+ docid = downloadId['docid']
1404
+ return f'{title_base_url}/{version}/{docid}:getFeatures'
1402
1405
 
1403
1406
  def getAlgorithms() -> Any:
1404
1407
  """Get the list of algorithms.
ee/ee_list.py CHANGED
@@ -422,8 +422,10 @@ class List(computedobject.ComputedObject):
422
422
  # TODO: Improve the type of `baseAlgorithm`.
423
423
  def map(
424
424
  self,
425
- baseAlgorithm: _arg_types.Any, # pylint: disable=invalid-name
425
+ # pylint: disable=invalid-name
426
+ baseAlgorithm: _arg_types.Any,
426
427
  dropNulls: _arg_types.Bool = False,
428
+ # pylint: enable=invalid-name
427
429
  ) -> List:
428
430
  """Map an algorithm over a list.
429
431
 
ee/ee_types.py CHANGED
@@ -77,6 +77,7 @@ def isSubtype(firstType: str, secondType: str) -> bool:
77
77
  'Collection', 'ImageCollection', 'FeatureCollection')
78
78
  elif firstType in ('FeatureCollection', 'Collection'):
79
79
  return secondType in ('Collection', 'ImageCollection', 'FeatureCollection')
80
+ # TODO: elif firstType == 'Object':
80
81
  elif firstType == object:
81
82
  return True
82
83
  else:
ee/encodable.py CHANGED
@@ -1,4 +1,7 @@
1
- """Interfaces implemented by serializable objects."""
1
+ """Interfaces implemented by serializable objects.
2
+
3
+ This file has no implementation, only interfaces.
4
+ """
2
5
 
3
6
  from collections.abc import Callable
4
7
  from typing import Any
ee/feature.py CHANGED
@@ -28,10 +28,10 @@ class Feature(element.Element):
28
28
  def __init__(
29
29
  self,
30
30
  geom: None | (
31
- Feature |
32
- geometry.Geometry |
33
- dict[str, Any] |
34
- computedobject.ComputedObject
31
+ Feature |
32
+ geometry.Geometry |
33
+ dict[str, Any] |
34
+ computedobject.ComputedObject
35
35
  ),
36
36
  properties: None | (
37
37
  dict[str, Any] | computedobject.ComputedObject
@@ -682,6 +682,7 @@ class Feature(element.Element):
682
682
  )
683
683
 
684
684
  def setGeometry(
685
+ # pylint: disable-next=redefined-outer-name
685
686
  self, geometry: _arg_types.Geometry | None = None
686
687
  ) -> Feature:
687
688
  """Returns the feature with the geometry replaced by the specified geometry.
ee/featurecollection.py CHANGED
@@ -35,12 +35,12 @@ class FeatureCollection(collection.Collection[feature.Feature]):
35
35
  def __init__(
36
36
  self,
37
37
  args: None | (
38
- dict[str, Any] |
39
- list[Any] |
40
- str |
41
- feature.Feature |
42
- geometry.Geometry |
43
- computedobject.ComputedObject
38
+ dict[str, Any] |
39
+ list[Any] |
40
+ str |
41
+ feature.Feature |
42
+ geometry.Geometry |
43
+ computedobject.ComputedObject
44
44
  ),
45
45
  column: Any | None = None,
46
46
  ):
ee/tests/data_test.py CHANGED
@@ -848,6 +848,131 @@ class DataTest(unittest.TestCase):
848
848
  cloud_api_resource.projects().maps().create.call_args_list[1]
849
849
  .kwargs['workloadTag'])
850
850
 
851
+ def test_get_map_id_with_string_image(self):
852
+ cloud_api_resource = mock.MagicMock()
853
+ with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
854
+ with self.assertRaisesRegex(
855
+ ee.ee_exception.EEException, '^Image as JSON string not supported.'
856
+ ):
857
+ ee.data.getMapId({'image': 'my-image'})
858
+
859
+ def test_get_map_id_with_version(self):
860
+ cloud_api_resource = mock.MagicMock()
861
+ with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
862
+ with self.assertRaisesRegex(
863
+ ee.ee_exception.EEException,
864
+ '^Image version specification not supported.',
865
+ ):
866
+ ee.data.getMapId({
867
+ 'image': image.Image('my-image'),
868
+ 'version': '123',
869
+ })
870
+
871
+ def test_get_map_id_with_vis_params(self):
872
+ cloud_api_resource = mock.MagicMock()
873
+ with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
874
+ mock_result = {
875
+ 'name': 'projects/earthengine-legacy/maps/DOCID',
876
+ }
877
+ cloud_api_resource.projects().maps().create().execute.return_value = (
878
+ mock_result
879
+ )
880
+ vis_params = {'paletteColors': ['FF0000']}
881
+ ee.data.getMapId({
882
+ 'image': image.Image('my-image'),
883
+ 'palette': 'FF0000',
884
+ })
885
+ self.assertEqual(
886
+ 2, cloud_api_resource.projects().maps().create.call_count
887
+ )
888
+ kwargs = (
889
+ cloud_api_resource.projects().maps().create.call_args_list[1].kwargs
890
+ )
891
+ self.assertIn('body', kwargs)
892
+ self.assertIn('visualizationOptions', kwargs['body'])
893
+ self.assertEqual(vis_params, kwargs['body']['visualizationOptions'])
894
+
895
+ def test_get_map_id_with_cloud_api_key(self):
896
+ cloud_api_resource = mock.MagicMock()
897
+ with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
898
+ ee.data._get_state().cloud_api_key = 'my-api-key'
899
+ mock_result = {
900
+ 'name': 'projects/earthengine-legacy/maps/DOCID',
901
+ }
902
+ cloud_api_resource.projects().maps().create().execute.return_value = (
903
+ mock_result
904
+ )
905
+ actual_result = ee.data.getMapId({
906
+ 'image': image.Image('my-image'),
907
+ })
908
+ self.assertIn('?key=my-api-key', actual_result['tile_fetcher'].url_format)
909
+
910
+ def test_get_thumbnail_default(self):
911
+ cloud_api_resource_raw = mock.MagicMock()
912
+ with apitestcase.UsingCloudApi(
913
+ cloud_api_resource_raw=cloud_api_resource_raw
914
+ ):
915
+ thumb_id = 'projects/earthengine-legacy/thumbnails/some-id'
916
+ thumb_id_result = {'thumbid': thumb_id}
917
+ img = image.Image('my-image')
918
+ img.getThumbId = mock.Mock(return_value=thumb_id_result)
919
+ params = {'image': img}
920
+ (
921
+ cloud_api_resource_raw.projects()
922
+ .thumbnails()
923
+ .getPixels.return_value.execute.return_value
924
+ ) = b'pixel data'
925
+ result = ee.data.getThumbnail(params)
926
+ self.assertEqual(b'pixel data', result)
927
+ img.getThumbId.assert_called_once_with(params)
928
+ cloud_api_resource_raw.projects().thumbnails().getPixels.assert_called_once_with(
929
+ name=thumb_id
930
+ )
931
+
932
+ def test_get_thumbnail_video(self):
933
+ cloud_api_resource_raw = mock.MagicMock()
934
+ with apitestcase.UsingCloudApi(
935
+ cloud_api_resource_raw=cloud_api_resource_raw
936
+ ):
937
+ thumb_id = 'projects/earthengine-legacy/videoThumbnails/some-id'
938
+ thumb_id_result = {'thumbid': thumb_id}
939
+ img = image.Image('my-image')
940
+ img.getThumbId = mock.Mock(return_value=thumb_id_result)
941
+ params = {'image': img}
942
+ (
943
+ cloud_api_resource_raw.projects()
944
+ .videoThumbnails()
945
+ .getPixels.return_value.execute.return_value
946
+ ) = b'video data'
947
+ result = ee.data.getThumbnail(params, thumbType='video')
948
+ self.assertEqual(b'video data', result)
949
+ img.getThumbId.assert_called_once_with(params)
950
+ cloud_api_resource_raw.projects().videoThumbnails().getPixels.assert_called_once_with(
951
+ name=thumb_id
952
+ )
953
+
954
+ def test_get_thumbnail_filmstrip(self):
955
+ cloud_api_resource_raw = mock.MagicMock()
956
+ with apitestcase.UsingCloudApi(
957
+ cloud_api_resource_raw=cloud_api_resource_raw
958
+ ):
959
+ thumb_id = 'projects/earthengine-legacy/filmstripThumbnails/some-id'
960
+ thumb_id_result = {'thumbid': thumb_id}
961
+ img = image.Image('my-image')
962
+ img.getThumbId = mock.Mock(return_value=thumb_id_result)
963
+ params = {'image': img}
964
+ (
965
+ cloud_api_resource_raw.projects()
966
+ .filmstripThumbnails()
967
+ .getPixels.return_value.execute.return_value
968
+ ) = b'filmstrip data'
969
+ result = ee.data.getThumbnail(params, thumbType='filmstrip')
970
+ self.assertEqual(b'filmstrip data', result)
971
+ img.getThumbId.assert_called_once_with(params)
972
+ cloud_api_resource_raw.projects().filmstripThumbnails().getPixels.assert_called_once_with(
973
+ name=thumb_id
974
+ )
975
+
851
976
  def test_get_download_id(self):
852
977
  cloud_api_resource = mock.MagicMock()
853
978
  with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
@@ -949,6 +1074,14 @@ class DataTest(unittest.TestCase):
949
1074
  cloud_api_resource.projects().thumbnails().create.call_args
950
1075
  .kwargs['workloadTag'])
951
1076
 
1077
+ def test_make_thumb_url_with_api_key(self):
1078
+ cloud_api_resource = mock.MagicMock()
1079
+ with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
1080
+ ee.data._get_state().cloud_api_key = 'my-api-key'
1081
+ thumb_id = {'thumbid': 'projects/earthengine-legacy/thumbnails/some-id'}
1082
+ url = ee.data.makeThumbUrl(thumb_id)
1083
+ self.assertIn('?key=my-api-key', url)
1084
+
952
1085
  def test_get_table_download_id(self):
953
1086
  cloud_api_resource = mock.MagicMock()
954
1087
  with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
@@ -272,6 +272,31 @@ class DateRangeTest(apitestcase.ApiTestCase):
272
272
  result = json.loads(expression.serialize())
273
273
  self.assertEqual(expect, result)
274
274
 
275
+ def test_unbounded(self):
276
+ expect = make_expression_graph({
277
+ 'functionName': 'DateRange.unbounded',
278
+ 'arguments': {},
279
+ })
280
+ expression = ee.DateRange.unbounded()
281
+ result = json.loads(expression.serialize())
282
+ self.assertEqual(expect, result)
283
+
284
+ is_unbounded_expect = make_expression_graph({
285
+ 'arguments': {
286
+ 'dateRange': {
287
+ 'functionInvocationValue': {
288
+ 'functionName': 'DateRange.unbounded',
289
+ 'arguments': {},
290
+ }
291
+ }
292
+ },
293
+ 'functionName': 'DateRange.isUnbounded',
294
+ })
295
+
296
+ is_unbounded_expression = expression.isUnbounded()
297
+ is_unbounded_result = json.loads(is_unbounded_expression.serialize())
298
+ self.assertEqual(is_unbounded_expect, is_unbounded_result)
299
+
275
300
  def test_union(self):
276
301
  expect = make_expression_graph({
277
302
  'arguments': {
ee/tests/ee_list_test.py CHANGED
@@ -47,6 +47,12 @@ class ListTest(apitestcase.ApiTestCase):
47
47
  self.assertEqual(expect, json.loads(ee.List(tuple([42])).serialize()))
48
48
  self.assertEqual(expect, json.loads(ee.List([42]).serialize()))
49
49
 
50
+ def test_bad_list(self):
51
+ with self.assertRaisesRegex(
52
+ ee.EEException, 'Invalid argument specified for ee.List'
53
+ ):
54
+ ee.List('not a list') # pytype: disable=wrong-arg-types
55
+
50
56
  def test_mapping(self):
51
57
  lst = ee.List(['foo', 'bar'])
52
58
  body = lambda s: ee.String(s).cat('bar')
@@ -359,7 +365,31 @@ class ListTest(apitestcase.ApiTestCase):
359
365
  result = json.loads(expression.serialize())
360
366
  self.assertEqual(expect, result)
361
367
 
362
- # TODO: test_iterate
368
+ def test_iterate(self):
369
+ lst = ee.List([1, 2, 3])
370
+ body = lambda n, p: ee.Number(p).add(n)
371
+ iterated = lst.iterate(body, 0)
372
+
373
+ self.assertIsInstance(iterated, ee.ComputedObject)
374
+ self.assertEqual(ee.ApiFunction.lookup('List.iterate'), iterated.func)
375
+ self.assertEqual(lst, iterated.args['list'])
376
+ self.assertEqual(0, iterated.args['first'])
377
+
378
+ sig = {
379
+ 'returns': 'Object',
380
+ 'args': [
381
+ {'name': '_MAPPING_VAR_0_0', 'type': 'Object'},
382
+ {'name': '_MAPPING_VAR_0_1', 'type': 'Object'},
383
+ ],
384
+ }
385
+ expected_function = ee.CustomFunction(sig, body)
386
+ self.assertEqual(
387
+ expected_function.serialize(), iterated.args['function'].serialize()
388
+ )
389
+ self.assertEqual(
390
+ expected_function.serialize(for_cloud_api=True),
391
+ iterated.args['function'].serialize(for_cloud_api=True),
392
+ )
363
393
 
364
394
  def test_join(self):
365
395
  expect = make_expression_graph({
ee/tests/ee_types_test.py CHANGED
@@ -4,12 +4,13 @@
4
4
  import datetime
5
5
 
6
6
  import unittest
7
+ from ee import apitestcase
7
8
  from ee import ee_list
8
9
  from ee import ee_number
9
10
  from ee import ee_types
10
11
 
11
12
 
12
- class EeTypesTest(unittest.TestCase):
13
+ class EeTypesTest(apitestcase.ApiTestCase):
13
14
 
14
15
  def test_class_to_name(self):
15
16
  self.assertEqual('List', ee_types.classToName(ee_list.List))
@@ -27,6 +28,31 @@ class EeTypesTest(unittest.TestCase):
27
28
 
28
29
  self.assertEqual('Object', ee_types.classToName(Foo))
29
30
 
31
+ def test_is_subtype(self):
32
+ self.assertTrue(ee_types.isSubtype('Image', 'Image'))
33
+ self.assertTrue(ee_types.isSubtype('Element', 'Image'))
34
+ self.assertFalse(ee_types.isSubtype('Image', 'Element'))
35
+
36
+ self.assertTrue(ee_types.isSubtype('Collection', 'ImageCollection'))
37
+ self.assertTrue(ee_types.isSubtype('Collection', 'FeatureCollection'))
38
+
39
+ self.assertFalse(ee_types.isSubtype('ImageCollection', 'Collection'))
40
+
41
+ self.assertFalse(ee_types.isSubtype('Image', 'Collection'))
42
+ self.assertFalse(ee_types.isSubtype('ImageCollection', 'FeatureCollection'))
43
+
44
+ # TODO: Theses should be false.
45
+ self.assertTrue(ee_types.isSubtype('FeatureCollection', 'Collection'))
46
+ self.assertTrue(ee_types.isSubtype('FeatureCollection', 'ImageCollection'))
47
+
48
+ def test_is_array(self):
49
+ self.assertTrue(ee_types.isArray([]))
50
+ self.assertTrue(ee_types.isArray(()))
51
+ self.assertTrue(ee_types.isArray(ee_list.List([1, 2])))
52
+ self.assertFalse(ee_types.isArray(1))
53
+ self.assertFalse(ee_types.isArray('string'))
54
+ self.assertFalse(ee_types.isArray(ee_number.Number(1)))
55
+
30
56
 
31
57
  if __name__ == '__main__':
32
58
  unittest.main()
ee/tests/element_test.py CHANGED
@@ -48,6 +48,87 @@ class ElementTestCase(apitestcase.ApiTestCase):
48
48
  CheckMultiProperties(image.set(computed_arg))
49
49
  CheckMultiProperties(image.set({'properties': computed_arg}))
50
50
 
51
+ def test_set_one_arg_exception(self):
52
+ with self.assertRaisesRegex(
53
+ ee.EEException,
54
+ r'When Element\.set\(\) is passed one argument, '
55
+ r'it must be a dictionary\.',
56
+ ):
57
+ ee.Image(1).set('not a dictionary')
58
+
59
+ def test_set_odd_args_exception(self):
60
+ with self.assertRaisesRegex(
61
+ ee.EEException,
62
+ r'When Element\.set\(\) is passed multiple arguments, there '
63
+ r'must be an even number of them\.',
64
+ ):
65
+ ee.Image(1).set('key1', 'value1', 'key2')
66
+
67
+ def test_get_array(self):
68
+ image = ee.Image(1)
69
+ array_property = image.getArray('array_prop')
70
+ self.assertIsInstance(array_property, ee.Array)
71
+ self.assertEqual(
72
+ ee.ApiFunction.lookup('Element.getArray'), array_property.func
73
+ )
74
+ self.assertEqual(
75
+ {'object': image, 'property': ee.String('array_prop')},
76
+ array_property.args,
77
+ )
78
+
79
+ def test_get_number(self):
80
+ image = ee.Image(1)
81
+ number_property = image.getNumber('number_prop')
82
+ self.assertIsInstance(number_property, ee.Number)
83
+ self.assertEqual(
84
+ ee.ApiFunction.lookup('Element.getNumber'), number_property.func
85
+ )
86
+ self.assertEqual(
87
+ {'object': image, 'property': ee.String('number_prop')},
88
+ number_property.args,
89
+ )
90
+
91
+ def test_get_string(self):
92
+ image = ee.Image(1)
93
+ string_property = image.getString('string_prop')
94
+ self.assertIsInstance(string_property, ee.String)
95
+ self.assertEqual(
96
+ ee.ApiFunction.lookup('Element.getString'), string_property.func
97
+ )
98
+ self.assertEqual(
99
+ {'object': image, 'property': ee.String('string_prop')},
100
+ string_property.args,
101
+ )
102
+
103
+ def test_property_names(self):
104
+ image = ee.Image(1)
105
+ property_names = image.propertyNames()
106
+ self.assertIsInstance(property_names, ee.List)
107
+ self.assertEqual(
108
+ ee.ApiFunction.lookup('Element.propertyNames'), property_names.func
109
+ )
110
+ self.assertEqual({'element': image}, property_names.args)
111
+
112
+ def test_to_dictionary(self):
113
+ image = ee.Image(1)
114
+ dictionary = image.toDictionary()
115
+ self.assertIsInstance(dictionary, ee.Dictionary)
116
+ self.assertEqual(
117
+ ee.ApiFunction.lookup('Element.toDictionary'), dictionary.func
118
+ )
119
+ self.assertEqual({'element': image, 'properties': None}, dictionary.args)
120
+
121
+ dictionary_with_props = image.toDictionary(['a', 'b'])
122
+ self.assertIsInstance(dictionary_with_props, ee.Dictionary)
123
+ self.assertEqual(
124
+ ee.ApiFunction.lookup('Element.toDictionary'),
125
+ dictionary_with_props.func,
126
+ )
127
+ self.assertEqual(
128
+ {'element': image, 'properties': ee.List(['a', 'b'])},
129
+ dictionary_with_props.args,
130
+ )
131
+
51
132
  def test_init_opt_params(self):
52
133
  result = ee.Element(func=None, args=None, opt_varName='test').serialize()
53
134
  self.assertIn('"0": {"argumentReference": "test"}', result)
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env python3
2
+ """Tests for ee.encodable."""
3
+
4
+ from collections.abc import Callable
5
+ from typing import Any
6
+ import unittest
7
+
8
+ from ee import encodable
9
+
10
+
11
+ class EncodableTest(unittest.TestCase):
12
+
13
+ def test_encodable(self):
14
+ encoder: Callable[[Any], Any] = lambda x: x
15
+ with self.assertRaises(NotImplementedError):
16
+ encodable.Encodable().encode(encoder)
17
+ with self.assertRaises(NotImplementedError):
18
+ encodable.Encodable().encode_cloud_value(encoder)
19
+
20
+ def test_encodable_function(self):
21
+ encoder: Callable[[Any], Any] = lambda x: x
22
+ with self.assertRaises(NotImplementedError):
23
+ encodable.EncodableFunction().encode_invocation(encoder)
24
+ with self.assertRaises(NotImplementedError):
25
+ encodable.EncodableFunction().encode_cloud_invocation(encoder)
26
+
27
+
28
+ if __name__ == '__main__':
29
+ unittest.main()
ee/tests/feature_test.py CHANGED
@@ -121,6 +121,30 @@ class FeatureTest(apitestcase.ApiTestCase):
121
121
  'system:index': 'bar'
122
122
  }, from_geo_json_feature.args['metadata'])
123
123
 
124
+ def test_feature_copy(self):
125
+ feature = ee.Feature(ee.Geometry.Point(1, 2), {'x': 1})
126
+ feature_copy = ee.Feature(feature)
127
+ self.assertEqual(feature.func, feature_copy.func)
128
+ self.assertEqual(feature.args, feature_copy.args)
129
+
130
+ def test_feature_with_properties_exception(self):
131
+ with self.assertRaisesRegex(
132
+ ee.EEException, 'Cannot create Feature out of a Feature and properties'
133
+ ):
134
+ ee.Feature(ee.Feature(None), {'x': 2})
135
+
136
+ def test_id_and_system_index_exception(self):
137
+ point = ee.Geometry.Point(1, 2)
138
+ with self.assertRaisesRegex(
139
+ ee.EEException, 'Cannot specify both "id" and "system:index"'
140
+ ):
141
+ ee.Feature({
142
+ 'type': 'Feature',
143
+ 'id': 'bar',
144
+ 'geometry': point.toGeoJSON(),
145
+ 'properties': {'system:index': 'bar'},
146
+ })
147
+
124
148
  def test_get_map(self):
125
149
  """Verifies that getMap() uses Collection.draw to rasterize Features."""
126
150
  feature = ee.Feature(None)
@@ -96,6 +96,19 @@ class FeatureCollectionTest(apitestcase.ApiTestCase):
96
96
  ee.ComputedObject(None, {'x': 'y'}))
97
97
  self.assertEqual({'x': 'y'}, from_computed_object.args)
98
98
 
99
+ def test_invalid_constructor_args(self):
100
+ with self.assertRaisesRegex(
101
+ ee.EEException,
102
+ 'Unrecognized argument type to convert to a FeatureCollection: 1',
103
+ ):
104
+ ee.FeatureCollection(1)
105
+
106
+ with self.assertRaisesRegex(
107
+ ee.EEException,
108
+ 'Unrecognized argument type to convert to a FeatureCollection: None',
109
+ ):
110
+ ee.FeatureCollection(None)
111
+
99
112
  def test_get_map_id(self):
100
113
  """Verifies that getMap() uses Collection.draw to draw."""
101
114
  collection = ee.FeatureCollection('test5')
@@ -126,6 +139,13 @@ class FeatureCollectionTest(apitestcase.ApiTestCase):
126
139
  ee.FeatureCollection('test7').getDownloadUrl('csv'),
127
140
  ee.FeatureCollection('test7').getDownloadURL('csv'))
128
141
 
142
+ ee.FeatureCollection('test9').getDownloadURL(selectors=['bar', 'baz'])
143
+ self.assertEqual(
144
+ ee.FeatureCollection('test9').serialize(),
145
+ self.last_table_call['data']['table'].serialize(),
146
+ )
147
+ self.assertEqual('bar,baz', self.last_table_call['data']['selectors'])
148
+
129
149
  def test_download_table_with_cloud_api(self):
130
150
  cloud_api_resource = mock.MagicMock()
131
151
  with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
ee/tests/function_test.py CHANGED
@@ -34,6 +34,11 @@ Args:
34
34
 
35
35
  class FunctionTest(unittest.TestCase):
36
36
 
37
+ def test_get_signature_not_implemented(self):
38
+ message = 'Function subclasses must implement getSignature()'
39
+ with self.assertRaisesRegex(NotImplementedError, message):
40
+ ee.Function().getSignature()
41
+
37
42
  def test_name_args(self):
38
43
  """Verifies that Functions can convert positional to named arguments."""
39
44
  self.assertEqual({}, TEST_FUNC.nameArgs([]))
@@ -43,6 +48,13 @@ class FunctionTest(unittest.TestCase):
43
48
 
44
49
  self.assertRaisesRegex(ee.EEException, 'Too many', TEST_FUNC.nameArgs,
45
50
  [1, 2, 3])
51
+ self.assertRaisesRegex(
52
+ ee.EEException,
53
+ 'Argument a specified as both positional and keyword',
54
+ TEST_FUNC.nameArgs,
55
+ [1],
56
+ {'a': 2},
57
+ )
46
58
 
47
59
  def test_promote_args(self):
48
60
  """Verifies that Functions can promote and verify their arguments."""
ee/tests/image_test.py CHANGED
@@ -97,6 +97,20 @@ class ImageTest(apitestcase.ApiTestCase):
97
97
  'value': 'foo'
98
98
  }, from_variable.encode(None))
99
99
 
100
+ def test_constructor_invalid_version(self):
101
+ with self.assertRaisesRegex(
102
+ ee_exception.EEException,
103
+ r'If version is specified, the arg to Image\(\) must be a string',
104
+ ):
105
+ ee.Image(123, version=456)
106
+
107
+ def test_constructor_unrecognized_type(self):
108
+ with self.assertRaisesRegex(
109
+ ee_exception.EEException,
110
+ 'Unrecognized argument type to convert to an Image:',
111
+ ):
112
+ ee.Image({'some': 'dict'})
113
+
100
114
  def test_image_signatures(self):
101
115
  """Verifies that the API functions are added to ee.Image."""
102
116
  self.assertTrue(hasattr(ee.Image(1), 'addBands'))
@@ -629,6 +643,19 @@ class CloudThumbnailAndExportImageTest(apitestcase.ApiTestCase):
629
643
  )
630
644
  self.assertEqual({}, params)
631
645
 
646
+ def test_prepare_for_export_with_crs_and_crsTransform(self):
647
+ with apitestcase.UsingCloudApi():
648
+ image, params = self._base_image.prepare_for_export(
649
+ {'crs': 'ABCD', 'crsTransform': '1,2,3,4,5,6'}
650
+ )
651
+ self.assertImageEqual(
652
+ self._base_image.reproject(
653
+ crs='ABCD', crsTransform=[1, 2, 3, 4, 5, 6]
654
+ ),
655
+ image,
656
+ )
657
+ self.assertEqual({}, params)
658
+
632
659
  def test_prepare_for_export_invalid_crs_and_transform(self):
633
660
  with apitestcase.UsingCloudApi():
634
661
  with self.assertRaises(ee_exception.EEException):
@@ -637,6 +664,20 @@ class CloudThumbnailAndExportImageTest(apitestcase.ApiTestCase):
637
664
  self._base_image.prepare_for_export(
638
665
  {'crs': 'ABCD', 'crs_transform': 'x'}
639
666
  )
667
+ with self.assertRaisesRegex(
668
+ ee_exception.EEException,
669
+ 'Both "crs_transform" and "crsTransform" are specified.',
670
+ ):
671
+ self._base_image.prepare_for_export({
672
+ 'crs': 'EPSG:4326',
673
+ 'crs_transform': [1, 2, 3, 4, 5, 6],
674
+ 'crsTransform': [1, 2, 3, 4, 5, 6],
675
+ })
676
+
677
+ def test_prepare_for_export_invalid_dimensions(self):
678
+ with apitestcase.UsingCloudApi():
679
+ with self.assertRaisesRegex(ee_exception.EEException, 'Invalid dimensions'):
680
+ self._base_image.prepare_for_export({'dimensions': [1, 2, 3]})
640
681
 
641
682
  def test_prepare_for_export_with_polygon(self):
642
683
  with apitestcase.UsingCloudApi():
@@ -84,6 +84,13 @@ class ImageCollectionTest(apitestcase.ApiTestCase):
84
84
  ee.ComputedObject(None, {'x': 'y'}))
85
85
  self.assertEqual({'x': 'y'}, from_computed_object.args)
86
86
 
87
+ def test_unrecognized_type(self):
88
+ with self.assertRaisesRegex(
89
+ ee.EEException,
90
+ 'Unrecognized argument type to convert to an ImageCollection: 123',
91
+ ):
92
+ ee.ImageCollection(123)
93
+
87
94
  def test_imperative_functions(self):
88
95
  """Verifies that imperative functions return ready values."""
89
96
  image_collection = ee.ImageCollection(ee.Image(1))
@@ -135,6 +142,18 @@ class ImageCollectionTest(apitestcase.ApiTestCase):
135
142
  collection.serialize(for_cloud_api=True))
136
143
  self.assertEqual({}, params)
137
144
 
145
+ def test_filmstrip_thumb_url_invalid_format(self):
146
+ """Verifies correct thumbnailing behavior."""
147
+ message = r'Invalid format specified for thumbnail\. gif'
148
+ with self.assertRaisesRegex(ee.EEException, message):
149
+ ee.ImageCollection(ee.Image(1)).getFilmstripThumbURL({'format': 'gif'})
150
+
151
+ def test_video_thumb_url_invalid_format(self):
152
+ """Verifies correct thumbnailing behavior."""
153
+ message = r'Invalid format specified for thumbnail\. png'
154
+ with self.assertRaisesRegex(ee.EEException, message):
155
+ ee.ImageCollection(ee.Image(1)).getVideoThumbURL({'format': 'png'})
156
+
138
157
  def test_select_opt_params(self):
139
158
  result = (
140
159
  ee.ImageCollection([])
@@ -143,6 +162,56 @@ class ImageCollectionTest(apitestcase.ApiTestCase):
143
162
  )
144
163
  self.assertIn('"newNames": {"constantValue": ["name_a", "name_b"]}', result)
145
164
 
165
+ def test_link_collection_all_args(self):
166
+ image_collection_1 = ee.ImageCollection('a')
167
+ image_collection_2 = ee.ImageCollection('b')
168
+ linked_bands = ['b1', 'b2']
169
+ linked_properties = ['p1', 'p2']
170
+ match_property_name = 'p3'
171
+ result = image_collection_1.linkCollection(
172
+ image_collection_2,
173
+ linked_bands,
174
+ linked_properties,
175
+ match_property_name,
176
+ )
177
+ self.assertIsInstance(result, ee.ImageCollection)
178
+ self.assertEqual(ee.ApiFunction.lookup('Collection.map'), result.func)
179
+ self.assertEqual(image_collection_1, result.args['collection'])
180
+ self.assertIsInstance(result.args['baseAlgorithm'], ee.Function)
181
+
182
+ serialized = result.serialize()
183
+ self.assertIn('"functionName": "ImageCollection.load"', serialized)
184
+ self.assertIn('"id": {"constantValue": "a"}', serialized)
185
+ self.assertIn('"functionName": "Image.linkCollection"', serialized)
186
+ self.assertIn('"id": {"constantValue": "b"}', serialized)
187
+ self.assertIn('"linkedBands": {"constantValue": ["b1", "b2"]}', serialized)
188
+ self.assertIn(
189
+ '"linkedProperties": {"constantValue": ["p1", "p2"]}', serialized
190
+ )
191
+ self.assertIn('"matchPropertyName": {"constantValue": "p3"}', serialized)
192
+
193
+ def test_link_collection_necessary_args(self):
194
+ image_collection_1 = ee.ImageCollection('a')
195
+ image_collection_2 = ee.ImageCollection('b')
196
+ linked_bands = ['b1', 'b2']
197
+ result = image_collection_1.linkCollection(
198
+ image_collection_2,
199
+ linkedBands=linked_bands,
200
+ )
201
+ self.assertIsInstance(result, ee.ImageCollection)
202
+ self.assertEqual(ee.ApiFunction.lookup('Collection.map'), result.func)
203
+ self.assertEqual(image_collection_1, result.args['collection'])
204
+ self.assertIsInstance(result.args['baseAlgorithm'], ee.Function)
205
+
206
+ serialized = result.serialize()
207
+ self.assertIn('"functionName": "ImageCollection.load"', serialized)
208
+ self.assertIn('"id": {"constantValue": "a"}', serialized)
209
+ self.assertIn('"functionName": "Image.linkCollection"', serialized)
210
+ self.assertIn('"id": {"constantValue": "b"}', serialized)
211
+ self.assertIn('"linkedBands": {"constantValue": ["b1", "b2"]}', serialized)
212
+ self.assertNotIn('"linkedProperties"', serialized)
213
+ self.assertNotIn('"matchPropertyName"', serialized)
214
+
146
215
  def test_aggregate_array(self):
147
216
  property_name = 'property name'
148
217
  expect = make_expression_graph({
ee/tests/join_test.py CHANGED
@@ -28,6 +28,11 @@ class JoinTest(apitestcase.ApiTestCase):
28
28
  with self.assertRaisesRegex(TypeError, message):
29
29
  ee.Join() # pytype:disable=missing-parameter
30
30
 
31
+ def test_join_type_error(self):
32
+ message = r"Join can only be used as a cast to Join\. Found <class 'str'>"
33
+ with self.assertRaisesRegex(TypeError, message):
34
+ ee.Join('some string') # pytype:disable=wrong-arg-types
35
+
31
36
  def test_apply(self):
32
37
  expect = make_expression_graph({
33
38
  'arguments': {