From 2170bb01474602f392f0dc98a5569cd525b59656 Mon Sep 17 00:00:00 2001 From: Google Earth Engine Authors Date: Fri, 13 Oct 2023 05:35:39 +0000 Subject: [PATCH 1/9] v0.1.375 PiperOrigin-RevId: 573107300 --- javascript/build/ee_api_js.js | 2 +- javascript/build/ee_api_js_debug.js | 36 +++++++----- javascript/build/ee_api_js_npm.js | 36 +++++++----- javascript/package.json | 2 +- javascript/src/apiclient.js | 2 +- .../CloudMasking/Sentinel2ClearComposite.js | 57 +++++++++++++++++++ python/ee/__init__.py | 2 +- python/ee/data.py | 31 ++++++++-- 8 files changed, 128 insertions(+), 40 deletions(-) create mode 100644 javascript/src/examples/CloudMasking/Sentinel2ClearComposite.js diff --git a/javascript/build/ee_api_js.js b/javascript/build/ee_api_js.js index 17cf974fc..ce5415eab 100644 --- a/javascript/build/ee_api_js.js +++ b/javascript/build/ee_api_js.js @@ -441,7 +441,7 @@ Uc(p,f[m]))});return b?b(l):l};return this.callback?(uj(d,null,function(f,l){ret vj.prototype.send=function(a,b){var c=[a.B+" "+a.path+" HTTP/1.1"];c.push("Content-Type: application/json; charset=utf-8");var d=yj();null!=d&&c.push("Authorization: "+d);a=a.body?JSON.stringify(a.body):"";return[c.join("\r\n")+"\r\n\r\n"+a,b]}; var zj=function(a,b,c){a=n(b.split("--"+a.split("; boundary=")[1]));for(b=a.next();!b.done;b=a.next())if(b=b.value.split("\r\n\r\n"),!(3>b.length)){var d=b[0].match(/\r\nContent-ID: ]*)>/)[1],e=Number(b[1].match(/^HTTP\S*\s(\d+)\s/)[1]);c(d,e,b.slice(2).join("\r\n\r\n"))}},sj=function(){var a=Aj.replace(/\/api$/,"");return"window"in r&&!a.match(/^https?:\/\/content-/)?a.replace(/^(https?:\/\/)(.*\.googleapis\.com)$/,"$1content-$2"):a},Cj=function(a,b,c){var d=[];a&&(d=d.concat(Bj)); b&&d.push("https://www.googleapis.com/auth/devstorage.read_write");a=d=d.concat(c);c=b=0;for(var e={};cg)break;y++}return Xj(C.status,function(Q){try{return C.getResponseHeader(Q)}catch(oa){return null}},C.responseText,l,void 0,e,d,f)},Vj=function(a,b,c,d,e,g,f){var l=0,m={url:a,method:c,content:d,headers:e},p=Pj,v=null!=g?g:10; m.callback=function(y){y=y.target;if(429==y.getStatus()&&l]/.test(stringifiedStyle)) { + throw Error("Forbidden characters in style string: " + stringifiedStyle); + } if (goog.DEBUG) { - if (0 === style.length) { - return module$contents$safevalues$internals$style_impl_createStyleInternal(style); - } - if (/[<>]/.test(style)) { - throw Error("Forbidden characters in style string: " + style); + if (0 === stringifiedStyle.length) { + return module$contents$safevalues$internals$style_impl_createStyleInternal(stringifiedStyle); } - if (!/;$/.test(style)) { - throw Error('Style string does not end with ";": ' + style); + if (!/;$/.test(stringifiedStyle)) { + throw Error('Style string does not end with ";": ' + stringifiedStyle); } - if (!/:/.test(style)) { - throw Error('Style string should contain one or more ":": ' + style); + if (!/:/.test(stringifiedStyle)) { + throw Error('Style string should contain one or more ":": ' + stringifiedStyle); } } - return module$contents$safevalues$internals$style_impl_createStyleInternal(style); + return module$contents$safevalues$internals$style_impl_createStyleInternal(stringifiedStyle); } module$exports$safevalues$builders$style_builders.safeStyle = module$contents$safevalues$builders$style_builders_safeStyle; function module$contents$safevalues$builders$style_builders_concatStyles(styles) { @@ -25209,6 +25212,9 @@ var module$contents$safevalues$builders$url_builders_DEFAULT_SCHEMES = [module$e module$exports$safevalues$builders$url_builders.SanitizableUrlScheme.FTP, module$exports$safevalues$builders$url_builders.SanitizableUrlScheme.RELATIVE]; function module$contents$safevalues$builders$url_builders_trySanitizeUrl(url, allowedSchemes) { allowedSchemes = void 0 === allowedSchemes ? module$contents$safevalues$builders$url_builders_DEFAULT_SCHEMES : allowedSchemes; + if (module$contents$safevalues$internals$url_impl_isUrl(url)) { + return url; + } for (var i = 0; i < allowedSchemes.length; ++i) { var scheme = allowedSchemes[i]; if (module$contents$safevalues$builders$url_builders_isValidScheme(scheme) && scheme.isValid(url)) { diff --git a/javascript/build/ee_api_js_npm.js b/javascript/build/ee_api_js_npm.js index e1e829721..2c2d5151a 100644 --- a/javascript/build/ee_api_js_npm.js +++ b/javascript/build/ee_api_js_npm.js @@ -17407,7 +17407,7 @@ goog.debug.entryPointRegistry.register(function(transformer) { ee.apiclient = {}; var module$contents$ee$apiclient_apiclient = {}; ee.apiclient.VERSION = module$exports$ee$apiVersion.V1; -ee.apiclient.API_CLIENT_VERSION = "0.1.374"; +ee.apiclient.API_CLIENT_VERSION = "0.1.375"; ee.apiclient.NULL_VALUE = module$exports$eeapiclient$domain_object.NULL_VALUE; ee.apiclient.PromiseRequestService = module$exports$eeapiclient$promise_request_service.PromiseRequestService; ee.apiclient.MakeRequestParams = module$contents$eeapiclient$request_params_MakeRequestParams; @@ -17698,8 +17698,8 @@ module$contents$ee$apiclient_apiclient.send = function(path, params, callback, m var profileHookAtCallTime = module$contents$ee$apiclient_apiclient.profileHook_, contentType = "application/x-www-form-urlencoded"; body && (contentType = "application/json", method && method.startsWith("multipart") && (contentType = method, method = "POST")); method = method || "POST"; - var headers = {"Content-Type":contentType}, version = "0.1.374"; - "0.1.374" === version && (version = "latest"); + var headers = {"Content-Type":contentType}, version = "0.1.375"; + "0.1.375" === version && (version = "latest"); headers[module$contents$ee$apiclient_apiclient.API_CLIENT_VERSION_HEADER] = "ee-js/" + version; var authToken = module$contents$ee$apiclient_apiclient.getAuthToken(); if (null != authToken) { @@ -25090,23 +25090,26 @@ function module$contents$safevalues$builders$script_builders_safeScriptWithArgs( module$exports$safevalues$builders$script_builders.safeScriptWithArgs = module$contents$safevalues$builders$script_builders_safeScriptWithArgs; var module$exports$safevalues$builders$style_builders = {}, module$contents$safevalues$builders$style_builders_module = module$contents$safevalues$builders$style_builders_module || {id:"third_party/javascript/safevalues/builders/style_builders.closure.js"}; function module$contents$safevalues$builders$style_builders_safeStyle(templateObj) { - goog.DEBUG && module$contents$safevalues$internals$string_literal_assertIsTemplateObject(templateObj, 0); - var style = templateObj[0]; + var rest = $jscomp.getRestArguments.apply(1, arguments); + goog.DEBUG && module$contents$safevalues$internals$string_literal_assertIsTemplateObject(templateObj, rest.length); + for (var stringifiedStyle = templateObj[0], i = 0; i < templateObj.length - 1; i++) { + stringifiedStyle += String(rest[i]) + templateObj[i + 1]; + } + if (/[<>]/.test(stringifiedStyle)) { + throw Error("Forbidden characters in style string: " + stringifiedStyle); + } if (goog.DEBUG) { - if (0 === style.length) { - return module$contents$safevalues$internals$style_impl_createStyleInternal(style); - } - if (/[<>]/.test(style)) { - throw Error("Forbidden characters in style string: " + style); + if (0 === stringifiedStyle.length) { + return module$contents$safevalues$internals$style_impl_createStyleInternal(stringifiedStyle); } - if (!/;$/.test(style)) { - throw Error('Style string does not end with ";": ' + style); + if (!/;$/.test(stringifiedStyle)) { + throw Error('Style string does not end with ";": ' + stringifiedStyle); } - if (!/:/.test(style)) { - throw Error('Style string should contain one or more ":": ' + style); + if (!/:/.test(stringifiedStyle)) { + throw Error('Style string should contain one or more ":": ' + stringifiedStyle); } } - return module$contents$safevalues$internals$style_impl_createStyleInternal(style); + return module$contents$safevalues$internals$style_impl_createStyleInternal(stringifiedStyle); } module$exports$safevalues$builders$style_builders.safeStyle = module$contents$safevalues$builders$style_builders_safeStyle; function module$contents$safevalues$builders$style_builders_concatStyles(styles) { @@ -25209,6 +25212,9 @@ var module$contents$safevalues$builders$url_builders_DEFAULT_SCHEMES = [module$e module$exports$safevalues$builders$url_builders.SanitizableUrlScheme.FTP, module$exports$safevalues$builders$url_builders.SanitizableUrlScheme.RELATIVE]; function module$contents$safevalues$builders$url_builders_trySanitizeUrl(url, allowedSchemes) { allowedSchemes = void 0 === allowedSchemes ? module$contents$safevalues$builders$url_builders_DEFAULT_SCHEMES : allowedSchemes; + if (module$contents$safevalues$internals$url_impl_isUrl(url)) { + return url; + } for (var i = 0; i < allowedSchemes.length; ++i) { var scheme = allowedSchemes[i]; if (module$contents$safevalues$builders$url_builders_isValidScheme(scheme) && scheme.isValid(url)) { diff --git a/javascript/package.json b/javascript/package.json index d72c78ae0..30193c8fe 100644 --- a/javascript/package.json +++ b/javascript/package.json @@ -1,6 +1,6 @@ { "name": "@google/earthengine", - "version": "0.1.374", + "version": "0.1.375", "description": "JavaScript client for Google Earth Engine API.", "author": "Google LLC", "license": "Apache-2.0", diff --git a/javascript/src/apiclient.js b/javascript/src/apiclient.js index 98a72a448..35f235963 100644 --- a/javascript/src/apiclient.js +++ b/javascript/src/apiclient.js @@ -25,7 +25,7 @@ const {PromiseRequestService} = goog.require('eeapiclient.promise_request_servic /** @namespace */ const apiclient = {}; -const API_CLIENT_VERSION = '0.1.374'; +const API_CLIENT_VERSION = '0.1.375'; exports.VERSION = apiVersion.VERSION; exports.API_CLIENT_VERSION = API_CLIENT_VERSION; diff --git a/javascript/src/examples/CloudMasking/Sentinel2ClearComposite.js b/javascript/src/examples/CloudMasking/Sentinel2ClearComposite.js new file mode 100644 index 000000000..78ce88242 --- /dev/null +++ b/javascript/src/examples/CloudMasking/Sentinel2ClearComposite.js @@ -0,0 +1,57 @@ +// This example demonstrates how to use Cloud Score+ QA bands to +// create a clear Sentinel-2 median composite. +// + + +// Region of interest. +var roi = ee.Geometry.Rectangle([-124.8697, 35.5806, -119.9917, 39.9554]); + +// Harmonized Sentinel-2 Level 2A collection. +var s2 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED'); + +// Cloud Score+ image collection. Note Cloud Score+ is produced +// from Sentinel-2 Level 1C data and can be applied to either L1C +// or L2A collections. +var csPlus = ee.ImageCollection('GOOGLE/CLOUD_SCORE_PLUS/V1/S2_HARMONIZED'); + +// Get list of Sentinel-2 bands. +var csPlusBands = csPlus.first().bandNames(); + +// Link S2 and CS+ results. +var linkedCollection = s2.linkCollection(csPlus, csPlusBands); +// Date range for median composite. +var dateStart = '2023-03-01'; +var dateEnd = '2023-09-01'; + +/** + * Masks pixels with low CS+ QA scores. + * @param {ee.Image} image Sentinel-2 image + * @return {ee.Image} masked Sentinel-2 image + */ +function maskLowQA(image) { + // CS+ QA band to use for masking. Options are 'cs' and 'cs_cdf'. + var qaBand = 'cs'; + + // Adjustable threshold for converting CS+ QA to a binary mask. + // Higher thresholds will mask out partial occlusions like thin clouds, haze & + // cirrus shadows. Lower thresholds will be more permissive of these + // occlusions. + var clearThreshold = 0.60; + var mask = image.select(qaBand).gte(clearThreshold); + + return image.updateMask(mask); +} + + +// Filter collection, mask pixels with low QA scores, and +// generate median composite. +var composite = linkedCollection.filterBounds(roi) + .filterDate(dateStart, dateEnd) + .map(maskLowQA) + .median(); + +// Sentinel-2 visualization parameters. +var s2Viz = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000}; + +Map.addLayer(composite, s2Viz, 'median composite'); +Map.centerObject(roi, 11); \ No newline at end of file diff --git a/python/ee/__init__.py b/python/ee/__init__.py index bb995c40d..10d5b3ff3 100644 --- a/python/ee/__init__.py +++ b/python/ee/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """The EE Python library.""" -__version__ = '0.1.374' +__version__ = '0.1.375' # Using lowercase function naming to match the JavaScript names. # pylint: disable=g-bad-name diff --git a/python/ee/data.py b/python/ee/data.py index 50d5dbcf6..f995dab12 100644 --- a/python/ee/data.py +++ b/python/ee/data.py @@ -760,10 +760,15 @@ def listFeatures(params: Dict[str, Any]) -> Any: GeoJSON geometry string (see RFC 7946). filter - If present, specifies additional simple property filters (see https://google.aip.dev/160). - fileFormat - The resulting output format. + fileFormat - If present, specifies an output format for the tabular data. + The function makes a network request for each page until the entire + table has been fetched. The number of fetches depends on the number of + rows in the table and pageSize. pageToken is ignored. Supported + formats are: PANDAS_DATAFRAME for a Pandas DataFrame and + GEOPANDAS_GEODATAFRAME for a GeoPandas GeoDataFrame. Returns: - A dictionary containing: + A Pandas DataFrame, GeoPandas GeoDataFrame, or a dictionary containing: - "type": always "FeatureCollection" marking this object as a GeoJSON feature collection. - "features": a list of GeoJSON features. @@ -795,7 +800,9 @@ def getPixels(params: Dict[str, Any]) -> Any: assetId - The asset ID for which to get pixels. Must be an image asset. fileFormat - The resulting file format. Defaults to png. See https://developers.google.com/earth-engine/reference/rest/v1/ImageFileFormat - for the available formats. + for the available formats. There are additional formats that convert + the downloaded object to a Python data object. These include: + NUMPY_NDARRAY, which converts to a structured NumPy array. grid - Parameters describing the pixel grid in which to fetch data. Defaults to the native pixel grid of the data. region - If present, the region of data to return, specified as a GeoJSON @@ -835,7 +842,9 @@ def computePixels(params: Dict[str, Any]) -> Any: expression - The expression to compute. fileFormat - The resulting file format. Defaults to png. See https://developers.google.com/earth-engine/reference/rest/v1/ImageFileFormat - for the available formats. + for the available formats. There are additional formats that convert + the downloaded object to a Python data object. These include: + NUMPY_NDARRAY, which converts to a structured NumPy array. grid - Parameters describing the pixel grid in which to fetch data. Defaults to the native pixel grid of the data. bandIds - If present, specifies a specific set of bands from which to get @@ -902,11 +911,21 @@ def computeFeatures(params: Dict[str, Any]) -> Any: 1000 results per page. pageToken - A token identifying a page of results the server should return. - fileFormat - The resulting output format. + fileFormat - If present, specifies an output format for the tabular data. + The function makes a network request for each page until the entire + table has been fetched. The number of fetches depends on the number of + rows in the table and pageSize. pageToken is ignored. Supported + formats are: PANDAS_DATAFRAME for a Pandas DataFrame and + GEOPANDAS_GEODATAFRAME for a GeoPandas GeoDataFrame. workloadTag - User supplied tag to track this computation. Returns: - A list with the results of the computation. + A Pandas DataFrame, GeoPandas GeoDataFrame, or a dictionary containing: + - "type": always "FeatureCollection" marking this object as a GeoJSON + feature collection. + - "features": a list of GeoJSON features. + - "next_page_token": A token to retrieve the next page of results in a + subsequent call to this function. """ params = params.copy() params['expression'] = serializer.encode(params['expression']) From 91df843ca2df4aab4672e78bcbf1aed496c7d495 Mon Sep 17 00:00:00 2001 From: Google Earth Engine Authors Date: Thu, 19 Oct 2023 13:40:31 -0700 Subject: [PATCH 2/9] v0.1.376 PiperOrigin-RevId: 574987676 --- javascript/build/ee_api_js.js | 29 +++++----- javascript/build/ee_api_js_debug.js | 76 ++++++++++--------------- javascript/build/ee_api_js_npm.js | 88 ++++++++++++----------------- javascript/package.json | 2 +- javascript/src/apiclient.js | 2 +- python/ee/__init__.py | 2 +- python/ee/data.py | 1 - 7 files changed, 84 insertions(+), 116 deletions(-) diff --git a/javascript/build/ee_api_js.js b/javascript/build/ee_api_js.js index ce5415eab..7bd2276f0 100644 --- a/javascript/build/ee_api_js.js +++ b/javascript/build/ee_api_js.js @@ -441,7 +441,7 @@ Uc(p,f[m]))});return b?b(l):l};return this.callback?(uj(d,null,function(f,l){ret vj.prototype.send=function(a,b){var c=[a.B+" "+a.path+" HTTP/1.1"];c.push("Content-Type: application/json; charset=utf-8");var d=yj();null!=d&&c.push("Authorization: "+d);a=a.body?JSON.stringify(a.body):"";return[c.join("\r\n")+"\r\n\r\n"+a,b]}; var zj=function(a,b,c){a=n(b.split("--"+a.split("; boundary=")[1]));for(b=a.next();!b.done;b=a.next())if(b=b.value.split("\r\n\r\n"),!(3>b.length)){var d=b[0].match(/\r\nContent-ID: ]*)>/)[1],e=Number(b[1].match(/^HTTP\S*\s(\d+)\s/)[1]);c(d,e,b.slice(2).join("\r\n\r\n"))}},sj=function(){var a=Aj.replace(/\/api$/,"");return"window"in r&&!a.match(/^https?:\/\/content-/)?a.replace(/^(https?:\/\/)(.*\.googleapis\.com)$/,"$1content-$2"):a},Cj=function(a,b,c){var d=[];a&&(d=d.concat(Bj)); b&&d.push("https://www.googleapis.com/auth/devstorage.read_write");a=d=d.concat(c);c=b=0;for(var e={};cg)break;y++}return Xj(C.status,function(Q){try{return C.getResponseHeader(Q)}catch(oa){return null}},C.responseText,l,void 0,e,d,f)},Vj=function(a,b,c,d,e,g,f){var l=0,m={url:a,method:c,content:d,headers:e},p=Pj,v=null!=g?g:10; m.callback=function(y){y=y.target;if(429==y.getStatus()&&la.y||a.y>=1<+~[\]()=\\^$|]+$/.test(selectorToCheck)) { - throw Error("Selector allows only [-_a-zA-Z0-9#.:* ,>+~[\\]()=\\^$|] and strings, got: " + selector); - } - if (!module$contents$goog$html$SafeStyleSheet_SafeStyleSheet.hasBalancedBrackets_(selectorToCheck)) { - throw Error("() and [] in selector must be balanced, got: " + selector); - } - style instanceof module$contents$goog$html$SafeStyle_SafeStyle || (style = module$contents$goog$html$SafeStyle_SafeStyle.create(style)); - var styleSheet = selector + "{" + module$contents$goog$html$SafeStyle_SafeStyle.unwrap(style).replace(/+~[\]()=\\^$|]+$/.test(selectorToCheck)) { - throw Error("Selector allows only [-_a-zA-Z0-9#.:* ,>+~[\\]()=\\^$|] and strings, got: " + selector); - } - if (!module$contents$goog$html$SafeStyleSheet_SafeStyleSheet.hasBalancedBrackets_(selectorToCheck)) { - throw Error("() and [] in selector must be balanced, got: " + selector); - } - style instanceof module$contents$goog$html$SafeStyle_SafeStyle || (style = module$contents$goog$html$SafeStyle_SafeStyle.create(style)); - var styleSheet = selector + "{" + module$contents$goog$html$SafeStyle_SafeStyle.unwrap(style).replace(/ None: def setUserAgent(user_agent: str) -> None: global _user_agent _user_agent = user_agent - _install_cloud_api_resource() def getUserAgent() -> Optional[str]: From fe9e3ea5cfb0da7bc3349047f4a0e0c0a538789b Mon Sep 17 00:00:00 2001 From: Google Earth Engine Authors Date: Thu, 26 Oct 2023 21:02:53 +0000 Subject: [PATCH 3/9] v0.1.377 PiperOrigin-RevId: 576980936 --- javascript/build/ee_api_js.js | 30 ++++++++--------- javascript/build/ee_api_js_debug.js | 18 +++++++---- javascript/build/ee_api_js_npm.js | 50 ++++++++++++++++------------- javascript/package.json | 2 +- javascript/src/apiclient.js | 2 +- javascript/src/data.js | 29 ++++++++++------- python/ee/__init__.py | 5 +-- python/ee/_cloud_api_utils.py | 26 ++++++++++++--- python/ee/data.py | 32 +++++++++++++----- python/ee/tests/algorithms.json | 23 +++++++++++++ 10 files changed, 144 insertions(+), 73 deletions(-) diff --git a/javascript/build/ee_api_js.js b/javascript/build/ee_api_js.js index 7bd2276f0..b5f5ff5b3 100644 --- a/javascript/build/ee_api_js.js +++ b/javascript/build/ee_api_js.js @@ -364,8 +364,8 @@ yf.prototype.i=function(){return{P:{ranges:of},keys:["gamma","opacity","paletteC k.Object.defineProperties(yf.prototype,{gamma:{configurable:!0,enumerable:!0,get:function(){return G(this,"gamma")?F(this,"gamma"):null},set:function(a){this.h.gamma=a}},opacity:{configurable:!0,enumerable:!0,get:function(){return G(this,"opacity")?F(this,"opacity"):null},set:function(a){this.h.opacity=a}},Xc:{configurable:!0,enumerable:!0,get:function(){return G(this,"paletteColors")?F(this,"paletteColors"):null},set:function(a){this.h.paletteColors=a}},Wf:{configurable:!0,enumerable:!0,get:function(){return G(this, "ranges")?F(this,"ranges"):null},set:function(a){this.h.ranges=a}}});var Qg=function(a){a=void 0===a?{}:a;this.h={};this.h.start=null==a.start?null:a.start;this.h.end=null==a.end?null:a.end};q(Qg,E);Qg.prototype.i=function(){return{keys:["end","start"]}}; k.Object.defineProperties(Qg.prototype,{end:{configurable:!0,enumerable:!0,get:function(){return G(this,"end")?F(this,"end"):null},set:function(a){this.h.end=a}},start:{configurable:!0,enumerable:!0,get:function(){return G(this,"start")?F(this,"start"):null},set:function(a){this.h.start=a}}}); -var Wd={$Xgafv:"$.xgafv",access_token:"access_token",alt:"alt",assetId:"assetId",billingAccount:"billingAccount",callback:"callback",fields:"fields",filter:"filter",key:"key",oauth_token:"oauth_token",overwrite:"overwrite",pageSize:"pageSize",pageToken:"pageToken",parent:"parent",prettyPrint:"prettyPrint",quotaUser:"quotaUser",region:"region",updateMask:"updateMask",uploadType:"uploadType",upload_protocol:"upload_protocol",view:"view",workloadTag:"workloadTag"},Ug=function(a){this.m="v1";this.j=new Zd(a, -null)};Ug.prototype.list=function(a,b,c){b=void 0===b?{}:b;c=void 0===c?{}:c;this.j.C(a,RegExp("^projects/[^/]+$"));return I(this.j,{body:null,B:"GET",D:"earthengine.projects.algorithms.list",path:"/"+this.m+"/"+a+"/algorithms",u:H(b,c),G:tg})};var Vg=function(a){this.m="v1";this.j=new Zd(a,null)}; +var Wd={$Xgafv:"$.xgafv",access_token:"access_token",alt:"alt",assetId:"assetId",callback:"callback",fields:"fields",filter:"filter",key:"key",oauth_token:"oauth_token",overwrite:"overwrite",pageSize:"pageSize",pageToken:"pageToken",parent:"parent",prettyPrint:"prettyPrint",quotaUser:"quotaUser",region:"region",updateMask:"updateMask",uploadType:"uploadType",upload_protocol:"upload_protocol",view:"view",workloadTag:"workloadTag"},Ug=function(a){this.m="v1";this.j=new Zd(a,null)}; +Ug.prototype.list=function(a,b,c){b=void 0===b?{}:b;c=void 0===c?{}:c;this.j.C(a,RegExp("^projects/[^/]+$"));return I(this.j,{body:null,B:"GET",D:"earthengine.projects.algorithms.list",path:"/"+this.m+"/"+a+"/algorithms",u:H(b,c),G:tg})};var Vg=function(a){this.m="v1";this.j=new Zd(a,null)}; Vg.prototype.getCapabilities=function(a,b,c){b=void 0===b?{}:b;c=void 0===c?{}:c;this.j.C(a,RegExp("^projects/[^/]+$"));return I(this.j,{body:null,B:"GET",D:"earthengine.projects.getCapabilities",path:"/"+this.m+"/"+a+"/capabilities",u:H(b,c),G:Ze})};Vg.prototype.Sd=function(a,b){b=void 0===b?{}:b;var c=void 0===c?{}:c;this.j.C(a,RegExp("^projects/[^/]+$"));return I(this.j,{body:null,B:"GET",D:"earthengine.projects.listAssets",path:"/"+this.m+"/"+a+":listAssets",u:H(b,c),G:ug})}; var Wg=function(a){this.m="v1";this.j=new Zd(a,null)};h=Wg.prototype;h.create=function(a,b,c,d){c=void 0===c?{}:c;d=void 0===d?{}:d;this.j.C(a,RegExp("^projects/[^/]+$"));return I(this.j,{body:b,B:"POST",D:"earthengine.projects.assets.create",path:"/"+this.m+"/"+a+"/assets",u:H(c,d),G:qf})}; h.delete=function(a,b,c){b=void 0===b?{}:b;c=void 0===c?{}:c;this.j.C(a,RegExp("^projects/[^/]+/assets/.*$"));return I(this.j,{body:null,B:"DELETE",D:"earthengine.projects.assets.delete",path:"/"+this.m+"/"+a,u:H(b,c),G:zf})};h.get=function(a,b,c){b=void 0===b?{}:b;c=void 0===c?{}:c;this.j.C(a,RegExp("^projects/[^/]+/assets/.*$"));return I(this.j,{body:null,B:"GET",D:"earthengine.projects.assets.get",path:"/"+this.m+"/"+a,u:H(b,c),G:qf})}; @@ -441,7 +441,7 @@ Uc(p,f[m]))});return b?b(l):l};return this.callback?(uj(d,null,function(f,l){ret vj.prototype.send=function(a,b){var c=[a.B+" "+a.path+" HTTP/1.1"];c.push("Content-Type: application/json; charset=utf-8");var d=yj();null!=d&&c.push("Authorization: "+d);a=a.body?JSON.stringify(a.body):"";return[c.join("\r\n")+"\r\n\r\n"+a,b]}; var zj=function(a,b,c){a=n(b.split("--"+a.split("; boundary=")[1]));for(b=a.next();!b.done;b=a.next())if(b=b.value.split("\r\n\r\n"),!(3>b.length)){var d=b[0].match(/\r\nContent-ID: ]*)>/)[1],e=Number(b[1].match(/^HTTP\S*\s(\d+)\s/)[1]);c(d,e,b.slice(2).join("\r\n\r\n"))}},sj=function(){var a=Aj.replace(/\/api$/,"");return"window"in r&&!a.match(/^https?:\/\/content-/)?a.replace(/^(https?:\/\/)(.*\.googleapis\.com)$/,"$1content-$2"):a},Cj=function(a,b,c){var d=[];a&&(d=d.concat(Bj)); b&&d.push("https://www.googleapis.com/auth/devstorage.read_write");a=d=d.concat(c);c=b=0;for(var e={};cg)break;y++}return Xj(C.status,function(Q){try{return C.getResponseHeader(Q)}catch(oa){return null}},C.responseText,l,void 0,e,d,f)},Vj=function(a,b,c,d,e,g,f){var l=0,m={url:a,method:c,content:d,headers:e},p=Pj,v=null!=g?g:10; m.callback=function(y){y=y.target;if(429==y.getStatus()&&la.y||a.y>=1<, string=)=} opt_callback * An optional callback. If not supplied, the call is made synchronously. - * @return {?Array} The list of writable folders. - * Null if a callback is specified. + * @return {?Array} The list of top-level assets and + * folders. Null if a callback is specified. * @export */ ee.data.getAssetRoots = function(opt_callback) { @@ -1683,13 +1689,14 @@ ee.data.getAssetRoots = function(opt_callback) { /** * Attempts to create a home root folder (e.g. "users/joe") for the current - * user. This results in an error if the user already has a home root folder or - * the requested ID is unavailable. + * user. This results in an error if the user already has a home root folder + * or the requested ID is unavailable. * * @param {string} requestedId The requested ID of the home folder * (e.g. "users/joe"). - * @param {function(?Array, string=)=} opt_callback - * An optional callback. If not supplied, the call is made synchronously. + * @param {function(?Array, string=)=} + * opt_callback An optional callback. If not supplied, the call is made + * synchronously. * @export */ ee.data.createAssetHome = function(requestedId, opt_callback) { diff --git a/python/ee/__init__.py b/python/ee/__init__.py index a3f97916f..4db575c69 100644 --- a/python/ee/__init__.py +++ b/python/ee/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """The EE Python library.""" -__version__ = '0.1.376' +__version__ = '0.1.377' # Using lowercase function naming to match the JavaScript names. # pylint: disable=g-bad-name @@ -13,6 +13,7 @@ import os from typing import Any, Hashable, List as ListType, Optional, Sequence, Type, Union +from ee import _cloud_api_utils from ee import batch from ee import data from ee import deserializer @@ -110,7 +111,7 @@ def Initialize( credentials: Optional[Any] = 'persistent', opt_url: Optional[str] = None, cloud_api_key: Optional[str] = None, - http_transport: Optional[Any] = None, + http_transport: Optional[_cloud_api_utils.HttpTransportable] = None, project: Optional[Union[str, int]] = None, ) -> None: """Initialize the EE library. diff --git a/python/ee/_cloud_api_utils.py b/python/ee/_cloud_api_utils.py index 1587c055b..b54aef1e9 100644 --- a/python/ee/_cloud_api_utils.py +++ b/python/ee/_cloud_api_utils.py @@ -12,9 +12,10 @@ import json import os import re -from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, Union +from typing import Any, Callable, Dict, List, Optional, Protocol, Sequence, Tuple, Type, Union import warnings +from google import auth import google_auth_httplib2 from googleapiclient import discovery from googleapiclient import http @@ -39,9 +40,24 @@ _cloud_api_user_project: Optional[str] = None +class HttpTransportable(Protocol): + """A protocol for HTTP transport objects.""" + + def request( # pylint: disable=invalid-name + self, + uri: str, + method: str, + body: Optional[str], + headers: Optional[Dict[str, str]], + redirections: Optional[int], + connection_type: Optional[Type[Any]], + ) -> Any: + """Make an HTTP request.""" + + class _Http: """A httplib2.Http-like object based on requests.""" - timeout: Optional[float] + _timeout: Optional[float] def __init__(self, timeout: Optional[float] = None): self._timeout = timeout @@ -128,13 +144,13 @@ def set_cloud_api_user_project(cloud_api_user_project: str) -> None: def build_cloud_resource( api_base_url: str, api_key: Optional[str] = None, - credentials: Optional[Any] = None, + credentials: Optional[auth.credentials.Credentials] = None, timeout: Optional[float] = None, headers_supplier: Optional[Callable[[], Dict[str, Any]]] = None, response_inspector: Optional[Callable[[Any], None]] = None, - http_transport: Optional[Any] = None, + http_transport: Optional[HttpTransportable] = None, raw: Optional[bool] = False, -) -> Any: +) -> discovery.Resource: """Builds an Earth Engine Cloud API resource. Args: diff --git a/python/ee/data.py b/python/ee/data.py index 12b4903ec..97da00879 100644 --- a/python/ee/data.py +++ b/python/ee/data.py @@ -59,7 +59,7 @@ _cloud_api_client_version: Optional[str] = None # The http_transport to use. -_http_transport = None +_http_transport: _cloud_api_utils.HttpTransportable = None # Whether the module has been initialized. _initialized: bool = False @@ -123,6 +123,7 @@ def __init__(self): # The default base URL for API calls. DEFAULT_API_BASE_URL = 'https://earthengine.googleapis.com/api' +HIGH_VOLUME_API_BASE_URL = 'https://earthengine-highvolume.googleapis.com' # The default base URL for media/tile calls. DEFAULT_TILE_BASE_URL = 'https://earthengine.googleapis.com' @@ -156,7 +157,7 @@ def initialize( cloud_api_base_url: Optional[str] = None, cloud_api_key: Optional[str] = None, project: Optional[str] = None, - http_transport: Any = None, + http_transport: Optional[_cloud_api_utils.HttpTransportable] = None, ) -> None: """Initializes the data module, setting credentials and base URLs. @@ -595,6 +596,19 @@ def listAssets(params: Dict[str, Any]) -> Dict[str, List[Any]]: def listBuckets(project: Optional[str] = None) -> Any: + """Returns top-level assets and folders for the Cloud Project or user. + + Args: + project: Project to query, e.g. "projects/my-project". Defaults to current + project. Use "projects/earthengine-legacy" for user home folders. + + Returns: + A dictionary with a list of top-level assets and folders like: + {"assets": [ + {"type": "FOLDER", "id": "projects/my-project/assets/my-folder", ...}, + {"type": "IMAGE", "id": "projects/my-project/assets/my-image", ...}, + ]} + """ if project is None: project = _get_projects_path() return _execute_cloud_call(_get_cloud_projects().listAssets(parent=project)) @@ -1887,16 +1901,18 @@ def startTableIngestion( def getAssetRoots() -> Any: - """Returns the list of the root folders the user owns. + """Returns a list of top-level assets and folders for the current project. - Note: The "id" values for roots are two levels deep, e.g. "users/johndoe" - not "users/johndoe/notaroot". + Note: The "id" values for Cloud Projects are + "projects/my-project/assets/my-asset", where legacy assets (if the + current project is set to "earthengine-legacy") are "users/my-username", + not "users/my-username/my-asset". Returns: - A list of folder descriptions formatted like: + The list of top-level assets and folders like: [ - {"type": "Folder", "id": "users/foo"}, - {"type": "Folder", "id": "projects/bar"}, + {"id": "users/foo", "type": "Folder", ...}, + {"id": "projects/bar", "type": "Folder", ...}, ] """ return _cloud_api_utils.convert_list_assets_result_to_get_list_result( diff --git a/python/ee/tests/algorithms.json b/python/ee/tests/algorithms.json index 64f7d99ff..f16508c90 100644 --- a/python/ee/tests/algorithms.json +++ b/python/ee/tests/algorithms.json @@ -3063,6 +3063,29 @@ "optional": true, "defaultValue": 0.0 }] + }, { + "name": "algorithms/Classifier.smileKNN", + "description": "Creates an empty kNN classifier.\nThe k-nearest neighbor algorithm (k-NN) is a method for classifying objects by a majority vote of its neighbors, with the object being assigned to the class most common amongst its k nearest neighbors (k is a positive integer, typically small, typically odd).", + "returnType": "Classifier", + "arguments": [{ + "argumentName": "k", + "type": "Integer", + "description": "The number of neighbors for classification.", + "optional": true, + "defaultValue": 1.0 + }, { + "argumentName": "searchMethod", + "type": "String", + "description": "Search method. The following are valid [AUTO, LINEAR_SEARCH, KD_TREE, COVER_TREE].\nAUTO Will choose between KD_TREE and COVER_TREE depending on the dimension count. Results may vary between the different search methods for distance ties and probability values. Since performance and results may vary consult with SMILE\u0027s documentation and other literature.", + "optional": true, + "defaultValue": "AUTO" + }, { + "argumentName": "metric", + "type": "String", + "description": "The distance metric to use. NOTE: KD_TREE (and AUTO for low dimensions) will not use the metric selected. Options are:\n \u0027EUCLIDEAN\u0027 - euclidean distance.\n \u0027MAHALANOBIS\u0027 - Mahalanobis distance.\n \u0027MANHATTAN\u0027 - Manhattan distance.", + "optional": true, + "defaultValue": "EUCLIDEAN" + }] }, { "name": "algorithms/Classifier.smileNaiveBayes", "description": "Creates an empty Naive Bayes classifier. This classifier assumes that the feature vector consists of positive integers, and negative inputs are discarded.", From 16871fffd4101906be3021c6bcb4649c4821351c Mon Sep 17 00:00:00 2001 From: Google Earth Engine Authors Date: Thu, 2 Nov 2023 22:11:34 +0000 Subject: [PATCH 4/9] v0.1.378 PiperOrigin-RevId: 578992235 --- javascript/build/ee_api_js.js | 4 +- javascript/build/ee_api_js_debug.js | 102 ++++++++++-------- javascript/build/ee_api_js_npm.js | 102 ++++++++++-------- javascript/package.json | 2 +- javascript/src/apiclient.js | 2 +- ...mposite.js => Sentinel2ClearComposites.js} | 57 +++++----- python/ee/__init__.py | 2 +- python/ee/cli/commands.py | 84 +++++++++++++++ python/ee/data.py | 44 ++++++++ python/ee/tests/data_test.py | 24 +++++ 10 files changed, 296 insertions(+), 127 deletions(-) rename javascript/src/examples/CloudMasking/{Sentinel2ClearComposite.js => Sentinel2ClearComposites.js} (53%) diff --git a/javascript/build/ee_api_js.js b/javascript/build/ee_api_js.js index b5f5ff5b3..158670e93 100644 --- a/javascript/build/ee_api_js.js +++ b/javascript/build/ee_api_js.js @@ -74,7 +74,7 @@ Gc.prototype[Symbol.iterator]=function(){return this.values()};Gc.prototype.lb=f function Yc(a){if(null==a)throw Error("Cannot deserialize, target constructor was null.");return new a} function Vc(a,b,c,d,e){e=d(e);for(var g=Zc(a,e),f=g.P||{},l=g.s||{},m=g.La||{},p=n(g.keys||[]),v=p.next(),y={};!v.done;y={Uc:y.Uc},v=p.next()){v=v.value;var C=b(v,a);if(null!=C){var Q=void 0;if(f.hasOwnProperty(v)){if(g.th&&0===C.length)continue;Q=$c(C,b,c,d,!0,!0,f[v])}else if(l.hasOwnProperty(v))Q=$c(C,b,c,d,!1,!0,l[v]);else if(m.hasOwnProperty(v))y.Uc=m[v],Q=y.Uc.oa?C.map(function(oa){return function(R){return ad(R,oa.Uc,b,c,d)}}(y)):ad(C,y.Uc,b,c,d);else if(Array.isArray(C)){if(g.th&&0===C.length)continue; Q=$c(C,b,c,d,!0,!1)}else Q=C instanceof Oc?null:C;c(v,e,Q)}}return e}function ad(a,b,c,d,e){for(var g={},f=n(Object.keys(a)),l=f.next();!l.done;l=f.next()){l=l.value;var m=a[l];null!=m&&(g[l]=$c(m,c,d,e,b.qa,b.pa,b.ja))}return g} -function $c(a,b,c,d,e,g,f){if(g&&null==f)throw Error("Cannot deserialize a reference object without a constructor.");return null==a?a:e&&g?a.map(function(l){return Vc(l,b,c,d,f)}):e&&!g?a.map(function(l){return l}):!e&&g?Vc(a,b,c,d,f):a instanceof Oc?null:"object"===typeof a?JSON.parse(JSON.stringify(a)):a}function Zc(a,b){if(b instanceof E)a=Qc(b.i());else if(a instanceof E)a=Qc(a.i());else throw Error("Cannot find ClassMetadata.");return a};var bd;var ed=function(a,b){this.pg=a===cd&&b||"";this.Ck=dd};ed.prototype.rc=!0;ed.prototype.qc=function(){return this.pg};ed.prototype.toString=function(){return"Const{"+this.pg+"}"};var fd=function(a){if(a instanceof ed&&a.constructor===ed&&a.Ck===dd)return a.pg;La("expected object of type Const, got '"+a+"'");return"type_error:Const"},dd={},cd={};var hd=function(a,b){if(b!==gd)throw Error("TrustedResourceUrl is not meant to be built directly");this.Uf=a};hd.prototype.toString=function(){return this.Uf+""};hd.prototype.rc=!0;hd.prototype.qc=function(){return this.Uf.toString()}; +function $c(a,b,c,d,e,g,f){if(g&&null==f)throw Error("Cannot deserialize a reference object without a constructor.");return null==a?a:e&&g?a.map(function(l){return Vc(l,b,c,d,f)}):e&&!g?a.map(function(l){return l}):!e&&g?Vc(a,b,c,d,f):a instanceof Oc?null:"object"===typeof a?JSON.parse(JSON.stringify(a)):a}function Zc(a,b){if(b instanceof E)a=Qc(b.i());else if(a instanceof E)a=Qc(a.i());else throw Error("Cannot find ClassMetadata.");return a};var bd;var ed=function(a,b){this.pg=a===cd&&b||"";this.Ck=dd};ed.prototype.rc=!0;ed.prototype.qc=function(){return this.pg};ed.prototype.toString=function(){return this.pg};var fd=function(a){if(a instanceof ed&&a.constructor===ed&&a.Ck===dd)return a.pg;La("expected object of type Const, got '"+a+"'");return"type_error:Const"},dd={},cd={};var hd=function(a,b){if(b!==gd)throw Error("TrustedResourceUrl is not meant to be built directly");this.Uf=a};hd.prototype.toString=function(){return this.Uf+""};hd.prototype.rc=!0;hd.prototype.qc=function(){return this.Uf.toString()}; var id=function(a){if(a instanceof hd&&a.constructor===hd)return a.Uf;La("expected object of type TrustedResourceUrl, got '%s' of type %s",a,ua(a));return"type_error:TrustedResourceUrl"},nd=function(a){var b=fd(jd);if(!kd.test(b))throw Error("Invalid TrustedResourceUrl format: "+b);var c=b.replace(ld,function(d,e){if(!Object.prototype.hasOwnProperty.call(a,e))throw Error('Found marker, "'+e+'", in format string, "'+b+'", but no valid label mapping found in args: '+JSON.stringify(a));d=a[e];return d instanceof ed?fd(d):encodeURIComponent(String(d))});return md(c)},ld=/%{(\w+)}/g,kd=RegExp("^((https:)?//[0-9a-z.:[\\]-]+/|/[^/\\\\]|[^:/\\\\%]+/|[^:/\\\\%]*[?#]|about:blank#)","i"),gd={},md=function(a){if(void 0===bd){var b=null;var c=r.trustedTypes;if(c&&c.createPolicy)try{b=c.createPolicy("goog#html",{createHTML:Ea,createScript:Ea,createScriptURL:Ea})}catch(d){r.console&&r.console.error(d.message)}bd=b}a=(b=bd)?b.createScriptURL(a):a;return new hd(a,gd)};var pd=function(a,b){if(b!==od)throw Error("SafeUrl is not meant to be built directly");this.Tf=a};pd.prototype.toString=function(){return this.Tf.toString()};pd.prototype.rc=!0;pd.prototype.qc=function(){return this.Tf.toString()};var qd=function(a){if(a instanceof pd&&a.constructor===pd)return a.Tf;La("expected object of type SafeUrl, got '"+a+"' of type "+ua(a));return"type_error:SafeUrl"},od={},rd=new pd("about:invalid#zClosurez",od);new pd("about:blank",od);var sd={},td=function(){if(sd!==sd)throw Error("SafeStyle is not meant to be built directly");this.pi="";this.rc=!0};td.prototype.qc=function(){return this.pi};td.prototype.toString=function(){return this.pi.toString()};new td;var ud={},vd=function(){if(ud!==ud)throw Error("SafeStyleSheet is not meant to be built directly");this.oi="";this.rc=!0};vd.prototype.toString=function(){return this.oi.toString()};vd.prototype.qc=function(){return this.oi};new vd;var wd={},xd=function(){var a=r.trustedTypes&&r.trustedTypes.emptyHTML||"";if(wd!==wd)throw Error("SafeHtml is not meant to be built directly");this.ni=a;this.rc=!0};xd.prototype.qc=function(){return this.ni.toString()};xd.prototype.toString=function(){return this.ni.toString()};new xd;var zd=function(a,b){if(!t(a)||!t(a)||!t(a)||1!==a.nodeType||a.namespaceURI&&"http://www.w3.org/1999/xhtml"!==a.namespaceURI||a.tagName.toUpperCase()!=="SCRIPT".toString()){var c="SCRIPT".toString()+"; got: ";if(t(a))try{var d=a.constructor.displayName||a.constructor.name||Object.prototype.toString.call(a)}catch(e){d=""}else d=void 0===a?"undefined":null===a?"null":typeof a;La("Argument is not an HTML Element with tag name "+(c+d))}a:{c=(a.ownerDocument&&a.ownerDocument.defaultView|| r).document;if(c.querySelector&&(c=c.querySelector("script[nonce]"))&&(c=c.nonce||c.getAttribute("nonce"))&&yd.test(c))break a;c=""}c&&a.setAttribute("nonce",c);a.src=id(b)},yd=/^[\w+/_-]+[=]{0,2}$/;var Ad=function(a,b){for(var c=a.split("%s"),d="",e=Array.prototype.slice.call(arguments,1);e.length&&1b.length)){var d=b[0].match(/\r\nContent-ID: ]*)>/)[1],e=Number(b[1].match(/^HTTP\S*\s(\d+)\s/)[1]);c(d,e,b.slice(2).join("\r\n\r\n"))}},sj=function(){var a=Aj.replace(/\/api$/,"");return"window"in r&&!a.match(/^https?:\/\/content-/)?a.replace(/^(https?:\/\/)(.*\.googleapis\.com)$/,"$1content-$2"):a},Cj=function(a,b,c){var d=[];a&&(d=d.concat(Bj)); b&&d.push("https://www.googleapis.com/auth/devstorage.read_write");a=d=d.concat(c);c=b=0;for(var e={};cg)break;y++}return Xj(C.status,function(Q){try{return C.getResponseHeader(Q)}catch(oa){return null}},C.responseText,l,void 0,e,d,f)},Vj=function(a,b,c,d,e,g,f){var l=0,m={url:a,method:c,content:d,headers:e},p=Pj,v=null!=g?g:10; m.callback=function(y){y=y.target;if(429==y.getStatus()&&l") + 1, serializedNewTree.lastIndexOf("") + 1, serializedNewTree.lastIndexOf(" None: + # pylint: disable=protected-access + original = ee._cloud_api_utils.VERSION + ee._cloud_api_utils.VERSION = 'v1alpha' + func(self, args, config) + ee._cloud_api_utils.VERSION = original + # pylint: enable=protected-access + + return inner + + +class ProjectConfigGetCommand: + """Prints the current project's ProjectConfig.""" + + name = 'get' + + def __init__(self, parser: argparse.ArgumentParser): + del parser # Unused. + pass + + @_using_v1alpha + def run( + self, args: argparse.Namespace, config: utils.CommandLineConfig + ) -> None: + del args # Unused. + config.ee_init() + print(ee.data.getProjectConfig()) + + +class ProjectConfigSetCommand: + """Updates the current project's ProjectConfig.""" + + name = 'set' + + def __init__(self, parser: argparse.ArgumentParser): + parser.add_argument( + '--max_concurrent_exports', + type=int, + help='Max concurrent exports that can run in the project.', + ) + + @_using_v1alpha + def run( + self, args: argparse.Namespace, config: utils.CommandLineConfig + ) -> None: + if args.max_concurrent_exports < 0: + raise ValueError('"max_concurrent_exports" must be >= 0.') + + config.ee_init() + print( + ee.data.updateProjectConfig( + {'maxConcurrentExports': args.max_concurrent_exports}, + ['max_concurrent_exports'], + ) + ) + + +class ProjectConfigCommand(Dispatcher): + """Prints or updates the current project's ProjectConfig.""" + + name = 'project_config' + + COMMANDS = [ + ProjectConfigGetCommand, + ProjectConfigSetCommand, + ] + + +class AlphaCommand(Dispatcher): + """Commands that are part of the v1alpha API.""" + + name = 'alpha' + + COMMANDS = [ + ProjectConfigCommand, + ] + + EXTERNAL_COMMANDS = [ AuthenticateCommand, AclCommand, @@ -1916,6 +1999,7 @@ class ModelCommand(Dispatcher): CopyCommand, CreateCommand, ListCommand, + AlphaCommand, SizeCommand, MoveCommand, ModelCommand, diff --git a/python/ee/data.py b/python/ee/data.py index 97da00879..43f94b377 100644 --- a/python/ee/data.py +++ b/python/ee/data.py @@ -2093,6 +2093,50 @@ def createAssetHome(requestedId: str) -> None: }) +def _get_config_path() -> str: + return f'{_get_projects_path()}/config' + + +def getProjectConfig() -> Dict[str, Any]: + """Gets the project config for the current project. + + Returns: + The project config as a dictionary. + """ + return _execute_cloud_call( + _get_cloud_projects().getConfig(name=_get_config_path()) + ) + + +def updateProjectConfig( + project_config: Dict[str, Any], update_mask: Optional[Sequence[str]] = None +) -> Dict[str, Any]: + """Updates the project config for the current project. + + Args: + project_config: The new project config as a dictionary. + update_mask: A list of the values to update. The only supported values right + now are: "max_concurrent_exports". If the list is empty or None, all + values will be updated. + + Returns: + The updated project config as a dictionary. + """ + if not update_mask: + update_mask = ['max_concurrent_exports'] + + update_mask = ','.join(update_mask) + if update_mask != 'max_concurrent_exports': + raise ValueError('Only "max_concurrent_exports" is supported right now.') + + config = _get_config_path() + return _execute_cloud_call( + _get_cloud_projects().updateConfig( + name=config, body=project_config, updateMask=update_mask + ) + ) + + def authorizeHttp(http: Any) -> Any: if _credentials: return google_auth_httplib2.AuthorizedHttp(_credentials) diff --git a/python/ee/tests/data_test.py b/python/ee/tests/data_test.py index f855c2214..4aba82417 100644 --- a/python/ee/tests/data_test.py +++ b/python/ee/tests/data_test.py @@ -518,6 +518,30 @@ def testGetFeatureViewTilesKey(self): f'base_url/{_cloud_api_utils.VERSION}/{mock_name}/tiles/7/5/6', actual_result['formatTileUrl'](5, 6, 7)) + def testGetProjectConfig(self) -> None: + cloud_api_resource = mock.MagicMock() + with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource): + mock_result = {'fake-project-config-value': 1} + cloud_api_resource.projects().getConfig().execute.return_value = ( + mock_result + ) + actual_result = ee.data.getProjectConfig() + cloud_api_resource.projects().getConfig().execute.assert_called_once() + self.assertEqual(mock_result, actual_result) + + def testUpdateProjectConfig(self) -> None: + cloud_api_resource = mock.MagicMock() + with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource): + mock_result = {'fake-project-config-value': 1} + cloud_api_resource.projects().updateConfig().execute.return_value = ( + mock_result + ) + actual_result = ee.data.updateProjectConfig( + {'maxConcurrentExports': 2}, ['max_concurrent_exports'] + ) + cloud_api_resource.projects().updateConfig().execute.assert_called_once() + self.assertEqual(mock_result, actual_result) + def testWorkloadTag(self): self.assertEqual('', ee.data.getWorkloadTag()) ee.data.setDefaultWorkloadTag(None) From 7211f0e7a4e33dda86f65940feb8313270cd6cda Mon Sep 17 00:00:00 2001 From: Google Earth Engine Authors Date: Fri, 10 Nov 2023 00:38:11 +0000 Subject: [PATCH 5/9] v0.1.379 PiperOrigin-RevId: 581060984 --- javascript/build/ee_api_js.js | 1294 +- javascript/build/ee_api_js_debug.js | 14406 +++++++-------- javascript/build/ee_api_js_npm.js | 14450 ++++++++-------- javascript/package.json | 2 +- javascript/src/apiclient.js | 9 +- javascript/src/ee.js | 3 +- javascript/src/eeapiclient/api_client.ts | 1 + .../src/eeapiclient/api_request_hook.ts | 1 + javascript/src/eeapiclient/domain_object.ts | 1 + javascript/src/eeapiclient/generated_types.ts | 1 + .../src/eeapiclient/multipart_request.ts | 1 + .../src/eeapiclient/promise_api_client.ts | 1 + .../eeapiclient/promise_request_service.ts | 1 + javascript/src/eeapiclient/request_params.ts | 1 + .../src/eeapiclient/request_params_test.ts | 1 + .../CloudMasking/Sentinel2ClearComposites.js | 58 - .../CloudMasking/Sentinel2CloudScorePlus.js | 35 + python/ee/__init__.py | 9 +- python/ee/_cloud_api_utils.py | 55 +- python/ee/_helpers.py | 3 +- python/ee/collection.py | 2 +- python/ee/data.py | 29 +- python/ee/deserializer.py | 5 +- python/ee/ee_number.py | 7 +- python/ee/ee_types.py | 5 +- python/ee/geometry.py | 19 +- python/ee/image.py | 2 +- python/ee/serializer.py | 7 +- python/ee/terrain.py | 10 +- python/ee/tests/algorithms.json | 68 +- 30 files changed, 15266 insertions(+), 15221 deletions(-) delete mode 100644 javascript/src/examples/CloudMasking/Sentinel2ClearComposites.js create mode 100644 javascript/src/examples/CloudMasking/Sentinel2CloudScorePlus.js diff --git a/javascript/build/ee_api_js.js b/javascript/build/ee_api_js.js index 158670e93..d270aa3f8 100644 --- a/javascript/build/ee_api_js.js +++ b/javascript/build/ee_api_js.js @@ -1,479 +1,486 @@ (function(){var h,aa=function(a){var b=0;return function(){return b>>0)+"_",d=0,e=function(g){if(this instanceof e)throw new TypeError("Symbol is not a constructor");return new b(c+(g||"")+"_"+d++,g)};return e}); +da("Symbol",function(a){if(a)return a;var b=function(g,f){this.Xi=g;ba(this,"description",{configurable:!0,writable:!0,value:f})};b.prototype.toString=function(){return this.Xi};var c="jscomp_symbol_"+(1E9*Math.random()>>>0)+"_",d=0,e=function(g){if(this instanceof e)throw new TypeError("Symbol is not a constructor");return new b(c+(g||"")+"_"+d++,g)};return e}); da("Symbol.iterator",function(a){if(a)return a;a=Symbol("Symbol.iterator");for(var b="Array Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array".split(" "),c=0;cb||1342177279>>=1)c+=c;return d}}); -var sa=function(a,b){a instanceof String&&(a+="");var c=0,d=!1,e={next:function(){if(!d&&c=e}});da("String.prototype.startsWith",function(a){return a?a:function(b,c){var d=ra(this,b,"startsWith");b+="";var e=d.length,g=b.length;c=Math.max(0,Math.min(c|0,d.length));for(var f=0;f=g}}); -da("Object.entries",function(a){return a?a:function(b){var c=[],d;for(d in b)ha(b,d)&&c.push([d,b[d]]);return c}});da("Object.is",function(a){return a?a:function(b,c){return b===c?0!==b||1/b===1/c:b!==b&&c!==c}});da("Array.prototype.includes",function(a){return a?a:function(b,c){var d=this;d instanceof String&&(d=String(d));var e=d.length;c=c||0;for(0>c&&(c=Math.max(c+e,0));cb||1342177279>>=1)c+=c;return d}}); +var ua=function(a,b){a instanceof String&&(a+="");var c=0,d=!1,e={next:function(){if(!d&&c=e}});da("String.prototype.startsWith",function(a){return a?a:function(b,c){var d=ta(this,b,"startsWith");b+="";var e=d.length,g=b.length;c=Math.max(0,Math.min(c|0,d.length));for(var f=0;f=g}}); +da("Object.entries",function(a){return a?a:function(b){var c=[],d;for(d in b)ja(b,d)&&c.push([d,b[d]]);return c}});da("Object.is",function(a){return a?a:function(b,c){return b===c?0!==b||1/b===1/c:b!==b&&c!==c}});da("Array.prototype.includes",function(a){return a?a:function(b,c){var d=this;d instanceof String&&(d=String(d));var e=d.length;c=c||0;for(0>c&&(c=Math.max(c+e,0));c>>0),ya=0,Ba=function(a,b,c){return a.call.apply(a.bind, -arguments)},Ca=function(a,b,c){if(!a)throw Error();if(2c&&(c=Math.max(0,a.length+c));if("string"===typeof a)return"string"!==typeof b|| -1!=b.length?-1:a.lastIndexOf(b,c);for(;0<=c;c--)if(c in a&&a[c]===b)return c;return-1},Sa=Array.prototype.forEach?function(a,b){B(null!=a.length);Array.prototype.forEach.call(a,b,void 0)}:function(a,b){for(var c=a.length,d="string"===typeof a?a.split(""):a,e=0;e=arguments.length?Array.prototype.slice.call(a,b):Array.prototype.slice.call(a,b,c)} -function db(a){var b=[];if(0>a-0)return[];for(var c=0;c>>0),ac=function(a){B(a,"Listener can not be null.");if("function"===typeof a)return a;B(a.handleEvent,"An object listener must have handleEvent method.");a[ic]||(a[ic]=function(b){return a.handleEvent(b)}); -return a[ic]};var D=function(){z.call(this);this.Ha=new Tb(this);this.Rk=this;this.Sf=null};x(D,z);D.prototype[yb]=!0;h=D.prototype;h.addEventListener=function(a,b,c,d){$b(this,a,b,c,d)};h.removeEventListener=function(a,b,c,d){gc(this,a,b,c,d)}; -h.dispatchEvent=function(a){jc(this);var b=this.Sf;if(b){var c=[];for(var d=1;b;b=b.Sf)c.push(b),B(1E3>++d,"infinite loop")}b=this.Rk;d=a.type||a;if("string"===typeof a)a=new A(a,b);else if(a instanceof A)a.target=a.target||b;else{var e=a;a=new A(d,b);Sb(a,e)}e=!0;if(c)for(var g=c.length-1;!a.ad&&0<=g;g--){var f=a.currentTarget=c[g];e=kc(f,d,!0,a)&&e}a.ad||(f=a.currentTarget=b,e=kc(f,d,!0,a)&&e,a.ad||(e=kc(f,d,!1,a)&&e));if(c)for(g=0;!a.ad&&g=a.length)return qc;if(b in a)return{value:a[b++],done:!1};b++}};return c}throw Error("Not implemented");},sc=function(a,b){if(va(a))Sa(a,b);else for(a=rc(a);;){var c=a.next();if(c.done)break;b.call(void 0,c.value,void 0,a)}};var wc=function(a){if(a instanceof tc||a instanceof uc||a instanceof vc)return a;if("function"==typeof a.next)return new tc(function(){return a});if("function"==typeof a[Symbol.iterator])return new tc(function(){return a[Symbol.iterator]()});if("function"==typeof a.Ca)return new tc(function(){return a.Ca()});throw Error("Not an iterator or iterable.");},tc=function(a){this.sf=a};tc.prototype.Ca=function(){return new uc(this.sf())};tc.prototype[Symbol.iterator]=function(){return new vc(this.sf())}; -tc.prototype.yg=function(){return new vc(this.sf())};var uc=function(a){this.Qc=a};q(uc,pc);uc.prototype.next=function(){return this.Qc.next()};uc.prototype[Symbol.iterator]=function(){return new vc(this.Qc)};uc.prototype.yg=function(){return new vc(this.Qc)};var vc=function(a){tc.call(this,function(){return a});this.Qc=a};q(vc,tc);vc.prototype.next=function(){return this.Qc.next()};var xc=function(a,b){this.A={};this.K=[];this.md=this.size=0;var c=arguments.length;if(12*this.size&&yc(this),!0):!1};var yc=function(a){if(a.size!=a.K.length){for(var b=0,c=0;b=d.K.length)return qc;var g=d.K[b++];return{value:a?g:d.A[g],done:!1}};return e};h.lb=function(a){this.size=a};var zc=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)};var Bc=function(a){if(a.R&&"function"==typeof a.R)a=a.R();else if(va(a)||"string"===typeof a)a=a.length;else{var b=0,c;for(c in a)b++;a=b}return a},Cc=function(a){if(a.Z&&"function"==typeof a.Z)return a.Z();if("undefined"!==typeof Map&&a instanceof Map||"undefined"!==typeof Set&&a instanceof Set)return Array.from(a.values());if("string"===typeof a)return a.split("");if(va(a)){for(var b=[],c=a.length,d=0;dc)return!1;!(b instanceof Gc)&&5e&&(e+=d);return[a.ol,c,e,b.y].join("/")};Lc.prototype.Lc=function(){return this.zb.length};var Nc=function(a){A.call(this,"tileevent");this.count=a};x(Nc,A);var Oc=function(){},Pc=new Oc;function Qc(a){return Object.assign({},{P:{},Ql:{},keys:[],La:{},s:{},H:{},th:!1},a)}var E=function(){this.h={}},F=function(a,b){return a.h.hasOwnProperty(b)?a.h[b]:null},G=function(a,b){return null!=a.h[b]};function Rc(a,b){return F(b,a)}function Sc(a,b,c){b[a]=c}function Tc(){return{}}function Uc(a,b){var c=new a;return null==b?c:Vc(b,Wc,Xc,Yc,a)}function Wc(a,b){return b[a]}function Xc(a,b,c){b.h[a]=c} -function Yc(a){if(null==a)throw Error("Cannot deserialize, target constructor was null.");return new a} -function Vc(a,b,c,d,e){e=d(e);for(var g=Zc(a,e),f=g.P||{},l=g.s||{},m=g.La||{},p=n(g.keys||[]),v=p.next(),y={};!v.done;y={Uc:y.Uc},v=p.next()){v=v.value;var C=b(v,a);if(null!=C){var Q=void 0;if(f.hasOwnProperty(v)){if(g.th&&0===C.length)continue;Q=$c(C,b,c,d,!0,!0,f[v])}else if(l.hasOwnProperty(v))Q=$c(C,b,c,d,!1,!0,l[v]);else if(m.hasOwnProperty(v))y.Uc=m[v],Q=y.Uc.oa?C.map(function(oa){return function(R){return ad(R,oa.Uc,b,c,d)}}(y)):ad(C,y.Uc,b,c,d);else if(Array.isArray(C)){if(g.th&&0===C.length)continue; -Q=$c(C,b,c,d,!0,!1)}else Q=C instanceof Oc?null:C;c(v,e,Q)}}return e}function ad(a,b,c,d,e){for(var g={},f=n(Object.keys(a)),l=f.next();!l.done;l=f.next()){l=l.value;var m=a[l];null!=m&&(g[l]=$c(m,c,d,e,b.qa,b.pa,b.ja))}return g} -function $c(a,b,c,d,e,g,f){if(g&&null==f)throw Error("Cannot deserialize a reference object without a constructor.");return null==a?a:e&&g?a.map(function(l){return Vc(l,b,c,d,f)}):e&&!g?a.map(function(l){return l}):!e&&g?Vc(a,b,c,d,f):a instanceof Oc?null:"object"===typeof a?JSON.parse(JSON.stringify(a)):a}function Zc(a,b){if(b instanceof E)a=Qc(b.i());else if(a instanceof E)a=Qc(a.i());else throw Error("Cannot find ClassMetadata.");return a};var bd;var ed=function(a,b){this.pg=a===cd&&b||"";this.Ck=dd};ed.prototype.rc=!0;ed.prototype.qc=function(){return this.pg};ed.prototype.toString=function(){return this.pg};var fd=function(a){if(a instanceof ed&&a.constructor===ed&&a.Ck===dd)return a.pg;La("expected object of type Const, got '"+a+"'");return"type_error:Const"},dd={},cd={};var hd=function(a,b){if(b!==gd)throw Error("TrustedResourceUrl is not meant to be built directly");this.Uf=a};hd.prototype.toString=function(){return this.Uf+""};hd.prototype.rc=!0;hd.prototype.qc=function(){return this.Uf.toString()}; -var id=function(a){if(a instanceof hd&&a.constructor===hd)return a.Uf;La("expected object of type TrustedResourceUrl, got '%s' of type %s",a,ua(a));return"type_error:TrustedResourceUrl"},nd=function(a){var b=fd(jd);if(!kd.test(b))throw Error("Invalid TrustedResourceUrl format: "+b);var c=b.replace(ld,function(d,e){if(!Object.prototype.hasOwnProperty.call(a,e))throw Error('Found marker, "'+e+'", in format string, "'+b+'", but no valid label mapping found in args: '+JSON.stringify(a));d=a[e];return d instanceof -ed?fd(d):encodeURIComponent(String(d))});return md(c)},ld=/%{(\w+)}/g,kd=RegExp("^((https:)?//[0-9a-z.:[\\]-]+/|/[^/\\\\]|[^:/\\\\%]+/|[^:/\\\\%]*[?#]|about:blank#)","i"),gd={},md=function(a){if(void 0===bd){var b=null;var c=r.trustedTypes;if(c&&c.createPolicy)try{b=c.createPolicy("goog#html",{createHTML:Ea,createScript:Ea,createScriptURL:Ea})}catch(d){r.console&&r.console.error(d.message)}bd=b}a=(b=bd)?b.createScriptURL(a):a;return new hd(a,gd)};var pd=function(a,b){if(b!==od)throw Error("SafeUrl is not meant to be built directly");this.Tf=a};pd.prototype.toString=function(){return this.Tf.toString()};pd.prototype.rc=!0;pd.prototype.qc=function(){return this.Tf.toString()};var qd=function(a){if(a instanceof pd&&a.constructor===pd)return a.Tf;La("expected object of type SafeUrl, got '"+a+"' of type "+ua(a));return"type_error:SafeUrl"},od={},rd=new pd("about:invalid#zClosurez",od);new pd("about:blank",od);var sd={},td=function(){if(sd!==sd)throw Error("SafeStyle is not meant to be built directly");this.pi="";this.rc=!0};td.prototype.qc=function(){return this.pi};td.prototype.toString=function(){return this.pi.toString()};new td;var ud={},vd=function(){if(ud!==ud)throw Error("SafeStyleSheet is not meant to be built directly");this.oi="";this.rc=!0};vd.prototype.toString=function(){return this.oi.toString()};vd.prototype.qc=function(){return this.oi};new vd;var wd={},xd=function(){var a=r.trustedTypes&&r.trustedTypes.emptyHTML||"";if(wd!==wd)throw Error("SafeHtml is not meant to be built directly");this.ni=a;this.rc=!0};xd.prototype.qc=function(){return this.ni.toString()};xd.prototype.toString=function(){return this.ni.toString()};new xd;var zd=function(a,b){if(!t(a)||!t(a)||!t(a)||1!==a.nodeType||a.namespaceURI&&"http://www.w3.org/1999/xhtml"!==a.namespaceURI||a.tagName.toUpperCase()!=="SCRIPT".toString()){var c="SCRIPT".toString()+"; got: ";if(t(a))try{var d=a.constructor.displayName||a.constructor.name||Object.prototype.toString.call(a)}catch(e){d=""}else d=void 0===a?"undefined":null===a?"null":typeof a;La("Argument is not an HTML Element with tag name "+(c+d))}a:{c=(a.ownerDocument&&a.ownerDocument.defaultView|| -r).document;if(c.querySelector&&(c=c.querySelector("script[nonce]"))&&(c=c.nonce||c.getAttribute("nonce"))&&yd.test(c))break a;c=""}c&&a.setAttribute("nonce",c);a.src=id(b)},yd=/^[\w+/_-]+[=]{0,2}$/;var Ad=function(a,b){for(var c=a.split("%s"),d="",e=Array.prototype.slice.call(arguments,1);e.length&&1b)throw Error("Bad port number "+b);a.wc=b}else a.wc=null};Ed.prototype.getPath=function(){return this.Yb};Ed.prototype.setPath=function(a,b){Gd(this);this.Yb=b?Jd(a,!0):a;return this};var Id=function(a,b,c){Gd(a);b instanceof Kd?(a.Aa=b,a.Aa.hg(a.ya)):(c||(b=Ld(b,Qd)),a.Aa=new Kd(b,a.ya))};Ed.prototype.getQuery=function(){return this.Aa.toString()}; -Ed.prototype.removeParameter=function(a){Gd(this);this.Aa.remove(a);return this};var Gd=function(a){if(a.jl)throw Error("Tried to modify a read-only Uri");};Ed.prototype.hg=function(a){this.ya=a;this.Aa&&this.Aa.hg(a)}; -var Rd=function(a){return a instanceof Ed?a.clone():new Ed(a)},Jd=function(a,b){return a?b?decodeURI(a.replace(/%25/g,"%2525")):decodeURIComponent(a):""},Ld=function(a,b,c){return"string"===typeof a?(a=encodeURI(a).replace(b,Sd),c&&(a=a.replace(/%25([0-9a-fA-F]{2})/g,"%$1")),a):null},Sd=function(a){a=a.charCodeAt(0);return"%"+(a>>4&15).toString(16)+(a&15).toString(16)},Md=/[#\/\?@]/g,Od=/[#\?:]/g,Nd=/[#\?]/g,Qd=/[#\?@]/g,Pd=/#/g,Kd=function(a,b){this.X=this.N=null;this.sa=a||null;this.ya=!!b},Td= -function(a){a.N||(a.N=new Map,a.X=0,a.sa&&Dd(a.sa,function(b,c){a.add(decodeURIComponent(b.replace(/\+/g," ")),c)}))};h=Kd.prototype;h.R=function(){Td(this);return this.X};h.add=function(a,b){Td(this);this.sa=null;a=Ud(this,a);var c=this.N.get(a);c||this.N.set(a,c=[]);c.push(b);this.X=Ma(this.X)+1;return this};h.remove=function(a){Td(this);a=Ud(this,a);return this.N.has(a)?(this.sa=null,this.X=Ma(this.X)-this.N.get(a).length,this.N.delete(a)):!1};h.clear=function(){this.N=this.sa=null;this.X=0}; -h.isEmpty=function(){Td(this);return 0==this.X};h.Ob=function(a){Td(this);a=Ud(this,a);return this.N.has(a)};h.Gc=function(a){var b=this.Z();return Ya(b,a)};h.forEach=function(a,b){Td(this);this.N.forEach(function(c,d){c.forEach(function(e){a.call(b,e,d,this)},this)},this)};h.sb=function(){Td(this);for(var a=Array.from(this.N.values()),b=Array.from(this.N.keys()),c=[],d=0;d