diff --git a/javascript/build/ee_api_js.js b/javascript/build/ee_api_js.js index 80b4a0aec..76fea2747 100644 --- a/javascript/build/ee_api_js.js +++ b/javascript/build/ee_api_js.js @@ -7,11 +7,11 @@ b)},ma="function"==typeof Object.assign?Object.assign:function(a,b){for(var c=1; if("function"==typeof Object.setPrototypeOf)oa=Object.setPrototypeOf;else{var pa;a:{var qa={a:!0},ra={};try{ra.__proto__=qa;pa=ra.a;break a}catch(a){}pa=!1}oa=pa?function(a,b){a.__proto__=b;if(a.__proto__!==b)throw new TypeError(a+" is not extensible");return a}:null} var sa=oa,q=function(a,b){a.prototype=na(b.prototype);a.prototype.constructor=a;if(sa)sa(a,b);else for(var c in b)if("prototype"!=c)if(Object.defineProperties){var d=Object.getOwnPropertyDescriptor(b,c);d&&Object.defineProperty(a,c,d)}else a[c]=b[c];a.L=b.prototype},ta=function(){for(var a=Number(this),b=[],c=a;c>>0),Ba=0,Da=function(a,b,c){return a.call.apply(a.bind, arguments)},Ea=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},Ua=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;ea-0)return[];for(var c=0;c>>0),cc=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[kc]||(a[kc]=function(b){return a.handleEvent(b)}); +a.Wc(b,c,t(d)?!!d.capture:!!d,e):dc(a,b,c,!0,d,e)},ic=function(a,b,c,d,e){if(Array.isArray(b))for(var g=0;g>>0),cc=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[kc]||(a[kc]=function(b){return a.handleEvent(b)}); return a[kc]};var D=function(){y.call(this);this.Ia=new Vb(this);this.Kk=this;this.Uf=null};x(D,y);D.prototype[Ab]=!0;h=D.prototype;h.addEventListener=function(a,b,c,d){bc(this,a,b,c,d)};h.removeEventListener=function(a,b,c,d){ic(this,a,b,c,d)}; -h.dispatchEvent=function(a){lc(this);var b=this.Uf;if(b){var c=[];for(var d=1;b;b=b.Uf)c.push(b),B(1E3>++d,"infinite loop")}b=this.Kk;d=a.type||a;if("string"===typeof a)a=new z(a,b);else if(a instanceof z)a.target=a.target||b;else{var e=a;a=new z(d,b);Ub(a,e)}e=!0;if(c)for(var g=c.length-1;!a.cd&&0<=g;g--){var f=a.currentTarget=c[g];e=mc(f,d,!0,a)&&e}a.cd||(f=a.currentTarget=b,e=mc(f,d,!0,a)&&e,a.cd||(e=mc(f,d,!1,a)&&e));if(c)for(g=0;!a.cd&&g++d,"infinite loop")}b=this.Kk;d=a.type||a;if("string"===typeof a)a=new z(a,b);else if(a instanceof z)a.target=a.target||b;else{var e=a;a=new z(d,b);Ub(a,e)}e=!0;if(c)for(var g=c.length-1;!a.ed&&0<=g;g--){var f=a.currentTarget=c[g];e=mc(f,d,!0,a)&&e}a.ed||(f=a.currentTarget=b,e=mc(f,d,!0,a)&&e,a.ed||(e=mc(f,d,!1,a)&&e));if(c)for(g=0;!a.ed&&g=a.length)return sc;if(b in a)return{value:a[b++],done:!1};b++}};return c}throw Error("Not implemented");},uc=function(a,b){if(ya(a))Ua(a,b);else for(a=tc(a);;){var c=a.next();if(c.done)break;b.call(void 0,c.value,void 0,a)}};var yc=function(a){if(a instanceof vc||a instanceof wc||a instanceof xc)return a;if("function"==typeof a.next)return new vc(function(){return a});if("function"==typeof a[Symbol.iterator])return new vc(function(){return a[Symbol.iterator]()});if("function"==typeof a.Da)return new vc(function(){return a.Da()});throw Error("Not an iterator or iterable.");},vc=function(a){this.uf=a};vc.prototype.Da=function(){return new wc(this.uf())};vc.prototype[Symbol.iterator]=function(){return new xc(this.uf())}; -vc.prototype.xg=function(){return new xc(this.uf())};var wc=function(a){this.Sc=a};q(wc,rc);wc.prototype.next=function(){return this.Sc.next()};wc.prototype[Symbol.iterator]=function(){return new xc(this.Sc)};wc.prototype.xg=function(){return new xc(this.Sc)};var xc=function(a){vc.call(this,function(){return a});this.Sc=a};q(xc,vc);xc.prototype.next=function(){return this.Sc.next()};var zc=function(a,b){this.A={};this.K=[];this.od=this.size=0;var c=arguments.length;if(12*this.size&&Ac(this),!0):!1};var Ac=function(a){if(a.size!=a.K.length){for(var b=0,c=0;b=d.K.length)return sc;var g=d.K[b++];return{value:a?g:d.A[g],done:!1}};return e};h.mb=function(a){this.size=a};var Bc=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)};var Dc=function(a){if(a.R&&"function"==typeof a.R)a=a.R();else if(ya(a)||"string"===typeof a)a=a.length;else{var b=0,c;for(c in a)b++;a=b}return a},Ec=function(a){if(a.aa&&"function"==typeof a.aa)return a.aa();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(ya(a)){for(var b=[],c=a.length,d=0;d2*this.size&&Ac(this),!0):!1};var Ac=function(a){if(a.size!=a.K.length){for(var b=0,c=0;b=d.K.length)return sc;var g=d.K[b++];return{value:a?g:d.A[g],done:!1}};return e};h.mb=function(a){this.size=a};var Bc=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)};var Dc=function(a){if(a.R&&"function"==typeof a.R)a=a.R();else if(ya(a)||"string"===typeof a)a=a.length;else{var b=0,c;for(c in a)b++;a=b}return a},Ec=function(a){if(a.aa&&"function"==typeof a.aa)return a.aa();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(ya(a)){for(var b=[],c=a.length,d=0;dc)return!1;!(b instanceof Ic)&&5e&&(e+=d);return[a.il,c,e,b.y].join("/")};Nc.prototype.Nc=function(){return this.Ab.length};var Pc=function(a){z.call(this,"tileevent");this.count=a};x(Pc,z);var Qc=function(){},Rc=new Qc;function Sc(a){return Object.assign({},{P:{},Nl:{},keys:[],Ma:{},s:{},H:{},sh:!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 Tc(a,b){return F(b,a)}function Uc(a,b,c){b[a]=c}function Vc(){return{}}function Wc(a,b){var c=new a;return null==b?c:Xc(b,Yc,Zc,$c,a)}function Yc(a,b){return b[a]}function Zc(a,b,c){b.h[a]=c} function $c(a){if(null==a)throw Error("Cannot deserialize, target constructor was null.");return new a} -function Xc(a,b,c,d,e){e=d(e);for(var g=ad(a,e),f=g.P||{},l=g.s||{},m=g.Ma||{},p=n(g.keys||[]),v=p.next(),A={};!v.done;A={Vd:void 0},v=p.next()){v=v.value;var C=b(v,a);if(null!=C){var J=void 0;if(f.hasOwnProperty(v)){if(g.sh&&0===C.length)continue;J=bd(C,b,c,d,!0,!0,f[v])}else if(l.hasOwnProperty(v))J=bd(C,b,c,d,!1,!0,l[v]);else if(m.hasOwnProperty(v))A.Vd=m[v],J=A.Vd.pa?C.map(function(fa){return function(X){return cd(X,fa.Vd,b,c,d)}}(A)):cd(C,A.Vd,b,c,d);else if(Array.isArray(C)){if(g.sh&&0===C.length)continue; +function Xc(a,b,c,d,e){e=d(e);for(var g=ad(a,e),f=g.P||{},l=g.s||{},m=g.Ma||{},p=n(g.keys||[]),v=p.next(),A={};!v.done;A={Wd:void 0},v=p.next()){v=v.value;var C=b(v,a);if(null!=C){var J=void 0;if(f.hasOwnProperty(v)){if(g.sh&&0===C.length)continue;J=bd(C,b,c,d,!0,!0,f[v])}else if(l.hasOwnProperty(v))J=bd(C,b,c,d,!1,!0,l[v]);else if(m.hasOwnProperty(v))A.Wd=m[v],J=A.Wd.pa?C.map(function(fa){return function(X){return cd(X,fa.Wd,b,c,d)}}(A)):cd(C,A.Wd,b,c,d);else if(Array.isArray(C)){if(g.sh&&0===C.length)continue; J=bd(C,b,c,d,!0,!1)}else J=C instanceof Qc?null:C;c(v,e,J)}}return e}function cd(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]=bd(m,c,d,e,b.ra,b.qa,b.ka))}return g} -function bd(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 Xc(l,b,c,d,f)}):e&&!g?a.map(function(l){return l}):!e&&g?Xc(a,b,c,d,f):a instanceof Qc?null:"object"===typeof a?JSON.parse(JSON.stringify(a)):a}function ad(a,b){if(b instanceof E)a=Sc(b.i());else if(a instanceof E)a=Sc(a.i());else throw Error("Cannot find ClassMetadata.");return a};var dd;var fd=function(a,b){if(b!==ed)throw Error("TrustedResourceUrl is not meant to be built directly");this.li=a};fd.prototype.toString=function(){return this.li+""}; -var gd=function(a){if(a instanceof fd&&a.constructor===fd)return a.li;Na("expected object of type TrustedResourceUrl, got '%s' of type %s",a,xa(a));return"type_error:TrustedResourceUrl"},ed={},hd=function(a){if(void 0===dd){var b=null;var c=r.trustedTypes;if(c&&c.createPolicy)try{b=c.createPolicy("goog#html",{createHTML:Ga,createScript:Ga,createScriptURL:Ga})}catch(d){r.console&&r.console.error(d.message)}dd=b}a=(b=dd)?b.createScriptURL(a):a;return new fd(a,ed)};/* +function bd(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 Xc(l,b,c,d,f)}):e&&!g?a.map(function(l){return l}):!e&&g?Xc(a,b,c,d,f):a instanceof Qc?null:"object"===typeof a?JSON.parse(JSON.stringify(a)):a}function ad(a,b){if(b instanceof E)a=Sc(b.i());else if(a instanceof E)a=Sc(a.i());else throw Error("Cannot find ClassMetadata.");return a};var dd;var fd=function(a,b){if(b!==ed)throw Error("TrustedResourceUrl is not meant to be built directly");this.ki=a};fd.prototype.toString=function(){return this.ki+""}; +var gd=function(a){if(a instanceof fd&&a.constructor===fd)return a.ki;Na("expected object of type TrustedResourceUrl, got '%s' of type %s",a,xa(a));return"type_error:TrustedResourceUrl"},ed={},hd=function(a){if(void 0===dd){var b=null;var c=r.trustedTypes;if(c&&c.createPolicy)try{b=c.createPolicy("goog#html",{createHTML:Ga,createScript:Ga,createScriptURL:Ga})}catch(d){r.console&&r.console.error(d.message)}dd=b}a=(b=dd)?b.createScriptURL(a):a;return new fd(a,ed)};/* SPDX-License-Identifier: Apache-2.0 */ -var id=ia([""]),jd=ja(["\x00"],["\\0"]),kd=ja(["\n"],["\\n"]),ld=ja(["\x00"],["\\u0000"]),md=ia([""]),nd=ja(["\x00"],["\\0"]),od=ja(["\n"],["\\n"]),pd=ja(["\x00"],["\\u0000"]);function qd(a){return Object.isFrozen(a)&&Object.isFrozen(a.raw)}function rd(a){return-1===a.toString().indexOf("`")}var sd=rd(function(a){return a(id)})||rd(function(a){return a(jd)})||rd(function(a){return a(kd)})||rd(function(a){return a(ld)}),td=qd(md)&&qd(nd)&&qd(od)&&qd(pd);var vd=function(a,b){if(b!==ud)throw Error("SafeUrl is not meant to be built directly");this.ki=a};vd.prototype.toString=function(){return this.ki.toString()};var wd=function(a){if(a instanceof vd&&a.constructor===vd)return a.ki;Na("expected object of type SafeUrl, got '"+a+"' of type "+xa(a));return"type_error:SafeUrl"},ud={},xd=function(a){return new vd(a,ud)};xd("about:invalid#zClosurez");xd("about:blank");xd("about:blank");var yd=xd("about:invalid#zClosurez");function zd(a){if("undefined"!==typeof MediaSource&&a instanceof MediaSource)return xd(URL.createObjectURL(a));var b=a.type.match(/^([^;]+)(?:;\w+=(?:\w+|"[\w;,= ]+"))*$/i);if(2!==(null==b?void 0:b.length)||!(/^image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp|x-icon|heic|heif|avif|x-ms-bmp)$/i.test(b[1])||/^video\/(?:mpeg|mp4|ogg|webm|x-matroska|quicktime|x-ms-wmv)$/i.test(b[1])||/^audio\/(?:3gpp2|3gpp|aac|amr|L16|midi|mp3|mp4|mpeg|oga|ogg|opus|x-m4a|x-matroska|x-wav|wav|webm)$/i.test(b[1])||/^font\/\w+/i.test(b[1])))throw Error("unsafe blob MIME type: "+ -a.type);return xd(URL.createObjectURL(a))}var Ad=[],Bd=function(a){console.warn("A URL with content '"+a+"' was sanitized away.")};-1===Ad.indexOf(Bd)&&Ad.push(Bd);var Cd={},Dd=function(){if(Cd!==Cd)throw Error("SafeStyle is not meant to be built directly");this.ql=""};Dd.prototype.toString=function(){return this.ql.toString()};new Dd;var Ed={},Fd=function(){if(Ed!==Ed)throw Error("SafeStyleSheet is not meant to be built directly");this.pl=""};Fd.prototype.toString=function(){return this.pl.toString()};new Fd;var Gd={},Hd=function(){var a=r.trustedTypes&&r.trustedTypes.emptyHTML||"";if(Gd!==Gd)throw Error("SafeHtml is not meant to be built directly");this.ol=a};Hd.prototype.toString=function(){return this.ol.toString()};new Hd;var Id=function(a,b){this.name=a;this.value=b};Id.prototype.toString=function(){return this.name};var Jd=new Id("OFF",Infinity),Kd=new Id("SEVERE",1E3),Ld=new Id("CONFIG",700),Md=new Id("FINE",500),Nd=function(){this.Bd=0;this.clear()},Od;Nd.prototype.clear=function(){this.yd=Array(this.Bd);this.kh=-1;this.Kh=!1};var Pd=function(a,b,c){this.reset(a||Jd,b,c,void 0,void 0)};Pd.prototype.reset=function(){}; +var id=ia([""]),jd=ja(["\x00"],["\\0"]),kd=ja(["\n"],["\\n"]),ld=ja(["\x00"],["\\u0000"]),md=ia([""]),nd=ja(["\x00"],["\\0"]),od=ja(["\n"],["\\n"]),pd=ja(["\x00"],["\\u0000"]);function qd(a){return Object.isFrozen(a)&&Object.isFrozen(a.raw)}function rd(a){return-1===a.toString().indexOf("`")}var sd=rd(function(a){return a(id)})||rd(function(a){return a(jd)})||rd(function(a){return a(kd)})||rd(function(a){return a(ld)}),td=qd(md)&&qd(nd)&&qd(od)&&qd(pd);var vd=function(a,b){if(b!==ud)throw Error("SafeUrl is not meant to be built directly");this.ji=a};vd.prototype.toString=function(){return this.ji.toString()};var wd=function(a){if(a instanceof vd&&a.constructor===vd)return a.ji;Na("expected object of type SafeUrl, got '"+a+"' of type "+xa(a));return"type_error:SafeUrl"},ud={},xd=function(a){return new vd(a,ud)};xd("about:invalid#zClosurez");xd("about:blank");xd("about:blank");var yd=xd("about:invalid#zClosurez");function zd(a){if("undefined"!==typeof MediaSource&&a instanceof MediaSource)return xd(URL.createObjectURL(a));var b=a.type.match(/^([^;]+)(?:;\w+=(?:\w+|"[\w;,= ]+"))*$/i);if(2!==(null==b?void 0:b.length)||!(/^image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp|x-icon|heic|heif|avif|x-ms-bmp)$/i.test(b[1])||/^video\/(?:mpeg|mp4|ogg|webm|x-matroska|quicktime|x-ms-wmv)$/i.test(b[1])||/^audio\/(?:3gpp2|3gpp|aac|amr|L16|midi|mp3|mp4|mpeg|oga|ogg|opus|x-m4a|x-matroska|x-wav|wav|webm)$/i.test(b[1])||/^font\/\w+/i.test(b[1])))throw Error("unsafe blob MIME type: "+ +a.type);return xd(URL.createObjectURL(a))}var Ad=[],Bd=function(a){console.warn("A URL with content '"+a+"' was sanitized away.")};-1===Ad.indexOf(Bd)&&Ad.push(Bd);var Cd={},Dd=function(){if(Cd!==Cd)throw Error("SafeStyle is not meant to be built directly");this.ql=""};Dd.prototype.toString=function(){return this.ql.toString()};new Dd;var Ed={},Fd=function(){if(Ed!==Ed)throw Error("SafeStyleSheet is not meant to be built directly");this.pl=""};Fd.prototype.toString=function(){return this.pl.toString()};new Fd;var Gd={},Hd=function(){var a=r.trustedTypes&&r.trustedTypes.emptyHTML||"";if(Gd!==Gd)throw Error("SafeHtml is not meant to be built directly");this.ol=a};Hd.prototype.toString=function(){return this.ol.toString()};new Hd;var Id=function(a,b){this.name=a;this.value=b};Id.prototype.toString=function(){return this.name};var Jd=new Id("OFF",Infinity),Kd=new Id("SEVERE",1E3),Ld=new Id("CONFIG",700),Md=new Id("FINE",500),Nd=function(){this.Dd=0;this.clear()},Od;Nd.prototype.clear=function(){this.Ad=Array(this.Dd);this.kh=-1;this.Jh=!1};var Pd=function(a,b,c){this.reset(a||Jd,b,c,void 0,void 0)};Pd.prototype.reset=function(){}; var Qd=function(a,b){this.level=null;this.Zk=[];this.parent=(void 0===b?null:b)||null;this.children=[];this.gl={wf:function(){return a}}},Rd=function(a){if(a.level)return a.level;if(a.parent)return Rd(a.parent);Na("Root logger has no level set.");return Jd},Sd=function(a,b){for(;a;)a.Zk.forEach(function(c){c(b)}),a=a.parent},Td=function(){this.entries={};var a=new Qd("");a.level=Ld;this.entries[""]=a},Ud,Vd=function(a,b){var c=a.entries[b];if(c)return c;c=Vd(a,b.slice(0,Math.max(b.lastIndexOf("."), -0)));var d=new Qd(b,c);a.entries[b]=d;c.children.push(d);return d},Wd=function(){Ud||(Ud=new Td);return Ud},Xd=function(a,b,c){var d;if(d=a)if(d=a&&b){d=b.value;var e=a?Rd(Vd(Wd(),a.wf())):Jd;d=d>=e.value}if(d){b=b||Jd;d=Vd(Wd(),a.wf());"function"===typeof c&&(c=c());Od||(Od=new Nd);e=Od;a=a.wf();if(0=e.value}if(d){b=b||Jd;d=Vd(Wd(),a.wf());"function"===typeof c&&(c=c());Od||(Od=new Nd);e=Od;a=a.wf();if(0"}else d=void 0===a?"undefined":null===a?"null":typeof a;Na("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"))&&Ph.test(c))break a;c=""}c&&a.setAttribute("nonce",c);a.src=gd(b)},Ph=/^[\w+/_-]+[=]{0,2}$/;var Sh=function(a,b){Fb(b,function(c,d){"style"==d?a.style.cssText=c:"class"==d?a.className=c:"for"==d?a.htmlFor=c:Rh.hasOwnProperty(d)?a.setAttribute(Rh[d],c):0==d.lastIndexOf("aria-",0)||0==d.lastIndexOf("data-",0)?a.setAttribute(d,c):a[d]=c})},Rh={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",frameborder:"frameBorder",height:"height",maxlength:"maxLength",nonce:"nonce",role:"role",rowspan:"rowSpan",type:"type",usemap:"useMap",valign:"vAlign",width:"width"},Uh=function(a, b,c){return Th(document,arguments)},Th=function(a,b){var c=b[1],d=Vh(a,String(b[0]));c&&("string"===typeof c?d.className=c:Array.isArray(c)?d.className=c.join(" "):Sh(d,c));2=b.Te&&b.cancel())}this.ci?this.ci.call(this.oh,this):this.kg=!0;this.pc||Li(this,new Mi(this))}};Ki.prototype.bh=function(a,b){this.Se=!1;Ni(this,a,b)}; -var Ni=function(a,b,c){a.pc=!0;a.ia=c;a.Pc=!b;Oi(a)},Qi=function(a){if(a.pc){if(!a.kg)throw new Pi(a);a.kg=!1}};Ki.prototype.callback=function(a){Qi(this);Ri(a);Ni(this,!0,a)};var Li=function(a,b){Qi(a);Ri(b);Ni(a,!1,b)},Ri=function(a){B(!(a instanceof Ki),"An execution sequence may not be initiated with a blocking Deferred.")},Ui=function(a){var b=Si();Ti(b,a,null)},Ti=function(a,b,c,d){B(!a.Qg,"Blocking Deferreds can not be re-used");a.he.push([b,c,d]);a.pc&&Oi(a)}; +var Ki=function(a,b){this.ie=[];this.bi=a;this.oh=b||null;this.Pc=this.pc=!1;this.ia=void 0;this.kg=this.Qg=this.Se=!1;this.se=0;this.X=null;this.Te=0};x(Ki,Ji);Ki.prototype.cancel=function(a){if(this.pc)this.ia instanceof Ki&&this.ia.cancel();else{if(this.X){var b=this.X;delete this.X;a?b.cancel(a):(b.Te--,0>=b.Te&&b.cancel())}this.bi?this.bi.call(this.oh,this):this.kg=!0;this.pc||Li(this,new Mi(this))}};Ki.prototype.bh=function(a,b){this.Se=!1;Ni(this,a,b)}; +var Ni=function(a,b,c){a.pc=!0;a.ia=c;a.Pc=!b;Oi(a)},Qi=function(a){if(a.pc){if(!a.kg)throw new Pi(a);a.kg=!1}};Ki.prototype.callback=function(a){Qi(this);Ri(a);Ni(this,!0,a)};var Li=function(a,b){Qi(a);Ri(b);Ni(a,!1,b)},Ri=function(a){B(!(a instanceof Ki),"An execution sequence may not be initiated with a blocking Deferred.")},Ui=function(a){var b=Si();Ti(b,a,null)},Ti=function(a,b,c,d){B(!a.Qg,"Blocking Deferreds can not be re-used");a.ie.push([b,c,d]);a.pc&&Oi(a)}; Ki.prototype.then=function(a,b,c){var d,e,g=new si(function(f,l){e=f;d=l});Ti(this,e,function(f){f instanceof Mi?g.cancel():d(f);return Vi},this);return g.then(a,b,c)};Ki.prototype.$goog_Thenable=!0; -var Wi=function(a){return Xa(a.he,function(b){return"function"===typeof b[1]})},Vi={},Oi=function(a){if(a.re&&a.pc&&Wi(a)){var b=a.re,c=Xi[b];c&&(r.clearTimeout(c.fa),delete Xi[b]);a.re=0}a.X&&(a.X.Te--,delete a.X);b=a.ia;for(var d=c=!1;a.he.length&&!a.Se;){var e=a.he.shift(),g=e[0],f=e[1];e=e[2];if(g=a.Pc?f:g)try{var l=g.call(e||a.oh,b);l===Vi&&(l=void 0);void 0!==l&&(a.Pc=a.Pc&&(l==b||l instanceof Error),a.ia=b=l);if(pi(b)||"function"===typeof r.Promise&&b instanceof r.Promise)d=!0,a.Se=!0}catch(m){b= -m,a.Pc=!0,Wi(a)||(c=!0)}}a.ia=b;d&&(l=u(a.bh,a,!0),d=u(a.bh,a,!1),b instanceof Ki?(Ti(b,l,d),b.Qg=!0):b.then(l,d));c&&(b=new Yi(b),Xi[b.fa]=b,a.re=b.fa)},Pi=function(){Ia.call(this)};x(Pi,Ia);Pi.prototype.message="Deferred has already fired";Pi.prototype.name="AlreadyCalledError";var Mi=function(){Ia.call(this)};x(Mi,Ia);Mi.prototype.message="Deferred was canceled";Mi.prototype.name="CanceledError";var Yi=function(a){this.fa=r.setTimeout(u(this.Dl,this),0);this.Gd=a}; -Yi.prototype.Dl=function(){B(Xi[this.fa],"Cannot throw an error that is not scheduled.");delete Xi[this.fa];throw this.Gd;};var Xi={};var Si=function(){var a=Oh(Zi),b={},c=b.document||document,d=gd(a).toString(),e=(new Zh(c)).createElement("SCRIPT"),g={xi:e,ld:void 0},f=new Ki($i,g),l=null,m=null!=b.timeout?b.timeout:5E3;0\r\n\r\n"+m+"\r\n"}).join("")+"--batch_EARTHENGINE_batch--\r\n",g=function(f){var l={};a.forEach(function(m){var p=n(m);m=p.next().value;p=n(p.next().value);p.next();p=p.next().value;null!=f[m]&&(l[m]= -Wc(p,f[m]))});return b?b(l):l};return this.callback?(Jj(d,null,function(f,l){return c.callback(l?f:g(f),l)},"multipart/mixed; boundary=batch_EARTHENGINE_batch",e,void 0,this.Rb),null):g(Jj(d,null,void 0,"multipart/mixed; boundary=batch_EARTHENGINE_batch",e,void 0,this.Rb))};var Kj=function(){};q(Kj,Mh); -Kj.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=Mj();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 Nj=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"))}},Hj=function(){var a=Oj.replace(/\/api$/,"");return"window"in r&&!a.match(/^https?:\/\/content-/)?a.replace(/^(https?:\/\/)(.*\.googleapis\.com)$/,"$1content-$2"):a},Qj=function(a,b,c){var d=[];a&&(d=d.concat(Pj)); -b&&d.push("https://www.googleapis.com/auth/devstorage.read_write");a=d=d.concat(c);c=b=0;for(var e={};cg)break;A++}return lk(C.status,function(J){try{return C.getResponseHeader(J)}catch(fa){return null}},C.responseText,l,void 0,e,d,f)},jk=function(a,b,c,d,e,g,f){var l=0,m={url:a,method:c,content:d,headers:e},p=dk,v=null!=g?g:10; -m.callback=function(A){A=A.target;if(429==A.getStatus()&&lX||300<=X)return"Server returned HTTP code: "+X+" for "+f+" "+g},A,C;b=b("Content-Type")||"application/json";d=b.replace(/;.*/,"");if("application/json"===d||"text/json"===d)c=p(c),c.parsed? -C=c.parsed:A=c;else if("multipart/mixed"===d){C={};var J=[];Nj(b,c,function(X,ea,Jb){Jb=p(Jb);Jb.parsed&&(C[X]=Jb.parsed);(ea=(Jb.parsed?"":Jb)||v(ea))&&J.push(X+": "+ea)});J.length&&(A=J.join("\n"))}else var fa="Response was unexpectedly not JSON, but "+d;A=A||v(a)||fa;if(e)return e(C,A),null;if(!A)return C;throw Error(A);},Uj=function(a){var b=function(){Rj||(Rj=function(c,d){r.google.accounts.oauth2.initTokenClient({client_id:c.client_id,callback:d,scope:c.scope}).requestAccessToken()});a()};t(r.default_gsi)? -b():Ui(b)},Vj=function(a,b,c){if(c.access_token){b=c.token_type+" "+c.access_token;if(c.expires_in||0===c.expires_in){c=900*c.expires_in;var d=setTimeout(Wj,.9*c);void 0!==d.unref&&d.unref();Xj=Date.now()+c}Zj=b;a&&a()}else b&&b(c.error||"Unknown error.")},Ij=function(a){var b=new ke;a=n(Object.entries(a));for(var c=a.next();!c.done;c=a.next()){var d=n(c.value);c=d.next().value;d=d.next().value;b.set(c,d)}return b},ik=[],kk=new Hi(function(){var a=ik.shift();if(a){var b=a.url,c=a.callback,d=a.method, -e=a.content;a=a.headers;var g=mk,f=new pj;sj.push(f);c&&f.Xa("complete",c);f.Vc("ready",f.Qk);g&&(f.zc=Math.max(0,g));f.send(b,d,e,a)}0==ik.length||kk.qf()},350),Oj=null,bk=null,ck=null,hk=null,gk=qc,Zj=null,Xj=null,Sj=null,Tj=[],Rj=null,Pj=["https://www.googleapis.com/auth/earthengine","https://www.googleapis.com/auth/cloud-platform"],fk=null,ak=!1,mk=0,dk=null;w("ee.api.ListAssetsResponse",Vg);w("ee.api.EarthEngineAsset",Rf);w("ee.api.Operation",Yg);w("ee.api.ListFeaturesResponse",Wg); -w("ee.api.FeatureViewLocation",Uf);var nk=function(){};nk.prototype.Eh=function(){return null};var ok=function(){};w("ee.Encodable.SourceFrame",ok); -var pk=function(a){if(void 0===a||null===a)a=Rc;return new rf({hb:a})},qk=function(a){return new rf({Oa:a})},rk=function(a){return new rf({Fa:new qf({values:a})})},sk=function(a){return new rf({Ga:new Of({values:a})})},tk=function(a,b){return new rf({ya:new Bg({functionName:a,arguments:b})})},uk=function(a,b){return new rf({ya:new Bg({Va:a,arguments:b})})},vk=function(a,b){return new rf({Ua:new Ag({lc:a,body:b})})},wk=function(a){if(!a)return"AUTO_JPEG_PNG";a=a.toUpperCase();switch(a){case "JPG":return"JPEG"; -case "AUTO":return"AUTO_JPEG_PNG";case "TIF":case "TIFF":case "GEOTIF":case "GEOTIFF":return"GEO_TIFF";case "TF_RECORD":case "TFRECORD":return"TF_RECORD_IMAGE";case "NUMPY":return"NPY";case "ZIPPED_TIF":case "ZIPPED_TIFF":case "ZIPPED_GEOTIF":case "ZIPPED_GEOTIFF":return"ZIPPED_GEO_TIFF";case "ZIPPED_TIF_PER_BAND":case "ZIPPED_TIFF_PER_BAND":case "ZIPPED_GEOTIF_PER_BAND":case "ZIPPED_GEOTIFF_PER_BAND":return"ZIPPED_GEO_TIFF_PER_BAND";default:return a}},xk=function(a){if(!a)return"CSV";a=a.toUpperCase(); -switch(a){case "TF_RECORD":case "TFRECORD":return"TF_RECORD_TABLE";case "JSON":case "GEOJSON":return"GEO_JSON";default:return a}},yk=function(a){if(!a)return"VERTICAL";a=a.toUpperCase();if("HORIZONTAL"!==a&&"VERTICAL"!==a)throw Error('Orientation must be "horizontal" or "vertical"');return a},zk=function(a){if(!a)return[];if("string"===typeof a)return a.split(",");if(Array.isArray(a))return a;throw Error("Invalid band list "+a);},Ck=function(a){var b=new Zf,c=!1;"palette"in a&&(c=a.palette,b.Yc="string"=== -typeof c?c.split(","):c,c=!0);var d=[];if("gain"in a||"bias"in a){if("min"in a||"max"in a)throw Error("Gain and bias can't be specified with min and max");var e=b.Yc?b.Yc.length-1:255;d=Ak(a,"bias","gain").map(function(g){var f=-g.bias/g.gain;return{min:f,max:e/g.gain+f}})}else if("min"in a||"max"in a)d=Ak(a,"min","max");0!==d.length&&(b.Wf=d.map(function(g){return new Pf(g)}),c=!0);a=Bk(a.gamma);if(1\r\n\r\n"+m+"\r\n"}).join("")+"--batch_EARTHENGINE_batch--\r\n",g=function(f){var l={};a.forEach(function(m){var p=n(m);m=p.next().value;p=n(p.next().value);p.next();p=p.next().value;null!=f[m]&&(l[m]= +Wc(p,f[m]))});return b?b(l):l};return this.callback?(Hj(d,null,function(f,l){return c.callback(l?f:g(f),l)},"multipart/mixed; boundary=batch_EARTHENGINE_batch",e,void 0,this.Rb),null):g(Hj(d,null,void 0,"multipart/mixed; boundary=batch_EARTHENGINE_batch",e,void 0,this.Rb))};var Ij=function(){};q(Ij,Mh); +Ij.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=Kj();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 Lj=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"))}},Fj=function(){var a=Mj.replace(/\/api$/,"");return"window"in r&&!a.match(/^https?:\/\/content-/)?a.replace(/^(https?:\/\/)(.*\.googleapis\.com)$/,"$1content-$2"):a},Oj=function(a,b,c){var d=[];a&&(d=d.concat(Nj)); +b&&d.push("https://www.googleapis.com/auth/devstorage.read_write");a=d=d.concat(c);c=b=0;for(var e={};cg)break;A++}return jk(C.status,function(J){try{return C.getResponseHeader(J)}catch(fa){return null}},C.responseText,l,void 0,e,d,f)},hk=function(a,b,c,d,e,g,f){var l=0,m={url:a,method:c,content:d,headers:e},p=bk,v=null!= +g?g:10;m.callback=function(A){A=A.target;if(429==A.getStatus()&&lX||300<=X)return"Server returned HTTP code: "+X+" for "+f+" "+g},A,C;b=b("Content-Type")||"application/json";d=b.replace(/;.*/,"");if("application/json"===d||"text/json"===d)c=p(c),c.parsed? +C=c.parsed:A=c;else if("multipart/mixed"===d){C={};var J=[];Lj(b,c,function(X,ea,Jb){Jb=p(Jb);Jb.parsed&&(C[X]=Jb.parsed);(ea=(Jb.parsed?"":Jb)||v(ea))&&J.push(X+": "+ea)});J.length&&(A=J.join("\n"))}else var fa="Response was unexpectedly not JSON, but "+d;A=A||v(a)||fa;if(e)return e(C,A),null;if(!A)return C;throw Error(A);},Sj=function(a){var b=function(){Pj||(Pj=function(c,d){r.google.accounts.oauth2.initTokenClient({client_id:c.client_id,callback:d,scope:c.scope}).requestAccessToken()});a()};t(r.default_gsi)? +b():Ui(b)},Tj=function(a,b,c){if(c.access_token){b=c.token_type+" "+c.access_token;if(c.expires_in||0===c.expires_in){c=900*c.expires_in;var d=setTimeout(Uj,.9*c);void 0!==d.unref&&d.unref();Vj=Date.now()+c}Xj=b;a&&a()}else b&&b(c.error||"Unknown error.")},Gj=function(a){var b=new ke;a=n(Object.entries(a));for(var c=a.next();!c.done;c=a.next()){var d=n(c.value);c=d.next().value;d=d.next().value;b.set(c,d)}return b},gk=[],ik=new Hi(function(){var a=gk.shift();if(a){var b=a.url,c=a.callback,d=a.method, +e=a.content;a=a.headers;var g=kk,f=new nj;qj.push(f);c&&f.Xa("complete",c);f.Wc("ready",f.Qk);g&&(f.zc=Math.max(0,g));f.send(b,d,e,a)}0==gk.length||ik.qf()},350),Mj=null,Zj=null,ak=null,fk=null,ek=qc,Xj=null,Vj=null,Qj=null,Rj=[],Pj=null,Nj=["https://www.googleapis.com/auth/earthengine","https://www.googleapis.com/auth/cloud-platform"],dk=null,Yj=!1,kk=0,bk=null;w("ee.api.ListAssetsResponse",Vg);w("ee.api.EarthEngineAsset",Rf);w("ee.api.Operation",Yg);w("ee.api.ListFeaturesResponse",Wg); +w("ee.api.FeatureViewLocation",Uf);var lk=function(){};lk.prototype.Eh=function(){return null};var mk=function(){};w("ee.Encodable.SourceFrame",mk); +var nk=function(a){if(void 0===a||null===a)a=Rc;return new rf({hb:a})},ok=function(a){return new rf({Oa:a})},pk=function(a){return new rf({Fa:new qf({values:a})})},qk=function(a){return new rf({Ga:new Of({values:a})})},rk=function(a,b){return new rf({ya:new Bg({functionName:a,arguments:b})})},sk=function(a,b){return new rf({ya:new Bg({Va:a,arguments:b})})},tk=function(a,b){return new rf({Ua:new Ag({lc:a,body:b})})},uk=function(a){if(!a)return"AUTO_JPEG_PNG";a=a.toUpperCase();switch(a){case "JPG":return"JPEG"; +case "AUTO":return"AUTO_JPEG_PNG";case "TIF":case "TIFF":case "GEOTIF":case "GEOTIFF":return"GEO_TIFF";case "TF_RECORD":case "TFRECORD":return"TF_RECORD_IMAGE";case "NUMPY":return"NPY";case "ZIPPED_TIF":case "ZIPPED_TIFF":case "ZIPPED_GEOTIF":case "ZIPPED_GEOTIFF":return"ZIPPED_GEO_TIFF";case "ZIPPED_TIF_PER_BAND":case "ZIPPED_TIFF_PER_BAND":case "ZIPPED_GEOTIF_PER_BAND":case "ZIPPED_GEOTIFF_PER_BAND":return"ZIPPED_GEO_TIFF_PER_BAND";default:return a}},vk=function(a){if(!a)return"CSV";a=a.toUpperCase(); +switch(a){case "TF_RECORD":case "TFRECORD":return"TF_RECORD_TABLE";case "JSON":case "GEOJSON":return"GEO_JSON";default:return a}},wk=function(a){if(!a)return"VERTICAL";a=a.toUpperCase();if("HORIZONTAL"!==a&&"VERTICAL"!==a)throw Error('Orientation must be "horizontal" or "vertical"');return a},xk=function(a){if(!a)return[];if("string"===typeof a)return a.split(",");if(Array.isArray(a))return a;throw Error("Invalid band list "+a);},Ak=function(a){var b=new Zf,c=!1;"palette"in a&&(c=a.palette,b.ad="string"=== +typeof c?c.split(","):c,c=!0);var d=[];if("gain"in a||"bias"in a){if("min"in a||"max"in a)throw Error("Gain and bias can't be specified with min and max");var e=b.ad?b.ad.length-1:255;d=yk(a,"bias","gain").map(function(g){var f=-g.bias/g.gain;return{min:f,max:e/g.gain+f}})}else if("min"in a||"max"in a)d=yk(a,"min","max");0!==d.length&&(b.Wf=d.map(function(g){return new Pf(g)}),c=!0);a=zk(a.gamma);if(1= "'+(new Date(a.startTime)).toISOString()+'"'),delete a.startTime);"endTime"in a&&(b.push('endTime < "'+(new Date(a.endTime)).toISOString()+ +crs:d.grid.ef,dimensions:void 0,crs_transform:void 0};if(d.grid){if(null!=d.grid.Lb){var g=d.grid.Lb;e.crs_transform=[g.dg||0,g.ig||0,g.zg||0,g.jg||0,g.eg||0,g.Ag||0]}null!=d.grid.dimensions&&(e.dimensions=[d.grid.dimensions.width,d.grid.dimensions.height])}d.dataType&&(g={type:"PixelType"},g.precision=(d.dataType.precision||"").toLowerCase(),d.dataType.fd&&(g.min=d.dataType.fd.min||0,g.max=d.dataType.fd.max),e.data_type=g);return e}));a.Jd&&(b.mapLocation=a.Jd);a.featureCount&&(b.featureCount=a.featureCount); +return b},Lk=function(a){var b=new Rf,c=function(g){return null===g?Rc:void 0},d=Object.assign({},a),e;a=function(g){e=d[g];delete d[g];return e};void 0!==a("system:asset_size")&&(b.sizeBytes=c(e)||String(e));void 0!==a("system:time_start")&&(b.startTime=c(e)||(new Date(Number(e))).toISOString());void 0!==a("system:time_end")&&(b.endTime=c(e)||(new Date(Number(e))).toISOString());void 0!==a("system:footprint")&&(b.geometry=c(e)||e);a("system:title");"string"!==typeof e&&null!==e||null!=d.title||(d.title= +c(e)||e);a("system:description");"string"!==typeof e&&null!==e||null!=d.description||(d.description=c(e)||e);Object.entries(d).forEach(function(g){var f=n(g);g=f.next().value;f=f.next().value;d[g]=c(f)||f});b.properties=d;return b},Kk=function(a,b){var c={};c.type=a;null!=b&&(a=b.split("/"),b="projects"===a[0]&&"assets"===a[2]&&["earthengine-legacy","earthengine-public"].includes(a[1])?a.slice(3).join("/"):b,c.id=b);return c},Ok=function(a){var b={};a.num&&(b.pageSize=a.num);a.starttime&&(b.startTime= +(new Date(a.starttime)).toISOString());a.endtime&&(b.endTime=(new Date(a.endtime)).toISOString());a.bbox&&(b.region=Mk(a.bbox));a.region&&(b.region=a.region);a.bbox&&a.region&&console.warn("Multiple request parameters converted to region");var c="id num starttime endtime bbox region".split(" ");a=n(Object.keys(a).filter(function(e){return!c.includes(e)}));for(var d=a.next();!d.done;d=a.next())console.warn("Unrecognized key "+d.value+" ignored");return Nk(b)},Mk=function(a){return'{"type":"Polygon","coordinates":[[['+ +[[0,1],[2,1],[2,3],[0,3],[0,1]].map(function(b){return a[b[0]]+","+a[b[1]]}).join("],[")+"]]]}"},Pk=function(a){var b={};(a.zd||[]).forEach(function(g){b[g.role]=g.Xb});var c=new Set,d=function(g){var f=g.replace(/^domain:|^group:|^serviceAccount:|^user:/,"");g.startsWith("group:")&&c.add(f);return f};a=b["roles/viewer"]||[];var e=a.filter(function(g){return"allUsers"!==g});d={owners:(b["roles/owner"]||[]).map(d),writers:(b["roles/editor"]||[]).map(d),readers:e.map(d)};0= "'+(new Date(a.startTime)).toISOString()+'"'),delete a.startTime);"endTime"in a&&(b.push('endTime < "'+(new Date(a.endTime)).toISOString()+ '"'),delete a.endTime);if("region"in a){var c=a.region;if("object"===typeof c)try{c=JSON.stringify(c)}catch(e){throw Error('Filter parameter "region" must be a GeoJSON or WKT string.');}else if("string"!==typeof c)throw Error('Filter parameter "region" must be a GeoJSON or WKT string.');c=c.replace(/"/g,"'");b.push('intersects("'+c+'")');delete a.region}if("properties"in a){if(!Array.isArray(a.properties)&&a.properties.some(function(e){return"string"!==typeof e}))throw Error('Filter parameter "properties" must be an array of strings'); -c=n(a.properties);for(var d=c.next();!d.done;d=c.next())b.push(d.value.trim().replace(/^(properties\.)?/,"properties."));delete a.properties}return b.join(" AND ")},Pk=function(a){a=Object.assign({},a);var b=bl(a);b&&(a.filter=a.filter?a.filter+" AND "+b:b);return a};var cl=function(){this.blockSize=-1};var dl=function(){this.blockSize=-1;this.blockSize=64;this.W=Array(4);this.Nk=Array(this.blockSize);this.qe=this.Gc=0;this.reset()};x(dl,cl);dl.prototype.reset=function(){this.W[0]=1732584193;this.W[1]=4023233417;this.W[2]=2562383102;this.W[3]=271733878;this.qe=this.Gc=0}; -var el=function(a,b,c){c||(c=0);var d=Array(16);if("string"===typeof b)for(var e=0;16>e;++e)d[e]=b.charCodeAt(c++)|b.charCodeAt(c++)<<8|b.charCodeAt(c++)<<16|b.charCodeAt(c++)<<24;else for(e=0;16>e;++e)d[e]=b[c++]|b[c++]<<8|b[c++]<<16|b[c++]<<24;b=a.W[0];c=a.W[1];e=a.W[2];var g=a.W[3];var f=b+(g^c&(e^g))+d[0]+3614090360&4294967295;b=c+(f<<7&4294967295|f>>>25);f=g+(e^b&(c^e))+d[1]+3905402710&4294967295;g=b+(f<<12&4294967295|f>>>20);f=e+(c^g&(b^c))+d[2]+606105819&4294967295;e=g+(f<<17&4294967295|f>>> +c=n(a.properties);for(var d=c.next();!d.done;d=c.next())b.push(d.value.trim().replace(/^(properties\.)?/,"properties."));delete a.properties}return b.join(" AND ")},Nk=function(a){a=Object.assign({},a);var b=$k(a);b&&(a.filter=a.filter?a.filter+" AND "+b:b);return a};var al=function(){this.blockSize=-1};var bl=function(){this.blockSize=-1;this.blockSize=64;this.W=Array(4);this.Nk=Array(this.blockSize);this.re=this.Gc=0;this.reset()};x(bl,al);bl.prototype.reset=function(){this.W[0]=1732584193;this.W[1]=4023233417;this.W[2]=2562383102;this.W[3]=271733878;this.re=this.Gc=0}; +var cl=function(a,b,c){c||(c=0);var d=Array(16);if("string"===typeof b)for(var e=0;16>e;++e)d[e]=b.charCodeAt(c++)|b.charCodeAt(c++)<<8|b.charCodeAt(c++)<<16|b.charCodeAt(c++)<<24;else for(e=0;16>e;++e)d[e]=b[c++]|b[c++]<<8|b[c++]<<16|b[c++]<<24;b=a.W[0];c=a.W[1];e=a.W[2];var g=a.W[3];var f=b+(g^c&(e^g))+d[0]+3614090360&4294967295;b=c+(f<<7&4294967295|f>>>25);f=g+(e^b&(c^e))+d[1]+3905402710&4294967295;g=b+(f<<12&4294967295|f>>>20);f=e+(c^g&(b^c))+d[2]+606105819&4294967295;e=g+(f<<17&4294967295|f>>> 15);f=c+(b^e&(g^b))+d[3]+3250441966&4294967295;c=e+(f<<22&4294967295|f>>>10);f=b+(g^c&(e^g))+d[4]+4118548399&4294967295;b=c+(f<<7&4294967295|f>>>25);f=g+(e^b&(c^e))+d[5]+1200080426&4294967295;g=b+(f<<12&4294967295|f>>>20);f=e+(c^g&(b^c))+d[6]+2821735955&4294967295;e=g+(f<<17&4294967295|f>>>15);f=c+(b^e&(g^b))+d[7]+4249261313&4294967295;c=e+(f<<22&4294967295|f>>>10);f=b+(g^c&(e^g))+d[8]+1770035416&4294967295;b=c+(f<<7&4294967295|f>>>25);f=g+(e^b&(c^e))+d[9]+2336552879&4294967295;g=b+(f<<12&4294967295| f>>>20);f=e+(c^g&(b^c))+d[10]+4294925233&4294967295;e=g+(f<<17&4294967295|f>>>15);f=c+(b^e&(g^b))+d[11]+2304563134&4294967295;c=e+(f<<22&4294967295|f>>>10);f=b+(g^c&(e^g))+d[12]+1804603682&4294967295;b=c+(f<<7&4294967295|f>>>25);f=g+(e^b&(c^e))+d[13]+4254626195&4294967295;g=b+(f<<12&4294967295|f>>>20);f=e+(c^g&(b^c))+d[14]+2792965006&4294967295;e=g+(f<<17&4294967295|f>>>15);f=c+(b^e&(g^b))+d[15]+1236535329&4294967295;c=e+(f<<22&4294967295|f>>>10);f=b+(e^g&(c^e))+d[1]+4129170786&4294967295;b=c+(f<< 5&4294967295|f>>>27);f=g+(c^e&(b^c))+d[6]+3225465664&4294967295;g=b+(f<<9&4294967295|f>>>23);f=e+(b^c&(g^b))+d[11]+643717713&4294967295;e=g+(f<<14&4294967295|f>>>18);f=c+(g^b&(e^g))+d[0]+3921069994&4294967295;c=e+(f<<20&4294967295|f>>>12);f=b+(e^g&(c^e))+d[5]+3593408605&4294967295;b=c+(f<<5&4294967295|f>>>27);f=g+(c^e&(b^c))+d[10]+38016083&4294967295;g=b+(f<<9&4294967295|f>>>23);f=e+(b^c&(g^b))+d[15]+3634488961&4294967295;e=g+(f<<14&4294967295|f>>>18);f=c+(g^b&(e^g))+d[4]+3889429448&4294967295;c= @@ -497,228 +498,229 @@ f>>>21);f=e+(g^b^c)+d[7]+4139469664&4294967295;e=g+(f<<16&4294967295|f>>>16);f=c 3873151461&4294967295;g=b+(f<<11&4294967295|f>>>21);f=e+(g^b^c)+d[15]+530742520&4294967295;e=g+(f<<16&4294967295|f>>>16);f=c+(e^g^b)+d[2]+3299628645&4294967295;c=e+(f<<23&4294967295|f>>>9);f=b+(e^(c|~g))+d[0]+4096336452&4294967295;b=c+(f<<6&4294967295|f>>>26);f=g+(c^(b|~e))+d[7]+1126891415&4294967295;g=b+(f<<10&4294967295|f>>>22);f=e+(b^(g|~c))+d[14]+2878612391&4294967295;e=g+(f<<15&4294967295|f>>>17);f=c+(g^(e|~b))+d[5]+4237533241&4294967295;c=e+(f<<21&4294967295|f>>>11);f=b+(e^(c|~g))+d[12]+1700485571& 4294967295;b=c+(f<<6&4294967295|f>>>26);f=g+(c^(b|~e))+d[3]+2399980690&4294967295;g=b+(f<<10&4294967295|f>>>22);f=e+(b^(g|~c))+d[10]+4293915773&4294967295;e=g+(f<<15&4294967295|f>>>17);f=c+(g^(e|~b))+d[1]+2240044497&4294967295;c=e+(f<<21&4294967295|f>>>11);f=b+(e^(c|~g))+d[8]+1873313359&4294967295;b=c+(f<<6&4294967295|f>>>26);f=g+(c^(b|~e))+d[15]+4264355552&4294967295;g=b+(f<<10&4294967295|f>>>22);f=e+(b^(g|~c))+d[6]+2734768916&4294967295;e=g+(f<<15&4294967295|f>>>17);f=c+(g^(e|~b))+d[13]+1309151649& 4294967295;c=e+(f<<21&4294967295|f>>>11);f=b+(e^(c|~g))+d[4]+4149444226&4294967295;b=c+(f<<6&4294967295|f>>>26);f=g+(c^(b|~e))+d[11]+3174756917&4294967295;g=b+(f<<10&4294967295|f>>>22);f=e+(b^(g|~c))+d[2]+718787259&4294967295;e=g+(f<<15&4294967295|f>>>17);f=c+(g^(e|~b))+d[9]+3951481745&4294967295;a.W[0]=a.W[0]+b&4294967295;a.W[1]=a.W[1]+(e+(f<<21&4294967295|f>>>11))&4294967295;a.W[2]=a.W[2]+e&4294967295;a.W[3]=a.W[3]+g&4294967295}; -dl.prototype.update=function(a,b){void 0===b&&(b=a.length);for(var c=b-this.blockSize,d=this.Nk,e=this.Gc,g=0;gthis.Gc?this.blockSize:2*this.blockSize)-this.Gc);a[0]=128;for(var b=1;bb;++b)for(var d=0;32>d;d+=8)a[c++]=this.W[b]>>>d&255;return a};var fl=function(a){this.qd="__ee_hash__";this.Sd=!1!==a;this.Ya=[];this.wa={};this.Dg=[];this.Od=new WeakMap;this.ng=new WeakMap;this.Qi=void 0};w("ee.Serializer",fl);var gl=new dj,hl=new dl,jl=function(a,b){return il(new fl(void 0!==b?b:!0),a)};w("ee.Serializer.encode",jl);var kl=function(a){return gl.va(jl(a))};w("ee.Serializer.toJSON",kl);var ml=function(a){return ll(jl(a,!1))};w("ee.Serializer.toReadableJSON",ml); -var ll=function(a){return"JSON"in r?r.JSON.stringify(a,null," "):gl.va(a)},il=function(a,b){b=a.Fd(b);a.Sd&&(b=t(b)&&"ValueRef"==b.type&&1==a.Ya.length?a.Ya[0][1]:{type:"CompoundValue",scope:a.Ya,value:b},a.Ya=[],Ua(a.Dg,u(function(c){delete c[this.qd]},a)),a.Dg=[],a.wa={});return b}; -fl.prototype.Fd=function(a){if(void 0===a)throw Error("Can't encode an undefined value.");var b=t(a)?a[this.qd]:null;if(this.Sd&&null!=b&&this.wa[b])return{type:"ValueRef",value:this.wa[b]};if(null===a||"boolean"===typeof a||"number"===typeof a||"string"===typeof a)return a;if(za(a))return{type:"Invocation",functionName:"Date",arguments:{value:Math.floor(a.getTime())}};if(a instanceof nk){var c=a.encode(u(this.Fd,this));if(!(Array.isArray(c)||t(c)&&"ArgumentRef"!=c.type))return c}else if(Array.isArray(a))c= -Wa(a,function(e){return this.Fd(e)},this);else if(t(a)&&"function"!==typeof a)c=Hb(a,function(e){if("function"!==typeof e)return this.Fd(e)},this),Qb(c,this.qd),c={type:"Dictionary",value:c};else throw Error("Can't encode object: "+a);if(this.Sd){b=nl(c);if(this.wa[b])var d=this.wa[b];else d=String(this.Ya.length),this.Ya.push([d,c]),this.wa[b]=d;a[this.qd]=b;this.Dg.push(a);return{type:"ValueRef",value:d}}return c}; -var nl=function(a){hl.reset();hl.update(gl.va(a));return hl.digest().toString()},pl=function(a){return Xc(ol(a),Tc,Uc,Vc)};w("ee.Serializer.encodeCloudApi",pl); -var ol=function(a,b){return ql(new fl(!0),a,b)},ql=function(a,b,c){a.Qi=c;return rl(a,b)},sl=function(a,b){var c={};b=b.values;for(var d in b){var e=a.ng.get(b[d]);c[d]=Object.assign({},e)}return c},tl=function(a){a=rl(new fl(!1),a);var b=a.values,c=function(d){if(!t(d))return d;var e=Array.isArray(d)?[]:{},g=d instanceof Object.getPrototypeOf(rf);d=n(Object.entries(g?d.h:d));for(var f=d.next();!f.done;f=d.next()){var l=n(f.value);f=l.next().value;l=l.next().value;g?null!==l&&(e[f]="functionDefinitionValue"=== -f&&null!=l.body?{argumentNames:l.lc,body:c(b[l.body])}:"functionInvocationValue"===f&&null!=l.Va?{arguments:Hb(l.arguments,c),functionReference:c(b[l.Va])}:"constantValue"===f?l===Rc?null:l:c(l)):e[f]=c(l)}return e};return a.result&&c(b[a.result])};w("ee.Serializer.encodeCloudApiPretty",tl);var ul=function(a){return gl.va(pl(a))};w("ee.Serializer.toCloudApiJSON",ul);var vl=function(a){return ll(tl(a))};w("ee.Serializer.toReadableCloudApiJSON",vl); -var rl=function(a,b){try{var c=wl(a,b),d=new xl(c,a.Ya,a.Sd,a.ng),e=yl(d,d.wi);return new Hf({result:e,values:d.hi})}finally{a.Od=new WeakMap,a.wa={},a.Ya=[]}},wl=function(a,b){var c=function(f){var l=nl(f);t(b)&&a.Od.set(b,l);if(a.wa[l])return a.wa[l];var m=String(a.Ya.length);a.Ya.push([m,f]);return a.wa[l]=m};if(t(b)&&a.wa[a.Od.get(b)])return a.wa[a.Od.get(b)];if(null===b||"boolean"===typeof b||"string"===typeof b||"number"===typeof b)return c(pk(b));if(za(b))return c(tk("Date",{value:pk(Math.floor(b.getTime()))})); -if(b instanceof nk){var d=b.na(a),e=b.Eh();null!==e&&a.ng.set(d,e);return c(d)}if(Array.isArray(b))return c(rk(b.map(function(f){return qk(wl(a,f))})));if(t(b)&&"function"!==typeof b){var g={};Object.keys(b).sort().forEach(function(f){g[f]=qk(wl(a,b[f]))});return c(sk(g))}throw Error("Can't encode object: "+b);},xl=function(a,b,c,d){var e=this;this.wi=a;this.values={};b.forEach(function(g){return e.values[g[0]]=g[1]});this.pi=c?zl(this):null;this.hi={};this.Zf={};this.ll=0;this.hd=d},yl=function(a, -b){if(b in a.Zf)return a.Zf[b];var c=String(a.ll++);a.Zf[b]=c;a.hi[c]=Al(a,a.values[b],0);return c},Al=function(a,b,c){var d=function(A){return null!==A.hb},e=function(A){return A===Rc?null:A},g=function(A,C){a.hd&&a.hd.has(A)&&!a.hd.has(C)&&a.hd.set(C,a.hd.get(A));return C};if(d(b)||null!=b.Wa||null!=b.qb||null!=b.Mb)return b;if(null!=b.Oa){d=a.values[b.Oa];if(null===a.pi||50>c&&1===a.pi[b.Oa])return d=Al(a,d,c),g(b,d);if(Bl(d))return g(b,d);d=qk(yl(a,b.Oa));return g(b,d)}if(null!=b.Fa){var f=b.Fa.values.map(function(A){return Al(a, -A,c+3)});d=f.every(d)?pk(f.map(function(A){return e(A.hb)})):rk(f);return g(b,d)}if(null!=b.Ga){f={};for(var l={},m=n(Object.entries(b.Ga.values||{})),p=m.next();!p.done;p=m.next()){var v=n(p.value);p=v.next().value;v=v.next().value;f[p]=Al(a,v,c+3);null!==l&&d(f[p])?l[p]=e(f[p].hb):l=null}return null!==l?g(b,pk(l)):g(f,sk(f))}if(null!=b.Ua)return d=b.Ua,d=vk(d.lc||[],yl(a,d.body||"")),g(b,d);if(null!=b.ya){d=b.ya;f={};l=n(Object.keys(d.arguments||{}));for(m=l.next();!m.done;m=l.next())m=m.value, -f[m]=Al(a,d.arguments[m],c+3);d=d.functionName?tk(d.functionName,f):uk(yl(a,d.Va||""),f);return g(b,d)}throw Error("Can't optimize value: "+b);},Bl=function(a){var b=a.hb;return null!==b?b===Rc||"number"===typeof b||"boolean"===typeof b:null!=a.Mb},zl=function(a){var b={},c=function(e){b[e]?b[e]++:(b[e]=1,d(a.values[e]))},d=function(e){null!=e.Fa?e.Fa.values.forEach(d):null!=e.Ga?Object.values(e.Ga.values).forEach(d):null!=e.Ua?c(e.Ua.body):null!=e.ya?(e=e.ya,null!=e.Va&&c(e.Va),Object.values(e.arguments).forEach(d)): -null!=e.Oa&&c(e.Oa)};c(a.wi);return b};var Hl=function(a){if(null==a.element)throw Error('"element" not found in params '+a);var b=a.selectors||null;null!=b&&"string"===typeof b&&(b=b.split(","));b=new hg({l:ol(a.element),description:L(a.description),oa:null,sa:null,Id:null,wd:null,selectors:b,Vb:M(a.maxErrorMeters),requestId:L(a.id),maxVertices:M(a.maxVertices),O:M(a.maxWorkers),priority:M(a.priority)});var c=Cl(a);switch(c){case "GOOGLE_CLOUD_STORAGE":case "DRIVE":var d=new kg({ja:null,ma:null,F:xk(a.fileFormat)});"GOOGLE_CLOUD_STORAGE"=== -c?d.ja=Dl(a):d.ma=El(a);b.oa=d;break;case "ASSET":b.sa=new ig({Ha:Fl(a)});break;case "FEATURE_VIEW":c=new sg({name:Ik(a.mapName)});d=null==a?null:new xg({Of:M(a.maxFeaturesPerTile),tg:a.thinningStrategy});var e=null==a?null:new wg({Eg:Gl(a.zOrderRanking),sg:Gl(a.thinningRanking)});d=new tg({rg:d,Yf:e});c=new jg({mf:c,rc:d});b.Id=c;break;case "BIGQUERY":b.wd=new wf({Re:new vf({table:L(a.table),overwrite:!!a.overwrite,append:!!a.append})});break;default:throw Error('Export destination "'+c+'" unknown'); +bl.prototype.update=function(a,b){void 0===b&&(b=a.length);for(var c=b-this.blockSize,d=this.Nk,e=this.Gc,g=0;gthis.Gc?this.blockSize:2*this.blockSize)-this.Gc);a[0]=128;for(var b=1;bb;++b)for(var d=0;32>d;d+=8)a[c++]=this.W[b]>>>d&255;return a};var dl=function(a){this.sd="__ee_hash__";this.Ud=!1!==a;this.Ya=[];this.wa={};this.Dg=[];this.Qd=new WeakMap;this.ng=new WeakMap;this.Pi=void 0};w("ee.Serializer",dl);var el=new dj,fl=new bl,hl=function(a,b){return gl(new dl(void 0!==b?b:!0),a)};w("ee.Serializer.encode",hl);var il=function(a){return el.va(hl(a))};w("ee.Serializer.toJSON",il);var kl=function(a){return jl(hl(a,!1))};w("ee.Serializer.toReadableJSON",kl); +var jl=function(a){return"JSON"in r?r.JSON.stringify(a,null," "):el.va(a)},gl=function(a,b){b=a.Hd(b);a.Ud&&(b=t(b)&&"ValueRef"==b.type&&1==a.Ya.length?a.Ya[0][1]:{type:"CompoundValue",scope:a.Ya,value:b},a.Ya=[],Ua(a.Dg,u(function(c){delete c[this.sd]},a)),a.Dg=[],a.wa={});return b}; +dl.prototype.Hd=function(a){if(void 0===a)throw Error("Can't encode an undefined value.");var b=t(a)?a[this.sd]:null;if(this.Ud&&null!=b&&this.wa[b])return{type:"ValueRef",value:this.wa[b]};if(null===a||"boolean"===typeof a||"number"===typeof a||"string"===typeof a)return a;if(za(a))return{type:"Invocation",functionName:"Date",arguments:{value:Math.floor(a.getTime())}};if(a instanceof lk){var c=a.encode(u(this.Hd,this));if(!(Array.isArray(c)||t(c)&&"ArgumentRef"!=c.type))return c}else if(Array.isArray(a))c= +Wa(a,function(e){return this.Hd(e)},this);else if(t(a)&&"function"!==typeof a)c=Hb(a,function(e){if("function"!==typeof e)return this.Hd(e)},this),Qb(c,this.sd),c={type:"Dictionary",value:c};else throw Error("Can't encode object: "+a);if(this.Ud){b=ll(c);if(this.wa[b])var d=this.wa[b];else d=String(this.Ya.length),this.Ya.push([d,c]),this.wa[b]=d;a[this.sd]=b;this.Dg.push(a);return{type:"ValueRef",value:d}}return c}; +var ll=function(a){fl.reset();fl.update(el.va(a));return fl.digest().toString()},nl=function(a){return Xc(ml(a),Tc,Uc,Vc)};w("ee.Serializer.encodeCloudApi",nl); +var ml=function(a,b){return ol(new dl(!0),a,b)},ol=function(a,b,c){a.Pi=c;return pl(a,b)},ql=function(a,b){var c={};b=b.values;for(var d in b){var e=a.ng.get(b[d]);c[d]=Object.assign({},e)}return c},rl=function(a){a=pl(new dl(!1),a);var b=a.values,c=function(d){if(!t(d))return d;var e=Array.isArray(d)?[]:{},g=d instanceof Object.getPrototypeOf(rf);d=n(Object.entries(g?d.h:d));for(var f=d.next();!f.done;f=d.next()){var l=n(f.value);f=l.next().value;l=l.next().value;g?null!==l&&(e[f]="functionDefinitionValue"=== +f&&null!=l.body?{argumentNames:l.lc,body:c(b[l.body])}:"functionInvocationValue"===f&&null!=l.Va?{arguments:Hb(l.arguments,c),functionReference:c(b[l.Va])}:"constantValue"===f?l===Rc?null:l:c(l)):e[f]=c(l)}return e};return a.result&&c(b[a.result])};w("ee.Serializer.encodeCloudApiPretty",rl);var sl=function(a){return el.va(nl(a))};w("ee.Serializer.toCloudApiJSON",sl);var tl=function(a){return jl(rl(a))};w("ee.Serializer.toReadableCloudApiJSON",tl); +var pl=function(a,b){try{var c=ul(a,b),d=new vl(c,a.Ya,a.Ud,a.ng),e=wl(d,d.vi);return new Hf({result:e,values:d.gi})}finally{a.Qd=new WeakMap,a.wa={},a.Ya=[]}},ul=function(a,b){var c=function(f){var l=ll(f);t(b)&&a.Qd.set(b,l);if(a.wa[l])return a.wa[l];var m=String(a.Ya.length);a.Ya.push([m,f]);return a.wa[l]=m};if(t(b)&&a.wa[a.Qd.get(b)])return a.wa[a.Qd.get(b)];if(null===b||"boolean"===typeof b||"string"===typeof b||"number"===typeof b)return c(nk(b));if(za(b))return c(rk("Date",{value:nk(Math.floor(b.getTime()))})); +if(b instanceof lk){var d=b.na(a),e=b.Eh();null!==e&&a.ng.set(d,e);return c(d)}if(Array.isArray(b))return c(pk(b.map(function(f){return ok(ul(a,f))})));if(t(b)&&"function"!==typeof b){var g={};Object.keys(b).sort().forEach(function(f){g[f]=ok(ul(a,b[f]))});return c(qk(g))}throw Error("Can't encode object: "+b);},vl=function(a,b,c,d){var e=this;this.vi=a;this.values={};b.forEach(function(g){return e.values[g[0]]=g[1]});this.oi=c?xl(this):null;this.gi={};this.Zf={};this.ll=0;this.kd=d},wl=function(a, +b){if(b in a.Zf)return a.Zf[b];var c=String(a.ll++);a.Zf[b]=c;a.gi[c]=yl(a,a.values[b],0);return c},yl=function(a,b,c){var d=function(A){return null!==A.hb},e=function(A){return A===Rc?null:A},g=function(A,C){a.kd&&a.kd.has(A)&&!a.kd.has(C)&&a.kd.set(C,a.kd.get(A));return C};if(d(b)||null!=b.Wa||null!=b.qb||null!=b.Mb)return b;if(null!=b.Oa){d=a.values[b.Oa];if(null===a.oi||50>c&&1===a.oi[b.Oa])return d=yl(a,d,c),g(b,d);if(zl(d))return g(b,d);d=ok(wl(a,b.Oa));return g(b,d)}if(null!=b.Fa){var f=b.Fa.values.map(function(A){return yl(a, +A,c+3)});d=f.every(d)?nk(f.map(function(A){return e(A.hb)})):pk(f);return g(b,d)}if(null!=b.Ga){f={};for(var l={},m=n(Object.entries(b.Ga.values||{})),p=m.next();!p.done;p=m.next()){var v=n(p.value);p=v.next().value;v=v.next().value;f[p]=yl(a,v,c+3);null!==l&&d(f[p])?l[p]=e(f[p].hb):l=null}return null!==l?g(b,nk(l)):g(f,qk(f))}if(null!=b.Ua)return d=b.Ua,d=tk(d.lc||[],wl(a,d.body||"")),g(b,d);if(null!=b.ya){d=b.ya;f={};l=n(Object.keys(d.arguments||{}));for(m=l.next();!m.done;m=l.next())m=m.value, +f[m]=yl(a,d.arguments[m],c+3);d=d.functionName?rk(d.functionName,f):sk(wl(a,d.Va||""),f);return g(b,d)}throw Error("Can't optimize value: "+b);},zl=function(a){var b=a.hb;return null!==b?b===Rc||"number"===typeof b||"boolean"===typeof b:null!=a.Mb},xl=function(a){var b={},c=function(e){b[e]?b[e]++:(b[e]=1,d(a.values[e]))},d=function(e){null!=e.Fa?e.Fa.values.forEach(d):null!=e.Ga?Object.values(e.Ga.values).forEach(d):null!=e.Ua?c(e.Ua.body):null!=e.ya?(e=e.ya,null!=e.Va&&c(e.Va),Object.values(e.arguments).forEach(d)): +null!=e.Oa&&c(e.Oa)};c(a.vi);return b};var Fl=function(a){if(null==a.element)throw Error('"element" not found in params '+a);var b=a.selectors||null;null!=b&&"string"===typeof b&&(b=b.split(","));b=new hg({l:ml(a.element),description:L(a.description),oa:null,sa:null,Kd:null,yd:null,selectors:b,Vb:M(a.maxErrorMeters),requestId:L(a.id),maxVertices:M(a.maxVertices),O:M(a.maxWorkers),priority:M(a.priority)});var c=Al(a);switch(c){case "GOOGLE_CLOUD_STORAGE":case "DRIVE":var d=new kg({ja:null,ma:null,F:vk(a.fileFormat)});"GOOGLE_CLOUD_STORAGE"=== +c?d.ja=Bl(a):d.ma=Cl(a);b.oa=d;break;case "ASSET":b.sa=new ig({Ha:Dl(a)});break;case "FEATURE_VIEW":c=new sg({name:Gk(a.mapName)});d=null==a?null:new xg({Of:M(a.maxFeaturesPerTile),tg:a.thinningStrategy});var e=null==a?null:new wg({Eg:El(a.zOrderRanking),sg:El(a.thinningRanking)});d=new tg({rg:d,Yf:e});c=new jg({mf:c,rc:d});b.Kd=c;break;case "BIGQUERY":b.yd=new wf({Re:new vf({table:L(a.table),overwrite:!!a.overwrite,append:!!a.append})});break;default:throw Error('Export destination "'+c+'" unknown'); }a.workloadTag&&(b.workloadTag=a.workloadTag);return b};function L(a){return null!=a?String(a):null}function M(a){return null!=a?Number(a):null} -var Cl=function(a){var b="DRIVE";if(null==a)return b;null!=a.outputBucket||null!=a.outputPrefix?b="GOOGLE_CLOUD_STORAGE":null!=a.assetId?b="ASSET":null!=a.mapName&&(b="FEATURE_VIEW");null!=a.table&&(b="BIGQUERY");return b},Jl=function(a){var b=new Lg({Ze:!!a.tfrecordCompressed,Wb:L(a.tfrecordMaxFileSize),gg:!!a.tfrecordSequenceData,Xe:!!a.tfrecordCollapseBands,Pf:M(a.tfrecordMaskedThreshold),defaultValue:M(a.tfrecordDefaultValue),cc:Il(a.tfrecordPatchDimensions),Lf:Il(a.tfrecordKernelSize),le:null}); -a=a.tfrecordTensorDepths;if(null!=a)if(t(a)){var c={};Fb(a,function(d,e){if("string"!==typeof e||"number"!==typeof d)throw Error('"tensorDepths" option must be an object of type Object');c[e]=d});b.le=c}else throw Error('"tensorDepths" option needs to have the form Object.');return b},Kl=function(a,b){var c=new dg({ja:null,ma:null,Jd:null,me:null,F:wk(a.fileFormat)});if("GEO_TIFF"===c.F){if(a.fileDimensions&&a.tiffFileDimensions)throw Error('Export cannot set both "fileDimensions" and "tiffFileDimensions".'); -var d=a.tiffShardSize||a.shardSize;var e=!!a.tiffCloudOptimized;var g=!(!a.skipEmptyTiles&&!a.tiffSkipEmptyFiles),f=Il(a.fileDimensions||a.tiffFileDimensions);d=M(d);var l=a.tiffNoData;l=null!=l?new Dg({rf:Number(l)}):null;e=new Cg({We:e,mg:g,cc:f,tileSize:d,Rf:l});c.Jd=e}else"TF_RECORD_IMAGE"===c.F&&(c.me=Jl(a));"GOOGLE_CLOUD_STORAGE"===b?c.ja=Dl(a):c.ma=El(a);return c},Ll=function(a,b){var c=new ng({ja:null,ma:null,F:"MP4"});"GOOGLE_CLOUD_STORAGE"===b?c.ja=Dl(a):c.ma=El(a);return c},Ml=function(a){var b, -c,d,e,g=M(null!=(b=a.endZoom)?b:a.maxZoom);b=M(null!=(c=a.startZoom)?c:a.minZoom);c=M(a.scale);var f=!(null!=(d=a.skipEmpty)?!d:!a.skipEmptyTiles);d=L(a.mapsApiKey);var l=Il(null!=(e=a.dimensions)?e:a.tileDimensions);e=M(a.stride);var m=M(a.minTimeMachineZoomSubset);a=M(a.maxTimeMachineZoomSubset);a=null==m&&null==a?null:new qh({start:null!=m?m:0,end:a});return new gg({hf:g,og:b,scale:c,lg:f,Kf:d,dimensions:l,stride:e,Fg:a})},Il=function(a){if(null==a)return null;var b=new Eg({height:0,width:0}); +var Al=function(a){var b="DRIVE";if(null==a)return b;null!=a.outputBucket||null!=a.outputPrefix?b="GOOGLE_CLOUD_STORAGE":null!=a.assetId?b="ASSET":null!=a.mapName&&(b="FEATURE_VIEW");null!=a.table&&(b="BIGQUERY");return b},Hl=function(a){var b=new Lg({Ze:!!a.tfrecordCompressed,Wb:L(a.tfrecordMaxFileSize),gg:!!a.tfrecordSequenceData,Xe:!!a.tfrecordCollapseBands,Pf:M(a.tfrecordMaskedThreshold),defaultValue:M(a.tfrecordDefaultValue),cc:Gl(a.tfrecordPatchDimensions),Lf:Gl(a.tfrecordKernelSize),me:null}); +a=a.tfrecordTensorDepths;if(null!=a)if(t(a)){var c={};Fb(a,function(d,e){if("string"!==typeof e||"number"!==typeof d)throw Error('"tensorDepths" option must be an object of type Object');c[e]=d});b.me=c}else throw Error('"tensorDepths" option needs to have the form Object.');return b},Il=function(a,b){var c=new dg({ja:null,ma:null,Ld:null,ne:null,F:uk(a.fileFormat)});if("GEO_TIFF"===c.F){if(a.fileDimensions&&a.tiffFileDimensions)throw Error('Export cannot set both "fileDimensions" and "tiffFileDimensions".'); +var d=a.tiffShardSize||a.shardSize;var e=!!a.tiffCloudOptimized;var g=!(!a.skipEmptyTiles&&!a.tiffSkipEmptyFiles),f=Gl(a.fileDimensions||a.tiffFileDimensions);d=M(d);var l=a.tiffNoData;l=null!=l?new Dg({rf:Number(l)}):null;e=new Cg({We:e,mg:g,cc:f,tileSize:d,Rf:l});c.Ld=e}else"TF_RECORD_IMAGE"===c.F&&(c.ne=Hl(a));"GOOGLE_CLOUD_STORAGE"===b?c.ja=Bl(a):c.ma=Cl(a);return c},Jl=function(a,b){var c=new ng({ja:null,ma:null,F:"MP4"});"GOOGLE_CLOUD_STORAGE"===b?c.ja=Bl(a):c.ma=Cl(a);return c},Kl=function(a){var b, +c,d,e,g=M(null!=(b=a.endZoom)?b:a.maxZoom);b=M(null!=(c=a.startZoom)?c:a.minZoom);c=M(a.scale);var f=!(null!=(d=a.skipEmpty)?!d:!a.skipEmptyTiles);d=L(a.mapsApiKey);var l=Gl(null!=(e=a.dimensions)?e:a.tileDimensions);e=M(a.stride);var m=M(a.minTimeMachineZoomSubset);a=M(a.maxTimeMachineZoomSubset);a=null==m&&null==a?null:new qh({start:null!=m?m:0,end:a});return new gg({hf:g,og:b,scale:c,lg:f,Kf:d,dimensions:l,stride:e,Fg:a})},Gl=function(a){if(null==a)return null;var b=new Eg({height:0,width:0}); "string"===typeof a&&(-1!==a.indexOf("x")?a=a.split("x").map(Number):-1!==a.indexOf(",")&&(a=a.split(",").map(Number)));if(Array.isArray(a))if(2===a.length)b.height=a[0],b.width=a[1];else if(1===a.length)b.height=a[0],b.width=a[0];else throw Error("Unable to construct grid from dimensions: "+a);else if("number"!==typeof a||isNaN(a))if(t(a)&&null!=a.height&&null!=a.width)b.height=a.height,b.width=a.width;else throw Error("Unable to construct grid from dimensions: "+a);else b.height=a,b.width=a;return b}, -Dl=function(a){var b=null;null!=a.writePublicTiles&&(b=a.writePublicTiles?"PUBLIC":"DEFAULT_OBJECT_ACL");return new Ef({Ue:L(a.outputBucket),xa:L(a.outputPrefix),Ve:a.bucketCorsUris||null,permissions:b})},El=function(a){return new Qf({tf:L(a.driveFolder),xa:L(a.driveFileNamePrefix)})},Fl=function(a){return new Cf({name:Ik(a.assetId)})},Gl=function(a){if(!a)return null;var b=a;"string"===typeof a&&(a=a.split(","));if(Array.isArray(a))return new lh({Xf:(a||[]).map(Nl)});throw Error("Unable to build ranking rule from rules: "+ -JSON.stringify(b)+". Rules should either be a comma-separated string or list of strings.");},Nl=function(a){var b=new kh({direction:null,ae:null,ce:null,be:null});a=a.trim();a=a.match(/^([\S]+.*)\s+(ASC|DESC)$/);if(null==a)throw Error("Ranking rule format is invalid. Each rule should be defined by a rule type and a direction (ASC or DESC), separated by a space. Valid rule types are: .geometryType, .minZoomLevel, or a feature property name.");var c=n(a);c.next();a=c.next().value;c=c.next().value;switch(c.toUpperCase()){case "ASC":b.direction= -"ASCENDING";break;case "DESC":b.direction="DESCENDING";break;default:throw Error("Ranking rule direction '"+c+"' is invalid. Directions are: ASC, DESC.");}switch(a.trim()){case ".geometryType":b.be=new hh({});break;case ".minZoomLevel":b.ce=new jh({});break;default:b.ae=new gh({attributeName:L(a)})}return b};var Ol=[],Pl=function(a){B(!Object.isSealed(a),"Cannot use getInstance() with a sealed constructor.");var b="Ef";if(a.Ef&&a.hasOwnProperty(b))return a.Ef;Ol.push(a);var c=new a;a.Ef=c;B(a.hasOwnProperty(b),"Could not instantiate singleton.");return c};var Rl=function(a,b,c,d,e,g){d=Qj(!g,!1,d||[]);Sj=a;Tj=d;null===a?Yj():Uj(function(){Wj(b,c,e||Fa(Ql,b,c))})};w("ee.data.authenticateViaOauth",Rl);var Sl=function(a,b,c,d,e){Rl(a,b,c,d,e)};w("ee.data.authenticate",Sl);var Ql=function(a,b){r.google.accounts.oauth2.initTokenClient({client_id:Sj,callback:Fa(Vj,a,b),scope:Tj.join(" ")}).requestAccessToken()};w("ee.data.authenticateViaPopup",Ql); -var Tl=function(a,b,c,d,e){if("window"in r)throw Error("Use of private key authentication in the browser is insecure. Consider using OAuth, instead.");d=Qj(!e,!e,d||[]);Sj=a.client_email;Tj=d;var g=new google.auth.JWT(a.client_email,null,a.private_key,d,null);Rj=function(f,l){g.authorize(function(m,p){m?l({error:m}):l({access_token:p.access_token,token_type:p.token_type,expires_in:(p.expiry_date-Date.now())/1E3})})};Wj(b,c)};w("ee.data.authenticateViaPrivateKey",Tl); -w("ee.data.setExpressionAugmenter",function(a){Ul=a||qc});var Ul=qc;w("ee.data.setAuthToken",function(a,b,c,d,e,g,f,l){e=Qj(!l,!1,e||[]);Sj=a;Tj=e;Vj(void 0,void 0,{token_type:b,access_token:c,state:e.join(" "),expires_in:d});!1===f?g&&g():Uj(function(){g&&g()})});w("ee.data.refreshAuthToken",Wj);w("ee.data.setAuthTokenRefresher",function(a){Rj=a});w("ee.data.getAuthToken",Mj);w("ee.data.clearAuthToken",Yj);w("ee.data.getAuthClientId",function(){return Sj});w("ee.data.getAuthScopes",function(){return Tj}); -w("ee.data.setDeadline",function(a){mk=a});w("ee.data.setParamAugmenter",function(a){gk=a||qc}); -var Vl=function(a){a=new K(a);return a.handle(a.Fc().list(Gj(),{prettyPrint:!1}).then(Dk))},Yl=function(a,b){if("string"===typeof a.image)throw Error("Image as JSON string not supported.");if(void 0!==a.version)throw Error("Image version specification not supported.");a=new Yf({name:null,l:Ul(ol(a.image)),F:wk(a.format),da:zk(a.bands),ic:Ck(a)});var c={fields:"name"},d=Wl();d&&(c.workloadTag=d);b=new K(b);return b.handle(b.maps().create(Gj(),a,c).then(function(e){return Xl(e.name,"")}))}; -w("ee.data.getMapId",Yl);var Zl=function(a,b,c,d){if(!a.formatTileUrl){var e=Xl(a.mapid,a.token,a.urlFormat);a.urlFormat=e.urlFormat;a.formatTileUrl=e.formatTileUrl}return a.formatTileUrl(b,c,d)};w("ee.data.getTileUrl",Zl); -var Xl=function(a,b,c){var d=void 0===c?"":c;d||(Ej(),c=bk,d=b?c+"/map/"+a+"/{z}/{x}/{y}?token="+b:c+"/v1/"+a+"/tiles/{z}/{x}/{y}");return{mapid:a,token:b,formatTileUrl:function(e,g,f){var l=Math.pow(2,f);e%=l;e=String(0>e?e+l:e);return d.replace("{x}",e).replace("{y}",g).replace("{z}",f)},urlFormat:d}},$l=function(a,b){var c=a.visParams?Ul(ol(a.visParams)):null;a=new rg({name:null,Nb:a.assetId,mapName:a.mapName,Cg:c});b=new K(b);return b.handle((new yh(b.M)).create(Gj(),a,{fields:["name"]}).then(function(d){var e= -d.name.split("/").pop();return{token:e,formatTileUrl:function(g,f,l){return bk+"/v1/"+d.name+"/tiles/"+l+"/"+g+"/"+f},rawTilesKey:e.substring(0,e.lastIndexOf("-"))}}))};w("ee.data.getFeatureViewTilesKey",$l); -var am=function(a,b,c){c=new K(c);var d=Ik(a);a=c.handle;var e=c.assets();b=void 0===b?{}:b;var g=void 0===g?{}:g;e.j.C(d,RegExp("^projects/[^/]+/assets/.*$"));g=I(e.j,{body:null,B:"GET",D:"earthengine.projects.assets.listFeatures",path:"/"+e.m+"/"+d+":listFeatures",u:H(b,g),G:Wg});return a.call(c,g)};w("ee.data.listFeatures",am); -var bm=function(a,b){var c=new fl(!0);a=ql(c,a);var d={};c=sl(c,a);Object.keys(c).length&&(d={__source_code_nodes__:c});c={l:Ul(a,d)};if(a=Wl())c.workloadTag=a;b=new K(b);return b.handle(Ih(b.value(),Gj(),new Gf(c)).then(function(e){return e.result}))};w("ee.data.computeValue",bm); -var cm=function(a,b){if("string"===typeof a.image)throw Error("Image as JSON string not supported.");if(void 0!==a.version)throw Error("Image version specification not supported.");if(void 0!==a.region)throw Error('"region" not supported in call to ee.data.getThumbId. Use ee.Image.getThumbURL.');if(void 0!==a.dimensions)throw Error('"dimensions" is not supported in call to ee.data.getThumbId. Use ee.Image.getThumbURL.');a=new ph({name:null,l:Ul(ol(a.image)),F:wk(a.format),xa:a.name,da:zk(a.bands), -ic:Ck(a),grid:null});var c={fields:"name"},d=Wl();d&&(c.workloadTag=d);b=new K(b);return b.handle((new Gh(b.M)).create(Gj(),a,c).then(function(e){return{thumbid:e.name,token:""}}))};w("ee.data.getThumbId",cm); -var dm=function(a,b){var c=new og({framesPerSecond:a.framesPerSecond||null,maxFrames:a.maxFrames||null,maxPixelsPerFrame:a.maxPixelsPerFrame||null});a=new th({name:null,l:Ul(ol(a.imageCollection)),F:wk(a.format),Pa:c,grid:null});c={fields:"name"};var d=Wl();d&&(c.workloadTag=d);b=new K(b);return b.handle((new Lh(b.M)).create(Gj(),a,c).then(function(e){return{thumbid:e.name,token:""}}))};w("ee.data.getVideoThumbId",dm); -var em=function(a,b){a=new zg({name:null,l:Ul(ol(a.imageCollection)),F:wk(a.format),orientation:yk(a.orientation),grid:null});var c={fields:"name"},d=Wl();d&&(c.workloadTag=d);b=new K(b);return b.handle((new zh(b.M)).create(Gj(),a,c).then(function(e){return{thumbid:e.name,token:""}}))};w("ee.data.getFilmstripThumbId",em);var fm=function(a){return bk+"/v1/"+a.thumbid+":getPixels"};w("ee.data.makeThumbUrl",fm); -var hm=function(a,b){a=Object.assign({},a);a.id&&(a.image=new N(a.id));if("string"===typeof a.image)throw Error("Image as serialized JSON string not supported.");if(!a.image)throw Error("Missing ID or image parameter.");a.filePerBand=!1!==a.filePerBand;a.format=a.format||(a.filePerBand?"ZIPPED_GEO_TIFF_PER_BAND":"ZIPPED_GEO_TIFF");if(null!=a.region&&(null!=a.scale||null!=a.crs_transform)&&null!=a.dimensions)throw Error("Cannot specify (bounding region, crs_transform/scale, dimensions) simultaneously."); -if("string"===typeof a.bands)try{a.bands=JSON.parse(a.bands)}catch(e){a.bands=zk(a.bands)}if(a.bands&&!Array.isArray(a.bands))throw Error("Bands parameter must be an array.");a.bands&&a.bands.every(function(e){return"string"===typeof e})&&(a.bands=a.bands.map(function(e){return{id:e}}));if(a.bands&&a.bands.some(function(e){return null==e.id}))throw Error("Each band dictionary must have an id.");"string"===typeof a.region&&(a.region=JSON.parse(a.region));if("string"===typeof a.crs_transform)try{a.crs_transform= -JSON.parse(a.crs_transform)}catch(e){}var c=gm(a.image,a);a=new ph({name:null,l:Ul(ol(c)),F:wk(a.format),xa:a.name,da:a.bands&&zk(a.bands.map(function(e){return e.id})),grid:null});c={fields:"name"};var d=Wl();d&&(c.workloadTag=d);b=new K(b);return b.handle((new Gh(b.M)).create(Gj(),a,c).then(function(e){return{docid:e.name,token:""}}))};w("ee.data.getDownloadId",hm);var im=function(a){Ej();return bk+"/v1/"+a.docid+":getPixels"};w("ee.data.makeDownloadUrl",im); -var jm=function(a,b){b=new K(b);var c=xk(a.format),d=Ul(ol(a.table)),e=null;if(null!=a.selectors)if("string"===typeof a.selectors)e=a.selectors.split(",");else if(Array.isArray(a.selectors)&&a.selectors.every(function(g){return"string"===typeof g}))e=a.selectors;else throw Error("'selectors' parameter must be an array of strings.");a=new nh({name:null,l:d,F:c,selectors:e,filename:a.filename||null});c={fields:"name"};if(d=Wl())c.workloadTag=d;return b.handle((new Fh(b.M)).create(Gj(),a,c).then(function(g){return{docid:g.name|| -"",token:""}}))};w("ee.data.getTableDownloadId",jm);var km=function(a){return bk+"/v1/"+a.docid+":getFeatures"};w("ee.data.makeTableDownloadUrl",km);var lm=function(a,b){var c=function(d){return Math.floor(Math.random()*Math.pow(2,4*d)).toString(16).padStart(d,"0")};a=fb(a||1).map(function(){return[c(8),c(4),"4"+c(3),(8+Math.floor(4*Math.random())).toString(16)+c(3),c(12)].join("-")});return b?b(a):a};w("ee.data.newTaskId",lm); -var mm=function(a){return a.name&&a.done&&a.error},om=function(a,b){var c=nm(a).map(Uk);if(1===c.length)return a=(new K(b)).pd(mm),a.handle(a.Na().get(c[0]).then(function(e){return[Vk(e)]}));a=(new Lj(b)).pd(mm);var d=a.Na();return a.send(c.map(function(e){return[e,d.get(e)]}),function(e){return c.map(function(g){return Vk(e[g])})})};w("ee.data.getTaskStatus",om); -var nm=function(a){if("string"===typeof a)return[a];if(Array.isArray(a))return a;throw Error("Invalid value: expected a string or an array of strings.");},qm=function(a){return pm(void 0,a)};w("ee.data.getTaskList",qm);var pm=function(a,b){return b?(rm(a,function(c,d){return b(c?{tasks:c.map(Vk)}:null,d)}),null):{tasks:rm(a).map(Vk)}};w("ee.data.getTaskListWithLimit",pm); -var rm=function(a,b){var c=[],d={pageSize:500},e=function(l){db(c,l.Na||[]);!l.nextPageToken||a&&c.length>=a?b&&b(a?c.slice(0,a):c):(d.pageToken=l.nextPageToken,g.handle(f.list(Gj(),d).then(e)));return null},g=new K(b?function(l,m){return m&&b(l,m)}:void 0),f=g.Na();g.handle(f.list(Gj(),d).then(e));return b?null:a?c.slice(0,a):c};w("ee.data.listOperations",rm); -var sm=function(a,b){a=nm(a).map(Uk);var c=new zf;if(1===a.length)b=new K(b),b.handle(b.Na().cancel(a[0],c));else{b=new Lj(b);var d=b.Na();b.send(a.map(function(e){return[e,d.cancel(e,c)]}))}};w("ee.data.cancelOperation",sm);var tm=function(a,b){var c=nm(a).map(Uk);if(!Array.isArray(a))return a=(new K(b)).pd(mm),a.handle(a.Na().get(c[0]));a=(new Lj(b)).pd(mm);var d=a.Na();return a.send(c.map(function(e){return[e,d.get(e)]}))};w("ee.data.getOperation",tm); -var vm=function(a,b){return um(a,"CANCEL",b)};w("ee.data.cancelTask",vm);var um=function(a,b,c){if(!Mb(wm,b))throw Error("Invalid action: "+b);a=nm(a);a=a.map(Uk);sm(a,c);return null};w("ee.data.updateTask",um); -var Cm=function(a,b,c){b.id=a;var d=b.type;a=null!=b.sourceUrl?{__source_url__:b.sourceUrl}:{};if("workloadTag"in b)b.workloadTag||delete b.workloadTag;else{var e=Wl();e&&(b.workloadTag=e)}var g=new K(c);c=function(f){return g.handle(f.then(Wk))};switch(d){case "EXPORT_IMAGE":return b=xm(b,a),c(g.image().Ka(Gj(),b));case "EXPORT_FEATURES":return b=Hl(b),b.l=Ul(b.l,a),c(g.table().Ka(Gj(),b));case "EXPORT_VIDEO":return b=ym(b,a),c(g.video().Ka(Gj(),b));case "EXPORT_TILES":return b=zm(b,a),c(g.map().Ka(Gj(), -b));case "EXPORT_VIDEO_MAP":return b=Am(b),c((new Kh(g.M)).Ka(Gj(),b));case "EXPORT_CLASSIFIER":return b=Bm(b,a),c((new xh(g.M)).Ka(Gj(),b));default:throw Error("Unable to start processing for task of type "+d);}};w("ee.data.startProcessing",Cm); -var xm=function(a,b){var c=Dm(a);if(null==c.element)throw Error('"element" not found in params '+c);var d=new bg({l:ol(c.element),description:L(c.description),oa:null,sa:null,grid:null,Qf:L(c.maxPixels),requestId:L(c.id),O:M(c.maxWorkers),priority:M(c.priority)}),e=Cl(c);switch(e){case "GOOGLE_CLOUD_STORAGE":case "DRIVE":d.oa=Kl(c,e);break;case "ASSET":e=c.pyramidingPolicy||{};try{e=JSON.parse(e)}catch(f){}var g="PYRAMIDING_POLICY_UNSPECIFIED";"string"===typeof e?(g=e,e={}):e[".default"]&&(g=e[".default"], -delete e[".default"]);c=new cg({Ha:Fl(c),pyramidingPolicy:g,Vf:Pb(e)?null:e,tileSize:M(c.shardSize)});d.sa=c;break;default:throw Error('Export destination "'+e+'" unknown');}d.l=Ul(d.l,b);a.workloadTag&&(d.workloadTag=a.workloadTag);return d},ym=function(a,b){var c=Em(a);if(null==c.element)throw Error('"element" not found in params '+c);var d=new pg({l:ol(c.element),description:L(c.description),Pa:new og({framesPerSecond:M(c.framesPerSecond),maxFrames:M(c.maxFrames),maxPixelsPerFrame:L(c.maxPixels)}), -oa:null,requestId:L(c.id),O:M(c.maxWorkers),priority:M(c.priority)});d.oa=Ll(c,Cl(c));d.l=Ul(d.l,b);a.workloadTag&&(d.workloadTag=a.workloadTag);return d},zm=function(a,b){var c=a.scale;delete a.scale;var d=Dm(a);d.scale=c;if(null==d.element)throw Error('"element" not found in params '+d);c=new fg({l:ol(d.element),description:L(d.description),ec:Ml(d),dc:Kl(d,"GOOGLE_CLOUD_STORAGE"),requestId:L(d.id),O:M(d.maxWorkers),priority:M(d.priority)});c.l=Ul(c.l,b);a.workloadTag&&(c.workloadTag=a.workloadTag); -return c},Am=function(a){var b=a.scale;delete a.scale;var c=Em(a);c.scale=b;if(null==c.element)throw Error('"element" not found in params '+c);b=new mg({l:ol(c.element),description:L(c.description),Pa:new og({framesPerSecond:M(c.framesPerSecond),maxFrames:M(c.maxFrames),maxPixelsPerFrame:null}),ec:Ml(c),dc:Ll(c,"GOOGLE_CLOUD_STORAGE"),requestId:L(c.id),version:L(c.version),O:M(c.maxWorkers),priority:M(c.priority)});b.l=Ul(b.l);a.workloadTag&&(b.workloadTag=a.workloadTag);return b},Bm=function(a,b){if(null== -a.element)throw Error('"element" not found in params '+a);var c=Cl(a);if("ASSET"!=c)throw Error('Export destination "'+c+'" unknown');c=new ag({l:ol(a.element),description:L(a.description),requestId:L(a.id),sa:new Bf({Ha:Fl(a)}),O:M(a.maxWorkers),priority:M(a.priority)});c.l=Ul(c.l,b);a.workloadTag&&(c.workloadTag=a.workloadTag);return c},Gm=function(a,b,c){b=$k(b);var d=function(e){return e?Wk(e):null};return d(Fm(a,b,c&&function(e,g){return c(d(e),g)}))};w("ee.data.startIngestion",Gm); -var Fm=function(a,b,c){b=new Rg({Bf:b,requestId:a,overwrite:null,description:null});a=new K(c,a?void 0:0);return a.handle(a.image().import(Gj(),b))},Hm=function(a,b,c){b=new Sg({qg:b,requestId:a,overwrite:null,description:null});a=new K(c,a?void 0:0);return a.handle(a.table().import(Gj(),b))},Im=function(a,b,c){b=al(b);var d=function(e){return e?Wk(e):null};return d(Hm(a,b,c&&function(e,g){return c(d(e),g)}))};w("ee.data.startTableIngestion",Im); -var Jm=function(a,b){b=new K(b);a=Ik(a);return b.handle(b.assets().get(a,{prettyPrint:!1}).then(Jk))};w("ee.data.getAsset",Jm);w("ee.data.getInfo",Jm);var Km=function(a,b,c,d){b=void 0===b?{}:b;d=void 0===d?qc:d;var e=Gk.test(a);c=new K(c);var g=e?new vh(c.M):c.assets();a=e?"projects/"+Hk(a):Ik(a);return c.handle(g.Ud(a,b).then(d))},Lm=function(a,b){return Km(a.id,Qk(a),b,function(c){return null==c?null:Kk(c)})};w("ee.data.getList",Lm);var Mm=function(a,b,c){b=void 0===b?{}:b;return Km(a,b,c)}; -w("ee.data.listAssets",Mm);var Nm=function(a,b,c){b=void 0===b?{}:b;b=Pk(b);return Km(a,b,c,function(d){if(null==d)return null;var e={};d.assets&&(e.images=d.assets);d.nextPageToken&&(e.nextPageToken=d.nextPageToken);return e})};w("ee.data.listImages",Nm);var Om=function(a,b){b=new K(b);return b.handle((new vh(b.M)).Ud(a||Gj()))};w("ee.data.listBuckets",Om);var Pm=function(a){a=new K(a);return a.handle((new vh(a.M)).Ud(Gj()).then(Kk))};w("ee.data.getAssetRoots",Pm); -var Qm=function(a,b){var c="projects/"+Hk(a);a="projects/earthengine-legacy"===c?a:void 0;var d=new Rf({type:"Folder"});b=new K(b);b.handle(b.assets().create(c,d,{assetId:a}).then(Jk))};w("ee.data.createAssetHome",Qm); -var Rm=function(a,b,c,d,e){if(c)throw Error("Asset overwrite not supported.");if("string"===typeof a)throw Error("Asset cannot be specified as string.");b=a.name||b&&Ik(b);if(!b)throw Error("Either asset name or opt_path must be specified.");c=b.indexOf("/assets/");if(-1===c)throw Error("Asset name must contain /assets/.");a=Object.assign({},a);a.tilestoreEntry&&!a.tilestoreLocation&&(a.tilestoreLocation=a.tilestoreEntry,delete a.tilestoreEntry);a.tilestoreLocation&&(a.tilestoreLocation=new Xf(a.tilestoreLocation)); +Bl=function(a){var b=null;null!=a.writePublicTiles&&(b=a.writePublicTiles?"PUBLIC":"DEFAULT_OBJECT_ACL");return new Ef({Ue:L(a.outputBucket),xa:L(a.outputPrefix),Ve:a.bucketCorsUris||null,permissions:b})},Cl=function(a){return new Qf({tf:L(a.driveFolder),xa:L(a.driveFileNamePrefix)})},Dl=function(a){return new Cf({name:Gk(a.assetId)})},El=function(a){if(!a)return null;var b=a;"string"===typeof a&&(a=a.split(","));if(Array.isArray(a))return new lh({Xf:(a||[]).map(Ll)});throw Error("Unable to build ranking rule from rules: "+ +JSON.stringify(b)+". Rules should either be a comma-separated string or list of strings.");},Ll=function(a){var b=new kh({direction:null,be:null,de:null,ce:null});a=a.trim();a=a.match(/^([\S]+.*)\s+(ASC|DESC)$/);if(null==a)throw Error("Ranking rule format is invalid. Each rule should be defined by a rule type and a direction (ASC or DESC), separated by a space. Valid rule types are: .geometryType, .minZoomLevel, or a feature property name.");var c=n(a);c.next();a=c.next().value;c=c.next().value;switch(c.toUpperCase()){case "ASC":b.direction= +"ASCENDING";break;case "DESC":b.direction="DESCENDING";break;default:throw Error("Ranking rule direction '"+c+"' is invalid. Directions are: ASC, DESC.");}switch(a.trim()){case ".geometryType":b.ce=new hh({});break;case ".minZoomLevel":b.de=new jh({});break;default:b.be=new gh({attributeName:L(a)})}return b};var Ml=[],Nl=function(a){B(!Object.isSealed(a),"Cannot use getInstance() with a sealed constructor.");var b="Ef";if(a.Ef&&a.hasOwnProperty(b))return a.Ef;Ml.push(a);var c=new a;a.Ef=c;B(a.hasOwnProperty(b),"Could not instantiate singleton.");return c};var Pl=function(a,b,c,d,e,g){d=Oj(!g,!1,d||[]);Qj=a;Rj=d;null===a?Wj():Sj(function(){Uj(b,c,e||Fa(Ol,b,c))})};w("ee.data.authenticateViaOauth",Pl);var Ql=function(a,b,c,d,e){Pl(a,b,c,d,e)};w("ee.data.authenticate",Ql);var Ol=function(a,b){r.google.accounts.oauth2.initTokenClient({client_id:Qj,callback:Fa(Tj,a,b),scope:Rj.join(" ")}).requestAccessToken()};w("ee.data.authenticateViaPopup",Ol); +var Rl=function(a,b,c,d,e){if("window"in r)throw Error("Use of private key authentication in the browser is insecure. Consider using OAuth, instead.");d=Oj(!e,!e,d||[]);Qj=a.client_email;Rj=d;var g=new google.auth.JWT(a.client_email,null,a.private_key,d,null);Pj=function(f,l){g.authorize(function(m,p){m?l({error:m}):l({access_token:p.access_token,token_type:p.token_type,expires_in:(p.expiry_date-Date.now())/1E3})})};Uj(b,c)};w("ee.data.authenticateViaPrivateKey",Rl); +w("ee.data.setExpressionAugmenter",function(a){Sl=a||qc});var Sl=qc;w("ee.data.setAuthToken",function(a,b,c,d,e,g,f,l){e=Oj(!l,!1,e||[]);Qj=a;Rj=e;Tj(void 0,void 0,{token_type:b,access_token:c,state:e.join(" "),expires_in:d});!1===f?g&&g():Sj(function(){g&&g()})});w("ee.data.refreshAuthToken",Uj);w("ee.data.setAuthTokenRefresher",function(a){Pj=a});w("ee.data.getAuthToken",Kj);w("ee.data.clearAuthToken",Wj);w("ee.data.getAuthClientId",function(){return Qj});w("ee.data.getAuthScopes",function(){return Rj}); +w("ee.data.setDeadline",function(a){kk=a});w("ee.data.setParamAugmenter",function(a){ek=a||qc}); +var Tl=function(a){a=new K(a);return a.handle(a.Fc().list(Ej(),{prettyPrint:!1}).then(Bk))},Wl=function(a,b){if("string"===typeof a.image)throw Error("Image as JSON string not supported.");if(void 0!==a.version)throw Error("Image version specification not supported.");a=new Yf({name:null,l:Sl(ml(a.image)),F:uk(a.format),da:xk(a.bands),ic:Ak(a)});var c={fields:"name"},d=Ul();d&&(c.workloadTag=d);b=new K(b);return b.handle(b.maps().create(Ej(),a,c).then(function(e){return Vl(e.name,"")}))}; +w("ee.data.getMapId",Wl);var Xl=function(a,b,c,d){if(!a.formatTileUrl){var e=Vl(a.mapid,a.token,a.urlFormat);a.urlFormat=e.urlFormat;a.formatTileUrl=e.formatTileUrl}return a.formatTileUrl(b,c,d)};w("ee.data.getTileUrl",Xl); +var Vl=function(a,b,c){var d=void 0===c?"":c;d||(Cj(),c=Zj,d=b?c+"/map/"+a+"/{z}/{x}/{y}?token="+b:c+"/v1/"+a+"/tiles/{z}/{x}/{y}");return{mapid:a,token:b,formatTileUrl:function(e,g,f){var l=Math.pow(2,f);e%=l;e=String(0>e?e+l:e);return d.replace("{x}",e).replace("{y}",g).replace("{z}",f)},urlFormat:d}},Yl=function(a,b){var c=a.visParams?Sl(ml(a.visParams)):null;a=new rg({name:null,Nb:a.assetId,mapName:a.mapName,Cg:c});b=new K(b);return b.handle((new yh(b.M)).create(Ej(),a,{fields:["name"]}).then(function(d){var e= +d.name.split("/").pop();return{token:e,formatTileUrl:function(g,f,l){return Zj+"/v1/"+d.name+"/tiles/"+l+"/"+g+"/"+f},rawTilesKey:e.substring(0,e.lastIndexOf("-"))}}))};w("ee.data.getFeatureViewTilesKey",Yl); +var Zl=function(a,b,c){c=new K(c);var d=Gk(a);a=c.handle;var e=c.assets();b=void 0===b?{}:b;var g=void 0===g?{}:g;e.j.C(d,RegExp("^projects/[^/]+/assets/.*$"));g=I(e.j,{body:null,B:"GET",D:"earthengine.projects.assets.listFeatures",path:"/"+e.m+"/"+d+":listFeatures",u:H(b,g),G:Wg});return a.call(c,g)};w("ee.data.listFeatures",Zl); +var $l=function(a,b){var c=new dl(!0);a=ol(c,a);var d={};c=ql(c,a);Object.keys(c).length&&(d={__source_code_nodes__:c});c={l:Sl(a,d)};if(a=Ul())c.workloadTag=a;b=new K(b);return b.handle(Ih(b.value(),Ej(),new Gf(c)).then(function(e){return e.result}))};w("ee.data.computeValue",$l); +var am=function(a,b){if("string"===typeof a.image)throw Error("Image as JSON string not supported.");if(void 0!==a.version)throw Error("Image version specification not supported.");if(void 0!==a.region)throw Error('"region" not supported in call to ee.data.getThumbId. Use ee.Image.getThumbURL.');if(void 0!==a.dimensions)throw Error('"dimensions" is not supported in call to ee.data.getThumbId. Use ee.Image.getThumbURL.');a=new ph({name:null,l:Sl(ml(a.image)),F:uk(a.format),xa:a.name,da:xk(a.bands), +ic:Ak(a),grid:null});var c={fields:"name"},d=Ul();d&&(c.workloadTag=d);b=new K(b);return b.handle((new Gh(b.M)).create(Ej(),a,c).then(function(e){return{thumbid:e.name,token:""}}))};w("ee.data.getThumbId",am); +var bm=function(a,b){var c=new og({framesPerSecond:a.framesPerSecond||null,maxFrames:a.maxFrames||null,maxPixelsPerFrame:a.maxPixelsPerFrame||null});a=new th({name:null,l:Sl(ml(a.imageCollection)),F:uk(a.format),Pa:c,grid:null});c={fields:"name"};var d=Ul();d&&(c.workloadTag=d);b=new K(b);return b.handle((new Lh(b.M)).create(Ej(),a,c).then(function(e){return{thumbid:e.name,token:""}}))};w("ee.data.getVideoThumbId",bm); +var cm=function(a,b){a=new zg({name:null,l:Sl(ml(a.imageCollection)),F:uk(a.format),orientation:wk(a.orientation),grid:null});var c={fields:"name"},d=Ul();d&&(c.workloadTag=d);b=new K(b);return b.handle((new zh(b.M)).create(Ej(),a,c).then(function(e){return{thumbid:e.name,token:""}}))};w("ee.data.getFilmstripThumbId",cm);var dm=function(a){return Zj+"/v1/"+a.thumbid+":getPixels"};w("ee.data.makeThumbUrl",dm); +var fm=function(a,b){a=Object.assign({},a);a.id&&(a.image=new N(a.id));if("string"===typeof a.image)throw Error("Image as serialized JSON string not supported.");if(!a.image)throw Error("Missing ID or image parameter.");a.filePerBand=!1!==a.filePerBand;a.format=a.format||(a.filePerBand?"ZIPPED_GEO_TIFF_PER_BAND":"ZIPPED_GEO_TIFF");if(null!=a.region&&(null!=a.scale||null!=a.crs_transform)&&null!=a.dimensions)throw Error("Cannot specify (bounding region, crs_transform/scale, dimensions) simultaneously."); +if("string"===typeof a.bands)try{a.bands=JSON.parse(a.bands)}catch(e){a.bands=xk(a.bands)}if(a.bands&&!Array.isArray(a.bands))throw Error("Bands parameter must be an array.");a.bands&&a.bands.every(function(e){return"string"===typeof e})&&(a.bands=a.bands.map(function(e){return{id:e}}));if(a.bands&&a.bands.some(function(e){return null==e.id}))throw Error("Each band dictionary must have an id.");"string"===typeof a.region&&(a.region=JSON.parse(a.region));if("string"===typeof a.crs_transform)try{a.crs_transform= +JSON.parse(a.crs_transform)}catch(e){}var c=em(a.image,a);a=new ph({name:null,l:Sl(ml(c)),F:uk(a.format),xa:a.name,da:a.bands&&xk(a.bands.map(function(e){return e.id})),grid:null});c={fields:"name"};var d=Ul();d&&(c.workloadTag=d);b=new K(b);return b.handle((new Gh(b.M)).create(Ej(),a,c).then(function(e){return{docid:e.name,token:""}}))};w("ee.data.getDownloadId",fm);var gm=function(a){Cj();return Zj+"/v1/"+a.docid+":getPixels"};w("ee.data.makeDownloadUrl",gm); +var hm=function(a,b){b=new K(b);var c=vk(a.format),d=Sl(ml(a.table)),e=null;if(null!=a.selectors)if("string"===typeof a.selectors)e=a.selectors.split(",");else if(Array.isArray(a.selectors)&&a.selectors.every(function(g){return"string"===typeof g}))e=a.selectors;else throw Error("'selectors' parameter must be an array of strings.");a=new nh({name:null,l:d,F:c,selectors:e,filename:a.filename||null});c={fields:"name"};if(d=Ul())c.workloadTag=d;return b.handle((new Fh(b.M)).create(Ej(),a,c).then(function(g){return{docid:g.name|| +"",token:""}}))};w("ee.data.getTableDownloadId",hm);var im=function(a){return Zj+"/v1/"+a.docid+":getFeatures"};w("ee.data.makeTableDownloadUrl",im);var jm=function(a,b){var c=function(d){return Math.floor(Math.random()*Math.pow(2,4*d)).toString(16).padStart(d,"0")};a=fb(a||1).map(function(){return[c(8),c(4),"4"+c(3),(8+Math.floor(4*Math.random())).toString(16)+c(3),c(12)].join("-")});return b?b(a):a};w("ee.data.newTaskId",jm); +var km=function(a){return a.name&&a.done&&a.error},mm=function(a,b){var c=lm(a).map(Sk);if(1===c.length)return a=(new K(b)).rd(km),a.handle(a.Na().get(c[0]).then(function(e){return[Tk(e)]}));a=(new Jj(b)).rd(km);var d=a.Na();return a.send(c.map(function(e){return[e,d.get(e)]}),function(e){return c.map(function(g){return Tk(e[g])})})};w("ee.data.getTaskStatus",mm); +var lm=function(a){if("string"===typeof a)return[a];if(Array.isArray(a))return a;throw Error("Invalid value: expected a string or an array of strings.");},om=function(a){return nm(void 0,a)};w("ee.data.getTaskList",om);var nm=function(a,b){return b?(pm(a,function(c,d){return b(c?{tasks:c.map(Tk)}:null,d)}),null):{tasks:pm(a).map(Tk)}};w("ee.data.getTaskListWithLimit",nm); +var pm=function(a,b){var c=[],d={pageSize:500},e=function(l){db(c,l.Na||[]);!l.nextPageToken||a&&c.length>=a?b&&b(a?c.slice(0,a):c):(d.pageToken=l.nextPageToken,g.handle(f.list(Ej(),d).then(e)));return null},g=new K(b?function(l,m){return m&&b(l,m)}:void 0),f=g.Na();g.handle(f.list(Ej(),d).then(e));return b?null:a?c.slice(0,a):c};w("ee.data.listOperations",pm); +var qm=function(a,b){a=lm(a).map(Sk);var c=new zf;if(1===a.length)b=new K(b),b.handle(b.Na().cancel(a[0],c));else{b=new Jj(b);var d=b.Na();b.send(a.map(function(e){return[e,d.cancel(e,c)]}))}};w("ee.data.cancelOperation",qm);var rm=function(a,b){var c=lm(a).map(Sk);if(!Array.isArray(a))return a=(new K(b)).rd(km),a.handle(a.Na().get(c[0]));a=(new Jj(b)).rd(km);var d=a.Na();return a.send(c.map(function(e){return[e,d.get(e)]}))};w("ee.data.getOperation",rm); +var tm=function(a,b){return sm(a,"CANCEL",b)};w("ee.data.cancelTask",tm);var sm=function(a,b,c){if(!Mb(um,b))throw Error("Invalid action: "+b);a=lm(a);a=a.map(Sk);qm(a,c);return null};w("ee.data.updateTask",sm); +var Am=function(a,b,c){b.id=a;var d=b.type;a=null!=b.sourceUrl?{__source_url__:b.sourceUrl}:{};if("workloadTag"in b)b.workloadTag||delete b.workloadTag;else{var e=Ul();e&&(b.workloadTag=e)}var g=new K(c);c=function(f){return g.handle(f.then(Uk))};switch(d){case "EXPORT_IMAGE":return b=vm(b,a),c(g.image().Ka(Ej(),b));case "EXPORT_FEATURES":return b=Fl(b),b.l=Sl(b.l,a),c(g.table().Ka(Ej(),b));case "EXPORT_VIDEO":return b=wm(b,a),c(g.video().Ka(Ej(),b));case "EXPORT_TILES":return b=xm(b,a),c(g.map().Ka(Ej(), +b));case "EXPORT_VIDEO_MAP":return b=ym(b),c((new Kh(g.M)).Ka(Ej(),b));case "EXPORT_CLASSIFIER":return b=zm(b,a),c((new xh(g.M)).Ka(Ej(),b));default:throw Error("Unable to start processing for task of type "+d);}};w("ee.data.startProcessing",Am); +var vm=function(a,b){var c=Bm(a);if(null==c.element)throw Error('"element" not found in params '+c);var d=new bg({l:ml(c.element),description:L(c.description),oa:null,sa:null,grid:null,Qf:L(c.maxPixels),requestId:L(c.id),O:M(c.maxWorkers),priority:M(c.priority)}),e=Al(c);switch(e){case "GOOGLE_CLOUD_STORAGE":case "DRIVE":d.oa=Il(c,e);break;case "ASSET":e=c.pyramidingPolicy||{};try{e=JSON.parse(e)}catch(f){}var g="PYRAMIDING_POLICY_UNSPECIFIED";"string"===typeof e?(g=e,e={}):e[".default"]&&(g=e[".default"], +delete e[".default"]);c=new cg({Ha:Dl(c),pyramidingPolicy:g,Vf:Pb(e)?null:e,tileSize:M(c.shardSize)});d.sa=c;break;default:throw Error('Export destination "'+e+'" unknown');}d.l=Sl(d.l,b);a.workloadTag&&(d.workloadTag=a.workloadTag);return d},wm=function(a,b){var c=Cm(a);if(null==c.element)throw Error('"element" not found in params '+c);var d=new pg({l:ml(c.element),description:L(c.description),Pa:new og({framesPerSecond:M(c.framesPerSecond),maxFrames:M(c.maxFrames),maxPixelsPerFrame:L(c.maxPixels)}), +oa:null,requestId:L(c.id),O:M(c.maxWorkers),priority:M(c.priority)});d.oa=Jl(c,Al(c));d.l=Sl(d.l,b);a.workloadTag&&(d.workloadTag=a.workloadTag);return d},xm=function(a,b){var c=a.scale;delete a.scale;var d=Bm(a);d.scale=c;if(null==d.element)throw Error('"element" not found in params '+d);c=new fg({l:ml(d.element),description:L(d.description),ec:Kl(d),dc:Il(d,"GOOGLE_CLOUD_STORAGE"),requestId:L(d.id),O:M(d.maxWorkers),priority:M(d.priority)});c.l=Sl(c.l,b);a.workloadTag&&(c.workloadTag=a.workloadTag); +return c},ym=function(a){var b=a.scale;delete a.scale;var c=Cm(a);c.scale=b;if(null==c.element)throw Error('"element" not found in params '+c);b=new mg({l:ml(c.element),description:L(c.description),Pa:new og({framesPerSecond:M(c.framesPerSecond),maxFrames:M(c.maxFrames),maxPixelsPerFrame:null}),ec:Kl(c),dc:Jl(c,"GOOGLE_CLOUD_STORAGE"),requestId:L(c.id),version:L(c.version),O:M(c.maxWorkers),priority:M(c.priority)});b.l=Sl(b.l);a.workloadTag&&(b.workloadTag=a.workloadTag);return b},zm=function(a,b){if(null== +a.element)throw Error('"element" not found in params '+a);var c=Al(a);if("ASSET"!=c)throw Error('Export destination "'+c+'" unknown');c=new ag({l:ml(a.element),description:L(a.description),requestId:L(a.id),sa:new Bf({Ha:Dl(a)}),O:M(a.maxWorkers),priority:M(a.priority)});c.l=Sl(c.l,b);a.workloadTag&&(c.workloadTag=a.workloadTag);return c},Em=function(a,b,c){b=Yk(b);var d=function(e){return e?Uk(e):null};return d(Dm(a,b,c&&function(e,g){return c(d(e),g)}))};w("ee.data.startIngestion",Em); +var Dm=function(a,b,c){b=new Rg({Bf:b,requestId:a,overwrite:null,description:null});a=new K(c,a?void 0:0);return a.handle(a.image().import(Ej(),b))},Fm=function(a,b,c){b=new Sg({qg:b,requestId:a,overwrite:null,description:null});a=new K(c,a?void 0:0);return a.handle(a.table().import(Ej(),b))},Gm=function(a,b,c){b=Zk(b);var d=function(e){return e?Uk(e):null};return d(Fm(a,b,c&&function(e,g){return c(d(e),g)}))};w("ee.data.startTableIngestion",Gm); +var Hm=function(a,b){b=new K(b);a=Gk(a);return b.handle(b.assets().get(a,{prettyPrint:!1}).then(Hk))};w("ee.data.getAsset",Hm);w("ee.data.getInfo",Hm); +var Im=function(a,b,c,d){b=void 0===b?{}:b;d=void 0===d?qc:d;var e=Object.assign({},b),g=new K(c),f=(b=Ek.test(a))?new vh(g.M):g.assets();a=b?"projects/"+Fk(a):Gk(a);var l=function(m){if(null!=e.pageSize||!m.nextPageToken)return m;var p=m.assets||[];e.pageToken=m.nextPageToken;m=f.Uc(a,e).then(function(v){v.assets=p.concat(v.assets);return v}).then(l);return c?m:g.handle(m)};return g.handle(f.Uc(a,e).then(l).then(d))},Jm=function(a,b){var c=Ok(a);c.pageSize||(c.pageSize=1E3);return Im(a.id,c,b,function(d){return null== +d?null:Ik(d)})};w("ee.data.getList",Jm);var Km=function(a,b,c){b=void 0===b?{}:b;return Im(a,b,c)};w("ee.data.listAssets",Km);var Lm=function(a,b,c){b=void 0===b?{}:b;b=Nk(b);return Im(a,b,c,function(d){if(null==d)return null;var e={};d.assets&&(e.images=d.assets);d.nextPageToken&&(e.nextPageToken=d.nextPageToken);return e})};w("ee.data.listImages",Lm);var Mm=function(a,b){b=new K(b);return b.handle((new vh(b.M)).Uc(a||Ej()))};w("ee.data.listBuckets",Mm);var Nm=function(a){a=new K(a);return a.handle((new vh(a.M)).Uc(Ej()).then(Ik))}; +w("ee.data.getAssetRoots",Nm);var Om=function(a,b){var c="projects/"+Fk(a);a="projects/earthengine-legacy"===c?a:void 0;var d=new Rf({type:"Folder"});b=new K(b);b.handle(b.assets().create(c,d,{assetId:a}).then(Hk))};w("ee.data.createAssetHome",Om); +var Pm=function(a,b,c,d,e){if(c)throw Error("Asset overwrite not supported.");if("string"===typeof a)throw Error("Asset cannot be specified as string.");b=a.name||b&&Gk(b);if(!b)throw Error("Either asset name or opt_path must be specified.");c=b.indexOf("/assets/");if(-1===c)throw Error("Asset name must contain /assets/.");a=Object.assign({},a);a.tilestoreEntry&&!a.tilestoreLocation&&(a.tilestoreLocation=a.tilestoreEntry,delete a.tilestoreEntry);a.tilestoreLocation&&(a.tilestoreLocation=new Xf(a.tilestoreLocation)); a.gcsLocation&&!a.cloudStorageLocation&&(a.cloudStorageLocation=a.gcsLocation,delete a.gcsLocation);a.cloudStorageLocation&&(a.cloudStorageLocation=new Ff(a.cloudStorageLocation));d&&!a.properties&&(a.properties=Object.assign({},d));d=n(["title","description"]);for(var g=d.next();!g.done;g=d.next())if(g=g.value,a[g]){var f={};a.properties=Object.assign((f[g]=a[g],f),a.properties||{});delete a[g]}a=new Rf(a);d=b.slice(0,c);b=b.slice(c+8);a.id=null;a.name=null;a:switch(c=a.type,c){case "ImageCollection":c= -"IMAGE_COLLECTION";break a;case "Folder":c="FOLDER"}a.type=c;e=new K(e);return e.handle(e.assets().create(d,a,{assetId:b}).then(Jk))};w("ee.data.createAsset",Rm);var Sm=function(a,b,c){return Rm({type:"Folder"},a,b,void 0,c)};w("ee.data.createFolder",Sm);var Tm=function(a,b,c){a=Ik(a);b=Ik(b);b=new $g({Qb:b});c=new K(c);c.handle(c.assets().move(a,b).then(Jk))};w("ee.data.renameAsset",Tm); -var Um=function(a,b,c,d){a=Ik(a);b=Ik(b);c=new Kf({Qb:b,overwrite:null!=c?c:null});d=new K(d);b=d.handle;var e=d.assets();var g=void 0===g?{}:g;var f=void 0===f?{}:f;e.j.C(a,RegExp("^projects/[^/]+/assets/.*$"));g=I(e.j,{body:c,B:"POST",D:"earthengine.projects.assets.copy",path:"/"+e.m+"/"+a+":copy",u:H(g,f),G:Rf});b.call(d,g.then(Jk))};w("ee.data.copyAsset",Um);var Vm=function(a,b){b=new K(b);b.handle(b.assets().delete(Ik(a)))};w("ee.data.deleteAsset",Vm); -var Wm=function(a,b){a=Ik(a);var c=new Fg;b=new K(b);var d=b.handle,e=b.assets(),g={prettyPrint:!1};g=void 0===g?{}:g;var f=void 0===f?{}:f;e.j.C(a,RegExp("^projects/[^/]+/assets/.*$"));f=I(e.j,{body:c,B:"POST",D:"earthengine.projects.assets.getIamPolicy",path:"/"+e.m+"/"+a+":getIamPolicy",u:H(g,f),G:lg});return d.call(b,f.then(Rk))};w("ee.data.getAssetAcl",Wm); -var Xm=function(a,b,c,d){b=new sh({Nb:b,updateMask:(c||[]).join(",")});d=new K(d);c=d.handle;var e=d.assets();a=Ik(a);var g=void 0===g?{}:g;var f=void 0===f?{}:f;e.j.C(a,RegExp("^projects/[^/]+/assets/.*$"));g=I(e.j,{body:b,B:"PATCH",D:"earthengine.projects.assets.patch",path:"/"+e.m+"/"+a,u:H(g,f),G:Rf});return c.call(d,g.then(Jk))};w("ee.data.updateAsset",Xm); -var Ym=function(a,b,c){a=Ik(a);b=Sk(b);b=new mh({kb:b});c=new K(c);var d=c.handle,e=c.assets(),g={prettyPrint:!1};g=void 0===g?{}:g;var f=void 0===f?{}:f;e.j.C(a,RegExp("^projects/[^/]+/assets/.*$"));f=I(e.j,{body:b,B:"POST",D:"earthengine.projects.assets.setIamPolicy",path:"/"+e.m+"/"+a+":setIamPolicy",u:H(g,f),G:lg});d.call(c,f)};w("ee.data.setAssetAcl",Ym); -var Zm=function(a,b,c){var d=Nk(b);b=Sc(d.i()).keys.filter(function(e){return"properties"!==e&&G(d,e)}).map(function(e){return e.replace(/([A-Z])/g,function(g,f){return"_"+f.toLowerCase()})}).concat(Object.keys(d.properties||{}).map(function(e){return'properties."'+e+'"'}));Xm(a,d,b,c)};w("ee.data.setAssetProperties",Zm); -var $m=function(a,b){var c=Ik(a);b=new K(b);var d=b.assets(),e=d.j.C;d.j.C=function(g,f){"^projects\\/[^/]+\\/assets\\/.+$"===f.source&&(f=RegExp("^projects/[^/]+/assets/.*$"));return e(g,f)};c=d.get(c,{prettyPrint:!1});return b.handle(c.then(function(g){if(!(g instanceof Rf&&g.quota))throw Error(a+" is not a root folder.");g=g.quota;return{asset_count:{usage:Number(g.Pe||0),limit:Number(g.Nf||0)},asset_size:{usage:Number(g.sizeBytes||0),limit:Number(g.Wb||0)}}}))};w("ee.data.getAssetRootQuota",$m); -var an=function(){this.default=this.tag=""};an.prototype.get=function(){return this.tag};an.prototype.set=function(a){this.tag=this.validate(a)};an.prototype.reset=function(){this.tag=this.default}; -an.prototype.validate=function(a){if(null==a||""===a)return"";a=String(a);if(!/^([a-z0-9]|[a-z0-9][-_a-z0-9]{0,61}[a-z0-9])$/g.test(a))throw Error('Invalid tag, "'+a+'". Tags must be 1-63 characters, beginning and ending with a lowercase alphanumeric character ([a-z0-9]) with dashes (-), underscores (_), and lowercase alphanumerics between.');return a};var Wl=function(){return Pl(an).get()};w("ee.data.getWorkloadTag",Wl);var bn=function(a){Pl(an).set(a)};w("ee.data.setWorkloadTag",bn); -var cn=function(a){var b=Pl(an);b.default=b.validate(a);Pl(an).set(a)};w("ee.data.setDefaultWorkloadTag",cn);var dn=function(a){a&&(a=Pl(an),a.default=a.validate(""));Pl(an).reset()};w("ee.data.resetWorkloadTag",dn);var wm={Hl:"CANCEL",Kl:"UPDATE"};var O=function(a,b,c){if(!(this instanceof O))return en(O,arguments);if(c&&(a||b))throw Error('When "opt_varName" is specified, "func" and "args" must be null.');if(a&&!b)throw Error('When "func" is specified, "args" must not be null.');this.I=a;this.args=b;this.U=c||null;this.zl=null};x(O,nk);w("ee.ComputedObject",O);O.prototype.Eh=function(){return this.zl};O.prototype.evaluate=function(a){if(!a||"function"!==typeof a)throw Error("evaluate() requires a callback function.");bm(this,a)}; -O.prototype.evaluate=O.prototype.evaluate;O.prototype.V=function(a){return bm(this,a)};O.prototype.getInfo=O.prototype.V;O.prototype.encode=function(a){if(null===this.I&&null===this.args)return{type:"ArgumentRef",value:this.U};var b={},c;for(c in this.args)void 0!==this.args[c]&&(b[c]=a(this.args[c]));b={type:"Invocation",arguments:b};a=a(this.I);b["string"===typeof a?"functionName":"function"]=a;return b}; -O.prototype.na=function(a){if(null===this.I&&null===this.args){a=this.U||a.Qi;if(!a)throw Error("A mapped function's arguments cannot be used in client-side operations");return new rf({Mb:a})}var b={},c;for(c in this.args)void 0!==this.args[c]&&(b[c]=qk(wl(a,this.args[c])));return"string"===typeof this.I?tk(String(this.I),b):this.I.Ed(a,b)};O.prototype.va=function(a){return(void 0===a?0:a)?kl(this):ul(this)};O.prototype.serialize=O.prototype.va; -O.prototype.toString=function(){return"ee."+this.name()+"("+ml(this)+")"};w("ee.ComputedObject.prototype.toString",O.prototype.toString);O.prototype.name=function(){return"ComputedObject"};O.prototype.Gg=function(a,b){var c=Array.from(arguments);c[0]=this;a.apply(r,c);return this};O.prototype.aside=O.prototype.Gg; -var fn=function(a,b){if(b instanceof a.constructor)return b;var c=function(){};c.prototype=a.constructor.prototype;a=new c;a.I=b.I;a.args=b.args;a.U=b.U;return a},en=function(a,b){function c(){return a.apply(this,b)}c.prototype=a.prototype;return new c};var gn={},hn=function(a,b){if(b==a)return!0;switch(a){case "Element":return"Element"==b||"Image"==b||"Feature"==b||"Collection"==b||"ImageCollection"==b||"FeatureCollection"==b;case "FeatureCollection":case "Collection":return"Collection"==b||"ImageCollection"==b||"FeatureCollection"==b;case "Object":return!0;default:return!1}},jn=function(a){return"number"===typeof a||a instanceof O&&"Number"==a.name()},kn=function(a){return"string"===typeof a||a instanceof O&&"String"==a.name()},ln=function(a){return t(a)&& -"function"!==typeof a?(a=Object.getPrototypeOf(a),null!==a&&null===Object.getPrototypeOf(a)):!1},mn=function(a,b,c){c=void 0===c?!1:c;return 1===a.length&&ln(a[0])&&(a=b.args,c&&(a=a.slice(1)),a.length)?!(1===a.length||a[1].optional)||"Dictionary"!==a[0].type:!1};var nn=function(){if(!(this instanceof nn))return new nn};x(nn,nk);w("ee.Function",nn);var on=qc;nn.prototype.call=function(a){return this.apply(pn(this,Array.prototype.slice.call(arguments,0)))};nn.prototype.call=nn.prototype.call;nn.prototype.apply=function(a){a=new O(this,qn(this,a));return on(a,this.Z().returns)};nn.prototype.apply=nn.prototype.apply; -var rn=function(a,b,c){var d=void 0!==b,e=a.Z();if(mn(c,e,d)){if(c=Rb(c[0]),d){d=e.args[0].name;if(d in c)throw Error("Named args for "+e.name+" can't contain keyword "+d);c[d]=b}}else c=pn(a,d?[b].concat(c):c);return a.apply(c)},qn=function(a,b){for(var c=a.Z().args,d={},e={},g=0;g/,"");for(var l=0;l/,"");return new P(f,g)}),a&&a())};a?Vl(c):c(Vl())}},An=function(a,b,c,d){wn();var e=d||"";Fb(un,function(g,f){var l=f.split(".");if(2==l.length&&l[0]==b){l=e+l[1];var m=g.Z();vn[f]=!0;var p=!1;m.args.length&&(f=m.args[0].type,p="Object"!=f&&hn(f,c)); -f=p?a.prototype:a;l in f&&!f[l].signature||(f[l]=function(v){return rn(g,p?this:void 0,Array.prototype.slice.call(arguments,0))},f[l].toString=u(g.toString,g,l,p),f[l].signature=m)}})},Bn=function(a){var b=function(c){for(var d in c)"function"===typeof c[d]&&c[d].signature&&delete c[d]};b(a);b(a.prototype||{})};var R=function(a,b){var c=Cn(a),d=Wa(c,function(m){return m.replace(/^opt_/,"")});a=(a=r.EXPORTED_FN_INFO?r.EXPORTED_FN_INFO[a.toString()].name.split(".").pop()+"()":null)?" to function "+a:"";var e={},g=b[0],f=t(g)&&"function"!==typeof g&&!Array.isArray(g)&&"Object"===Object.getPrototypeOf(g).constructor.name;if(1d.length)throw Error("Received too many arguments"+a+". Expected at most "+d.length+" but got "+b.length+".");for(g=0;g=arguments.length){var c=arguments[0];if(b=ln(c))a:{b=Lb(c);var d=["properties"];if(ya(b)&&ya(d)&&b.length==d.length){for(var e=b.length,g=0;g/,"");for(var l=0;l/,"");return new P(f,g)}),a&&a())};a?Tl(c):c(Tl())}},yn=function(a,b,c,d){un();var e=d||"";Fb(sn,function(g,f){var l=f.split(".");if(2==l.length&&l[0]==b){l=e+l[1];var m=g.Z();tn[f]=!0;var p=!1;m.args.length&&(f=m.args[0].type,p="Object"!=f&&fn(f,c)); +f=p?a.prototype:a;l in f&&!f[l].signature||(f[l]=function(v){return pn(g,p?this:void 0,Array.prototype.slice.call(arguments,0))},f[l].toString=u(g.toString,g,l,p),f[l].signature=m)}})},zn=function(a){var b=function(c){for(var d in c)"function"===typeof c[d]&&c[d].signature&&delete c[d]};b(a);b(a.prototype||{})};var R=function(a,b){var c=An(a),d=Wa(c,function(m){return m.replace(/^opt_/,"")});a=(a=r.EXPORTED_FN_INFO?r.EXPORTED_FN_INFO[a.toString()].name.split(".").pop()+"()":null)?" to function "+a:"";var e={},g=b[0],f=t(g)&&"function"!==typeof g&&!Array.isArray(g)&&"Object"===Object.getPrototypeOf(g).constructor.name;if(1d.length)throw Error("Received too many arguments"+a+". Expected at most "+d.length+" but got "+b.length+".");for(g=0;g=arguments.length){var c=arguments[0];if(b=jn(c))a:{b=Lb(c);var d=["properties"];if(ya(b)&&ya(d)&&b.length==d.length){for(var e=b.length,g=0;ga))throw Error("Geometry.BBox: west must not be "+a);if(!(-Infinity=b))throw Error("Geometry.BBox: south must be at most +90\u00b0, but was "+b+"\u00b0");if(!(-90<=d))throw Error("Geometry.BBox: north must be at least -90\u00b0, but was "+ -d+"\u00b0");b=Math.max(b,-90);d=Math.min(d,90);360<=c-a?(a=-180,c=180):(a=Tn(a),c=Tn(c),ca&&(a+=360);return a},Un=function(a,b,c,d){if(!(this instanceof Un))return Nn(Un,arguments);T.call(this,On(Un,"LineString",2,arguments))};x(Un,T);T.LineString=Un; -var Vn=function(a,b,c,d){if(!(this instanceof Vn))return Nn(Vn,arguments);T.call(this,On(Vn,"LinearRing",2,arguments))};x(Vn,T);T.LinearRing=Vn;var Wn=function(a,b,c,d){if(!(this instanceof Wn))return Nn(Wn,arguments);T.call(this,On(Wn,"MultiLineString",3,arguments))};x(Wn,T);T.MultiLineString=Wn;var Xn=function(a,b,c,d,e){if(!(this instanceof Xn))return Nn(Xn,arguments);T.call(this,On(Xn,"Polygon",3,arguments))};x(Xn,T);T.Polygon=Xn; -var Yn=function(a,b,c,d,e){if(!(this instanceof Yn))return Nn(Yn,arguments);T.call(this,On(Yn,"MultiPolygon",4,arguments))};x(Yn,T);T.MultiPolygon=Yn; +var Jn=!1,Hn=function(){Jn||(yn(T,"Geometry","Geometry"),Jn=!0)},Kn=function(a,b){if(!(this instanceof Kn))return Ln(Kn,arguments);var c=Mn(Kn,"Point",1,arguments);if(!(c instanceof O)){var d=c.coordinates;if(!Array.isArray(d)||2!=d.length)throw Error("The Geometry.Point constructor requires 2 coordinates.");}T.call(this,c)};x(Kn,T);T.Point=Kn;var Nn=function(a,b){if(!(this instanceof Nn))return Ln(Nn,arguments);T.call(this,Mn(Nn,"MultiPoint",2,arguments))};x(Nn,T);T.MultiPoint=Nn; +var On=function(a,b,c,d){if(!(this instanceof On))return Ln(On,arguments);var e=Mn(On,"Rectangle",2,arguments);if(!(e instanceof O)){var g=e.coordinates;if(2!=g.length)throw Error("The Geometry.Rectangle constructor requires 2 points or 4 coordinates.");var f=g[0][0],l=g[0][1],m=g[1][0];g=g[1][1];e.coordinates=[[[f,g],[f,l],[m,l],[m,g]]];e.type="Polygon"}T.call(this,e)};x(On,T);T.Rectangle=On; +var Pn=function(a,b,c,d){if(!(this instanceof Pn))return Ln(Pn,arguments);var e=R(Pn,arguments);a=e.west;b=e.south;c=e.east;d=e.north;e=[a,b,c,d];if(Qn(e)){var g;return(g=new P("GeometryConstructors.BBox")).call.apply(g,ka(e))}if(!(Infinity>a))throw Error("Geometry.BBox: west must not be "+a);if(!(-Infinity=b))throw Error("Geometry.BBox: south must be at most +90\u00b0, but was "+b+"\u00b0");if(!(-90<=d))throw Error("Geometry.BBox: north must be at least -90\u00b0, but was "+ +d+"\u00b0");b=Math.max(b,-90);d=Math.min(d,90);360<=c-a?(a=-180,c=180):(a=Rn(a),c=Rn(c),ca&&(a+=360);return a},Sn=function(a,b,c,d){if(!(this instanceof Sn))return Ln(Sn,arguments);T.call(this,Mn(Sn,"LineString",2,arguments))};x(Sn,T);T.LineString=Sn; +var Tn=function(a,b,c,d){if(!(this instanceof Tn))return Ln(Tn,arguments);T.call(this,Mn(Tn,"LinearRing",2,arguments))};x(Tn,T);T.LinearRing=Tn;var Un=function(a,b,c,d){if(!(this instanceof Un))return Ln(Un,arguments);T.call(this,Mn(Un,"MultiLineString",3,arguments))};x(Un,T);T.MultiLineString=Un;var Vn=function(a,b,c,d,e){if(!(this instanceof Vn))return Ln(Vn,arguments);T.call(this,Mn(Vn,"Polygon",3,arguments))};x(Vn,T);T.Polygon=Vn; +var Wn=function(a,b,c,d,e){if(!(this instanceof Wn))return Ln(Wn,arguments);T.call(this,Mn(Wn,"MultiPolygon",4,arguments))};x(Wn,T);T.MultiPolygon=Wn; T.prototype.encode=function(a){if(!this.ob){if(!a)throw Error("Must specify an encode function when encoding a computed geometry.");return O.prototype.encode.call(this,a)}a={type:this.ob};"GeometryCollection"==this.ob?a.geometries=this.Dh:a.coordinates=this.dh;null!=this.ac&&(a.crs={type:"name",properties:{name:this.ac}});null!=this.qc&&(a.geodesic=this.qc);null!=this.oc&&(a.evenOdd=this.oc);return a}; -T.prototype.oe=function(){if(this.I)throw Error("Can't convert a computed Geometry to GeoJSON. Use evaluate() instead.");return this.encode()};T.prototype.toGeoJSON=T.prototype.oe;T.prototype.yg=function(){if(this.I)throw Error("Can't convert a computed Geometry to GeoJSON. Use evaluate() instead.");return(new dj).va(this.oe())};T.prototype.toGeoJSONString=T.prototype.yg;T.prototype.va=function(a){return(void 0===a?0:a)?kl(this):ul(this)};T.prototype.serialize=T.prototype.va; +T.prototype.pe=function(){if(this.I)throw Error("Can't convert a computed Geometry to GeoJSON. Use evaluate() instead.");return this.encode()};T.prototype.toGeoJSON=T.prototype.pe;T.prototype.yg=function(){if(this.I)throw Error("Can't convert a computed Geometry to GeoJSON. Use evaluate() instead.");return(new dj).va(this.pe())};T.prototype.toGeoJSONString=T.prototype.yg;T.prototype.va=function(a){return(void 0===a?0:a)?il(this):sl(this)};T.prototype.serialize=T.prototype.va; T.prototype.toString=function(){return"ee.Geometry("+this.yg()+")"}; T.prototype.na=function(a){if(!this.ob){if(!a)throw Error("Must specify a serializer when encoding a computed geometry.");return O.prototype.na.call(this,a)}var b={},c="";"GeometryCollection"===this.ob?(b.geometries=this.Dh.map(function(e){return new T(e)}),c="GeometryConstructors.MultiGeometry"):(b.coordinates=this.dh,c="GeometryConstructors."+this.ob);null!=this.ac&&(b.crs="string"===typeof this.ac?(new P("Projection")).call(this.ac):this.ac);var d="Point"!==this.ob&&"MultiPoint"!==this.ob;null!= this.qc&&d&&(b.geodesic=this.qc);null!=this.oc&&(b.evenOdd=this.oc);return(new P(c)).apply(b).na(a)}; -var Kn=function(a){var b=a.type;if("GeometryCollection"==b){b=a.geometries;if(!Array.isArray(b))return!1;for(a=0;aa||4');}d.geometry=f;break;case "region":null!= -d.geometry&&console.warn("Multiple request parameters converted to region.");if(!(f instanceof T)){l=f;if("string"===typeof f)try{l=JSON.parse(f)}catch(m){throw Error('Region string "'+f+'" is not valid GeoJSON.');}if(Array.isArray(l))f=new Xn(l,null,!1);else if(t(l))f=new T(l,null,!1);else throw Error("Region {region} was not convertible to an ee.Geometry.");}d.geometry=f;break;case "scale":d.scale=Number(f);break;default:c[l]=f}});Pb(d)||(d.input=a,a=g.some(function(f){return f in d})||e?tn("Image.clipToBoundsAndScale", -d):tn("Image.clip",d));return a},wo=function(a,b){var c=b.crs||"",d=b.crsTransform||b.crs_transform;null!=d&&(d=yo(d));if(!c&&!d)return a;if(d&&!c)throw Error('Must specify "crs" if "crsTransform" is specified.');if(d){if(a=tn("Image.reproject",{image:a,crs:c,crsTransform:d}),null!=b.dimensions&&null==b.scale&&null==b.region){var e=b.dimensions;"string"===typeof e&&(e=e.split("x").map(Number));2===e.length&&(delete b.dimensions,b.dimensions_consumed=!0,c=(new P("Projection")).call(c,d),b.region=new Qn([0, -0,e[0],e[1]],c,!1))}}else a=tn("Image.setDefaultProjection",{image:a,crs:c,crsTransform:[1,0,0,0,-1,0]});return a},yo=function(a){if("string"===typeof a)try{a=JSON.parse(a)}catch(b){}if(Array.isArray(a)){if(6===a.length&&Ya(a,function(b){return"number"===typeof b}))return a;throw Error("Invalid argument, crs transform must be a list of 6 numbers.");}throw Error("Invalid argument, crs transform was not a string or array.");},Ao=function(a,b){var c={};b=zo(b,c);Pb(b)||(b.image=a,a=tn("Image.visualize", -b));c.image=a;return c},zo=function(a,b){var c="bands gain bias min max gamma palette opacity forceRgbOutput".split(" "),d={};Fb(a,function(e,g){$a(c,g)?d[g]=e:b[g]=e});return d},gm=function(a,b){b=Object.assign({},b);var c=function(e){var g={};["crs","crs_transform","dimensions","region"].forEach(function(f){f in e&&(g[f]=e[f])});null!=e.scale&&null==e.dimensions&&(g.scale=e.scale);return g},d=function(e){var g=e.id;if(void 0===g)throw Error("Each band dictionary must have an id.");g=a.select(g); -var f=c(b);e=c(e);e=c(Object.assign(f,e));g=wo(g,e);return g=xo(g,e,{})};"ZIPPED_GEO_TIFF_PER_BAND"===b.format&&b.bands&&b.bands.length?(d=b.bands.map(d),a=d.reduce(function(e,g){return Q("Image.addBands",e,g,null,!0)},d.shift())):(d=c(b),a=wo(a,d),a=xo(a,d,{}));return a};var N=function(a){if(!(this instanceof N))return en(N,arguments);if(a instanceof N)return a;Bo();var b=arguments.length;if(0==b||1==b&&void 0===a)S.call(this,new P("Image.mask"),{image:new N(0),mask:new N(0)});else if(1==b)if(jn(a))S.call(this,new P("Image.constant"),{value:a});else if(kn(a))S.call(this,new P("Image.load"),{id:a});else{if(Array.isArray(a))return Co(Wa(a,function(d){return new N(d)}));if(a instanceof O)"Array"==a.name()?S.call(this,new P("Image.constant"),{value:a}):S.call(this,a.I, -a.args,a.U);else throw Error("Unrecognized argument type to convert to an Image: "+a);}else if(2==b){b=arguments[0];var c=arguments[1];if(kn(b)&&jn(c))S.call(this,new P("Image.load"),{id:b,version:c});else throw Error("Unrecognized argument types to convert to an Image: "+arguments);}else throw Error("The Image constructor takes at most 2 arguments ("+b+" given)");};x(N,S);w("ee.Image",N);var Do=!1,Bo=function(){Do||(An(N,"Image","Image"),Do=!0)};N.prototype.V=function(a){return N.L.V.call(this,a)}; -N.prototype.getInfo=N.prototype.V;N.prototype.S=function(a,b){var c=this,d=R(N.prototype.getMap,arguments),e=Ao(this,d.visParams);if(d.callback){var g=d.callback;Yl(e,function(f,l){f=f?Object.assign(f,{image:c}):void 0;g(f,l)})}else return d=Yl(e),d.image=this,d};N.prototype.getMapId=N.prototype.S;N.prototype.getMap=N.prototype.S;N.prototype.getMap=N.prototype.getMap; -N.prototype.sb=function(a,b){var c=R(N.prototype.sb,arguments),d=c.params?Rb(c.params):{};d.image=this;if(c.callback){var e=c.callback;hm(d,function(g,f){g?e(im(g)):e(null,f)})}else return im(hm(d))};N.prototype.getDownloadURL=N.prototype.sb;N.prototype.Kd=function(a,b){var c=R(N.prototype.sb,arguments),d=c.params?Rb(c.params):{},e={},g=wo(this,d);g=xo(g,d,e);d=Ao(g,e);return c.callback?(cm(d,c.callback),null):cm(d)};N.prototype.getThumbId=N.prototype.Kd; -N.prototype.xf=function(a,b){var c=R(N.prototype.xf,arguments);if(c.callback)this.Kd(c.params,function(d,e){var g="";if(void 0===e)try{g=fm(d)}catch(f){e=String(f.message)}c.callback(g,e)});else return fm(this.Kd(c.params))};N.prototype.getThumbURL=N.prototype.xf;var Eo=function(a,b,c){var d=R(Eo,arguments);return Co([d.r,d.g,d.b],["vis-red","vis-green","vis-blue"])};N.rgb=Eo;var Fo=function(a){return Co(Array.prototype.slice.call(arguments),null)};N.cat=Fo; -var Co=function(a,b){if(0==a.length)return Q("Image.constant",[]);for(var c=new N(a[0]),d=1;da||4');}d.geometry=f;break;case "region":null!= +d.geometry&&console.warn("Multiple request parameters converted to region.");if(!(f instanceof T)){l=f;if("string"===typeof f)try{l=JSON.parse(f)}catch(m){throw Error('Region string "'+f+'" is not valid GeoJSON.');}if(Array.isArray(l))f=new Vn(l,null,!1);else if(t(l))f=new T(l,null,!1);else throw Error("Region {region} was not convertible to an ee.Geometry.");}d.geometry=f;break;case "scale":d.scale=Number(f);break;default:c[l]=f}});Pb(d)||(d.input=a,a=g.some(function(f){return f in d})||e?rn("Image.clipToBoundsAndScale", +d):rn("Image.clip",d));return a},uo=function(a,b){var c=b.crs||"",d=b.crsTransform||b.crs_transform;null!=d&&(d=wo(d));if(!c&&!d)return a;if(d&&!c)throw Error('Must specify "crs" if "crsTransform" is specified.');if(d){if(a=rn("Image.reproject",{image:a,crs:c,crsTransform:d}),null!=b.dimensions&&null==b.scale&&null==b.region){var e=b.dimensions;"string"===typeof e&&(e=e.split("x").map(Number));2===e.length&&(delete b.dimensions,b.dimensions_consumed=!0,c=(new P("Projection")).call(c,d),b.region=new On([0, +0,e[0],e[1]],c,!1))}}else a=rn("Image.setDefaultProjection",{image:a,crs:c,crsTransform:[1,0,0,0,-1,0]});return a},wo=function(a){if("string"===typeof a)try{a=JSON.parse(a)}catch(b){}if(Array.isArray(a)){if(6===a.length&&Ya(a,function(b){return"number"===typeof b}))return a;throw Error("Invalid argument, crs transform must be a list of 6 numbers.");}throw Error("Invalid argument, crs transform was not a string or array.");},yo=function(a,b){var c={};b=xo(b,c);Pb(b)||(b.image=a,a=rn("Image.visualize", +b));c.image=a;return c},xo=function(a,b){var c="bands gain bias min max gamma palette opacity forceRgbOutput".split(" "),d={};Fb(a,function(e,g){$a(c,g)?d[g]=e:b[g]=e});return d},em=function(a,b){b=Object.assign({},b);var c=function(e){var g={};["crs","crs_transform","dimensions","region"].forEach(function(f){f in e&&(g[f]=e[f])});null!=e.scale&&null==e.dimensions&&(g.scale=e.scale);return g},d=function(e){var g=e.id;if(void 0===g)throw Error("Each band dictionary must have an id.");g=a.select(g); +var f=c(b);e=c(e);e=c(Object.assign(f,e));g=uo(g,e);return g=vo(g,e,{})};"ZIPPED_GEO_TIFF_PER_BAND"===b.format&&b.bands&&b.bands.length?(d=b.bands.map(d),a=d.reduce(function(e,g){return Q("Image.addBands",e,g,null,!0)},d.shift())):(d=c(b),a=uo(a,d),a=vo(a,d,{}));return a};var N=function(a){if(!(this instanceof N))return cn(N,arguments);if(a instanceof N)return a;zo();var b=arguments.length;if(0==b||1==b&&void 0===a)S.call(this,new P("Image.mask"),{image:new N(0),mask:new N(0)});else if(1==b)if(gn(a))S.call(this,new P("Image.constant"),{value:a});else if(hn(a))S.call(this,new P("Image.load"),{id:a});else{if(Array.isArray(a))return Ao(Wa(a,function(d){return new N(d)}));if(a instanceof O)"Array"==a.name()?S.call(this,new P("Image.constant"),{value:a}):S.call(this,a.I, +a.args,a.U);else throw Error("Unrecognized argument type to convert to an Image: "+a);}else if(2==b){b=arguments[0];var c=arguments[1];if(hn(b)&&gn(c))S.call(this,new P("Image.load"),{id:b,version:c});else throw Error("Unrecognized argument types to convert to an Image: "+arguments);}else throw Error("The Image constructor takes at most 2 arguments ("+b+" given)");};x(N,S);w("ee.Image",N);var Bo=!1,zo=function(){Bo||(yn(N,"Image","Image"),Bo=!0)};N.prototype.V=function(a){return N.L.V.call(this,a)}; +N.prototype.getInfo=N.prototype.V;N.prototype.S=function(a,b){var c=this,d=R(N.prototype.getMap,arguments),e=yo(this,d.visParams);if(d.callback){var g=d.callback;Wl(e,function(f,l){f=f?Object.assign(f,{image:c}):void 0;g(f,l)})}else return d=Wl(e),d.image=this,d};N.prototype.getMapId=N.prototype.S;N.prototype.getMap=N.prototype.S;N.prototype.getMap=N.prototype.getMap; +N.prototype.sb=function(a,b){var c=R(N.prototype.sb,arguments),d=c.params?Rb(c.params):{};d.image=this;if(c.callback){var e=c.callback;fm(d,function(g,f){g?e(gm(g)):e(null,f)})}else return gm(fm(d))};N.prototype.getDownloadURL=N.prototype.sb;N.prototype.Md=function(a,b){var c=R(N.prototype.sb,arguments),d=c.params?Rb(c.params):{},e={},g=uo(this,d);g=vo(g,d,e);d=yo(g,e);return c.callback?(am(d,c.callback),null):am(d)};N.prototype.getThumbId=N.prototype.Md; +N.prototype.xf=function(a,b){var c=R(N.prototype.xf,arguments);if(c.callback)this.Md(c.params,function(d,e){var g="";if(void 0===e)try{g=dm(d)}catch(f){e=String(f.message)}c.callback(g,e)});else return dm(this.Md(c.params))};N.prototype.getThumbURL=N.prototype.xf;var Co=function(a,b,c){var d=R(Co,arguments);return Ao([d.r,d.g,d.b],["vis-red","vis-green","vis-blue"])};N.rgb=Co;var Do=function(a){return Ao(Array.prototype.slice.call(arguments),null)};N.cat=Do; +var Ao=function(a,b){if(0==a.length)return Q("Image.constant",[]);for(var c=new N(a[0]),d=1;d"))+"_";for(e=0;e< -d.length;e++){var g=d[e],f=c+e;b[g].U=f;a.args[g].name=f}return a};var up=function(a,b){if(!(this instanceof up))return en(up,arguments);if(a instanceof up)return a;vp();var c=R(up,arguments);a=c.date;c=c.tz;var d=new P("Date"),e={},g=null;if(kn(a)){if(e.value=a,c)if(kn(c))e.timeZone=c;else throw Error("Invalid argument specified for ee.Date(..., opt_tz): "+c);}else if(jn(a))e.value=a;else if(za(a))e.value=Math.floor(a.getTime());else if(a instanceof O)a.I&&"Date"==a.I.Z().returns?(d=a.I,e=a.args,g=a.U):e.value=a;else throw Error("Invalid argument specified for ee.Date(): "+ -a);O.call(this,d,e,g)};x(up,O);w("ee.Date",up);var wp=!1,vp=function(){wp||(An(up,"Date","Date"),wp=!0)};up.prototype.name=function(){return"Date"};w("ee.Deserializer",function(){});var yp=function(a){return xp(JSON.parse(a))};w("ee.Deserializer.fromJSON",yp);var xp=function(a){if("result"in a&&"values"in a)return zp(a);var b={};if(t(a)&&"CompoundValue"===a.type){for(var c=a.scope,d=0;d/,"");c[g]=!0}var f=r.ee,l;for(l in b)l in c&&!(l in f)&&(f[l]=Up(l),Op.push(l),a[l]?(f[l].signature=a[l],f[l].signature.isConstructor=!0,vn[l]=!0):f[l].signature={});gn=f;Vp()}catch(m){Mp(m);return}Ip="ready";for(Jp=[];0a.y||a.y>=d)return c.createElement("div");var e=a.x%d;0>e&&(e+=d);d=new google.maps.Point(e,a.y);a=[a.x,a.y,b,this.yb++].join("-");e=this.ug.getUniqueId();a=[a,e].join("-");b=this.fh(d,b,c,a);b.tileSize=this.tileSize;iq(b.la,this.opacity);this.zb.set(a,b);lq(this,b);this.dispatchEvent(new mq(this.Nc()));this.ug.If(b,(new Date).getTime()/1E3);return b.la};h.releaseTile=function(a){var b=this.zb.get(a.id);this.zb.remove(a.id);b&&(b.abort(),Ha(b))}; -var lq=function(a,b){a.jb.Xa(b,"status-changed",function(){switch(b.getStatus()){case "loaded":var c=b.fl,d=(new Date).getTime();$p(this.ie,b.zoom).tileLatencies.push(d-c);this.dispatchEvent(new nq(this.Nc()));break;case "throttled":$p(this.ie,b.zoom).throttleCount++;this.dispatchEvent(new oq(b.Ca));break;case "failed":$p(this.ie,b.zoom).errorCount++;this.dispatchEvent(new pq(b.Ca,b.Tk));break;case "aborted":this.dispatchEvent(new qq(this.Nc()))}})}; -jq.prototype.J=function(){D.prototype.J.call(this);this.zb.forEach(Ha);this.zb.clear();this.zb=null;Ha(this.jb);this.ug=this.jb=null};var kq=function(a,b){return Za(a.zb.aa(),function(c){return c.getStatus()==b})};w("ee.layers.AbstractOverlay",jq);jq.prototype.removeTileCallback=jq.prototype.bg;jq.prototype.addTileCallback=jq.prototype.Oe;var nq=function(){z.call(this,"tile-load")};q(nq,z);var mq=function(){z.call(this,"tile-start")};q(mq,z);var oq=function(){z.call(this,"tile-throttle")};q(oq,z); -var pq=function(a,b){z.call(this,"tile-fail");this.errorMessage=b};q(pq,z);var qq=function(){z.call(this,"tile-abort")};q(qq,z);var rq=function(a,b,c,d){D.call(this);this.rb=a;this.zoom=b;this.la=c.createElement("div");this.la.id=d;this.jl=5;this.de=function(){};this.ke="new";this.vi=0;this.Ff=!1};q(rq,D);rq.prototype.getDiv=function(){return this.la}; -var tq=function(a){if(!a.Ff&&"loading"==a.getStatus())throw Error("startLoad() can only be invoked once. Use retryLoad() after the first attempt.");sq(a,"loading");a.fl=(new Date).getTime();a.Za=new pj;a.Za.xc="blob";a.Za.Xa("complete",function(){var b=Bj(a.Za),c=a.Za.getStatus();429==c&&sq(a,"throttled");if(ij(c)){var d={};Fb(Cj(a.Za),function(g,f){d[f.toLowerCase()]=g});a.Al=d;a.Ci=b;a.Lc()}else if(b){var e=new hq;e.Xa("loadend",function(){a.gd(e.ca.result)});e.readAsText(b)}else a.gd("Failed to load tile.")}, -!1);a.Za.Vc("ready",Fa(Ha,a.Za));a.Ca&&a.Ca.endsWith("&profiling=1")&&(a.Ca=a.Ca.replace("&profiling=1",""),a.Za.headers.set("X-Earth-Engine-Computation-Profiling","1"));a.Za.send(a.Ca,"GET")};h=rq.prototype;h.Lc=function(){this.de(this);sq(this,"loaded")};h.Hc=function(){Ha(this.Za)}; -h.gd=function(a){var b=function(c){try{if(c=JSON.parse(c),c.error&&c.error.message)return c.error.message}catch(d){}return c};this.vi>=this.jl?(this.Tk=b(a),sq(this,"failed")):(this.Hc(),setTimeout(u(function(){this.Sa||(this.Ff=!0,tq(this),this.Ff=!1)},this),1E3*Math.pow(2,this.vi++)))};h.abort=function(){this.Hc();"aborted"!=this.getStatus()&&"removed"!=this.getStatus()&&sq(this,this.ke in uq?"removed":"aborted")};h.getStatus=function(){return this.ke};var sq=function(a,b){a.ke=b;a.dispatchEvent("status-changed")}; -rq.prototype.J=function(){D.prototype.J.call(this);this.Hc();this.la.remove();this.de=null};var uq={aborted:!0,failed:!0,loaded:!0,removed:!0};var vq=function(){y.call(this)};q(vq,y);var wq=function(a,b){jq.call(this,a,b);this.Tg=new zc;this.rh=new zc};q(wq,jq);wq.prototype.fh=function(a,b,c,d){var e=new xq(a,b,c,d);this.jb.Xa(e,"status-changed",function(){"loaded"==e.getStatus()&&(this.Tg.set(a,new Float32Array(e.yd)),this.rh.set(a,e.la))});return e};wq.prototype.J=function(){jq.prototype.J.call(this);this.rh=this.Tg=null};w("ee.layers.BinaryOverlay",wq);var xq=function(a,b,c,d){rq.call(this,a,b,c,d)};q(xq,rq); -xq.prototype.Lc=function(){var a=new hq;a.Xa("loadend",function(){this.yd=a.ca.result;rq.prototype.Lc.call(this)},void 0,this);a.readAsArrayBuffer(this.Ci)};var yq=function(a){D.call(this);this.Rc={};this.Qc={};this.Tb=new aq(this);this.X=a;this.Ye=!1};x(yq,D);var zq=["load","abort","error"],Aq=function(a,b,c){if(c="string"===typeof c?c:c.src)a.Ye=!1,a.Rc[b]={src:c,eh:null}},Bq=function(a,b){delete a.Rc[b];var c=a.Qc[b];c&&(delete a.Qc[b],a.Tb.se(c,zq,a.di))}; -yq.prototype.start=function(){var a=this.Rc;Lb(a).forEach(function(b){var c=a[b];if(c&&(delete a[b],!this.Sa)){if(this.X){var d=this.X;d=(d?new Zh(Yh(d)):Ja||(Ja=new Zh)).Rk("IMG")}else d=new Image;c.eh&&(d.crossOrigin=c.eh);this.Tb.Xa(d,zq,this.di);this.Qc[b]=d;d.id=b;d.src=c.src}},this)}; -yq.prototype.di=function(a){var b=a.currentTarget;if(b){if("readystatechange"==a.type)if("complete"==b.readyState)a.type="load";else return;"undefined"==typeof b.naturalWidth&&("load"==a.type?(b.naturalWidth=b.width,b.naturalHeight=b.height):(b.naturalWidth=0,b.naturalHeight=0));Bq(this,b.id);this.dispatchEvent({type:a.type,target:b});!this.Sa&&Pb(this.Qc)&&Pb(this.Rc)&&!this.Ye&&(this.Ye=!0,this.dispatchEvent("complete"))}};yq.prototype.J=function(){delete this.Rc;delete this.Qc;Ha(this.Tb);yq.L.J.call(this)};var Cq=function(a,b){jq.call(this,a,b)};q(Cq,jq);Cq.prototype.fh=function(a,b,c,d){return new Dq(a,b,c,d)};w("ee.layers.ImageOverlay",Cq);var Dq=function(a,b,c,d){rq.call(this,a,b,c,d);this.de=Eq;this.Ih=this.ga=this.Qd=null;this.Xc=""};q(Dq,rq); -Dq.prototype.Lc=function(){try{var a=zd(this.Ci);this.Xc=wd(a);var b=this.Xc!==yd.toString()?this.Xc:this.Ca}catch(c){b=this.Ca}this.ga=new yq;Aq(this.ga,this.la.id+"-image",b);this.Ih=ac(this.ga,Fq,function(c){"load"==c.type?(this.Qd=c.target,rq.prototype.Lc.call(this)):this.gd()},void 0,this);this.ga.start()};Dq.prototype.Hc=function(){rq.prototype.Hc.call(this);this.ga&&(jc(this.Ih),Ha(this.ga))};Dq.prototype.J=function(){rq.prototype.J.call(this);this.Xc&&URL.revokeObjectURL(this.Xc)}; -var Eq=function(a){a.la.appendChild(a.Qd)},Fq=["load","abort","error"];var Gq=function(a){for(var b=arguments[0],c=1;cthis.Wd)throw Error("[goog.structs.Pool] Min can not be greater than max");this.Ta=new Jq;this.vb=new Ic;this.delay=0;this.Gf=null;this.rd()};x(Kq,y);Kq.prototype.Oc=function(){var a=Date.now();if(!(null!=this.Gf&&a-this.Gfthis.Wd&&0=this.R()){for(var c=this.Aa,d=0;d>1,a[d].getKey()>c.getKey())a[b]=a[d],b=d;else break;a[b]=c};h=Nq.prototype; -h.remove=function(){var a=this.Aa,b=a.length,c=a[0];if(!(0>=b)){if(1==b)a.length=0;else{a[0]=a.pop();a=0;b=this.Aa;for(var d=b.length,e=b[a];a>1;){var g=2*a+1,f=2*a+2;g=fe.getKey())break;b[a]=b[g];a=g}b[a]=e}return c.ue}};h.aa=function(){for(var a=this.Aa,b=[],c=a.length,d=0;dthis.kl)return!1;this.Kg++;Bq(this.ga,this.fa);setTimeout(u(this.Bl,this),0);return!0}; -h.Bl=function(){if(!this.kc){var a=u(function(d){this.kc||(Aq(this.ga,this.fa,d),ac(this.ga,Zq,u(this.Yk,this)),this.ga.start())},this),b=this.getUrl();if(re(b).Ba.Pb("profiling")){var c=new pj;c.xc="blob";c.Xa("complete",u(function(){this.mi=c.getResponseHeader("X-Earth-Engine-Computation-Profile")||null;if(200<=c.getStatus()&&300>c.getStatus())try{var d=wd(zd(Bj(c)));var e=d!==yd.toString()}catch(g){}a(e?d:b)},this));c.Vc("ready",u(c.Ra,c));c.send(b,"GET")}else a(b)}};h.Kg=0;h.kc=!1;h.ga=null; -h.Ni=null;h.Ja=null;h.mi=null;var Zq=["load","abort","error"],$q=function(){y.call(this);this.Ea=!1};q($q,y);$q.prototype.setActive=function(a){this.Ea=a};$q.prototype.isActive=function(){return this.Ea};var Vq=function(a,b){Qq.call(this,a,b)};q(Vq,Qq);Vq.prototype.bf=function(){return new $q};Vq.prototype.Cd=function(a){a.Ra()};Vq.prototype.Sf=function(a){return!a.Sa&&!a.isActive()};var ar=function(a,b,c,d,e){Nc.call(this,a,b,c,d,e);this.minZoom=d.minZoom||0;this.maxZoom=d.maxZoom||20;if(!window.google||!window.google.maps)throw Error("Google Maps API hasn't been initialized.");this.tileSize=d.tileSize||new google.maps.Size(256,256);this.name=d.name;this.wg=new Ic;this.Tf=1;this.ua=e||null};q(ar,Nc);h=ar.prototype;h.Oe=function(a){return bc(this,"tileevent",a)};h.bg=function(a){jc(a)}; -h.getTile=function(a,b,c){if(ba.y||a.y>=1<"))+"_";for(e=0;e< +d.length;e++){var g=d[e],f=c+e;b[g].U=f;a.args[g].name=f}return a};var sp=function(a,b){if(!(this instanceof sp))return cn(sp,arguments);if(a instanceof sp)return a;tp();var c=R(sp,arguments);a=c.date;c=c.tz;var d=new P("Date"),e={},g=null;if(hn(a)){if(e.value=a,c)if(hn(c))e.timeZone=c;else throw Error("Invalid argument specified for ee.Date(..., opt_tz): "+c);}else if(gn(a))e.value=a;else if(za(a))e.value=Math.floor(a.getTime());else if(a instanceof O)a.I&&"Date"==a.I.Z().returns?(d=a.I,e=a.args,g=a.U):e.value=a;else throw Error("Invalid argument specified for ee.Date(): "+ +a);O.call(this,d,e,g)};x(sp,O);w("ee.Date",sp);var up=!1,tp=function(){up||(yn(sp,"Date","Date"),up=!0)};sp.prototype.name=function(){return"Date"};w("ee.Deserializer",function(){});var wp=function(a){return vp(JSON.parse(a))};w("ee.Deserializer.fromJSON",wp);var vp=function(a){if("result"in a&&"values"in a)return xp(a);var b={};if(t(a)&&"CompoundValue"===a.type){for(var c=a.scope,d=0;d/,"");c[g]=!0}var f=r.ee,l;for(l in b)l in c&&!(l in f)&&(f[l]=Sp(l),Mp.push(l),a[l]?(f[l].signature=a[l],f[l].signature.isConstructor=!0,tn[l]=!0):f[l].signature={});en=f;Tp()}catch(m){Kp(m);return}Gp="ready";for(Hp=[];0a.y||a.y>=d)return c.createElement("div");var e=a.x%d;0>e&&(e+=d);d=new google.maps.Point(e,a.y);a=[a.x,a.y,b,this.yb++].join("-");e=this.ug.getUniqueId();a=[a,e].join("-");b=this.fh(d,b,c,a);b.tileSize=this.tileSize;gq(b.la,this.opacity);this.zb.set(a,b);jq(this,b);this.dispatchEvent(new kq(this.Nc()));this.ug.If(b,(new Date).getTime()/1E3);return b.la};h.releaseTile=function(a){var b=this.zb.get(a.id);this.zb.remove(a.id);b&&(b.abort(),Ha(b))}; +var jq=function(a,b){a.jb.Xa(b,"status-changed",function(){switch(b.getStatus()){case "loaded":var c=b.fl,d=(new Date).getTime();Yp(this.je,b.zoom).tileLatencies.push(d-c);this.dispatchEvent(new lq(this.Nc()));break;case "throttled":Yp(this.je,b.zoom).throttleCount++;this.dispatchEvent(new mq(b.Ca));break;case "failed":Yp(this.je,b.zoom).errorCount++;this.dispatchEvent(new nq(b.Ca,b.Tk));break;case "aborted":this.dispatchEvent(new oq(this.Nc()))}})}; +hq.prototype.J=function(){D.prototype.J.call(this);this.zb.forEach(Ha);this.zb.clear();this.zb=null;Ha(this.jb);this.ug=this.jb=null};var iq=function(a,b){return Za(a.zb.aa(),function(c){return c.getStatus()==b})};w("ee.layers.AbstractOverlay",hq);hq.prototype.removeTileCallback=hq.prototype.bg;hq.prototype.addTileCallback=hq.prototype.Oe;var lq=function(){z.call(this,"tile-load")};q(lq,z);var kq=function(){z.call(this,"tile-start")};q(kq,z);var mq=function(){z.call(this,"tile-throttle")};q(mq,z); +var nq=function(a,b){z.call(this,"tile-fail");this.errorMessage=b};q(nq,z);var oq=function(){z.call(this,"tile-abort")};q(oq,z);var pq=function(a,b,c,d){D.call(this);this.rb=a;this.zoom=b;this.la=c.createElement("div");this.la.id=d;this.jl=5;this.fe=function(){};this.le="new";this.ui=0;this.Ff=!1};q(pq,D);pq.prototype.getDiv=function(){return this.la}; +var rq=function(a){if(!a.Ff&&"loading"==a.getStatus())throw Error("startLoad() can only be invoked once. Use retryLoad() after the first attempt.");qq(a,"loading");a.fl=(new Date).getTime();a.Za=new nj;a.Za.xc="blob";a.Za.Xa("complete",function(){var b=zj(a.Za),c=a.Za.getStatus();429==c&&qq(a,"throttled");if(ij(c)){var d={};Fb(Aj(a.Za),function(g,f){d[f.toLowerCase()]=g});a.Al=d;a.Bi=b;a.Lc()}else if(b){var e=new fq;e.Xa("loadend",function(){a.jd(e.ca.result)});e.readAsText(b)}else a.jd("Failed to load tile.")}, +!1);a.Za.Wc("ready",Fa(Ha,a.Za));a.Ca&&a.Ca.endsWith("&profiling=1")&&(a.Ca=a.Ca.replace("&profiling=1",""),a.Za.headers.set("X-Earth-Engine-Computation-Profiling","1"));a.Za.send(a.Ca,"GET")};h=pq.prototype;h.Lc=function(){this.fe(this);qq(this,"loaded")};h.Hc=function(){Ha(this.Za)}; +h.jd=function(a){var b=function(c){try{if(c=JSON.parse(c),c.error&&c.error.message)return c.error.message}catch(d){}return c};this.ui>=this.jl?(this.Tk=b(a),qq(this,"failed")):(this.Hc(),setTimeout(u(function(){this.Sa||(this.Ff=!0,rq(this),this.Ff=!1)},this),1E3*Math.pow(2,this.ui++)))};h.abort=function(){this.Hc();"aborted"!=this.getStatus()&&"removed"!=this.getStatus()&&qq(this,this.le in sq?"removed":"aborted")};h.getStatus=function(){return this.le};var qq=function(a,b){a.le=b;a.dispatchEvent("status-changed")}; +pq.prototype.J=function(){D.prototype.J.call(this);this.Hc();this.la.remove();this.fe=null};var sq={aborted:!0,failed:!0,loaded:!0,removed:!0};var tq=function(){y.call(this)};q(tq,y);var uq=function(a,b){hq.call(this,a,b);this.Tg=new zc;this.rh=new zc};q(uq,hq);uq.prototype.fh=function(a,b,c,d){var e=new vq(a,b,c,d);this.jb.Xa(e,"status-changed",function(){"loaded"==e.getStatus()&&(this.Tg.set(a,new Float32Array(e.Ad)),this.rh.set(a,e.la))});return e};uq.prototype.J=function(){hq.prototype.J.call(this);this.rh=this.Tg=null};w("ee.layers.BinaryOverlay",uq);var vq=function(a,b,c,d){pq.call(this,a,b,c,d)};q(vq,pq); +vq.prototype.Lc=function(){var a=new fq;a.Xa("loadend",function(){this.Ad=a.ca.result;pq.prototype.Lc.call(this)},void 0,this);a.readAsArrayBuffer(this.Bi)};var wq=function(a){D.call(this);this.Rc={};this.Qc={};this.Tb=new Zp(this);this.X=a;this.Ye=!1};x(wq,D);var xq=["load","abort","error"],yq=function(a,b,c){if(c="string"===typeof c?c:c.src)a.Ye=!1,a.Rc[b]={src:c,eh:null}},zq=function(a,b){delete a.Rc[b];var c=a.Qc[b];c&&(delete a.Qc[b],a.Tb.te(c,xq,a.ci))}; +wq.prototype.start=function(){var a=this.Rc;Lb(a).forEach(function(b){var c=a[b];if(c&&(delete a[b],!this.Sa)){if(this.X){var d=this.X;d=(d?new Zh(Yh(d)):Ja||(Ja=new Zh)).Rk("IMG")}else d=new Image;c.eh&&(d.crossOrigin=c.eh);this.Tb.Xa(d,xq,this.ci);this.Qc[b]=d;d.id=b;d.src=c.src}},this)}; +wq.prototype.ci=function(a){var b=a.currentTarget;if(b){if("readystatechange"==a.type)if("complete"==b.readyState)a.type="load";else return;"undefined"==typeof b.naturalWidth&&("load"==a.type?(b.naturalWidth=b.width,b.naturalHeight=b.height):(b.naturalWidth=0,b.naturalHeight=0));zq(this,b.id);this.dispatchEvent({type:a.type,target:b});!this.Sa&&Pb(this.Qc)&&Pb(this.Rc)&&!this.Ye&&(this.Ye=!0,this.dispatchEvent("complete"))}};wq.prototype.J=function(){delete this.Rc;delete this.Qc;Ha(this.Tb);wq.L.J.call(this)};var Aq=function(a,b){hq.call(this,a,b)};q(Aq,hq);Aq.prototype.fh=function(a,b,c,d){return new Bq(a,b,c,d)};w("ee.layers.ImageOverlay",Aq);var Bq=function(a,b,c,d){pq.call(this,a,b,c,d);this.fe=Cq;this.Hh=this.ga=this.Sd=null;this.Zc=""};q(Bq,pq); +Bq.prototype.Lc=function(){try{var a=zd(this.Bi);this.Zc=wd(a);var b=this.Zc!==yd.toString()?this.Zc:this.Ca}catch(c){b=this.Ca}this.ga=new wq;yq(this.ga,this.la.id+"-image",b);this.Hh=ac(this.ga,Dq,function(c){"load"==c.type?(this.Sd=c.target,pq.prototype.Lc.call(this)):this.jd()},void 0,this);this.ga.start()};Bq.prototype.Hc=function(){pq.prototype.Hc.call(this);this.ga&&(jc(this.Hh),Ha(this.ga))};Bq.prototype.J=function(){pq.prototype.J.call(this);this.Zc&&URL.revokeObjectURL(this.Zc)}; +var Cq=function(a){a.la.appendChild(a.Sd)},Dq=["load","abort","error"];var Eq=function(a){for(var b=arguments[0],c=1;cthis.Xd)throw Error("[goog.structs.Pool] Min can not be greater than max");this.Ta=new Hq;this.vb=new Ic;this.delay=0;this.Gf=null;this.td()};x(Iq,y);Iq.prototype.Oc=function(){var a=Date.now();if(!(null!=this.Gf&&a-this.Gfthis.Xd&&0=this.R()){for(var c=this.Aa,d=0;d>1,a[d].getKey()>c.getKey())a[b]=a[d],b=d;else break;a[b]=c};h=Lq.prototype; +h.remove=function(){var a=this.Aa,b=a.length,c=a[0];if(!(0>=b)){if(1==b)a.length=0;else{a[0]=a.pop();a=0;b=this.Aa;for(var d=b.length,e=b[a];a>1;){var g=2*a+1,f=2*a+2;g=fe.getKey())break;b[a]=b[g];a=g}b[a]=e}return c.ve}};h.aa=function(){for(var a=this.Aa,b=[],c=a.length,d=0;dthis.kl)return!1;this.Kg++;zq(this.ga,this.fa);setTimeout(u(this.Bl,this),0);return!0}; +h.Bl=function(){if(!this.kc){var a=u(function(d){this.kc||(yq(this.ga,this.fa,d),ac(this.ga,Xq,u(this.Yk,this)),this.ga.start())},this),b=this.getUrl();if(re(b).Ba.Pb("profiling")){var c=new nj;c.xc="blob";c.Xa("complete",u(function(){this.li=c.getResponseHeader("X-Earth-Engine-Computation-Profile")||null;if(200<=c.getStatus()&&300>c.getStatus())try{var d=wd(zd(zj(c)));var e=d!==yd.toString()}catch(g){}a(e?d:b)},this));c.Wc("ready",u(c.Ra,c));c.send(b,"GET")}else a(b)}};h.Kg=0;h.kc=!1;h.ga=null; +h.Mi=null;h.Ja=null;h.li=null;var Xq=["load","abort","error"],Yq=function(){y.call(this);this.Ea=!1};q(Yq,y);Yq.prototype.setActive=function(a){this.Ea=a};Yq.prototype.isActive=function(){return this.Ea};var Tq=function(a,b){Oq.call(this,a,b)};q(Tq,Oq);Tq.prototype.bf=function(){return new Yq};Tq.prototype.Ed=function(a){a.Ra()};Tq.prototype.Sf=function(a){return!a.Sa&&!a.isActive()};var Zq=function(a,b,c,d,e){Nc.call(this,a,b,c,d,e);this.minZoom=d.minZoom||0;this.maxZoom=d.maxZoom||20;if(!window.google||!window.google.maps)throw Error("Google Maps API hasn't been initialized.");this.tileSize=d.tileSize||new google.maps.Size(256,256);this.name=d.name;this.wg=new Ic;this.Tf=1;this.ua=e||null};q(Zq,Nc);h=Zq.prototype;h.Oe=function(a){return bc(this,"tileevent",a)};h.bg=function(a){jc(a)}; +h.getTile=function(a,b,c){if(ba.y||a.y>=1< { + // We currently treat pageSize as a cap on the results, if this param was + // provided we should break fast and not return more than the asked for + // amount. + if (params.pageSize != null || !response.nextPageToken) { + return response; + } + const previousAssets = response.assets || []; + params.pageToken = response.nextPageToken; + const nextResponse = methodRoot.listAssets(parent, params) + .then((response) => { + // Add previous assets to front. + response.assets = previousAssets.concat(response.assets); + return response; + }) + .then(getNextPageIfNeeded); + if (opt_callback) { + // For async, make sure we have only a single chained `call.handle` call. + return nextResponse; + } + // For sync mode, the response data needs to be uplifted from the fake + // Promise object to be returned immediately. + return call.handle(nextResponse); + }; + return call.handle(methodRoot.listAssets(parent, params) + .then(getNextPageIfNeeded) + .then(opt_postProcessing)); }; @@ -1686,9 +1713,15 @@ ee.data.makeListAssetsCall_ = function( * @export */ ee.data.getList = function(params, opt_callback) { + const convertedParams = ee.rpc_convert.getListToListAssets(params); + // Force a single page of results by explicitly specifying a page size. + // This maintains backward compatibility, as well as compatibility with the + // Python implementation. + if (!convertedParams.pageSize) { + convertedParams.pageSize = 1000; + } return ee.data.makeListAssetsCall_( - params['id'], ee.rpc_convert.getListToListAssets(params), opt_callback, - (r) => { + params['id'], convertedParams, opt_callback, (r) => { if (r == null) { return null; } @@ -1709,7 +1742,7 @@ ee.data.getList = function(params, opt_callback) { * * * + * return. If not specified, all results are returned. * * *
pageSize (string) The number of results to - * return. Defaults to 1000.
pageToken (string) The token for the page of @@ -1753,7 +1786,7 @@ ee.data.listAssets = function( * * * + * If not specified, all results are returned. * * *
pageSize (string) The number of results to return. - * Defaults to 1000.
pageToken (string) The token page of results to diff --git a/javascript/src/examples/Demos/Landsat8HarmonicModeling.js b/javascript/src/examples/Demos/Landsat8HarmonicModeling.js index 4f667130f..ded6bf373 100644 --- a/javascript/src/examples/Demos/Landsat8HarmonicModeling.js +++ b/javascript/src/examples/Demos/Landsat8HarmonicModeling.js @@ -69,8 +69,10 @@ var addHarmonics = function(freqs) { }; }; -// Filter to the area of interest, mask clouds, add variables. +// Filter to the desired date range and area of interest, mask clouds, +// and add variables. var harmonicLandsat = landsatCollection + .filterDate('2015-01-01', '2020-01-01') .filterBounds(roi) .map(maskClouds) .map(addNDVI) diff --git a/python/ee/__init__.py b/python/ee/__init__.py index 38c1d2f98..f59b4716e 100644 --- a/python/ee/__init__.py +++ b/python/ee/__init__.py @@ -1,6 +1,6 @@ """The EE Python library.""" -__version__ = '0.1.395' +__version__ = '0.1.397' # Using lowercase function naming to match the JavaScript names. # pylint: disable=g-bad-name diff --git a/python/ee/_cloud_api_utils.py b/python/ee/_cloud_api_utils.py index a38027e4b..986fed564 100644 --- a/python/ee/_cloud_api_utils.py +++ b/python/ee/_cloud_api_utils.py @@ -62,9 +62,19 @@ def request( # pylint: disable=invalid-name del connection_type # Ignored del redirections # Ignored - response = self._session.request( - method, uri, data=body, headers=headers, timeout=self._timeout - ) + try: + # googleapiclient is expecting an httplib2 object, and doesn't include + # requests error in the list of transient errors. Therefore, transient + # requests errors should be converted to kinds that googleapiclient + # consider transient. + response = self._session.request( + method, uri, data=body, headers=headers, timeout=self._timeout + ) + except requests.exceptions.ConnectionError as connection_error: + raise ConnectionError(connection_error) from connection_error + except requests.exceptions.ChunkedEncodingError as encoding_error: + # This is not a one-to-one match, but it's close enough. + raise ConnectionError(encoding_error) from encoding_error headers = dict(response.headers) headers['status'] = response.status_code content = response.content diff --git a/python/ee/batch.py b/python/ee/batch.py index aefb68162..9b2c501f3 100644 --- a/python/ee/batch.py +++ b/python/ee/batch.py @@ -20,6 +20,18 @@ from ee import geometry +def _transform_operation_to_task(operation: Dict[str, Any]) -> Task: + """Converts an operation to a task.""" + status = _cloud_api_utils.convert_operation_to_task(operation) + return Task( + status['id'], + status.get('task_type'), + status.get('state'), + {'description': status.get('description')}, + status.get('name'), + ) + + class Task: """A batch task that can be run on the EE batch processing system.""" @@ -108,6 +120,14 @@ def __init__( self.state = state self.name = name + @property + def operation_name(self) -> Optional[str]: + if self.name: + return self.name + if self.id: + return _cloud_api_utils.convert_task_id_to_operation_name(self.id) + return None + def start(self) -> None: """Starts the task. No-op for started tasks.""" if not self.config: @@ -152,8 +172,9 @@ def status(self) -> Dict[str, Any]: - error_message: Failure reason. Appears only if state is FAILED. May also include other fields. """ - if self.id: - result = data.getTaskStatus(self.id)[0] + if self.operation_name: + operation = data.getOperation(self.operation_name) + result = _cloud_api_utils.convert_operation_to_task(operation) if result['state'] == 'UNKNOWN': result['state'] = Task.State.UNSUBMITTED else: @@ -166,7 +187,7 @@ def active(self) -> bool: def cancel(self) -> None: """Cancels the task.""" - data.cancelTask(self.id) + data.cancelOperation(self.operation_name) @staticmethod def list() -> List[Task]: @@ -178,15 +199,7 @@ def list() -> List[Task]: Returns: A list of Tasks. """ - statuses = data.getTaskList() - tasks = [] - for status in statuses: - tasks.append(Task(status['id'], - status.get('task_type'), - status.get('state'), - {'description': status.get('description')}, - status.get('name'))) - return tasks + return list(map(_transform_operation_to_task, data.listOperations())) def __repr__(self) -> str: """Returns a string representation of the task.""" diff --git a/python/ee/data.py b/python/ee/data.py index f9f0e9fb8..b89fae9a5 100644 --- a/python/ee/data.py +++ b/python/ee/data.py @@ -556,14 +556,18 @@ def getList(params: Dict[str, Any]) -> Any: return result -def listImages(params: Dict[str, Any]) -> Dict[str, Optional[List[int]]]: +def listImages( + params: Union[str, Dict[str, Any]], +) -> Dict[str, Optional[List[int]]]: """Returns the images in an image collection or folder. Args: - params: An object containing request parameters with the following possible + params: Either a string representing the ID of the image collection to list, + or an object containing request parameters with the following possible values, all but 'parent` are optional: parent - (string) The ID of the image collection to list, required. - pageSize - (string) The number of results to return. Defaults to 1000. + pageSize - (string) The number of results to return. If not specified, all + results are returned. pageToken - (string) The token page of results to return. startTime - (ISO 8601 string): The minimum start time (inclusive). endTime - (ISO 8601 string): The maximum end time (exclusive). @@ -591,14 +595,16 @@ def listImages(params: Dict[str, Any]) -> Dict[str, Optional[List[int]]]: return images -def listAssets(params: Dict[str, Any]) -> Dict[str, List[Any]]: +def listAssets(params: Union[str, Dict[str, Any]]) -> Dict[str, List[Any]]: """Returns the assets in a folder. Args: - params: An object containing request parameters with the following possible - values, all but 'parent` are optional: + params: Either a string representing the ID of the collection or folder to + list, or an object containing request parameters with the following + possible values, all but 'parent` are optional: parent - (string) The ID of the collection or folder to list, required. - pageSize - (string) The number of results to return. Defaults to 1000. + pageSize - (string) The number of results to return. If not specified, all + results are returned. pageToken - (string) The token page of results to return. filter - (string) An additional filter query to apply. Example query: '''properties.my_property>=1 AND properties.my_property<2 AND diff --git a/python/ee/daterange.py b/python/ee/daterange.py index 729ad67ee..4a223deac 100644 --- a/python/ee/daterange.py +++ b/python/ee/daterange.py @@ -9,7 +9,7 @@ from ee import ee_string _DateRangeType = Union['DateRange', computedobject.ComputedObject] -_DateType = Union[float, str, ee_date.Date, computedobject.ComputedObject] +_DateType = Union[float, str, 'ee_date.Date', computedobject.ComputedObject] _StringType = Union[str, ee_string.String, computedobject.ComputedObject] @@ -87,3 +87,84 @@ def reset(cls) -> None: @staticmethod def name() -> str: return 'DateRange' + + def contains( + self, other: Union[_DateType, _DateRangeType] + ) -> computedobject.ComputedObject: + """Returns true if the given Date or DateRange is within this DateRange. + + Args: + other: The Date or DateRange to check if it is inside the DateRange. + + Returns: + A Boolean ComputedObject. + """ + + return apifunction.ApiFunction.call_(self.name() + '.contains', self, other) + + def end(self) -> ee_date.Date: + """Returns the (exclusive) end of this DateRange.""" + + return apifunction.ApiFunction.call_(self.name() + '.end', self) + + def intersection( + self, other: Union[_DateType, _DateRangeType] + ) -> 'DateRange': + """Returns a DateRange that contains all the timespan of this and other. + + Args: + other: The other DateRange to include in the intersection. + + Raises: + EEException if the result is an empty DateRange. + + Returns: + An ee.DateRange. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.intersection', self, other + ) + + def intersects( + self, other: Union[_DateType, _DateRangeType] + ) -> computedobject.ComputedObject: + """Returns true if the other DateRange has at least one time in common. + + Args: + other: The other DateRange to check against. + + Returns: + A Boolean ComputedObject. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.intersects', self, other + ) + + def isEmpty(self) -> computedobject.ComputedObject: + """Returns true if this DateRange contains no dates, i.e. start >= end.""" + + return apifunction.ApiFunction.call_(self.name() + '.isEmpty', self) + + def isUnbounded(self) -> computedobject.ComputedObject: + """Returns true if this DateRange contains all dates.""" + + return apifunction.ApiFunction.call_(self.name() + '.isUnbounded', self) + + def start(self) -> ee_date.Date: + """Returns the (inclusive) start of this DateRange.""" + + return apifunction.ApiFunction.call_(self.name() + '.start', self) + + def union(self, other: Union[_DateType, _DateRangeType]) -> DateRange: + """Returns a DateRange that contains all points in this and other. + + Args: + other: The DateRange to union with. + + Returns: + An ee.DateRange. + """ + + return apifunction.ApiFunction.call_(self.name() + '.union', self, other) diff --git a/python/ee/deprecation.py b/python/ee/deprecation.py index 00851954b..70fd9ed77 100644 --- a/python/ee/deprecation.py +++ b/python/ee/deprecation.py @@ -177,7 +177,8 @@ def _IssueAssetDeprecationWarning(asset: _DeprecatedAsset) -> None: f'\n\nAttention required for {asset.id}! You are using a deprecated' ' asset.\nTo ensure continued functionality, please update it' ) - if removal_date := asset.removal_date: + removal_date = asset.removal_date + if removal_date: formatted_date = removal_date.strftime('%B %-d, %Y') warning = warning + f' by {formatted_date}.' else: diff --git a/python/ee/ee_array.py b/python/ee/ee_array.py index 93a532740..1a4e68ea1 100644 --- a/python/ee/ee_array.py +++ b/python/ee/ee_array.py @@ -5,13 +5,15 @@ from ee import apifunction from ee import computedobject +# pylint: disable=unused-import from ee import ee_list from ee import ee_string +# pylint: enable=unused-import _ArrayType = Union[ - Any, List[Any], 'Array', ee_list.List, computedobject.ComputedObject + Any, List[Any], 'Array', 'ee_list.List', computedobject.ComputedObject ] -_StringType = Union[str, ee_string.String, computedobject.ComputedObject] +_StringType = Union[str, 'ee_string.String', computedobject.ComputedObject] class Array(computedobject.ComputedObject): diff --git a/python/ee/ee_date.py b/python/ee/ee_date.py index eea6441ad..cdc47a732 100644 --- a/python/ee/ee_date.py +++ b/python/ee/ee_date.py @@ -8,14 +8,18 @@ from ee import _utils from ee import apifunction from ee import computedobject +from ee import daterange +from ee import ee_number from ee import ee_string from ee import ee_types as types from ee import serializer -# TODO: b/291072742 - Have a separate type when datetime.Datetime unavailable. +# TODO: Have a separate type when datetime.Datetime unavailable. _DateType = Union[ datetime.datetime, float, str, 'Date', computedobject.ComputedObject ] +_IntegerType = Union[int, ee_number.Number, computedobject.ComputedObject] +_NumberType = Union[float, ee_number.Number, computedobject.ComputedObject] _StringType = Union[str, ee_string.String, computedobject.ComputedObject] @@ -98,3 +102,217 @@ def reset(cls) -> None: @staticmethod def name() -> str: return 'Date' + + def advance( + self, + delta: _NumberType, + unit: _StringType, + timeZone: Optional[_StringType] = None, # pylint: disable=invalid-name + ) -> 'Date': + """Create a new Date by adding the specified units to the given Date. + + Args: + delta: The amount to move in the unit. Negative values go back in time. + unit: One of 'year', 'month', 'week', 'day', 'hour', 'minute', or + 'second'. + timeZone: The time zone (e.g. 'America/Los_Angeles'); defaults to UTC. + + Returns: + An ee.Date. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.advance', self, delta, unit, timeZone + ) + + def difference(self, start: _DateType, unit: _StringType) -> ee_number.Number: + """Returns the difference between two Dates in the specified units. + + Args: + start: The date to compare to. + unit: One of 'year', 'month', 'week', 'day', 'hour', 'minute', or + 'second'. + + Returns: + Returns an ee.Number based on the average length of the unit. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.difference', self, start, unit + ) + + def format( + self, + format: Optional[_StringType] = None, # pylint: disable=redefined-builtin + timeZone: Optional[_StringType] = None, # pylint: disable=invalid-name + ) -> ee_string.String: + """Convert a date to string. + + The format string is described here: + + http://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html + + Args: + format: A Joda Time pattern. If omitted, it uses the ISO default date. + timeZone: The time zone (e.g. 'America/Los_Angeles'); defaults to UTC. + + Returns: + An ee.String. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.format', self, format, timeZone + ) + + def get( + self, unit: _StringType, timeZone: Optional[_StringType] = None # pylint: disable=invalid-name + ) -> ee_number.Number: + """Returns the specified unit of this date. + + Args: + unit: One of 'year', 'month' (returns 1-12), 'week' (1-53), 'day' (1-31), + 'hour' (0-23), 'minute' (0-59), or 'second' (0-59). + timeZone: The time zone (e.g. 'America/Los_Angeles'); defaults to UTC. + + Returns: + An ee.Number. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.get', self, unit, timeZone + ) + + def getFraction( + self, unit: _StringType, timeZone: Optional[_StringType] = None # pylint: disable=invalid-name + ) -> ee_number.Number: + """Returns this date's elapsed fraction of the specified unit. + + Args: + unit: One of 'year', 'month', 'week', 'day', 'hour', 'minute', or + 'second'. + timeZone: The time zone (e.g. 'America/Los_Angeles'); defaults to UTC. + + Returns: + An ee.Number between 0 and 1. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.getFraction', self, unit, timeZone + ) + + def getRange( + self, unit: _StringType, timeZone: Optional[_StringType] = None # pylint: disable=invalid-name + ) -> daterange.DateRange: + """Returns a DateRange covering the unit that contains this date. + + For example, + + Date('2013-3-15').getRange('year') + + returns + + DateRange('2013-1-1', '2014-1-1'). + + Args: + unit: One of 'year', 'month', 'week', 'day', 'hour', 'minute', or + 'second'. + timeZone: The time zone (e.g. 'America/Los_Angeles'); defaults to UTC. + + Returns: + An ee.DateRange. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.getRange', self, unit, timeZone + ) + + def getRelative( + self, + unit: _StringType, + inUnit: _StringType, # pylint: disable=invalid-name + timeZone: Optional[_StringType] = None, # pylint: disable=invalid-name + ) -> ee_number.Number: + """Returns the specified unit of this date relative to a larger unit. + + For example, getRelative('day', 'year') returns a value between 0 and 365. + + Args: + unit: One of 'month', 'week', 'day', 'hour', 'minute', or 'second'. + inUnit: One of 'year', 'month', 'week', 'day', 'hour', or 'minute'. + timeZone: The time zone (e.g., 'America/Los_Angeles'); defaults to UTC. + + Returns: + A 0-based ee.Number. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.getRelative', self, unit, inUnit, timeZone + ) + + def millis(self) -> ee_number.Number: + """The number of milliseconds since 1970-01-01T00:00:00Z.""" + + return apifunction.ApiFunction.call_(self.name() + '.millis', self) + + @classmethod + def unitRatio( + cls, numerator: _StringType, denominator: _StringType + ) -> ee_number.Number: + """Returns the ratio of the length of one unit to the length of another. + + For example, unitRatio('day', 'minute') returns 1440. + + Valid units are 'year', 'month', 'week', 'day', 'hour', 'minute', and + 'second'. + + Args: + numerator: Unit to be divided by the denominator. + denominator: Unit to divide into the numerator. + + Returns: + An ee.Number. + """ + + return apifunction.ApiFunction.call_( + cls.name() + '.unitRatio', numerator, denominator + ) + + def update( + self, + year: Optional[_IntegerType] = None, + month: Optional[_IntegerType] = None, + day: Optional[_IntegerType] = None, + hour: Optional[_IntegerType] = None, + minute: Optional[_IntegerType] = None, + second: Optional[_NumberType] = None, + timeZone: Optional[_StringType] = None, # pylint: disable=invalid-name + ) -> 'Date': + """Create a new Date by setting one or more of the units of the given Date. + + If a timeZone is given the new value(s) is interpreted in that zone. Skip or + set to None any argument to keep the same as the input Date. + + Args: + year: Set the year. + month: Set the month. + day: Set the day. + hour: Set the hour. + minute: Set the minute. + second: Set the second. + timeZone: The time zone (e.g. 'America/Los_Angeles'); defaults to UTC. + + Returns: + An ee.Date. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.update', + self, + year, + month, + day, + hour, + minute, + second, + timeZone, + ) diff --git a/python/ee/ee_list.py b/python/ee/ee_list.py index 9a615d3fa..8725f4464 100644 --- a/python/ee/ee_list.py +++ b/python/ee/ee_list.py @@ -1,4 +1,5 @@ """A wrapper for lists.""" +from __future__ import annotations # List clashes with the class List, so call it ListType from typing import Any, List as ListType, Optional, Tuple, Union @@ -6,11 +7,22 @@ from ee import _utils from ee import apifunction from ee import computedobject +from ee import ee_array from ee import ee_exception +from ee import ee_number +from ee import ee_string +from ee import filter as ee_filter +from ee import geometry +from ee import reducer +_EeAnyType = Union[Any, computedobject.ComputedObject] +# TODO: What will the backend accept for bools? +_EeBoolType = Union[Any, computedobject.ComputedObject] _EeListType = Union[ ListType[Any], Tuple[Any, Any], computedobject.ComputedObject ] +_IntegerType = Union[int, ee_number.Number, computedobject.ComputedObject] +_StringType = Union[str, 'ee_string.String', computedobject.ComputedObject] class List(computedobject.ComputedObject): @@ -49,6 +61,9 @@ def __init__(self, arg: Optional[_EeListType]): raise ee_exception.EEException( 'Invalid argument specified for ee.List(): %s' % arg) + # TODO: Make a staticmethod for repeat. + # TODO: Make a staticmethod for sequence. + @classmethod def initialize(cls) -> None: """Imports API functions to this class.""" @@ -80,3 +95,573 @@ def encode_cloud_value(self, encoder: Optional[Any] = None) -> Any: return {'valueReference': encoder(self._list)} else: return super().encode_cloud_value(encoder) + + def add(self, element: Any) -> 'List': + """Appends the element to the end of list. + + Args: + element: The object to add. + + Returns: + An ee.List with the element added to the end. + """ + + return apifunction.ApiFunction.call_(self.name() + '.add', self, element) + + def cat(self, other: _EeListType) -> 'List': + """Concatenates the contents of other onto list. + + Args: + other: Another list to add to the end of the list. + + Returns: + An ee.List with the elements of the original list followed by the elements + of other. + """ + + return apifunction.ApiFunction.call_(self.name() + '.cat', self, other) + + def contains(self, element: Any) -> computedobject.ComputedObject: + """Returns true if list contains element. + + Args: + element: An object to test for presence in the list. + + Returns: + Returns a Boolean ComputedObject. True if the list has the element. False + if the element is not in the list. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.contains', self, element + ) + + def containsAll(self, other: _EeListType) -> computedobject.ComputedObject: + """Returns true if list contains all of the elements of other. + + The results are independent of the order. + + Args: + other: A list of elements to check for presence in the list. + + Returns: + Boolean ComputedObject. True if the list has all of the elements of other. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.containsAll', self, other + ) + + def distinct(self) -> 'List': + """Returns a copy of list without duplicate elements.""" + + return apifunction.ApiFunction.call_(self.name() + '.distinct', self) + + def equals(self, other: _EeListType) -> computedobject.ComputedObject: + """Returns true if the list contains in order the same elements as other. + + Args: + other: List to compare to. + + Returns: + Boolean ComputedObject. + """ + + return apifunction.ApiFunction.call_(self.name() + '.equals', self, other) + + # pylint: disable-next=redefined-builtin + def filter(self, filter: ee_filter.Filter) -> 'List': + """Filters a list to only the elements that match the given filter. + + To filter list items that aren't images or features, test a property + named 'item', e.g.: ee.Filter.gt('item', 3). + + Args: + filter: The ee.Filter instance to apply. + + Returns: + An ee.List with only the elements that pass the filter. + """ + + return apifunction.ApiFunction.call_(self.name() + '.filter', self, filter) + + def flatten(self) -> 'List': + """Flattens any sublists into a single list.""" + + return apifunction.ApiFunction.call_(self.name() + '.flatten', self) + + def frequency(self, element: Any) -> ee_number.Number: + """Returns the number of elements in list equal to element. + + Args: + element: The value to match against. + + Returns: + Returns the count of elements that match element. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.frequency', self, element + ) + + def get(self, index: _IntegerType) -> computedobject.ComputedObject: + """Returns the element at the specified position in list. + + Args: + index: Offset from where to get the element. A negative index counts + backwards from the end of the list. + + Returns: + An ee.ComputedObject. + """ + + return apifunction.ApiFunction.call_(self.name() + '.get', self, index) + + def getArray(self, index: _IntegerType) -> ee_array.Array: + """Returns the array at the specified position in list. + + If the value is not a array, an error will occur. + + Args: + index: Offset from where to get the element. A negative index counts + backwards from the end of the list. + + Returns: + An ee.Array. + """ + + return apifunction.ApiFunction.call_(self.name() + '.getArray', self, index) + + def getGeometry(self, index: _IntegerType) -> geometry.Geometry: + """Returns the geometry at the specified position in list. + + If the value is not a geometry, an error will occur. + + Args: + index: Offset from where to get the element. A negative index counts + backwards from the end of the list. + + Returns: + An ee.Geometry. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.getGeometry', self, index + ) + + def getNumber(self, index: _IntegerType) -> ee_number.Number: + """Returns the number at the specified position in list. + + If the value is not a number, an error will occur. + + Args: + index: Offset from where to get the element. A negative index counts + backwards from the end of the list. + + Returns: + An ee.Number. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.getNumber', self, index + ) + + def getString(self, index: _IntegerType) -> ee_string.String: + """Returns the string at the specified position in list. + + If the value is not a string, an error will occur. + + Args: + index: Offset from where to get the element. A negative index counts + backwards from the end of the list. + + Returns: + An ee.String. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.getString', self, index + ) + + def indexOf(self, element: _EeAnyType) -> ee_number.Number: + """Returns the position of the first occurrence of element. + + Returns the position of the first occurrence of target in list, or -1 if + list does not contain the target. + + Args: + element: ComputedObject to search for. + + Returns: + An integer as an ee.Number. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.indexOf', self, element + ) + + def indexOfSublist(self, target: _EeListType) -> ee_number.Number: + """Returns the position of the first occurrence of target. + + Returns the starting position of the first occurrence of target within list, + or -1 if there is no such occurrence. + + Args: + target: An ee.List to search for. + + Returns: + An integer as an ee.Number. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.indexOfSublist', self, target + ) + + def insert(self, index: _IntegerType, element: _EeAnyType) -> 'List': + """Inserts element at the specified position in list. + + A negative index counts backwards from the end of the list. + + Args: + index: Offset from where to get the element. A negative index counts + backwards from the end of the list. + element: A ComputedObject to insert. + + Returns: + An ee.List. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.insert', self, index, element + ) + + # TODO: Improve the type of `function` + def iterate(self, function: Any, first: _EeAnyType) -> 'List': + """Iterate an algorithm over a list. + + The algorithm is expected to take two objects, the current list item, and + the result from the previous iteration or the value of first for the first + iteration. + + Args: + function: A function to apply to the list. + first: The first item to pass the function. + + Returns: + An ee.List. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.iterate', self, function, first + ) + + def join(self, separator: _StringType = '') -> ee_string.String: + """Returns a string with the list elements with the separator between them. + + Returns a string containing the elements of the list joined together with + the specified separator between elements. + + Note: The string form of list elements which are not strings, numbers, or + booleans is currently not well-defined and subject to change. + + Args: + separator: A string to but between elements. + + Returns: + An ee.String. + """ + + return apifunction.ApiFunction.call_(self.name() + '.join', self, separator) + + def lastIndexOfSubList(self, target: _EeListType) -> ee_number.Number: + """Returns the position of that last instance of target in the list. + + Returns the starting position of the last occurrence of target within list, + or -1 if there is no such occurrence. + + Args: + target: A list to search for. + + Returns: + An integer as an ee.Number. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.lastIndexOfSubList', self, target + ) + + def length(self) -> ee_number.Number: + """Returns the number of elements in list.""" + + return apifunction.ApiFunction.call_(self.name() + '.length', self) + + # TODO: Improve the type of `baseAlgorithm`. + # pylint: disable=invalid-name + def map( + self, baseAlgorithm: _EeAnyType, dropNulls: _EeBoolType = False + ) -> 'List': + # pylint: enable=invalid-name + """Map an algorithm over a list. + + The algorithm is expected to take an Object and return an Object. + + Args: + baseAlgorithm: The function to apply. + dropNulls: If true, the mapped algorithm is allowed to return nulls, and + the elements for which it returns nulls will be dropped. + + Returns: + An ee.List. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.map', self, baseAlgorithm, dropNulls + ) + + # pylint: disable-next=redefined-outer-name + def reduce(self, reducer: reducer.Reducer) -> computedobject.ComputedObject: + """Apply a reducer to a list. + + If the reducer takes more than 1 input, then each element in the list is + assumed to be a list of inputs. If the reducer returns a single output, it + is returned directly, otherwise returns a dictionary containing the named + reducer outputs. + + Args: + reducer: An ee.Reducer instance. + + Returns: + Return depends on the specific reducer used. + """ + + return apifunction.ApiFunction.call_(self.name() + '.reduce', self, reducer) + + def remove(self, element: _EeAnyType) -> 'List': + """Removes the first occurrence of the specified element from list. + + Args: + element: The item to remove from the list. + + Returns: + An ee.List. + """ + + return apifunction.ApiFunction.call_(self.name() + '.remove', self, element) + + def removeAll(self, other: _EeListType) -> 'List': + """Removes from list all of the elements that are contained in other list. + + Args: + other: A list of the elements to remove. + + Returns: + An ee.List. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.removeAll', self, other + ) + + def replace(self, oldval: _EeAnyType, newval: _EeAnyType) -> 'List': + """Replaces the first occurrence of oldval in list with newval. + + Args: + oldval: The value to be replaced. + newval: The value to be put in place of oldval. + + Returns: + An ee.List. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.replace', self, oldval, newval + ) + + def replaceAll(self, oldval: _EeAnyType, newval: _EeAnyType) -> 'List': + """Replaces all occurrences of oldval in list with newval. + + Args: + oldval: The value to be replaced. + newval: The value to be put in place of oldval.. + + Returns: + An ee.List. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.replaceAll', self, oldval, newval + ) + + def reverse(self) -> 'List': + """Reverses the order of the elements in list.""" + + return apifunction.ApiFunction.call_(self.name() + '.reverse', self) + + def rotate(self, distance: _IntegerType) -> 'List': + """Rotates the elements of the list by the specified distance. + + Elements rotated off the end are pushed onto the other end of the list. + + Args: + distance: How many positions to shift all the values. + + Returns: + An ee.List. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.rotate', self, distance + ) + + def set(self, index: _IntegerType, element: _EeAnyType) -> 'List': + """Sets the value at the specified position in list. + + Replaces the value at the specified position in list with element. A + negative index counts backwards from the end of the list. + + Args: + index: Offset from where to get the element. A negative index counts + backwards from the end of the list. + element: A ComputedObject to set at the position of index. + + Returns: + An ee.List. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.set', self, index, element + ) + + def shuffle(self, seed: Optional[_IntegerType] = None) -> 'List': + """Randomly permute the specified list. + + Note that the permutation order will always be the same for any given seed, + unless the value for seed is false. + + Args: + seed: A long integer to use as a seed for the randomization. If the + boolean value of false is passed, then a completely random and + unreproducible order will be generated. + + Returns: + An ee.List. + """ + + return apifunction.ApiFunction.call_(self.name() + '.shuffle', self, seed) + + def size(self) -> ee_number.Number: + """Returns the number of elements in list.""" + + return apifunction.ApiFunction.call_(self.name() + '.size', self) + + def slice( + self, + start: _IntegerType, + end: Optional[_IntegerType] = None, + step: Optional[_IntegerType] = None, + ) -> 'List': + """Returns a range of elements from a list. + + Negative values for start or end count backwards from the end of the list. + Values greater than the size of the list are valid but are truncated to the + size of list. + + For start and end, a negative index counts backwards from the end of the + list. + + Args: + start: Offset from where to get the elements (inclusive). + end: Offset from where to stop getting the elements (exclusive). + step: How many elements to move forward to get the next element. + + Returns: + An ee.List. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.slice', self, start, end, step + ) + + def sort(self, keys: Optional[_EeListType] = None) -> 'List': + """Sorts the list into ascending order. + + If the keys argument is provided, then it is sorted first, and the + elements of list are placed in the same order. + + Args: + keys: Optional keys to sort by. If keys is provided, it must have the + same length as list. + + Returns: + An ee.List. + """ + + return apifunction.ApiFunction.call_(self.name() + '.sort', self, keys) + + def splice( + self, + start: _IntegerType, + count: _IntegerType, + other: Optional[_EeListType] = None, + ) -> 'List': + """Removes elements from list and replaces with elements from other. + + Args: + start: Offset from where to begin getting elements. A negative index + counts backwards from the end of the list. + count: How many elements to replace. + other: Elements to put in at the splice location. + + Returns: + An ee.List. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.splice', self, start, count, other + ) + + def swap(self, pos1: _IntegerType, pos2: _IntegerType) -> 'List': + """Swaps the elements at the specified positions. + + A negative position counts backwards from the end of the list. + + Args: + pos1: Offset of one element. + pos2: Offset of the second element. + + Returns: + An ee.List. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.swap', self, pos1, pos2 + ) + + def unzip(self) -> 'List': + """Rearranges a list of lists. + + Transposes a list of lists, extracting the first element of each inner list + into one list, the second elements into another, etc., up to the length of + the shortest inner list. The remaining items are discarded. The result is a + list of lists. + + Returns: + An ee.List. + """ + + return apifunction.ApiFunction.call_(self.name() + '.unzip', self) + + def zip(self, other: _EeListType) -> 'List': + """Pairs the elements of two lists to create a list of two-element lists. + + When the input lists are of different sizes, the final list has the same + size as the shortest one. + + Args: + other: The list to merge into the current list. + + Returns: + An ee.List with two elements that are both ee.Lists. + """ + + return apifunction.ApiFunction.call_(self.name() + '.zip', self, other) diff --git a/python/ee/featurecollection.py b/python/ee/featurecollection.py index a06166c52..4ae8e36d8 100644 --- a/python/ee/featurecollection.py +++ b/python/ee/featurecollection.py @@ -29,6 +29,7 @@ class FeatureCollection(collection.Collection): _HAS_DYNAMIC_ATTRIBUTES = True @_utils.accept_opt_prefix('opt_column') + @deprecation.WarnForDeprecatedAsset('args') def __init__( self, args: Optional[ diff --git a/python/ee/image.py b/python/ee/image.py index 0c7441815..4f0cf81ae 100644 --- a/python/ee/image.py +++ b/python/ee/image.py @@ -29,6 +29,7 @@ class Image(element.Element): # Tell pytype to not complain about dynamic attributes. _HAS_DYNAMIC_ATTRIBUTES = True + @deprecation.WarnForDeprecatedAsset('args') def __init__( self, args: Optional[Any] = None, version: Optional[float] = None ): diff --git a/python/ee/imagecollection.py b/python/ee/imagecollection.py index 3aceffc4e..5b59afcdd 100644 --- a/python/ee/imagecollection.py +++ b/python/ee/imagecollection.py @@ -9,6 +9,7 @@ from ee import collection from ee import computedobject from ee import data +from ee import deprecation from ee import ee_exception from ee import ee_list from ee import ee_types @@ -23,6 +24,7 @@ class ImageCollection(collection.Collection): # Tell pytype to not complain about dynamic attributes. _HAS_DYNAMIC_ATTRIBUTES = True + @deprecation.WarnForDeprecatedAsset('args') def __init__(self, args: Any): """ImageCollection constructor. diff --git a/python/ee/pixeltype.py b/python/ee/pixeltype.py index d797d12ee..414b42189 100644 --- a/python/ee/pixeltype.py +++ b/python/ee/pixeltype.py @@ -101,3 +101,31 @@ def reset(cls) -> None: @staticmethod def name() -> str: return 'PixelType' + + def dimensions(self) -> ee_number.Number: + """Returns the number of dimensions for this type. + + Returns: + An ee.Number. 0 for scalar values and >= 1 for array values. + """ + + return apifunction.ApiFunction.call_(self.name() + '.dimensions', self) + + def maxValue(self) -> ee_number.Number: + """Returns the maximum value of the PixelType.""" + + return apifunction.ApiFunction.call_(self.name() + '.maxValue', self) + + def minValue(self) -> ee_number.Number: + """Returns the minimum value of the PixelType.""" + + return apifunction.ApiFunction.call_(self.name() + '.minValue', self) + + def precision(self) -> ee_string.String: + """Returns the precision of the PixelType. + + Returns: + An ee.String. One of 'int', 'float', or 'double'. + """ + + return apifunction.ApiFunction.call_(self.name() + '.precision', self) diff --git a/python/ee/tests/algorithms.json b/python/ee/tests/algorithms.json index 50f2a99fa..2cd379f98 100644 --- a/python/ee/tests/algorithms.json +++ b/python/ee/tests/algorithms.json @@ -11200,7 +11200,7 @@ }] }, { "name": "algorithms/List.get", - "description": "Returns the element at the specified position in list. A negative index counts backwards from the end of the list.", + "description": "Returns the element at the specified position in list. A negative index counts backwards from the end of the list.", "returnType": "Object", "arguments": [{ "argumentName": "list", @@ -11211,7 +11211,7 @@ }] }, { "name": "algorithms/List.getArray", - "description": "Returns the array at the specified position in list. A negative index counts backwards from the end of the list. If the value is not a array, an error will occur.", + "description": "Returns the array at the specified position in list. A negative index counts backwards from the end of the list. If the value is not a array, an error will occur.", "returnType": "Array", "arguments": [{ "argumentName": "list", @@ -11222,7 +11222,7 @@ }] }, { "name": "algorithms/List.getGeometry", - "description": "Returns the geometry at the specified position in list. A negative index counts backwards from the end of the list. If the value is not a geometry, an error will occur.", + "description": "Returns the geometry at the specified position in list. A negative index counts backwards from the end of the list. If the value is not a geometry, an error will occur.", "returnType": "Geometry", "arguments": [{ "argumentName": "list", @@ -11233,7 +11233,7 @@ }] }, { "name": "algorithms/List.getNumber", - "description": "Returns the number at the specified position in list. A negative index counts backwards from the end of the list. If the value is not a number, an error will occur.", + "description": "Returns the number at the specified position in list. A negative index counts backwards from the end of the list. If the value is not a number, an error will occur.", "returnType": "Number", "arguments": [{ "argumentName": "list", @@ -11244,7 +11244,7 @@ }] }, { "name": "algorithms/List.getString", - "description": "Returns the string at the specified position in list. A negative index counts backwards from the end of the list. If the value is not a string, an error will occur.", + "description": "Returns the string at the specified position in list. A negative index counts backwards from the end of the list. If the value is not a string, an error will occur.", "returnType": "String", "arguments": [{ "argumentName": "list", @@ -11255,7 +11255,7 @@ }] }, { "name": "algorithms/List.indexOf", - "description": "Returns the position of the first occurrence of target in list, or -1 if list does not contain target.", + "description": "Returns the position of the first occurrence of element in list, or -1 if list does not contain element.", "returnType": "Integer", "arguments": [{ "argumentName": "list", @@ -11291,7 +11291,7 @@ }] }, { "name": "algorithms/List.iterate", - "description": "Iterate an algorithm over a list. The algorithm is expected to take two objects, the current list item, and the result from the previous iteration or the value of first for the first iteration.", + "description": "Iterate an algorithm over a list. The algorithm is expected to take two objects, the current list item, and the result from the previous iteration or the value of first for the first iteration.", "returnType": "Object", "arguments": [{ "argumentName": "list", @@ -11337,7 +11337,7 @@ }] }, { "name": "algorithms/List.map", - "description": "Map an algorithm over a list. The algorithm is expected to take an Object and return an Object.", + "description": "Map an algorithm over a list. The algorithm is expected to take an Object and return an Object.", "returnType": "List\u003cObject\u003e", "arguments": [{ "argumentName": "list", @@ -11354,7 +11354,7 @@ }] }, { "name": "algorithms/List.reduce", - "description": "Apply a reducer to a list. If the reducer takes more than 1 input, then each element in the list is assumed to be a list of inputs. If the reducer returns a single output, it is returned directly, otherwise returns a dictionary containing the named reducer outputs.", + "description": "Apply a reducer to a list. If the reducer takes more than 1 input, then each element in the list is assumed to be a list of inputs. If the reducer returns a single output, it is returned directly, otherwise returns a dictionary containing the named reducer outputs.", "returnType": "Object", "arguments": [{ "argumentName": "list", @@ -11398,7 +11398,7 @@ }] }, { "name": "algorithms/List.replace", - "description": "Replaces the first occurrence of oldVal in list with newVal.", + "description": "Replaces the first occurrence of oldval in list with newval.", "returnType": "List\u003cObject\u003e", "arguments": [{ "argumentName": "list", @@ -11412,7 +11412,7 @@ }] }, { "name": "algorithms/List.replaceAll", - "description": "Replaces all occurrences of oldVal in list with newVal.", + "description": "Replaces all occurrences of oldval in list with newval.", "returnType": "List\u003cObject\u003e", "arguments": [{ "argumentName": "list", @@ -11445,7 +11445,7 @@ }] }, { "name": "algorithms/List.sequence", - "description": "Generate a sequence of numbers from start to end (inclusive) in increments of step, or in count equally-spaced increments. If end is not specified it is computed from start + step * count, so at least one of end or count must be specified.", + "description": "Generate a sequence of numbers from start to end (inclusive) in increments of step, or in count equally-spaced increments. If end is not specified it is computed from start + step * count, so at least one of end or count must be specified.", "returnType": "List\u003cNumber\u003e", "arguments": [{ "argumentName": "start", @@ -11468,7 +11468,7 @@ }] }, { "name": "algorithms/List.set", - "description": "Replaces the value at the specified position in list with element. A negative index counts backwards from the end of the list.", + "description": "Replaces the value at the specified position in list with element. A negative index counts backwards from the end of the list.", "returnType": "List\u003cObject\u003e", "arguments": [{ "argumentName": "list", @@ -11482,7 +11482,7 @@ }] }, { "name": "algorithms/List.shuffle", - "description": "Randomly permute the specified list. Note that the permutation order will always be the same for any given seed, unless the value for seed is \u0027false\u0027.", + "description": "Randomly permute the specified list. Note that the permutation order will always be the same for any given seed, unless the value for seed is \u0027false\u0027.", "returnType": "List\u003cObject\u003e", "arguments": [{ "argumentName": "list", @@ -11490,7 +11490,7 @@ }, { "argumentName": "seed", "type": "Object", - "description": "A long integer to use as a seed for the randomization. If the boolean value of \u0027false\u0027 is passed, then a completely random and unreproducible order will be generated.", + "description": "A long integer to use as a seed for the randomization. If the boolean value of \u0027false\u0027 is passed, then a completely random and unreproducible order will be generated.", "optional": true, "defaultValue": null }] @@ -11504,7 +11504,7 @@ }] }, { "name": "algorithms/List.slice", - "description": "Returns a portion of list between the start index, inclusive, and end index, exclusive. Negative values for start or end count backwards from the end of the list. Values greater than the size of the list are legal but are truncated to the size of list.", + "description": "Returns a portion of list between the start index, inclusive, and end index, exclusive. Negative values for start or end count backwards from the end of the list. Values greater than the size of the list are legal but are truncated to the size of list.", "returnType": "List\u003cObject\u003e", "arguments": [{ "argumentName": "list", @@ -11525,7 +11525,7 @@ }] }, { "name": "algorithms/List.sort", - "description": "Sorts the list into ascending order. If the \u0027keys\u0027 argument is provided, then it is sorted first, and the elements of \u0027list\u0027 are placed in the same order.", + "description": "Sorts the list into ascending order. If the \u0027keys\u0027 argument is provided, then it is sorted first, and the elements of \u0027list\u0027 are placed in the same order.", "returnType": "List\u003cObject\u003e", "arguments": [{ "argumentName": "list", @@ -11534,13 +11534,13 @@ }, { "argumentName": "keys", "type": "List\u003cObject\u003e", - "description": "Optional keys to sort by. If \u0027keys\u0027 is provided, it must have the same length as \u0027list\u0027.", + "description": "Optional keys to sort by. If \u0027keys\u0027 is provided, it must have the same length as \u0027list\u0027.", "optional": true, "defaultValue": null }] }, { "name": "algorithms/List.splice", - "description": "Starting at the start index, removes count elements from list and insert the contents of other at that location. If start is negative, it counts backwards from the end of the list.", + "description": "Starting at the start index, removes count elements from list and insert the contents of other at that location. If start is negative, it counts backwards from the end of the list.", "returnType": "List\u003cObject\u003e", "arguments": [{ "argumentName": "list", @@ -11559,7 +11559,7 @@ }] }, { "name": "algorithms/List.swap", - "description": "Swaps the elements at the specified positions. A negative position counts backwards from the end of the list.", + "description": "Swaps the elements at the specified positions. A negative position counts backwards from the end of the list.", "returnType": "List\u003cObject\u003e", "arguments": [{ "argumentName": "list", @@ -11581,7 +11581,7 @@ }] }, { "name": "algorithms/List.zip", - "description": "Pairs the elements of two lists to create a list of two-element lists. When the input lists are of different sizes, the final list has the same size as the shortest one.", + "description": "Pairs the elements of two lists to create a list of two-element lists. When the input lists are of different sizes, the final list has the same size as the shortest one.", "returnType": "List\u003cList\u003cObject\u003e\u003e", "arguments": [{ "argumentName": "list", @@ -16055,7 +16055,7 @@ "hidden": true }, { "name": "algorithms/Test.Clustering.InterTileRegionMerge", - "description": "Performs post-processing inter-tile region merging phase according to Berkeley image segmentation algorithm. Takes image that is a result of running tile-independent segmentation. Input image should have first band named \u0027clusters\u0027 containing segment labels. The other bands are spectral bands. Examines tile boundaries and merges adjacent segments across tiles if sufficient merging critieria is satisfied. Merging criteria is determined by user specified parameters indicating relative importance of spectral and shape properties.", + "description": "Performs post-processing inter-tile region merging phase according to Berkeley image segmentation algorithm. Takes image that is a result of running tile-independent segmentation. Input image should have first band named \u0027clusters\u0027 containing segment labels. The other bands are spectral bands. Examines tile boundaries and merges adjacent segments across tiles if sufficient merging criteria is satisfied. Merging criteria is determined by user specified parameters indicating relative importance of spectral and shape properties.", "returnType": "Image\u003cunknown bands\u003e", "arguments": [{ "argumentName": "image", diff --git a/python/ee/tests/batch_test.py b/python/ee/tests/batch_test.py index 0a6e2c115..668120018 100644 --- a/python/ee/tests/batch_test.py +++ b/python/ee/tests/batch_test.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 """Test for the ee.batch module.""" + from typing import Any, Optional import unittest from unittest import mock @@ -10,30 +11,51 @@ from ee import data import unittest -TASK_STATUS_1 = { - 'description': 'FirstTestTask', - 'id': 'TEST1', - 'source_url': 'http://example.org/', - 'state': 'RUNNING', - 'task_type': 'EXPORT_IMAGE', - 'creation_timestamp_ms': 7, - 'start_timestamp_ms': 13, - 'update_timestamp_ms': 42, +RUNNING_OPERATION = { + 'metadata': { + 'createTime': '1970-01-01T00:00:00.00Z', + 'startTime': '1970-01-01T00:00:01.00Z', + 'updateTime': '1970-01-01T00:01:00.00Z', + 'description': 'FirstTestTask', + 'state': 'RUNNING', + 'type': 'EXPORT_IMAGE', + 'destinationUris': ['https://test.com'], + 'attempt': 42, + 'priority': 100, + }, + 'done': False, + 'name': 'projects/test-project/operations/TEST1', +} +SUCCEEDED_OPERATION = { + 'metadata': { + 'createTime': '1970-01-01T00:00:00.00Z', + 'startTime': '1970-01-01T00:00:01.00Z', + 'updateTime': '1970-01-01T00:01:00.00Z', + 'description': 'Ingest image: "an/image"', + 'state': 'SUCCEEDED', + 'type': 'EXPORT_IMAGE', + 'destinationUris': ['https://test.com'], + 'attempt': 42, + 'priority': 100, + }, + 'done': False, + 'name': 'projects/test-project/operations/TEST2', } -TASK_STATUS_2 = { - 'description': 'SecondTestTask', - 'id': 'TEST2', - 'state': 'FAILED', - 'task_type': 'EXPORT_FEATURES', - 'creation_timestamp_ms': 17, - 'start_timestamp_ms': 113, - 'update_timestamp_ms': 142, - 'error_message': 'Explosions.', +UNKNOWN_OPERATION = { + 'metadata': { + 'state': 'UNKNOWN', + }, + 'done': True, + 'name': 'projects/test-project/operations/TEST2', } class TaskTest(unittest.TestCase): + def setUp(self): + super().setUp() + data.setCloudApiUserProject('test-project') + def testStartWithoutConfig(self): task = batch.Task('an id', 'a task type', 'a state') self.assertIsNone((task.config)) @@ -48,30 +70,60 @@ def testStartUnknownTaskType(self): ): task.start() + @unittest.skip('Does not work on github') def testStatusWithId(self): - task = batch.Task('an id', 'a task type', 'a state') - with mock.patch.object(data, 'getTaskStatus', return_value=[TASK_STATUS_1]): + task = batch.Task('test_1', 'a task type', 'a state') + with mock.patch.object( + data, 'getOperation', return_value=RUNNING_OPERATION + ) as m: + self.assertEqual('RUNNING', task.status()['state']) + self.assertEqual( + m.call_args.args[0], 'projects/test-project/operations/test_1' + ) + + @unittest.skip('Does not work on github') + def testStatusWithName(self): + task = batch.Task( + None, + 'a task type', + 'a state', + name='projects/test-project/operations/test_1', + ) + with mock.patch.object( + data, 'getOperation', return_value=RUNNING_OPERATION + ) as m: self.assertEqual('RUNNING', task.status()['state']) + self.assertEqual( + m.call_args.args[0], 'projects/test-project/operations/test_1' + ) + @unittest.skip('Does not work on github') def testStatusWithIdStateUnknown(self): task = batch.Task('an id', 'a task type', 'a state') with mock.patch.object( - data, 'getTaskStatus', return_value=[{'state': 'UNKNOWN'}] - ): - self.assertEqual({'state': 'UNSUBMITTED'}, task.status()) + data, 'getOperation', return_value=UNKNOWN_OPERATION + ) as m: + self.assertEqual('UNSUBMITTED', task.status()['state']) + self.assertEqual( + m.call_args.args[0], 'projects/test-project/operations/an id' + ) - def testStatusWithoutId(self): + def testStatusWithoutIdOrName(self): task = batch.Task(None, 'a task type', 'a state') - self.assertEqual({'state': 'UNSUBMITTED'}, task.status()) + self.assertEqual('UNSUBMITTED', task.status()['state']) def testActive(self): task = batch.Task('an id', 'a task type', 'a state') - with mock.patch.object(data, 'getTaskStatus', return_value=[TASK_STATUS_1]): + with mock.patch.object( + data, 'getOperation', return_value=RUNNING_OPERATION + ): self.assertTrue(task.active()) def testNotActive(self): task = batch.Task('an id', 'a task type', 'a state') - with mock.patch.object(data, 'getTaskStatus', return_value=[TASK_STATUS_2]): + with mock.patch.object( + data, 'getOperation', return_value=SUCCEEDED_OPERATION + ): self.assertFalse(task.active()) def testReprWithoutConfig(self): @@ -128,6 +180,7 @@ def testExportVideoCannotInit(self): class BatchTestCase(apitestcase.ApiTestCase): """A test case for batch functionality.""" + start_call_params: Optional[Any] update_call_params: Optional[Any] @@ -154,33 +207,35 @@ def testTaskStartCloudApi(self): def testTaskCancelCloudApi(self): mock_cloud_api_resource = mock.MagicMock() - mock_cloud_api_resource.projects().operations().list( - ).execute.return_value = { + mock_cloud_api_resource.projects().operations().list().execute.return_value = { 'operations': [{ 'name': 'projects/earthengine-legacy/operations/TEST1', 'metadata': {}, }] } - mock_cloud_api_resource.projects().operations( - ).list_next.return_value = None + mock_cloud_api_resource.projects().operations().list_next.return_value = ( + None + ) with apitestcase.UsingCloudApi(cloud_api_resource=mock_cloud_api_resource): task = ee.batch.Task.list()[0] task.cancel() - cancel_args = mock_cloud_api_resource.projects().operations( - ).cancel.call_args + cancel_args = ( + mock_cloud_api_resource.projects().operations().cancel.call_args + ) self.assertEqual( - cancel_args[1]['name'], - 'projects/earthengine-legacy/operations/TEST1') + cancel_args[1]['name'], 'projects/earthengine-legacy/operations/TEST1' + ) def testExportImageTrivialRegionCloudApi(self): """Verifies the task created by Export.image() with a trivial region.""" with apitestcase.UsingCloudApi(): region = [0, 0, 1, 0, 1, 1] task = ee.batch.Export.image.toAsset( - ee.Image(42), assetId='users/foo/bar', region=region, scale=1000) + ee.Image(42), assetId='users/foo/bar', region=region, scale=1000 + ) expected_expression = ee.Image(42).clipToBoundsAndScale( - geometry=ee.Geometry.LineString(region), - scale=1000) + geometry=ee.Geometry.LineString(region), scale=1000 + ) self.assertIsNone(task.id) self.assertIsNone(task.name) self.assertEqual('EXPORT_IMAGE', task.task_type) @@ -190,13 +245,15 @@ def testExportImageTrivialRegionCloudApi(self): 'expression': expected_expression, 'assetExportOptions': { 'earthEngineDestination': { - 'name': - 'projects/earthengine-legacy/assets/users/foo/bar', + 'name': ( + 'projects/earthengine-legacy/assets/users/foo/bar' + ), } }, 'description': 'myExportImageTask', }, - task.config) + task.config, + ) def testExportImageCloudApi(self): """Verifies the task created by Export.image().""" @@ -213,10 +270,11 @@ def testExportImageCloudApi(self): formatOptions={'noData': 1}, ) task = ee.batch.Export.image(ee.Image(1), 'TestDescription', config) - expected_expression = ee.Image(1).reproject( - 'foo', crsTransform=[ - 9.0, 8.0, 7.0, 6.0, 5.0, 4.0 - ]).clip(region) + expected_expression = ( + ee.Image(1) + .reproject('foo', crsTransform=[9.0, 8.0, 7.0, 6.0, 5.0, 4.0]) + .clip(region) + ) self.assertIsNone(task.id) self.assertIsNone(task.name) self.assertEqual('EXPORT_IMAGE', task.task_type) @@ -272,48 +330,45 @@ def testExportImageWithTfRecordCloudApi(self): 'tensorDepths': {'b1': 1, 'b2': 2}, 'sequenceData': True, 'collapseBands': True, - 'maskedThreshold': .5, + 'maskedThreshold': 0.5, }, ) task = ee.batch.Export.image(ee.Image(1), 'TestDescription', config) - expected_expression = ee.Image(1).reproject( - 'foo', crsTransform=[ - 9.0, 8.0, 7.0, 6.0, 5.0, 4.0 - ]).clip(region) + expected_expression = ( + ee.Image(1) + .reproject('foo', crsTransform=[9.0, 8.0, 7.0, 6.0, 5.0, 4.0]) + .clip(region) + ) self.assertIsNone(task.id) self.assertIsNone(task.name) self.assertEqual('EXPORT_IMAGE', task.task_type) self.assertEqual('UNSUBMITTED', task.state) - self.assertEqual({ - 'expression': expected_expression, - 'description': 'TestDescription', - 'fileExportOptions': { - 'fileFormat': 'TF_RECORD_IMAGE', - 'driveDestination': { - 'filenamePrefix': 'TestDescription' - }, - 'tfRecordOptions': { - 'tileDimensions': { - 'width': 256, - 'height': 256 - }, - 'marginDimensions': { - 'width': 32, - 'height': 32, + self.assertEqual( + { + 'expression': expected_expression, + 'description': 'TestDescription', + 'fileExportOptions': { + 'fileFormat': 'TF_RECORD_IMAGE', + 'driveDestination': {'filenamePrefix': 'TestDescription'}, + 'tfRecordOptions': { + 'tileDimensions': {'width': 256, 'height': 256}, + 'marginDimensions': { + 'width': 32, + 'height': 32, + }, + 'compress': True, + 'maxSizeBytes': {'value': '1000000000'}, + 'defaultValue': -999, + 'tensorDepths': {'b1': 1, 'b2': 2}, + 'sequenceData': True, + 'collapseBands': True, + 'maxMaskedRatio': {'value': 0.5}, }, - 'compress': True, - 'maxSizeBytes': {'value': '1000000000'}, - 'defaultValue': -999, - 'tensorDepths': {'b1': 1, 'b2': 2}, - 'sequenceData': True, - 'collapseBands': True, - 'maxMaskedRatio': {'value': 0.5}, }, + 'maxPixels': {'value': '10000000000'}, }, - 'maxPixels': { - 'value': '10000000000' - }, - }, task.config) + task.config, + ) def testExportImageToAssetCloudApi(self): """Verifies the Asset export task created by Export.image.toAsset().""" @@ -321,14 +376,16 @@ def testExportImageToAssetCloudApi(self): config = dict( image=ee.Image(1), assetId='users/foo/bar', - pyramidingPolicy={'B1': 'min'}) + pyramidingPolicy={'B1': 'min'}, + ) expected_expression = ee.Image(1) # Test keyed parameters. task_keyed = ee.batch.Export.image.toAsset( image=config['image'], assetId=config['assetId'], - pyramidingPolicy=config['pyramidingPolicy']) + pyramidingPolicy=config['pyramidingPolicy'], + ) self.assertIsNone(task_keyed.id) self.assertIsNone(task_keyed.name) self.assertEqual('EXPORT_IMAGE', task_keyed.task_type) @@ -339,15 +396,15 @@ def testExportImageToAssetCloudApi(self): 'description': 'myExportImageTask', 'assetExportOptions': { 'earthEngineDestination': { - 'name': - 'projects/earthengine-legacy/assets/users/foo/bar', + 'name': ( + 'projects/earthengine-legacy/assets/users/foo/bar' + ), }, - 'pyramidingPolicyOverrides': { - 'B1': 'MIN' - } + 'pyramidingPolicyOverrides': {'B1': 'MIN'}, }, }, - task_keyed.config) + task_keyed.config, + ) task_ordered = ee.batch.Export.image.toAsset( config['image'], @@ -355,7 +412,8 @@ def testExportImageToAssetCloudApi(self): config['assetId'], maxPixels=1000, maxWorkers=100, - shardSize=4) + shardSize=4, + ) self.assertEqual('EXPORT_IMAGE', task_ordered.task_type) self.assertEqual('UNSUBMITTED', task_ordered.state) self.assertEqual( @@ -364,48 +422,17 @@ def testExportImageToAssetCloudApi(self): 'description': 'TestDescription', 'assetExportOptions': { 'earthEngineDestination': { - 'name': - 'projects/earthengine-legacy/assets/users/foo/bar', + 'name': ( + 'projects/earthengine-legacy/assets/users/foo/bar' + ), }, - 'tileSize': { - 'value': 4 - } - }, - 'maxPixels': { - 'value': '1000' + 'tileSize': {'value': 4}, }, - 'maxWorkers': { - 'value': 100 - } + 'maxPixels': {'value': '1000'}, + 'maxWorkers': {'value': 100}, }, - task_ordered.config) - - task_overwrite_with_priority = ee.batch.Export.image.toAsset( - image=config['image'], - assetId=config['assetId'], - overwrite=True, - priority=999, + task_ordered.config, ) - self.assertIsNone(task_overwrite_with_priority.id) - self.assertIsNone(task_overwrite_with_priority.name) - self.assertEqual('EXPORT_IMAGE', task_overwrite_with_priority.task_type) - self.assertEqual('UNSUBMITTED', task_overwrite_with_priority.state) - self.assertEqual( - { - 'expression': expected_expression, - 'description': 'myExportImageTask', - 'assetExportOptions': { - 'earthEngineDestination': { - 'name': - 'projects/earthengine-legacy/assets/users/foo/bar', - 'overwrite': - True - } - }, - 'priority': { - 'value': 999 - } - }, task_overwrite_with_priority.config) def testExportImageToAssetCloudApi_withTileSize(self): """Verifies the Asset export task created by Export.image.toAsset().""" @@ -413,7 +440,8 @@ def testExportImageToAssetCloudApi_withTileSize(self): config = dict( image=ee.Image(1), assetId='users/foo/bar', - pyramidingPolicy={'B1': 'min'}) + pyramidingPolicy={'B1': 'min'}, + ) expected_expression = ee.Image(1) task_ordered = ee.batch.Export.image.toAsset( @@ -422,7 +450,8 @@ def testExportImageToAssetCloudApi_withTileSize(self): config['assetId'], maxPixels=1000, maxWorkers=100, - tileSize=4) + tileSize=4, + ) self.assertEqual('EXPORT_IMAGE', task_ordered.task_type) self.assertEqual('UNSUBMITTED', task_ordered.state) self.assertEqual( @@ -431,21 +460,17 @@ def testExportImageToAssetCloudApi_withTileSize(self): 'description': 'TestDescription', 'assetExportOptions': { 'earthEngineDestination': { - 'name': - 'projects/earthengine-legacy/assets/users/foo/bar', + 'name': ( + 'projects/earthengine-legacy/assets/users/foo/bar' + ), }, - 'tileSize': { - 'value': 4 - } - }, - 'maxPixels': { - 'value': '1000' + 'tileSize': {'value': 4}, }, - 'maxWorkers': { - 'value': 100 - } + 'maxPixels': {'value': '1000'}, + 'maxWorkers': {'value': 100}, }, - task_ordered.config) + task_ordered.config, + ) def testExportImageToCloudStorageCloudApi(self): """Verifies the Cloud Storage export task created by Export.image().""" @@ -454,71 +479,92 @@ def testExportImageToCloudStorageCloudApi(self): config = dict( region=region['coordinates'], maxPixels=10**10, - outputBucket='test-bucket') + outputBucket='test-bucket', + ) task = ee.batch.Export.image.toCloudStorage( - ee.Image(1), 'TestDescription', config['outputBucket'], None, None, - config['region'], None, None, None, config['maxPixels'], None, - [512, 2048], True) + ee.Image(1), + 'TestDescription', + config['outputBucket'], + None, + None, + config['region'], + None, + None, + None, + config['maxPixels'], + None, + [512, 2048], + True, + ) expected_expression = ee.Image(1).clip(region) self.assertIsNone(task.id) self.assertIsNone(task.name) self.assertEqual('EXPORT_IMAGE', task.task_type) self.assertEqual('UNSUBMITTED', task.state) - self.assertEqual({ - 'expression': expected_expression, - 'description': 'TestDescription', - 'fileExportOptions': { - 'fileFormat': 'GEO_TIFF', - 'cloudStorageDestination': { - 'bucket': 'test-bucket', - 'filenamePrefix': 'TestDescription' - }, - 'geoTiffOptions': { - 'tileDimensions': { - 'width': 512, - 'height': 2048 + self.assertEqual( + { + 'expression': expected_expression, + 'description': 'TestDescription', + 'fileExportOptions': { + 'fileFormat': 'GEO_TIFF', + 'cloudStorageDestination': { + 'bucket': 'test-bucket', + 'filenamePrefix': 'TestDescription', + }, + 'geoTiffOptions': { + 'tileDimensions': {'width': 512, 'height': 2048}, + 'skipEmptyFiles': True, }, - 'skipEmptyFiles': True, }, + 'maxPixels': {'value': '10000000000'}, }, - 'maxPixels': { - 'value': '10000000000' - }, - }, task.config) + task.config, + ) config = dict( region=region['coordinates'], maxPixels=10**10, outputBucket='test-bucket', - priority=999) + priority=999, + ) task_with_priority = ee.batch.Export.image.toCloudStorage( - ee.Image(1), 'TestDescription', config['outputBucket'], None, None, - config['region'], None, None, None, config['maxPixels'], None, - [512, 2048], True, None, None, config['priority']) - self.assertEqual({ - 'expression': expected_expression, - 'description': 'TestDescription', - 'fileExportOptions': { - 'fileFormat': 'GEO_TIFF', - 'cloudStorageDestination': { - 'bucket': 'test-bucket', - 'filenamePrefix': 'TestDescription' - }, - 'geoTiffOptions': { - 'tileDimensions': { - 'width': 512, - 'height': 2048 + ee.Image(1), + 'TestDescription', + config['outputBucket'], + None, + None, + config['region'], + None, + None, + None, + config['maxPixels'], + None, + [512, 2048], + True, + None, + None, + config['priority'], + ) + self.assertEqual( + { + 'expression': expected_expression, + 'description': 'TestDescription', + 'fileExportOptions': { + 'fileFormat': 'GEO_TIFF', + 'cloudStorageDestination': { + 'bucket': 'test-bucket', + 'filenamePrefix': 'TestDescription', + }, + 'geoTiffOptions': { + 'tileDimensions': {'width': 512, 'height': 2048}, + 'skipEmptyFiles': True, }, - 'skipEmptyFiles': True, }, + 'maxPixels': {'value': '10000000000'}, + 'priority': {'value': 999}, }, - 'maxPixels': { - 'value': '10000000000' - }, - 'priority': { - 'value': 999 - } - }, task_with_priority.config) + task_with_priority.config, + ) def testExportImageToGoogleDriveCloudApi(self): """Verifies the Drive destined task created by Export.image.toDrive().""" @@ -531,9 +577,13 @@ def testExportImageToGoogleDriveCloudApi(self): maxPixels=10**10, crs='foo', crsTransform='[9,8,7,6,5,4]', - shardSize=512) - expected_expression = ee.Image(1).reproject( - 'foo', crsTransform=[9.0, 8.0, 7.0, 6.0, 5.0, 4.0]).clip(region) + shardSize=512, + ) + expected_expression = ( + ee.Image(1) + .reproject('foo', crsTransform=[9.0, 8.0, 7.0, 6.0, 5.0, 4.0]) + .clip(region) + ) self.assertIsNone(drive_task_by_keys.id) self.assertIsNone(drive_task_by_keys.name) self.assertEqual('EXPORT_IMAGE', drive_task_by_keys.task_type) @@ -546,44 +596,47 @@ def testExportImageToGoogleDriveCloudApi(self): 'fileFormat': 'GEO_TIFF', 'driveDestination': { 'folder': 'foo', - 'filenamePrefix': 'myExportImageTask' + 'filenamePrefix': 'myExportImageTask', }, - 'geoTiffOptions': { - 'tileSize': { - 'value': 512 - } - } + 'geoTiffOptions': {'tileSize': {'value': 512}}, }, - 'maxPixels': { - 'value': '10000000000' - }, - }, drive_task_by_keys.config) + 'maxPixels': {'value': '10000000000'}, + }, + drive_task_by_keys.config, + ) drive_task_with_old_keys = ee.batch.Export.image.toDrive( - image=ee.Image(1), region=region['coordinates'], driveFolder='foo', - driveFileNamePrefix='fooExport', maxPixels=10**10, - crs='foo', crs_transform='[9,8,7,6,5,4]') + image=ee.Image(1), + region=region['coordinates'], + driveFolder='foo', + driveFileNamePrefix='fooExport', + maxPixels=10**10, + crs='foo', + crs_transform='[9,8,7,6,5,4]', + ) self.assertIsNone(drive_task_with_old_keys.id) self.assertIsNone(drive_task_by_keys.name) self.assertEqual('EXPORT_IMAGE', drive_task_with_old_keys.task_type) self.assertEqual('UNSUBMITTED', drive_task_with_old_keys.state) - self.assertEqual({ - 'expression': expected_expression, - 'description': 'myExportImageTask', - 'fileExportOptions': { - 'fileFormat': 'GEO_TIFF', - 'driveDestination': { - 'folder': 'foo', - 'filenamePrefix': 'fooExport' - } - }, - 'maxPixels': { - 'value': '10000000000' + self.assertEqual( + { + 'expression': expected_expression, + 'description': 'myExportImageTask', + 'fileExportOptions': { + 'fileFormat': 'GEO_TIFF', + 'driveDestination': { + 'folder': 'foo', + 'filenamePrefix': 'fooExport', + }, + }, + 'maxPixels': {'value': '10000000000'}, }, - }, drive_task_with_old_keys.config) + drive_task_with_old_keys.config, + ) - with self.assertRaisesRegex(ee.EEException, - 'Unknown configuration options.*'): + with self.assertRaisesRegex( + ee.EEException, 'Unknown configuration options.*' + ): ee.batch.Export.image.toDrive(image=ee.Image(1), framesPerSecond=30) drive_task_with_priority = ee.batch.Export.image.toDrive( @@ -594,9 +647,13 @@ def testExportImageToGoogleDriveCloudApi(self): crs='foo', crsTransform='[9,8,7,6,5,4]', shardSize=512, - priority=999) - expected_expression = ee.Image(1).reproject( - 'foo', crsTransform=[9.0, 8.0, 7.0, 6.0, 5.0, 4.0]).clip(region) + priority=999, + ) + expected_expression = ( + ee.Image(1) + .reproject('foo', crsTransform=[9.0, 8.0, 7.0, 6.0, 5.0, 4.0]) + .clip(region) + ) self.assertEqual( { 'expression': expected_expression, @@ -605,21 +662,15 @@ def testExportImageToGoogleDriveCloudApi(self): 'fileFormat': 'GEO_TIFF', 'driveDestination': { 'folder': 'foo', - 'filenamePrefix': 'myExportImageTask' + 'filenamePrefix': 'myExportImageTask', }, - 'geoTiffOptions': { - 'tileSize': { - 'value': 512 - } - } + 'geoTiffOptions': {'tileSize': {'value': 512}}, }, - 'maxPixels': { - 'value': '10000000000' - }, - 'priority': { - 'value': 999 - } - }, drive_task_with_priority.config) + 'maxPixels': {'value': '10000000000'}, + 'priority': {'value': 999}, + }, + drive_task_with_priority.config, + ) def testExportMapToCloudStorageCloudApi(self): """Verifies the task created by Export.map.toCloudStorage().""" @@ -629,7 +680,8 @@ def testExportMapToCloudStorageCloudApi(self): bucket='test-bucket', maxZoom=7, path='foo/gcs/path', - maxWorkers=100) + maxWorkers=100, + ) # Test keyed parameters. task_keyed = ee.batch.Export.map.toCloudStorage( @@ -638,60 +690,80 @@ def testExportMapToCloudStorageCloudApi(self): maxZoom=config['maxZoom'], path=config['path'], maxWorkers=config['maxWorkers'], - bucketCorsUris=['*']) + bucketCorsUris=['*'], + ) expected_expression = ee.Image(1) self.assertIsNone(task_keyed.id) self.assertIsNone(task_keyed.name) self.assertEqual('EXPORT_TILES', task_keyed.task_type) self.assertEqual('UNSUBMITTED', task_keyed.state) - self.assertEqual({ - 'expression': expected_expression, - 'description': 'myExportMapTask', - 'tileOptions': { - 'endZoom': config['maxZoom'], - }, - 'tileExportOptions': { - 'fileFormat': 'AUTO_JPEG_PNG', - 'cloudStorageDestination': { - 'bucket': config['bucket'], - 'filenamePrefix': config['path'], - 'permissions': 'PUBLIC', - 'bucketCorsUris': ['*'], + self.assertEqual( + { + 'expression': expected_expression, + 'description': 'myExportMapTask', + 'tileOptions': { + 'endZoom': config['maxZoom'], + }, + 'tileExportOptions': { + 'fileFormat': 'AUTO_JPEG_PNG', + 'cloudStorageDestination': { + 'bucket': config['bucket'], + 'filenamePrefix': config['path'], + 'permissions': 'PUBLIC', + 'bucketCorsUris': ['*'], + }, }, + 'maxWorkers': {'value': 100}, }, - 'maxWorkers': {'value': 100} - }, task_keyed.config) + task_keyed.config, + ) - with self.assertRaisesRegex(ee.EEException, - 'Unknown configuration options.*'): + with self.assertRaisesRegex( + ee.EEException, 'Unknown configuration options.*' + ): config_with_bogus_option = config.copy() config_with_bogus_option['framesPerSecond'] = 30 ee.batch.Export.map.toCloudStorage(**config_with_bogus_option) # Test ordered parameters. task_ordered = ee.batch.Export.map.toCloudStorage( - config['image'], 'TestDescription', config['bucket'], 'jpeg', None, - False, None, 30, None, None, None, 'aFakeKey', maxWorkers=100) + config['image'], + 'TestDescription', + config['bucket'], + 'jpeg', + None, + False, + None, + 30, + None, + None, + None, + 'aFakeKey', + maxWorkers=100, + ) self.assertIsNone(task_ordered.id) self.assertIsNone(task_ordered.name) self.assertEqual('EXPORT_TILES', task_ordered.task_type) self.assertEqual('UNSUBMITTED', task_ordered.state) - self.assertEqual({ - 'expression': expected_expression, - 'description': 'TestDescription', - 'tileOptions': { - 'scale': 30, - 'mapsApiKey': 'aFakeKey', - }, - 'tileExportOptions': { - 'fileFormat': 'JPEG', - 'cloudStorageDestination': { - 'bucket': config['bucket'], - 'filenamePrefix': 'TestDescription', + self.assertEqual( + { + 'expression': expected_expression, + 'description': 'TestDescription', + 'tileOptions': { + 'scale': 30, + 'mapsApiKey': 'aFakeKey', }, + 'tileExportOptions': { + 'fileFormat': 'JPEG', + 'cloudStorageDestination': { + 'bucket': config['bucket'], + 'filenamePrefix': 'TestDescription', + }, + }, + 'maxWorkers': {'value': 100}, }, - 'maxWorkers': {'value': 100} - }, task_ordered.config) + task_ordered.config, + ) config = dict( image=ee.Image(1), @@ -699,7 +771,8 @@ def testExportMapToCloudStorageCloudApi(self): maxZoom=7, path='foo/gcs/path', maxWorkers=100, - priority=999) + priority=999, + ) task_with_priority = ee.batch.Export.map.toCloudStorage( image=config['image'], bucket=config['bucket'], @@ -707,28 +780,30 @@ def testExportMapToCloudStorageCloudApi(self): path=config['path'], maxWorkers=config['maxWorkers'], bucketCorsUris=['*'], - priority=config['priority']) + priority=config['priority'], + ) expected_expression = ee.Image(1) - self.assertEqual({ - 'expression': expected_expression, - 'description': 'myExportMapTask', - 'tileOptions': { - 'endZoom': config['maxZoom'], - }, - 'tileExportOptions': { - 'fileFormat': 'AUTO_JPEG_PNG', - 'cloudStorageDestination': { - 'bucket': config['bucket'], - 'filenamePrefix': config['path'], - 'permissions': 'PUBLIC', - 'bucketCorsUris': ['*'], + self.assertEqual( + { + 'expression': expected_expression, + 'description': 'myExportMapTask', + 'tileOptions': { + 'endZoom': config['maxZoom'], + }, + 'tileExportOptions': { + 'fileFormat': 'AUTO_JPEG_PNG', + 'cloudStorageDestination': { + 'bucket': config['bucket'], + 'filenamePrefix': config['path'], + 'permissions': 'PUBLIC', + 'bucketCorsUris': ['*'], + }, }, + 'maxWorkers': {'value': 100}, + 'priority': {'value': 999}, }, - 'maxWorkers': {'value': 100}, - 'priority': { - 'value': 999 - } - }, task_with_priority.config) + task_with_priority.config, + ) def testExportMapToCloudStorageCloudApi_WithV1Parameters(self): """Verifies Export.map.toCloudStorage() tasks with v1 parameters.""" @@ -743,7 +818,8 @@ def testExportMapToCloudStorageCloudApi_WithV1Parameters(self): path='foo/gcs/path', skipEmptyTiles=True, skipEmpty=False, # Takes precedence over skipEmpty. - maxWorkers=100) + maxWorkers=100, + ) # Test keyed parameters. task_keyed = ee.batch.Export.map.toCloudStorage( @@ -756,7 +832,8 @@ def testExportMapToCloudStorageCloudApi_WithV1Parameters(self): path=config['path'], maxWorkers=config['maxWorkers'], skipEmptyTiles=config['skipEmptyTiles'], - skipEmpty=config['skipEmpty']) + skipEmpty=config['skipEmpty'], + ) expected_expression = ee.Image(1) self.assertIsNone(task_keyed.id) self.assertIsNone(task_keyed.name) @@ -779,39 +856,45 @@ def testExportMapToCloudStorageCloudApi_WithV1Parameters(self): 'permissions': 'PUBLIC', }, }, - 'maxWorkers': { - 'value': 100 - } - }, task_keyed.config) + 'maxWorkers': {'value': 100}, + }, + task_keyed.config, + ) def testExportTableCloudApi(self): """Verifies the task created by Export.table().""" with apitestcase.UsingCloudApi(): task = ee.batch.Export.table( - ee.FeatureCollection('drive test FC'), config={'maxWorkers': 100}) + ee.FeatureCollection('drive test FC'), config={'maxWorkers': 100} + ) self.assertIsNone(task.id) self.assertIsNone(task.name) self.assertEqual('EXPORT_FEATURES', task.task_type) self.assertEqual('UNSUBMITTED', task.state) - self.assertEqual({ - 'expression': ee.FeatureCollection('drive test FC'), - 'description': 'myExportTableTask', - 'fileExportOptions': { - 'fileFormat': 'CSV', - 'driveDestination': { - 'filenamePrefix': 'myExportTableTask', - } + self.assertEqual( + { + 'expression': ee.FeatureCollection('drive test FC'), + 'description': 'myExportTableTask', + 'fileExportOptions': { + 'fileFormat': 'CSV', + 'driveDestination': { + 'filenamePrefix': 'myExportTableTask', + }, + }, + 'maxWorkers': {'value': 100}, }, - 'maxWorkers': {'value': 100}, - }, task.config) + task.config, + ) def testExportTableCloudApiBogusParameter(self): """Verifies that bogus parameters are rejected.""" with apitestcase.UsingCloudApi(): - with self.assertRaisesRegex(ee.EEException, - 'Unknown configuration options.*'): + with self.assertRaisesRegex( + ee.EEException, 'Unknown configuration options.*' + ): ee.batch.Export.table.toDrive( - ee.FeatureCollection('drive test FC'), framesPerSecond=30) + ee.FeatureCollection('drive test FC'), framesPerSecond=30 + ) def testExportTableSelectorsCloudApi(self): """Verifies that table export accepts a list or tuple of selectors.""" @@ -819,18 +902,21 @@ def testExportTableSelectorsCloudApi(self): task = ee.batch.Export.table.toCloudStorage( collection=ee.FeatureCollection('foo'), selectors=['ab', 'bb', 'c'], - outputBucket='foo') + outputBucket='foo', + ) self.assertEqual(['ab', 'bb', 'c'], task.config['selectors']) task = ee.batch.Export.table.toCloudStorage( collection=ee.FeatureCollection('foo'), selectors=('x', 'y'), - outputBucket='foo') + outputBucket='foo', + ) self.assertEqual(['x', 'y'], task.config['selectors']) # Single string should work too. task = ee.batch.Export.table.toCloudStorage( collection=ee.FeatureCollection('foo'), selectors='ab,cd,ef', - outputBucket='foo') + outputBucket='foo', + ) self.assertEqual(['ab', 'cd', 'ef'], task.config['selectors']) def testExportTableToCloudStorageCloudApi(self): @@ -839,7 +925,8 @@ def testExportTableToCloudStorageCloudApi(self): task = ee.batch.Export.table.toCloudStorage( collection=ee.FeatureCollection('foo'), outputBucket='test-bucket', - maxVertices=1e6) + maxVertices=1e6, + ) self.assertIsNone(task.id) self.assertIsNone(task.name) self.assertEqual('EXPORT_FEATURES', task.task_type) @@ -855,10 +942,10 @@ def testExportTableToCloudStorageCloudApi(self): 'filenamePrefix': 'myExportTableTask', }, }, - 'maxVertices': { - 'value': int(1e6) - }, - }, task.config) + 'maxVertices': {'value': int(1e6)}, + }, + task.config, + ) task_with_priority = ee.batch.Export.table.toCloudStorage( collection=ee.FeatureCollection('foo'), @@ -897,11 +984,9 @@ def testExportTableToGoogleDriveCloudApi(self): 'fileFormat': test_format, 'driveDestination': { 'filenamePrefix': test_file_name_prefix, - } + }, }, - 'maxVertices': { - 'value': 0 - } + 'maxVertices': {'value': 0}, } # Ordered parameters @@ -911,7 +996,8 @@ def testExportTableToGoogleDriveCloudApi(self): None, test_file_name_prefix, test_format, - maxVertices=0) + maxVertices=0, + ) self.assertIsNone(task_ordered.id) self.assertIsNone(task_ordered.name) self.assertEqual('EXPORT_FEATURES', task_ordered.task_type) @@ -922,7 +1008,8 @@ def testExportTableToGoogleDriveCloudApi(self): expected_config['description'] = 'myExportTableTask' expected_config['fileExportOptions']['fileFormat'] = 'CSV' expected_config['fileExportOptions']['driveDestination'][ - 'folder'] = 'fooFolder' + 'folder' + ] = 'fooFolder' # Test that deprecated parameters (driveFolder and driveFileNamePrefix) # still work. @@ -930,7 +1017,8 @@ def testExportTableToGoogleDriveCloudApi(self): collection=test_collection, driveFolder='fooFolder', driveFileNamePrefix='fooDriveFileNamePrefix', - maxVertices=0) + maxVertices=0, + ) self.assertEqual('EXPORT_FEATURES', task_old_keys.task_type) self.assertEqual('UNSUBMITTED', task_old_keys.state) self.assertEqual(expected_config, task_old_keys.config) @@ -940,7 +1028,8 @@ def testExportTableToGoogleDriveCloudApi(self): collection=test_collection, folder='fooFolder', fileNamePrefix='fooDriveFileNamePrefix', - maxVertices=0) + maxVertices=0, + ) self.assertEqual('EXPORT_FEATURES', task_new_keys.task_type) self.assertEqual('UNSUBMITTED', task_new_keys.state) self.assertEqual(expected_config, task_new_keys.config) @@ -974,7 +1063,8 @@ def testExportTableToAssetCloudApi(self): task = ee.batch.Export.table.toAsset( collection=ee.FeatureCollection('foo'), description='foo', - assetId='users/foo/bar') + assetId='users/foo/bar', + ) self.assertIsNone(task.id) self.assertIsNone(task.name) self.assertEqual('EXPORT_FEATURES', task.task_type) @@ -985,20 +1075,14 @@ def testExportTableToAssetCloudApi(self): 'description': 'foo', 'assetExportOptions': { 'earthEngineDestination': { - 'name': - 'projects/earthengine-legacy/assets/users/foo/bar', + 'name': ( + 'projects/earthengine-legacy/assets/users/foo/bar' + ), } - } + }, }, - task.config) - - task = ee.batch.Export.table.toAsset( - collection=ee.FeatureCollection('foo'), - description='foo', - assetId='users/foo/bar', - overwrite=True) - self.assertTrue(task.config['assetExportOptions'] - ['earthEngineDestination']['overwrite']) + task.config, + ) task_with_priority = ee.batch.Export.table.toAsset( collection=ee.FeatureCollection('foo'), description='foo', @@ -1014,7 +1098,6 @@ def testExportTableToAssetCloudApi(self): 'name': ( 'projects/earthengine-legacy/assets/users/foo/bar' ), - 'overwrite': False, } }, 'priority': {'value': 999}, @@ -1028,7 +1111,8 @@ def testExportTableWithFileFormatCloudApi(self): task = ee.batch.Export.table.toCloudStorage( collection=ee.FeatureCollection('foo'), outputBucket='test-bucket', - fileFormat='tfRecord') + fileFormat='tfRecord', + ) self.assertIsNone(task.id) self.assertIsNone(task.name) self.assertEqual('EXPORT_FEATURES', task.task_type) @@ -1042,9 +1126,11 @@ def testExportTableWithFileFormatCloudApi(self): 'cloudStorageDestination': { 'bucket': 'test-bucket', 'filenamePrefix': 'myExportTableTask', - } - } - }, task.config) + }, + }, + }, + task.config, + ) def testExportTableToFeatureViewCloudApi(self): """Verifies the export task created by Export.table.toFeatureView().""" @@ -1055,8 +1141,9 @@ def testExportTableToFeatureViewCloudApi(self): assetId='users/foo/bar', ingestionTimeParameters={ 'maxFeaturesPerTile': 10, - 'zOrderRanking': [] - }) + 'zOrderRanking': [], + }, + ) self.assertIsNone(task.id) self.assertIsNone(task.name) self.assertEqual('EXPORT_FEATURES', task.task_type) @@ -1067,16 +1154,17 @@ def testExportTableToFeatureViewCloudApi(self): 'description': 'foo', 'featureViewExportOptions': { 'featureViewDestination': { - 'name': - 'projects/earthengine-legacy/assets/users/foo/bar', + 'name': ( + 'projects/earthengine-legacy/assets/users/foo/bar' + ), }, 'ingestionTimeParameters': { - 'thinningOptions': { - 'maxFeaturesPerTile': 10 - }, - } - } - }, task.config) + 'thinningOptions': {'maxFeaturesPerTile': 10}, + }, + }, + }, + task.config, + ) task_with_priority = ee.batch.Export.table.toFeatureView( collection=ee.FeatureCollection('foo'), @@ -1113,7 +1201,8 @@ def testExportTableToFeatureViewEmptyParamsCloudApi(self): task = ee.batch.Export.table.toFeatureView( collection=ee.FeatureCollection('foo'), description='foo', - assetId='users/foo/bar') + assetId='users/foo/bar', + ) with self.subTest(name='TaskIdAndName'): self.assertIsNone(task.id) self.assertIsNone(task.name) @@ -1128,12 +1217,15 @@ def testExportTableToFeatureViewEmptyParamsCloudApi(self): 'description': 'foo', 'featureViewExportOptions': { 'featureViewDestination': { - 'name': - 'projects/earthengine-legacy/assets/users/foo/bar', + 'name': ( + 'projects/earthengine-legacy/assets/users/foo/bar' + ), }, - 'ingestionTimeParameters': {} - } - }, task.config) + 'ingestionTimeParameters': {}, + }, + }, + task.config, + ) def testExportTableToFeatureViewAllIngestionParams(self): """Verifies the task ingestion params created by toFeatureView().""" @@ -1145,59 +1237,63 @@ def testExportTableToFeatureViewAllIngestionParams(self): 'maxFeaturesPerTile': 10, 'thinningStrategy': 'GLOBALLY_CONSISTENT', 'thinningRanking': 'my-attribute ASC, other-attr DESC', - 'zOrderRanking': ['.minZoomLevel DESC', '.geometryType ASC'] - }) + 'zOrderRanking': ['.minZoomLevel DESC', '.geometryType ASC'], + }, + ) expected_ingestion_params = { 'rankingOptions': { 'thinningRankingRule': { - 'rankByOneThingRule': [{ - 'direction': 'ASCENDING', - 'rankByAttributeRule': { - 'attributeName': 'my-attribute' - } - }, { - 'direction': 'DESCENDING', - 'rankByAttributeRule': { - 'attributeName': 'other-attr' - } - }] + 'rankByOneThingRule': [ + { + 'direction': 'ASCENDING', + 'rankByAttributeRule': { + 'attributeName': 'my-attribute' + }, + }, + { + 'direction': 'DESCENDING', + 'rankByAttributeRule': {'attributeName': 'other-attr'}, + }, + ] }, 'zOrderRankingRule': { - 'rankByOneThingRule': [{ - 'direction': 'DESCENDING', - 'rankByMinZoomLevelRule': {} - }, { - 'direction': 'ASCENDING', - 'rankByGeometryTypeRule': {} - }] - } + 'rankByOneThingRule': [ + {'direction': 'DESCENDING', 'rankByMinZoomLevelRule': {}}, + {'direction': 'ASCENDING', 'rankByGeometryTypeRule': {}}, + ] + }, }, 'thinningOptions': { 'maxFeaturesPerTile': 10, - 'thinningStrategy': 'GLOBALLY_CONSISTENT' - } + 'thinningStrategy': 'GLOBALLY_CONSISTENT', + }, } self.assertEqual( expected_ingestion_params, - task.config['featureViewExportOptions']['ingestionTimeParameters']) + task.config['featureViewExportOptions']['ingestionTimeParameters'], + ) def testExportTableToFeatureViewBadRankByOneThingRule(self): """Verifies a bad RankByOneThingRule throws an exception.""" - with self.assertRaisesRegex(ee.EEException, - 'Ranking rule format is invalid.*'): + with self.assertRaisesRegex( + ee.EEException, 'Ranking rule format is invalid.*' + ): ee.batch.Export.table.toFeatureView( collection=ee.FeatureCollection('foo'), assetId='users/foo/bar', - ingestionTimeParameters={'thinningRanking': 'my-attribute BAD_DIR'}) + ingestionTimeParameters={'thinningRanking': 'my-attribute BAD_DIR'}, + ) def testExportTableToFeatureViewBadRankingRule(self): """Verifies a bad RankingRule throws an exception.""" - with self.assertRaisesRegex(ee.EEException, - 'Unable to build ranking rule from rules.*'): + with self.assertRaisesRegex( + ee.EEException, 'Unable to build ranking rule from rules.*' + ): ee.batch.Export.table.toFeatureView( collection=ee.FeatureCollection('foo'), assetId='users/foo/bar', - ingestionTimeParameters={'thinningRanking': {'key': 'val'}}) + ingestionTimeParameters={'thinningRanking': {'key': 'val'}}, + ) def testExportTableToFeatureViewBadIngestionTimeParams(self): """Verifies a bad set of ingestion time params throws an exception.""" @@ -1211,7 +1307,8 @@ def testExportTableToFeatureViewBadIngestionTimeParams(self): ee.batch.Export.table.toFeatureView( collection=ee.FeatureCollection('foo'), assetId='users/foo/bar', - ingestionTimeParameters={'badThinningKey': {'key': 'val'}}) + ingestionTimeParameters={'badThinningKey': {'key': 'val'}}, + ) def testExportTableToBigQueryRequiredParams(self): """Verifies the export task created by Export.table.toBigQuery().""" @@ -1313,7 +1410,8 @@ def testExportVideoCloudApi(self): framesPerSecond=30, maxFrames=10000, maxPixels=10000000, - maxWorkers=100) + maxWorkers=100, + ) collection = ee.ImageCollection([ee.Image(1), ee.Image(2)]) task = ee.batch.Export.video(collection, 'TestVideoName', config) self.assertIsNone(task.id) @@ -1324,7 +1422,8 @@ def testExportVideoCloudApi(self): def expected_preparation_function(img): img = img.setDefaultProjection( - crs='SR-ORG:6627', crsTransform=[1, 0, 0, 0, -1, 0]) + crs='SR-ORG:6627', crsTransform=[1, 0, 0, 0, -1, 0] + ) img = img.clipToBoundsAndScale(geometry=region, maxDimension=16) return img @@ -1333,24 +1432,26 @@ def expected_preparation_function(img): # serialised forms instead. self.assertEqual( expected_collection.serialize(for_cloud_api=True), - task.config.pop('expression').serialize(for_cloud_api=True)) - self.assertEqual({ - 'description': 'TestVideoName', - 'videoOptions': { - 'framesPerSecond': 30, - 'maxFrames': 10000, - 'maxPixelsPerFrame': { - 'value': '10000000' - } - }, - 'fileExportOptions': { - 'fileFormat': 'MP4', - 'driveDestination': { - 'filenamePrefix': 'TestVideoName', - } + task.config.pop('expression').serialize(for_cloud_api=True), + ) + self.assertEqual( + { + 'description': 'TestVideoName', + 'videoOptions': { + 'framesPerSecond': 30, + 'maxFrames': 10000, + 'maxPixelsPerFrame': {'value': '10000000'}, + }, + 'fileExportOptions': { + 'fileFormat': 'MP4', + 'driveDestination': { + 'filenamePrefix': 'TestVideoName', + }, + }, + 'maxWorkers': {'value': 100}, }, - 'maxWorkers': {'value': 100} - }, task.config) + task.config, + ) config['outputBucket'] = 'test-bucket' gcs_task = ee.batch.Export.video(collection, 'TestVideoName', config) @@ -1358,41 +1459,47 @@ def expected_preparation_function(img): self.assertEqual('UNSUBMITTED', gcs_task.state) self.assertEqual( expected_collection.serialize(for_cloud_api=True), - gcs_task.config.pop('expression').serialize(for_cloud_api=True)) - self.assertEqual({ - 'description': 'TestVideoName', - 'videoOptions': { - 'framesPerSecond': 30, - 'maxFrames': 10000, - 'maxPixelsPerFrame': { - 'value': '10000000' - } - }, - 'fileExportOptions': { - 'fileFormat': 'MP4', - 'cloudStorageDestination': { - 'bucket': 'test-bucket', - 'filenamePrefix': 'TestVideoName', - } + gcs_task.config.pop('expression').serialize(for_cloud_api=True), + ) + self.assertEqual( + { + 'description': 'TestVideoName', + 'videoOptions': { + 'framesPerSecond': 30, + 'maxFrames': 10000, + 'maxPixelsPerFrame': {'value': '10000000'}, + }, + 'fileExportOptions': { + 'fileFormat': 'MP4', + 'cloudStorageDestination': { + 'bucket': 'test-bucket', + 'filenamePrefix': 'TestVideoName', + }, + }, + 'maxWorkers': {'value': 100}, }, - 'maxWorkers': {'value': 100} - }, gcs_task.config) + gcs_task.config, + ) - with self.assertRaisesRegex(ee.EEException, - 'Unknown configuration options.*'): + with self.assertRaisesRegex( + ee.EEException, 'Unknown configuration options.*' + ): config_with_bogus_option = config.copy() config_with_bogus_option['flamesPerSleestak'] = 30 - ee.batch.Export.video(collection, 'TestVideoName', - config_with_bogus_option) + ee.batch.Export.video( + collection, 'TestVideoName', config_with_bogus_option + ) def testExportVideoToCloudStorageCloudApi(self): """Verifies the task created by Export.video.toCloudStorage().""" with apitestcase.UsingCloudApi(): region = ee.Geometry.Rectangle(1, 2, 3, 4) collection = ee.ImageCollection([ee.Image(1), ee.Image(2)]) + def expected_preparation_function(img): img = img.reproject( - crs='foo', crsTransform=[9.0, 8.0, 7.0, 6.0, 5.0, 4.0]) + crs='foo', crsTransform=[9.0, 8.0, 7.0, 6.0, 5.0, 4.0] + ) img = img.clipToBoundsAndScale(geometry=region, maxDimension=16) return img @@ -1404,8 +1511,8 @@ def expected_preparation_function(img): 'cloudStorageDestination': { 'bucket': 'test-bucket', 'filenamePrefix': 'TestVideoName', - } - } + }, + }, } # Test keyed parameters. @@ -1416,25 +1523,37 @@ def expected_preparation_function(img): dimensions=16, region=region['coordinates'], crsTransform='[9,8,7,6,5,4]', - crs='foo') + crs='foo', + ) self.assertIsNone(task_keyed.id) self.assertIsNone(task_keyed.name) self.assertEqual('EXPORT_VIDEO', task_keyed.task_type) self.assertEqual('UNSUBMITTED', task_keyed.state) self.assertEqual( expected_collection.serialize(for_cloud_api=True), - task_keyed.config.pop('expression').serialize(for_cloud_api=True)) + task_keyed.config.pop('expression').serialize(for_cloud_api=True), + ) self.assertEqual(expected_config, task_keyed.config) # Test ordered parameters. task_ordered = ee.batch.Export.video.toCloudStorage( - collection, 'TestVideoName', 'test-bucket', None, None, 16, - region['coordinates'], None, 'foo', '[9,8,7,6,5,4]') + collection, + 'TestVideoName', + 'test-bucket', + None, + None, + 16, + region['coordinates'], + None, + 'foo', + '[9,8,7,6,5,4]', + ) self.assertEqual('EXPORT_VIDEO', task_ordered.task_type) self.assertEqual('UNSUBMITTED', task_ordered.state) self.assertEqual( expected_collection.serialize(for_cloud_api=True), - task_ordered.config.pop('expression').serialize(for_cloud_api=True)) + task_ordered.config.pop('expression').serialize(for_cloud_api=True), + ) self.assertEqual(expected_config, task_ordered.config) expected_config_with_priority = { @@ -1471,9 +1590,11 @@ def testExportVideoToDriveCloudApi(self): with apitestcase.UsingCloudApi(): region = ee.Geometry.Rectangle(1, 2, 3, 4) collection = ee.ImageCollection([ee.Image(1), ee.Image(2)]) + def expected_preparation_function(img): img = img.reproject( - crs='SR-ORG:6627', crsTransform=[9.0, 8.0, 7.0, 6.0, 5.0, 4.0]) + crs='SR-ORG:6627', crsTransform=[9.0, 8.0, 7.0, 6.0, 5.0, 4.0] + ) img = img.clipToBoundsAndScale(geometry=region, maxDimension=16) return img @@ -1485,8 +1606,8 @@ def expected_preparation_function(img): 'driveDestination': { 'folder': 'test-folder', 'filenamePrefix': 'TestVideoName', - } - } + }, + }, } # Test keyed parameters. @@ -1496,25 +1617,37 @@ def expected_preparation_function(img): folder='test-folder', dimensions=16, crsTransform='[9,8,7,6,5,4]', - region=region['coordinates']) + region=region['coordinates'], + ) self.assertIsNone(task_keyed.id) self.assertIsNone(task_keyed.name) self.assertEqual('EXPORT_VIDEO', task_keyed.task_type) self.assertEqual('UNSUBMITTED', task_keyed.state) self.assertEqual( expected_collection.serialize(for_cloud_api=True), - task_keyed.config.pop('expression').serialize(for_cloud_api=True)) + task_keyed.config.pop('expression').serialize(for_cloud_api=True), + ) self.assertEqual(expected_config, task_keyed.config) # Test ordered parameters. task_ordered = ee.batch.Export.video.toDrive( - collection, 'TestVideoName', 'test-folder', None, None, 16, - region['coordinates'], None, 'SR-ORG:6627', '[9,8,7,6,5,4]') + collection, + 'TestVideoName', + 'test-folder', + None, + None, + 16, + region['coordinates'], + None, + 'SR-ORG:6627', + '[9,8,7,6,5,4]', + ) self.assertEqual('EXPORT_VIDEO', task_ordered.task_type) self.assertEqual('UNSUBMITTED', task_ordered.state) self.assertEqual( expected_collection.serialize(for_cloud_api=True), - task_ordered.config.pop('expression').serialize(for_cloud_api=True)) + task_ordered.config.pop('expression').serialize(for_cloud_api=True), + ) self.assertEqual(expected_config, task_ordered.config) expected_config_with_priority = { diff --git a/python/ee/tests/classifier_test.py b/python/ee/tests/classifier_test.py index b4fe8eff7..ab284d932 100644 --- a/python/ee/tests/classifier_test.py +++ b/python/ee/tests/classifier_test.py @@ -45,6 +45,7 @@ def test_cast(self): result = json.loads(classifier.serialize()) self.assertEqual(_CLASSIFIER_JSON, result) + @unittest.skip('Does not work on github with python <= 3.9') def test_no_args(self): message = ( r'Classifier\.__init__\(\) missing 1 required positional argument:' diff --git a/python/ee/tests/clusterer_test.py b/python/ee/tests/clusterer_test.py index 96a0b2a25..c05e019ea 100644 --- a/python/ee/tests/clusterer_test.py +++ b/python/ee/tests/clusterer_test.py @@ -55,6 +55,7 @@ def test_cast(self): result = json.loads(clusterer.serialize()) self.assertEqual(_WEKA_COBWEB_SERIALIZED, result) + @unittest.skip('Does not work on github with python <= 3.9') def test_no_args(self): message = ( r'Clusterer\.__init__\(\) missing 1 required positional argument:' diff --git a/python/ee/tests/data_test.py b/python/ee/tests/data_test.py index 83afd4ee4..ec8de2744 100644 --- a/python/ee/tests/data_test.py +++ b/python/ee/tests/data_test.py @@ -157,11 +157,54 @@ def testListAssets(self): cloud_api_resource.projects().assets().listAssets( ).execute.return_value = mock_result cloud_api_resource.projects().assets().listAssets_next.return_value = None - actual_result = ee.data.listAssets({'p': 'q'}) + actual_result = ee.data.listAssets('path/to/folder') cloud_api_resource.projects().assets().listAssets( ).execute.assert_called_once() self.assertEqual(mock_result, actual_result) + def testListAssetsWithPageSize(self): + mock_http = mock.MagicMock(httplib2.Http) + ok_resp = httplib2.Response({'status': 200}) + page = ( + b'{"assets": [{"path": "id1", "type": "type1"}], "nextPageToken": "t1"}' + ) + mock_http.request.side_effect = [(ok_resp, page)] + with apitestcase.UsingCloudApi(mock_http=mock_http): + actual_result = ee.data.listAssets( + {'parent': 'path/to/folder', 'pageSize': 3} + ) + expected_result = { + 'assets': [{'path': 'id1', 'type': 'type1'}], + 'nextPageToken': 't1', + } + self.assertEqual(expected_result, actual_result) + + def testListAssetsMultiplePages(self): + mock_http = mock.MagicMock(httplib2.Http) + ok_resp = httplib2.Response({'status': 200}) + page1 = ( + b'{"assets": [{"path": "id1", "type": "type1"}], "nextPageToken": "t1"}' + ) + page2 = ( + b'{"assets": [{"path": "id2", "type": "type2"}], "nextPageToken": "t2"}' + ) + page3 = b'{"assets": [{"path": "id3", "type": "type3"}]}' + mock_http.request.side_effect = [ + (ok_resp, page1), + (ok_resp, page2), + (ok_resp, page3), + ] + with apitestcase.UsingCloudApi(mock_http=mock_http): + actual_result = ee.data.listAssets('path/to/folder') + expected_result = { + 'assets': [ + {'path': 'id1', 'type': 'type1'}, + {'path': 'id2', 'type': 'type2'}, + {'path': 'id3', 'type': 'type3'}, + ] + } + self.assertEqual(expected_result, actual_result) + def testListImages(self): cloud_api_resource = mock.MagicMock() with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource): @@ -169,7 +212,7 @@ def testListImages(self): cloud_api_resource.projects().assets().listAssets( ).execute.return_value = mock_result cloud_api_resource.projects().assets().listAssets_next.return_value = None - actual_result = ee.data.listImages({'p': 'q'}) + actual_result = ee.data.listImages('path/to/folder') cloud_api_resource.projects().assets().listAssets( ).execute.assert_called_once() self.assertEqual({'images': [{ @@ -177,6 +220,49 @@ def testListImages(self): 'type': 'type1' }]}, actual_result) + def testListImagesWithPageSize(self): + mock_http = mock.MagicMock(httplib2.Http) + ok_resp = httplib2.Response({'status': 200}) + page = ( + b'{"assets": [{"path": "id1", "type": "type1"}], "nextPageToken": "t1"}' + ) + mock_http.request.side_effect = [(ok_resp, page)] + with apitestcase.UsingCloudApi(mock_http=mock_http): + actual_result = ee.data.listImages( + {'parent': 'path/to/folder', 'pageSize': 3} + ) + expected_result = { + 'images': [{'path': 'id1', 'type': 'type1'}], + 'nextPageToken': 't1', + } + self.assertEqual(expected_result, actual_result) + + def testListImagesMultiplePages(self): + mock_http = mock.MagicMock(httplib2.Http) + ok_resp = httplib2.Response({'status': 200}) + page1 = ( + b'{"assets": [{"path": "id1", "type": "type1"}], "nextPageToken": "t1"}' + ) + page2 = ( + b'{"assets": [{"path": "id2", "type": "type2"}], "nextPageToken": "t2"}' + ) + page3 = b'{"assets": [{"path": "id3", "type": "type3"}]}' + mock_http.request.side_effect = [ + (ok_resp, page1), + (ok_resp, page2), + (ok_resp, page3), + ] + with apitestcase.UsingCloudApi(mock_http=mock_http): + actual_result = ee.data.listImages('path/to/folder') + expected_result = { + 'images': [ + {'path': 'id1', 'type': 'type1'}, + {'path': 'id2', 'type': 'type2'}, + {'path': 'id3', 'type': 'type3'}, + ] + } + self.assertEqual(expected_result, actual_result) + def testListBuckets(self): cloud_api_resource = mock.MagicMock() with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource): diff --git a/python/ee/tests/daterange_test.py b/python/ee/tests/daterange_test.py index 30381d888..1c4568803 100644 --- a/python/ee/tests/daterange_test.py +++ b/python/ee/tests/daterange_test.py @@ -5,6 +5,7 @@ """ import json +from typing import Any, Dict import ee from ee import apitestcase @@ -19,6 +20,24 @@ DATERANGE = 'DateRange' +def make_expression_graph( + function_invocation_value: Dict[str, Any] +) -> Dict[str, Any]: + return { + 'result': '0', + 'values': {'0': {'functionInvocationValue': function_invocation_value}}, + } + + +def daterange_function_expr(value: int) -> Dict[str, Any]: + return { + 'functionInvocationValue': { + 'functionName': 'DateRange', + 'arguments': {'start': {'constantValue': value}}, + } + } + + class DateRangeTest(apitestcase.ApiTestCase): def test_init_all(self): @@ -103,7 +122,6 @@ def test_init_ee_dates(self): end = ee.Date('2017-06-24T07:00:00') daterange = ee.DateRange(start, end) result = json.loads(daterange.serialize()) - print(result) expect = { 'result': '0', 'values': { @@ -141,6 +159,118 @@ def test_no_args(self): with self.assertRaisesRegex(TypeError, message): ee.DateRange() # pytype:disable=missing-parameter + def test_contains(self): + expect = make_expression_graph({ + 'functionName': 'DateRange.contains', + 'arguments': { + 'dateRange': daterange_function_expr(1), + 'other': {'constantValue': 2}, + }, + }) + expression = ee.DateRange(1).contains(2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.DateRange(1).contains(other=2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_end(self): + expect = make_expression_graph({ + 'functionName': 'DateRange.end', + 'arguments': {'dateRange': daterange_function_expr(1)}, + }) + expression = ee.DateRange(1).end() + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_intersection(self): + expect = make_expression_graph({ + 'functionName': 'DateRange.intersection', + 'arguments': { + 'dateRange': daterange_function_expr(1), + 'other': daterange_function_expr(2), + }, + }) + expression = ee.DateRange(1).intersection(2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.DateRange(1).intersection(other=2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_intersects(self): + expect = make_expression_graph({ + 'arguments': { + 'dateRange': daterange_function_expr(1), + 'other': daterange_function_expr(2), + }, + 'functionName': 'DateRange.intersects', + }) + expression = ee.DateRange(1).intersects(2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.DateRange(1).intersects(other=2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_isEmpty(self): + expect = make_expression_graph({ + 'arguments': { + 'dateRange': daterange_function_expr(1), + }, + 'functionName': 'DateRange.isEmpty', + }) + expression = ee.DateRange(1).isEmpty() + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_isUnbounded(self): + expect = make_expression_graph({ + 'arguments': { + 'dateRange': daterange_function_expr(1), + }, + 'functionName': 'DateRange.isUnbounded', + }) + expression = ee.DateRange(1).isUnbounded() + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_start(self): + expect = make_expression_graph({ + 'functionName': 'DateRange.start', + 'arguments': { + 'dateRange': { + 'functionInvocationValue': { + 'functionName': 'DateRange', + 'arguments': {'start': {'constantValue': 1}}, + } + }, + }, + }) + expression = ee.DateRange(1).start() + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_union(self): + expect = make_expression_graph({ + 'arguments': { + 'dateRange': daterange_function_expr(1), + 'other': daterange_function_expr(2), + }, + 'functionName': 'DateRange.union', + }) + + expression = ee.DateRange(1).union(2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.DateRange(1).union(other=2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + if __name__ == '__main__': unittest.main() diff --git a/python/ee/tests/deprecation_test.py b/python/ee/tests/deprecation_test.py index ad175c3ba..3236d12b9 100644 --- a/python/ee/tests/deprecation_test.py +++ b/python/ee/tests/deprecation_test.py @@ -1,2 +1,183 @@ #!/usr/bin/env python3 """Tests for the ee.deprecation module.""" + +import contextlib +from typing import Any, Dict +import warnings +import unittest + +from absl.testing import parameterized + +from ee import apitestcase +from ee import deprecation +from ee import ee_string +from ee import image +from ee import imagecollection +import unittest + + +_STAC_JSON = { + 'stac_version': '1.0.0', + 'type': 'Catalog', + 'id': 'GEE_catalog', + 'title': 'Google Earth Engine Catalog (Flat Version)', + 'description': 'A description.', + 'links': [ + { + 'href': 'https://example.test/catalog/catalog.json', + 'rel': 'root', + 'type': 'application/json', + }, + { + 'href': 'https://example.test/catalog/catalog.json', + 'rel': 'self', + 'type': 'application/json', + }, + { + 'href': 'https://example.test/date_and_learn_more.json', + 'title': 'date_and_learn_more', + 'deprecated': True, + 'gee:replacement_id': 'replacement_id', + 'gee:removal_date': '2024-07-01T00:00:00Z', + 'gee:learn_more_url': 'learn_more_url', + }, + { + 'href': 'https://example.test/date_only.json', + 'title': 'date_only', + 'deprecated': True, + 'gee:replacement_id': 'replacement_id', + 'gee:removal_date': '2024-07-01T00:00:00Z', + }, + { + 'href': 'https://example.test/learn_more_url_only.json', + 'title': 'learn_more_url_only', + 'deprecated': True, + 'gee:replacement_id': 'replacement_id', + 'gee:learn_more_url': 'learn_more_url', + }, + { + 'href': 'https://example.test/deprecated_asset.json', + 'title': 'deprecated_asset', + 'deprecated': True, + 'gee:replacement_id': 'replacement_id', + }, + { + 'href': 'https://example.test/non_deprecated_asset.json', + 'title': 'non_deprecated_asset', + }, + ], +} + + +_EXPECTED_WARNINGS = { + 'deprecated_asset': ( + r'Attention required for deprecated_asset! You are using a deprecated' + r' asset.\nTo ensure continued functionality, please update it.' + ), + 'date_and_learn_more': ( + r'Attention required for date_and_learn_more! You are using a' + r' deprecated asset.\nTo ensure continued functionality, please update' + r' it by July 1, 2024.\nLearn more: learn_more_url' + ), + 'date_only': ( + r'Attention required for date_only! You are using a deprecated asset.\n' + r'To ensure continued functionality, please update it by July 1, 2024.' + ), + 'learn_more_url_only': ( + r'Attention required for learn_more_url_only! You are using a' + r' deprecated asset.\nTo ensure continued functionality, please update' + r' it.\nLearn more: learn_more_url' + ), +} + + +class FakeClass: + + @deprecation.WarnForDeprecatedAsset('arg1') + def __init__(self, arg1=None, arg2=None): + pass + + @deprecation.WarnForDeprecatedAsset('arg2') + def some_function(self, arg1=None, arg2=None): + pass + + +class DeprecationTest(apitestcase.ApiTestCase, parameterized.TestCase): + + @contextlib.contextmanager + def assertDoesNotWarn(self): + """Asserts that no warnings are thrown.""" + with warnings.catch_warnings(): + warnings.simplefilter('error') + yield + + # Overridden from apitestcase.ApiTestCase. + def _MockFetchDataCatalogStac(self) -> Dict[str, Any]: + return _STAC_JSON + + def test_no_warnings_thrown(self): + with self.assertDoesNotWarn(): + FakeClass('valid-asset') + + def test_no_warnings_thrown_second_arg(self): + with self.assertDoesNotWarn(): + FakeClass().some_function('some-value', 'valid-asset') + + @unittest.skip('Does not work on github') + @parameterized.named_parameters( + ('deprecated_asset', 'deprecated_asset'), + ('date_and_learn_more', 'date_and_learn_more'), + ('date_only', 'date_only'), + ('learn_more_url_only', 'learn_more_url_only'), + ) + def test_warning_thrown_args_init(self, asset_id: str): + with self.assertWarnsRegex( + DeprecationWarning, _EXPECTED_WARNINGS[asset_id] + ): + FakeClass(asset_id, 'some-value') + + @unittest.skip('Does not work on github') + def test_warning_thrown_args_instance_method(self): + asset = 'deprecated_asset' + with self.assertWarnsRegex(DeprecationWarning, _EXPECTED_WARNINGS[asset]): + FakeClass().some_function('some-value', asset) + + @unittest.skip('Does not work on github') + def test_warning_thrown_kwargs_init(self): + asset = 'deprecated_asset' + with self.assertWarnsRegex(DeprecationWarning, _EXPECTED_WARNINGS[asset]): + FakeClass(arg1=asset) + + @unittest.skip('Does not work on github') + def test_warning_thrown_kwargs_instance_method(self): + asset = 'deprecated_asset' + with self.assertWarnsRegex(DeprecationWarning, _EXPECTED_WARNINGS[asset]): + FakeClass().some_function(arg2=asset) + + @unittest.skip('Does not work on github') + def test_same_warning_not_thrown(self): + # Verifies the same warning message is not thrown twice. + asset = 'deprecated_asset' + with self.assertWarnsRegex(DeprecationWarning, _EXPECTED_WARNINGS[asset]): + FakeClass(arg1=asset) + with self.assertDoesNotWarn(): + FakeClass(arg1=asset) + + # Verifies that a different warning message is thrown. + asset = 'date_only' + with self.assertWarnsRegex(DeprecationWarning, _EXPECTED_WARNINGS[asset]): + FakeClass(arg1=asset) + + def test_ee_object_warning_not_thrown(self): + with self.assertDoesNotWarn(): + FakeClass(arg1=ee_string.String('non_deprecated_asset')) + with self.assertDoesNotWarn(): + FakeClass( + imagecollection.ImageCollection([image.Image(0), image.Image(1)]) + ) + with self.assertDoesNotWarn(): + FakeClass(None) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/ee/tests/ee_date_test.py b/python/ee/tests/ee_date_test.py index add8e4f42..47764aec3 100644 --- a/python/ee/tests/ee_date_test.py +++ b/python/ee/tests/ee_date_test.py @@ -3,11 +3,35 @@ import datetime import json +from typing import Any, Dict import ee from ee import apitestcase import unittest +DAY = 'day' +WEEK = 'week' + +UTC = 'UTC' + + +def make_expression_graph( + function_invocation_value: Dict[str, Any] +) -> Dict[str, Any]: + return { + 'result': '0', + 'values': {'0': {'functionInvocationValue': function_invocation_value}}, + } + + +def date_function_expr(value: int) -> Dict[str, Any]: + return { + 'functionInvocationValue': { + 'functionName': 'Date', + 'arguments': {'value': {'constantValue': value}}, + } + } + class DateTest(apitestcase.ApiTestCase): @@ -103,6 +127,216 @@ def test_with_reducer(self): def test_name(self): self.assertEqual('Date', ee.Date.name()) + def test_advance(self): + expect = make_expression_graph({ + 'arguments': { + 'date': date_function_expr(1), + 'delta': {'constantValue': 2}, + 'unit': {'constantValue': DAY}, + 'timeZone': {'constantValue': UTC}, + }, + 'functionName': 'Date.advance', + }) + expression = ee.Date(1).advance(2, DAY, UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.Date(1).advance(delta=2, unit=DAY, timeZone=UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_difference(self): + expect = make_expression_graph({ + 'arguments': { + 'date': date_function_expr(1), + 'start': date_function_expr(2), + 'unit': {'constantValue': DAY}, + }, + 'functionName': 'Date.difference', + }) + expression = ee.Date(1).difference(2, DAY) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.Date(1).difference(start=2, unit=DAY) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_format(self): + a_format = 'a format' + expect = make_expression_graph({ + 'arguments': { + 'date': date_function_expr(1), + 'format': {'constantValue': a_format}, + 'timeZone': {'constantValue': UTC}, + }, + 'functionName': 'Date.format', + }) + expression = ee.Date(1).format(a_format, UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.Date(1).format(format=a_format, timeZone=UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_from_ymd(self): + expect = make_expression_graph({ + 'arguments': { + 'year': {'constantValue': 1}, + 'month': {'constantValue': 2}, + 'day': {'constantValue': 3}, + 'timeZone': {'constantValue': UTC}, + }, + 'functionName': 'Date.fromYMD', + }) + expression = ee.Date.fromYMD(1, 2, 3, UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.Date.fromYMD(year=1, month=2, day=3, timeZone=UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_get(self): + expect = make_expression_graph({ + 'arguments': { + 'date': date_function_expr(1), + 'unit': {'constantValue': DAY}, + 'timeZone': {'constantValue': UTC}, + }, + 'functionName': 'Date.get', + }) + expression = ee.Date(1).get(DAY, UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.Date(1).get(unit=DAY, timeZone=UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_get_fraction(self): + expect = make_expression_graph({ + 'arguments': { + 'date': date_function_expr(1), + 'unit': {'constantValue': DAY}, + 'timeZone': {'constantValue': UTC}, + }, + 'functionName': 'Date.getFraction', + }) + expression = ee.Date(1).getFraction(DAY, UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.Date(1).getFraction(unit=DAY, timeZone=UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_get_range(self): + expect = make_expression_graph({ + 'arguments': { + 'date': date_function_expr(1), + 'unit': {'constantValue': DAY}, + 'timeZone': {'constantValue': UTC}, + }, + 'functionName': 'Date.getRange', + }) + expression = ee.Date(1).getRange(DAY, UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.Date(1).getRange(unit=DAY, timeZone=UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_get_relative(self): + expect = make_expression_graph({ + 'arguments': { + 'date': date_function_expr(1), + 'unit': {'constantValue': DAY}, + 'inUnit': {'constantValue': WEEK}, + 'timeZone': {'constantValue': UTC}, + }, + 'functionName': 'Date.getRelative', + }) + expression = ee.Date(1).getRelative(DAY, WEEK, UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.Date(1).getRelative(unit=DAY, inUnit=WEEK, timeZone=UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_millis(self): + expect = make_expression_graph({ + 'arguments': { + 'date': date_function_expr(1), + }, + 'functionName': 'Date.millis', + }) + expression = ee.Date(1).millis() + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_parse(self): + a_format = 'a format' + date = 'a date' + expect = make_expression_graph({ + 'arguments': { + 'format': {'constantValue': a_format}, + 'date': {'constantValue': date}, + 'timeZone': {'constantValue': UTC}, + }, + 'functionName': 'Date.parse', + }) + expression = ee.Date(1).parse(a_format, date, UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.Date(1).parse(format=a_format, date=date, timeZone=UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_unit_ratio(self): + expect = make_expression_graph({ + 'arguments': { + 'numerator': {'constantValue': WEEK}, + 'denominator': {'constantValue': DAY}, + }, + 'functionName': 'Date.unitRatio', + }) + expression = ee.Date(1).unitRatio(WEEK, DAY) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.Date(1).unitRatio(numerator=WEEK, denominator=DAY) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_update(self): + expect = make_expression_graph({ + 'arguments': { + 'date': date_function_expr(1), + 'year': {'constantValue': 2}, + 'month': {'constantValue': 3}, + 'day': {'constantValue': 4}, + 'hour': {'constantValue': 5}, + 'minute': {'constantValue': 6}, + 'second': {'constantValue': 7}, + 'timeZone': {'constantValue': UTC}, + }, + 'functionName': 'Date.update', + }) + expression = ee.Date(1).update(2, 3, 4, 5, 6, 7, UTC) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.Date(1).update( + year=2, month=3, day=4, hour=5, minute=6, second=7, timeZone=UTC + ) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + if __name__ == '__main__': unittest.main() diff --git a/python/ee/tests/ee_list_test.py b/python/ee/tests/ee_list_test.py index 53be6f1e2..b25be0ee5 100644 --- a/python/ee/tests/ee_list_test.py +++ b/python/ee/tests/ee_list_test.py @@ -1,11 +1,22 @@ #!/usr/bin/env python3 """Test for the ee.list module.""" +import json +from typing import Any, Dict import ee from ee import apitestcase import unittest +def make_expression_graph( + function_invocation_value: Dict[str, Any] +) -> Dict[str, Any]: + return { + 'result': '0', + 'values': {'0': {'functionInvocationValue': function_invocation_value}}, + } + + class ListTest(apitestcase.ApiTestCase): def testList(self): @@ -16,10 +27,15 @@ def testList(self): computed = ee.List([1, 2, 3]).slice(0) # pylint: disable=no-member self.assertIsInstance(computed, ee.List) self.assertEqual(ee.ApiFunction.lookup('List.slice'), computed.func) - self.assertEqual({ - 'list': ee.List([1, 2, 3]), - 'start': ee.Number(0) - }, computed.args) + self.assertEqual( + { + 'list': ee.List([1, 2, 3]), + 'start': ee.Number(0), + 'end': None, + 'step': None, + }, + computed.args, + ) def testMapping(self): lst = ee.List(['foo', 'bar']) @@ -57,6 +73,588 @@ def testInternals(self): self.assertNotEqual(a.__hash__(), b.__hash__()) self.assertEqual(a.__hash__(), c.__hash__()) + def test_add(self): + expect = make_expression_graph({ + 'arguments': { + 'element': {'constantValue': 'b'}, + 'list': {'constantValue': ['a']}, + }, + 'functionName': 'List.add', + }) + expression = ee.List(['a']).add('b') + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List(['a']).add(element='b') + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_cat(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'other': {'constantValue': [2]}, + }, + 'functionName': 'List.cat', + }) + expression = ee.List([1]).cat([2]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).cat(other=[2]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_contains(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'element': {'constantValue': 2}, + }, + 'functionName': 'List.contains', + }) + expression = ee.List([1]).contains(2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).contains(element=2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_containsAll(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1, 2]}, + 'other': {'constantValue': [3, 4]}, + }, + 'functionName': 'List.containsAll', + }) + expression = ee.List([1, 2]).containsAll([3, 4]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1, 2]).containsAll(other=[3, 4]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_distinct(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + }, + 'functionName': 'List.distinct', + }) + expression = ee.List([1]).distinct() + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).distinct() + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_equals(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'other': {'constantValue': [2]}, + }, + 'functionName': 'List.equals', + }) + expression = ee.List([1]).equals([2]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).equals(other=[2]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_filter(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'filter': { + 'functionInvocationValue': { + 'functionName': 'Filter.greaterThan', + 'arguments': { + 'leftField': {'constantValue': 'item'}, + 'rightValue': {'constantValue': 3}, + }, + } + }, + }, + 'functionName': 'List.filter', + }) + a_filter = ee.Filter.gt('item', 3) + expression = ee.List([1]).filter(a_filter) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).filter(filter=a_filter) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_flatten(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + }, + 'functionName': 'List.flatten', + }) + expression = ee.List([1]).flatten() + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_frequency(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'element': {'constantValue': 2}, + }, + 'functionName': 'List.frequency', + }) + expression = ee.List([1]).frequency(2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).frequency(element=2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_get(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'index': {'constantValue': 2}, + }, + 'functionName': 'List.get', + }) + expression = ee.List([1]).get(2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).get(index=2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_getArray(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'index': {'constantValue': 2}, + }, + 'functionName': 'List.getArray', + }) + expression = ee.List([1]).getArray(2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).getArray(index=2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_getGeometry(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'index': {'constantValue': 2}, + }, + 'functionName': 'List.getGeometry', + }) + expression = ee.List([1]).getGeometry(2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).getGeometry(index=2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_getNumber(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'index': {'constantValue': 2}, + }, + 'functionName': 'List.getNumber', + }) + expression = ee.List([1]).getNumber(2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).getNumber(index=2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_get_string(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'index': {'constantValue': 2}, + }, + 'functionName': 'List.getString', + }) + expression = ee.List([1]).getString(2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).getString(index=2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_index_of(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'element': {'constantValue': 2}, + }, + 'functionName': 'List.indexOf', + }) + expression = ee.List([1]).indexOf(2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).indexOf(element=2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_index_of_sublist(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'target': {'constantValue': [2, 3]}, + }, + 'functionName': 'List.indexOfSublist', + }) + expression = ee.List([1]).indexOfSublist([2, 3]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).indexOfSublist(target=[2, 3]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_insert(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'index': {'constantValue': 2}, + 'element': {'constantValue': 3}, + }, + 'functionName': 'List.insert', + }) + expression = ee.List([1]).insert(2, 3) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).insert(index=2, element=3) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + # TODO: test_iterate + + def test_join(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'separator': {'constantValue': 'a'}, + }, + 'functionName': 'List.join', + }) + expression = ee.List([1]).join('a') + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).join(separator='a') + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_last_index_of_sublist(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'target': {'constantValue': [2, 3]}, + }, + 'functionName': 'List.lastIndexOfSubList', + }) + expression = ee.List([1]).lastIndexOfSubList([2, 3]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).lastIndexOfSubList(target=[2, 3]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_length(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + }, + 'functionName': 'List.length', + }) + expression = ee.List([1]).length() + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + # TODO: test_map + + def test_reduce(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'reducer': { + 'functionInvocationValue': { + 'functionName': 'Reducer.count', + 'arguments': {}, + } + }, + }, + 'functionName': 'List.reduce', + }) + reducer = ee.Reducer.count() + expression = ee.List([1]).reduce(reducer) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).reduce(reducer=reducer) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_remove(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'element': {'constantValue': 2}, + }, + 'functionName': 'List.remove', + }) + expression = ee.List([1]).remove(2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).remove(element=2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_remove_all(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'other': {'constantValue': [1, 2]}, + }, + 'functionName': 'List.removeAll', + }) + expression = ee.List([1]).removeAll([1, 2]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).removeAll(other=[1, 2]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + # TODO(user): test_repeat constructor. + + def test_replace(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'oldval': {'constantValue': 2}, + 'newval': {'constantValue': 3}, + }, + 'functionName': 'List.replace', + }) + expression = ee.List([1]).replace(2, 3) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).replace(oldval=2, newval=3) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_replaceAll(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'oldval': {'constantValue': 2}, + 'newval': {'constantValue': 3}, + }, + 'functionName': 'List.replaceAll', + }) + expression = ee.List([1]).replaceAll(2, 3) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).replaceAll(oldval=2, newval=3) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_reverse(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + }, + 'functionName': 'List.reverse', + }) + expression = ee.List([1]).reverse() + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_rotate(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'distance': {'constantValue': 2}, + }, + 'functionName': 'List.rotate', + }) + expression = ee.List([1]).rotate(2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).rotate(distance=2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + # TODO: test_sequence + + def test_set(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'index': {'constantValue': 2}, + 'element': {'constantValue': 3}, + }, + 'functionName': 'List.set', + }) + expression = ee.List([1]).set(2, 3) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).set(index=2, element=3) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_shuffle(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'seed': {'constantValue': 2}, + }, + 'functionName': 'List.shuffle', + }) + expression = ee.List([1]).shuffle(2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).shuffle(seed=2) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_size(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + }, + 'functionName': 'List.size', + }) + expression = ee.List([1]).size() + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_slice(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'start': {'constantValue': 2}, + 'end': {'constantValue': 3}, + 'step': {'constantValue': 4}, + }, + 'functionName': 'List.slice', + }) + expression = ee.List([1]).slice(2, 3, 4) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).slice(start=2, end=3, step=4) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_sort(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'keys': {'constantValue': [2]}, + }, + 'functionName': 'List.sort', + }) + expression = ee.List([1]).sort([2]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).sort(keys=[2]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_splice(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'start': {'constantValue': 2}, + 'count': {'constantValue': 3}, + 'other': {'constantValue': [4]}, + }, + 'functionName': 'List.splice', + }) + expression = ee.List([1]).splice(2, 3, [4]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).splice(start=2, count=3, other=[4]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_swap(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'pos1': {'constantValue': 2}, + 'pos2': {'constantValue': 3}, + }, + 'functionName': 'List.swap', + }) + expression = ee.List([1]).swap(2, 3) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).swap(pos1=2, pos2=3) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_unzip(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + }, + 'functionName': 'List.unzip', + }) + expression = ee.List([1]).unzip() + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_zip(self): + expect = make_expression_graph({ + 'arguments': { + 'list': {'constantValue': [1]}, + 'other': {'constantValue': [2]}, + }, + 'functionName': 'List.zip', + }) + expression = ee.List([1]).zip([2]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.List([1]).zip(other=[2]) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + if __name__ == '__main__': unittest.main() diff --git a/python/ee/tests/kernel_test.py b/python/ee/tests/kernel_test.py index ec0c3257b..5fa464ba3 100644 --- a/python/ee/tests/kernel_test.py +++ b/python/ee/tests/kernel_test.py @@ -51,6 +51,7 @@ def test_cast(self): result = json.loads(kernel.serialize()) self.assertEqual(_KERNEL_JSON, result) + @unittest.skip('Does not work on github with python <= 3.9') def test_no_args(self): message = ( r'Kernel\.__init__\(\) missing 1 required positional argument:' diff --git a/python/ee/tests/pixeltype_test.py b/python/ee/tests/pixeltype_test.py index a4941688e..c189c05ae 100644 --- a/python/ee/tests/pixeltype_test.py +++ b/python/ee/tests/pixeltype_test.py @@ -6,6 +6,7 @@ import enum import json +from typing import Any, Dict import ee from ee import apitestcase @@ -26,6 +27,24 @@ class Type(str, enum.Enum): PIXELTYPE = 'PixelType' +def make_expression_graph( + function_invocation_value: Dict[str, Any] +) -> Dict[str, Any]: + return { + 'result': '0', + 'values': {'0': {'functionInvocationValue': function_invocation_value}}, + } + + +def pixeltype_function_expr(value: Type) -> Dict[str, Any]: + return { + 'functionInvocationValue': { + 'functionName': 'PixelType', + 'arguments': {'precision': {'constantValue': value}}, + } + } + + class PixelTypeTest(apitestcase.ApiTestCase): def test_int(self): @@ -196,6 +215,51 @@ def test_float_no_dimensions(self): } self.assertEqual(expect, result) + def test_dimensions(self): + expect = make_expression_graph({ + 'arguments': { + 'pixelType': pixeltype_function_expr(Type.FLOAT), + }, + 'functionName': 'PixelType.dimensions', + }) + expression = ee.PixelType(Type.FLOAT).dimensions() + result = json.loads(expression.serialize()) + + self.assertEqual(expect, result) + + def test_maxValue(self): + expect = make_expression_graph({ + 'arguments': { + 'pixelType': pixeltype_function_expr(Type.FLOAT), + }, + 'functionName': 'PixelType.maxValue', + }) + expression = ee.PixelType(Type.FLOAT).maxValue() + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_minValue(self): + expect = make_expression_graph({ + 'arguments': { + 'pixelType': pixeltype_function_expr(Type.FLOAT), + }, + 'functionName': 'PixelType.minValue', + }) + expression = ee.PixelType(Type.FLOAT).minValue() + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_precision(self): + expect = make_expression_graph({ + 'arguments': { + 'pixelType': pixeltype_function_expr(Type.FLOAT), + }, + 'functionName': 'PixelType.precision', + }) + expression = ee.PixelType(Type.FLOAT).precision() + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + if __name__ == '__main__': unittest.main() diff --git a/python/pyproject.toml b/python/pyproject.toml index dc5e12593..91af9e5fc 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "earthengine-api" -version = "0.1.395" +version = "0.1.397" description = "Earth Engine Python API" requires-python = ">=3.7" keywords = [