From 219936fc054114ff95f6f6594efe173713dca641 Mon Sep 17 00:00:00 2001 From: Max Heinritz Date: Fri, 19 Jun 2015 18:26:40 -0700 Subject: [PATCH] Updating to version 0.1.57 --- README.md | 31 +- javascript/build/ee_api_js.js | 300 +++++------ javascript/build/ee_api_js_debug.js | 480 ++++++++++-------- javascript/src/computedobject.js | 24 + javascript/src/data.js | 175 ++++++- javascript/src/deps.js | 6 +- javascript/src/ee_api_js.externs.js | 65 +++ .../src/examples/faq/gettingStarted01.js | 36 -- .../src/examples/faq/gettingStarted02.js | 12 - .../src/examples/faq/gettingStarted03.js | 23 - .../src/examples/faq/gettingStarted04.js | 38 -- .../src/examples/faq/gettingStarted05.js | 37 -- .../src/examples/faq/gettingStarted06.js | 24 - .../src/examples/faq/gettingStarted07.js | 26 - .../src/examples/faq/gettingStarted08.js | 21 - .../src/examples/faq/gettingStarted09.js | 21 - .../src/examples/faq/gettingStarted10.js | 23 - .../src/examples/faq/gettingStarted11.js | 43 -- javascript/src/examples/faq/overlay.js | 57 --- javascript/src/examples/faq/pca.js | 92 ---- javascript/src/featurecollection.js | 3 + javascript/src/geometry.js | 4 +- javascript/src/image.js | 22 + javascript/src/maplayeroverlay.js | 34 +- javascript/src/maptilemanager.js | 32 -- INSTALL.md => python/README.md | 56 +- python/ee/__init__.py | 2 +- python/ee/apitestcase.py | 18 + python/ee/computedobject.py | 21 + python/ee/featurecollection.py | 2 + python/ee/geometry.py | 4 +- python/ee/image.py | 25 + python/ee/tests/image_test.py | 10 + .../examples/AppEngine/hello-world/README.md | 66 +++ .../{hello_world => hello-world}/app.yaml | 0 .../examples/AppEngine/hello-world/build.sh | 51 ++ .../{hello_world => hello-world}/config.py | 12 +- .../ee_appengine.py | 1 + .../{hello_world => hello-world}/index.html | 0 .../examples/AppEngine/hello_world/README.txt | 73 --- .../AppEngine/trendy-lights/README.md | 2 +- .../examples/AppEngine/trendy-lights/build.sh | 51 ++ .../AppEngine/trendy-lights/index.html | 8 + .../AppEngine/trendy-lights/static/script.js | 16 +- python/setup.py | 1 + trendy-lights.png | Bin 0 -> 1193656 bytes 46 files changed, 1054 insertions(+), 994 deletions(-) delete mode 100644 javascript/src/examples/faq/gettingStarted01.js delete mode 100644 javascript/src/examples/faq/gettingStarted02.js delete mode 100644 javascript/src/examples/faq/gettingStarted03.js delete mode 100644 javascript/src/examples/faq/gettingStarted04.js delete mode 100644 javascript/src/examples/faq/gettingStarted05.js delete mode 100644 javascript/src/examples/faq/gettingStarted06.js delete mode 100644 javascript/src/examples/faq/gettingStarted07.js delete mode 100644 javascript/src/examples/faq/gettingStarted08.js delete mode 100644 javascript/src/examples/faq/gettingStarted09.js delete mode 100644 javascript/src/examples/faq/gettingStarted10.js delete mode 100644 javascript/src/examples/faq/gettingStarted11.js delete mode 100644 javascript/src/examples/faq/overlay.js delete mode 100644 javascript/src/examples/faq/pca.js rename INSTALL.md => python/README.md (78%) create mode 100644 python/examples/AppEngine/hello-world/README.md rename python/examples/AppEngine/{hello_world => hello-world}/app.yaml (100%) create mode 100644 python/examples/AppEngine/hello-world/build.sh rename python/examples/AppEngine/{hello_world => hello-world}/config.py (64%) rename python/examples/AppEngine/{hello_world => hello-world}/ee_appengine.py (99%) rename python/examples/AppEngine/{hello_world => hello-world}/index.html (100%) delete mode 100644 python/examples/AppEngine/hello_world/README.txt create mode 100644 python/examples/AppEngine/trendy-lights/build.sh create mode 100644 trendy-lights.png diff --git a/README.md b/README.md index ae2b5468d..018f6997b 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,36 @@ -Python and JavaScript bindings for calling the Earth Engine API. +Google Earth Engine API +======================= + +Python and JavaScript client libraries for calling the Earth Engine API. _**Important Note: Access to Google Earth Engine is currently only available to trusted testers. The API is in active development, and users should expect the API to change. When (not if) API changes occur, applications that use the API will likely need to be updated.**_ -[Installation Instructions](INSTALL.md) +- [Earth Engine Homepage](https://earthengine.google.org/) +- [Playground Web IDE](https://ee-api.appspot.com/) +- [Python Installation](python/README.md) + +Here's an example screenshot and the corresponding Playground JavaScript code: + +![Trendy Lights Image](https://raw.github.com/google/earthengine-api/master/trendy-lights.png) + + // Compute the trend of nighttime lights from DMSP. + + // Add a band containing image date as years since 1991. + function createTimeBand(img) { + var year = ee.Date(img.get('system:time_start')).get('year').subtract(1991); + return ee.Image(year).byte().addBands(img); + } + + // Fit a linear trend to the nighttime lights collection. + var collection = ee.ImageCollection('NOAA/DMSP-OLS/NIGHTTIME_LIGHTS') + .select('stable_lights') + .map(createTimeBand); + // Display trend in red/blue, brightness in green. + Map.addLayer( + collection.reduce(ee.Reducer.linearFit()), + {min: 0, max: [0.18, 20, -0.18], bands: ['scale', 'offset', 'scale']}, + 'stable lights trend'); diff --git a/javascript/build/ee_api_js.js b/javascript/build/ee_api_js.js index 1f6baca9c..767fbe2f6 100644 --- a/javascript/build/ee_api_js.js +++ b/javascript/build/ee_api_js.js @@ -1,90 +1,89 @@ -(function() {var h,aa=aa||{},k=this,l=function(a){return void 0!==a},ba=function(){},ca=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!= +(function() {var h,aa=aa||{},l=this,m=function(a){return void 0!==a},ba=function(){},ca=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!= typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"==b&&"undefined"==typeof a.call)return"object";return b},n=function(a){return"array"==ca(a)},da=function(a){var b=ca(a);return"array"==b||"object"==b&&"number"==typeof a.length},p=function(a){return"string"==typeof a},q=function(a){return"number"==typeof a},r=function(a){return"function"==ca(a)},t=function(a){var b=typeof a;return"object"==b&&null!=a||"function"==b},ea="closure_uid_"+ (1E9*Math.random()>>>0),fa=0,ga=function(a,b,c){return a.call.apply(a.bind,arguments)},ha=function(a,b,c){if(!a)throw Error();if(2")&&(a=a.replace(Ea,">"));-1!=a.indexOf('"')&&(a=a.replace(Fa,"""));-1!=a.indexOf("'")&& (a=a.replace(Ga,"'"));-1!=a.indexOf("\x00")&&(a=a.replace(Ha,"�"));return a},Ca=/&/g,Da=//g,Fa=/"/g,Ga=/'/g,Ha=/\x00/g,Ba=/[\x00&<>"']/,Ja=function(a,b){return ab?1:0};var Ka=function(a,b){b.unshift(a);x.call(this,za.apply(null,b));b.shift()};w(Ka,x);Ka.prototype.name="AssertionError"; -var La=function(a,b,c,d){var e="Assertion failed";if(c)var e=e+(": "+c),f=d;else a&&(e+=": "+a,f=b);throw new Ka(""+e,f||[]);},y=function(a,b,c){a||La("",null,b,Array.prototype.slice.call(arguments,2));return a},Ma=function(a,b){throw new Ka("Failure"+(a?": "+a:""),Array.prototype.slice.call(arguments,1));},Na=function(a,b,c){r(a)||La("Expected function but got %s: %s.",[ca(a),a],b,Array.prototype.slice.call(arguments,2))};var z=Array.prototype,Oa=z.indexOf?function(a,b,c){y(null!=a.length);return z.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(p(a))return p(b)&&1==b.length?a.indexOf(b,c):-1;for(;cc&&(c=Math.max(0,a.length+c));if(p(a))return p(b)&&1==b.length?a.lastIndexOf(b,c):-1;for(;0<= -c;c--)if(c in a&&a[c]===b)return c;return-1},Qa=z.forEach?function(a,b,c){y(null!=a.length);z.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=p(a)?a.split(""):a,f=0;fb?null:p(a)?a.charAt(b):a[b]},Xa=function(a){if(!n(a))for(var b=a.length-1;0<=b;b--)delete a[b];a.length=0},Za=function(a,b){var c=Oa(a,b),d;(d=0<=c)&&Ya(a,c);return d},Ya=function(a,b){y(null!=a.length);z.splice.call(a,b,1)},$a=function(a){return z.concat.apply(z,arguments)},bb=function(a){var b=a.length;if(0=arguments.length?z.slice.call(a,b):z.slice.call(a,b,c)},eb=function(a){for(var b={},c=0,d=0;dparseFloat(a))?String(b):a}(),xb={},C=function(a){var b;if(!(b=xb[a])){b=0;for(var c=Aa(String(wb)).split("."),d=Aa(String(a)).split("."),e=Math.max(c.length,d.length),f=0;0==b&&f");c=c.join("")}c=a.createElement(c);d&&(p(d)?c.className=d:n(d)?c.className=d.join(" "):Eb(c,d));2c?Math.max(0,a.length+c):c;if(p(a))return p(b)&&1==b.length?a.indexOf(b,c):-1;for(;cc&&(c=Math.max(0,a.length+c));if(p(a))return p(b)&&1==b.length?a.lastIndexOf(b,c):-1;for(;0<= +c;c--)if(c in a&&a[c]===b)return c;return-1},Qa=A.forEach?function(a,b,c){z(null!=a.length);A.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=p(a)?a.split(""):a,f=0;fb?null:p(a)?a.charAt(b):a[b]},Xa=function(a){if(!n(a))for(var b=a.length-1;0<=b;b--)delete a[b];a.length=0},Za=function(a,b){var c=Oa(a,b),d;(d=0<=c)&&Ya(a,c);return d},Ya=function(a,b){z(null!=a.length);A.splice.call(a,b,1)},$a=function(a){return A.concat.apply(A,arguments)},bb=function(a){var b=a.length;if(0=arguments.length?A.slice.call(a,b):A.slice.call(a,b,c)},eb=function(a){for(var b={},c=0,d=0;dparseFloat(a))?String(b):a}(),yb={},D=function(a){var b;if(!(b=yb[a])){b=0;for(var c=Aa(String(xb)).split("."),d=Aa(String(a)).split("."),e=Math.max(c.length,d.length),f=0;0==b&&f");c=c.join("")}c=a.createElement(c);d&&(p(d)?c.className=d:n(d)?c.className=d.join(" "):Eb(c,d));2=b.qb&&b.cancel())}this.nc?this.nc.call(this.$b,this):this.Ob=!0;this.ia||sc(this,new tc)}};rc.prototype.Yb=function(a,b){this.pb=!1;uc(this,a,b)}; -var uc=function(a,b,c){a.ia=!0;a.T=c;a.va=!b;vc(a)},xc=function(a){if(a.ia){if(!a.Ob)throw new wc;a.Ob=!1}},sc=function(a,b){xc(a);yc(b);uc(a,!1,b)},yc=function(a){y(!(a instanceof rc),"An execution sequence may not be initiated with a blocking Deferred.")},zc=function(a,b,c){y(!a.Vb,"Blocking Deferreds can not be re-used");a.cb.push([b,c,void 0]);a.ia&&vc(a)}; -rc.prototype.then=function(a,b,c){var d,e,f=new dc(function(a,b){d=a;e=b});zc(this,d,function(a){a instanceof tc?f.cancel():e(a)});return f.then(a,b,c)};Zb(rc); -var Ac=function(a){return Ta(a.cb,function(a){return r(a[1])})},vc=function(a){if(a.ib&&a.ia&&Ac(a)){var b=a.ib,c=Bc[b];c&&(k.clearTimeout(c.S),delete Bc[b]);a.ib=0}a.f&&(a.f.qb--,delete a.f);for(var b=a.T,d=c=!1;a.cb.length&&!a.pb;){var e=a.cb.shift(),f=e[0],g=e[1],e=e[2];if(f=a.va?g:f)try{var m=f.call(e||a.$b,b);l(m)&&(a.va=a.va&&(m==b||m instanceof Error),a.T=b=m);if($b(b)||"function"===typeof k.Promise&&b instanceof k.Promise)d=!0,a.pb=!0}catch(F){b=F,a.va=!0,Ac(a)||(c=!0)}}a.T=b;d&&(m=u(a.Yb, -a,!0),d=u(a.Yb,a,!1),b instanceof rc?(zc(b,m,d),b.Vb=!0):b.then(m,d));c&&(b=new Cc(b),Bc[b.S]=b,a.ib=b.S)},wc=function(){x.call(this)};w(wc,x);wc.prototype.message="Deferred has already fired";wc.prototype.name="AlreadyCalledError";var tc=function(){x.call(this)};w(tc,x);tc.prototype.message="Deferred was canceled";tc.prototype.name="CanceledError";var Cc=function(a){this.S=k.setTimeout(u(this.ud,this),0);this.Na=a}; -Cc.prototype.ud=function(){y(Bc[this.S],"Cannot throw an error that is not scheduled.");delete Bc[this.S];throw this.Na;};var Bc={};var Hc=function(a){var b={},c=b.document||document,d=document.createElement("SCRIPT"),e={tc:d,Ba:void 0},f=new rc(Dc,e),g=null,m=null!=b.timeout?b.timeout:5E3;0=a.keyCode)a.keyCode=-1}catch(b){}};var Oc="closure_listenable_"+(1E6*Math.random()|0),Pc=function(a){return!(!a||!a[Oc])},Qc=0;var Rc=function(a,b,c,d,e){this.listener=a;this.Za=null;this.src=b;this.type=c;this.Ja=!!d;this.Ra=e;this.key=++Qc;this.la=this.Ia=!1},Sc=function(a){a.la=!0;a.listener=null;a.Za=null;a.src=null;a.Ra=null};var Tc=function(a){this.src=a;this.A={};this.Ca=0};Tc.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.A[f];a||(a=this.A[f]=[],this.Ca++);var g=Uc(a,b,d,e);-1e.keyCode||void 0!=e.returnValue)){a:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break a}catch(g){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.currentTarget;f;f=f.parentNode)e.push(f);for(var f=a.type,m=e.length-1;!c.ka&&0<=m;m--){c.currentTarget=e[m];var F=jd(e[m],f,!0,c),d=d&&F}for(m=0;!c.ka&& -m>>0),$c=function(a){y(a,"Listener can not be null.");if(r(a))return a;y(a.handleEvent,"An object listener must have handleEvent method.");a[kd]||(a[kd]=function(b){return a.handleEvent(b)});return a[kd]};var D=function(){Ic.call(this);this.L=new Tc(this);this.Cc=this;this.Mb=null};w(D,Ic);D.prototype[Oc]=!0;h=D.prototype;h.addEventListener=function(a,b,c,d){Zc(this,a,b,c,d)};h.removeEventListener=function(a,b,c,d){gd(this,a,b,c,d)}; -h.dispatchEvent=function(a){ld(this);var b,c=this.Mb;if(c){b=[];for(var d=1;c;c=c.Mb)b.push(c),y(1E3>++d,"infinite loop")}c=this.Cc;d=a.type||a;if(p(a))a=new Jc(a,c);else if(a instanceof Jc)a.target=a.target||c;else{var e=a;a=new Jc(d,c);wa(a,e)}var e=!0,f;if(b)for(var g=b.length-1;!a.ka&&0<=g;g--)f=a.currentTarget=b[g],e=md(f,d,!0,a)&&e;a.ka||(f=a.currentTarget=c,e=md(f,d,!0,a)&&e,a.ka||(e=md(f,d,!1,a)&&e));if(b)for(g=0;!a.ka&&g=b.qb&&b.cancel())}this.nc?this.nc.call(this.$b,this):this.Ob=!0;this.ia||rc(this,new sc)}};qc.prototype.Yb=function(a,b){this.pb=!1;tc(this,a,b)}; +var tc=function(a,b,c){a.ia=!0;a.T=c;a.va=!b;uc(a)},wc=function(a){if(a.ia){if(!a.Ob)throw new vc;a.Ob=!1}},rc=function(a,b){wc(a);xc(b);tc(a,!1,b)},xc=function(a){z(!(a instanceof qc),"An execution sequence may not be initiated with a blocking Deferred.")},yc=function(a,b,c){z(!a.Vb,"Blocking Deferreds can not be re-used");a.cb.push([b,c,void 0]);a.ia&&uc(a)}; +qc.prototype.then=function(a,b,c){var d,e,f=new cc(function(a,b){d=a;e=b});yc(this,d,function(a){a instanceof sc?f.cancel():e(a)});return f.then(a,b,c)};Zb(qc); +var zc=function(a){return Ta(a.cb,function(a){return r(a[1])})},uc=function(a){if(a.ib&&a.ia&&zc(a)){var b=a.ib,c=Ac[b];c&&(l.clearTimeout(c.S),delete Ac[b]);a.ib=0}a.f&&(a.f.qb--,delete a.f);for(var b=a.T,d=c=!1;a.cb.length&&!a.pb;){var e=a.cb.shift(),f=e[0],g=e[1],e=e[2];if(f=a.va?g:f)try{var k=f.call(e||a.$b,b);m(k)&&(a.va=a.va&&(k==b||k instanceof Error),a.T=b=k);if($b(b)||"function"===typeof l.Promise&&b instanceof l.Promise)d=!0,a.pb=!0}catch(y){b=y,a.va=!0,zc(a)||(c=!0)}}a.T=b;d&&(k=u(a.Yb, +a,!0),d=u(a.Yb,a,!1),b instanceof qc?(yc(b,k,d),b.Vb=!0):b.then(k,d));c&&(b=new Bc(b),Ac[b.S]=b,a.ib=b.S)},vc=function(){x.call(this)};w(vc,x);vc.prototype.message="Deferred has already fired";vc.prototype.name="AlreadyCalledError";var sc=function(){x.call(this)};w(sc,x);sc.prototype.message="Deferred was canceled";sc.prototype.name="CanceledError";var Bc=function(a){this.S=l.setTimeout(u(this.wd,this),0);this.Na=a}; +Bc.prototype.wd=function(){z(Ac[this.S],"Cannot throw an error that is not scheduled.");delete Ac[this.S];throw this.Na;};var Ac={};var Gc=function(a){var b={},c=b.document||document,d=document.createElement("SCRIPT"),e={tc:d,Ba:void 0},f=new qc(Cc,e),g=null,k=null!=b.timeout?b.timeout:5E3;0=a.keyCode)a.keyCode=-1}catch(b){}};var Nc="closure_listenable_"+(1E6*Math.random()|0),Oc=function(a){return!(!a||!a[Nc])},Pc=0;var Qc=function(a,b,c,d,e){this.listener=a;this.Za=null;this.src=b;this.type=c;this.Ja=!!d;this.Ra=e;this.key=++Pc;this.la=this.Ia=!1},Rc=function(a){a.la=!0;a.listener=null;a.Za=null;a.src=null;a.Ra=null};var Sc=function(a){this.src=a;this.A={};this.Ca=0};Sc.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.A[f];a||(a=this.A[f]=[],this.Ca++);var g=Tc(a,b,d,e);-1e.keyCode||void 0!=e.returnValue)){a:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break a}catch(g){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.currentTarget;f;f=f.parentNode)e.push(f);for(var f=a.type,k=e.length-1;!c.ka&&0<=k;k--){c.currentTarget=e[k];var y=id(e[k],f,!0,c),d=d&&y}for(k=0;!c.ka&&k>>0),Zc=function(a){z(a,"Listener can not be null.");if(r(a))return a;z(a.handleEvent,"An object listener must have handleEvent method.");a[jd]||(a[jd]=function(b){return a.handleEvent(b)});return a[jd]};var kd=function(){Hc.call(this);this.L=new Sc(this);this.Ac=this;this.Mb=null};w(kd,Hc);kd.prototype[Nc]=!0;h=kd.prototype;h.addEventListener=function(a,b,c,d){Yc(this,a,b,c,d)};h.removeEventListener=function(a,b,c,d){fd(this,a,b,c,d)}; +h.dispatchEvent=function(a){ld(this);var b,c=this.Mb;if(c){b=[];for(var d=1;c;c=c.Mb)b.push(c),z(1E3>++d,"infinite loop")}c=this.Ac;d=a.type||a;if(p(a))a=new Ic(a,c);else if(a instanceof Ic)a.target=a.target||c;else{var e=a;a=new Ic(d,c);wa(a,e)}var e=!0,f;if(b)for(var g=b.length-1;!a.ka&&0<=g;g--)f=a.currentTarget=b[g],e=md(f,d,!0,a)&&e;a.ka||(f=a.currentTarget=c,e=md(f,d,!0,a)&&e,a.ka||(e=md(f,d,!1,a)&&e));if(b)for(g=0;!a.ka&&g=a.length)throw nd;if(b in a)return a[b++];b++}};return c}throw Error("Not implemented");},qd=function(a,b){if(da(a))try{Qa(a,b,void 0)}catch(c){if(c!==nd)throw c;}else{a=pd(a);try{for(;;)b.call(void 0,a.next(),void 0,a)}catch(d){if(d!==nd)throw d;}}};var rd=function(a,b){this.j={};this.c=[];this.Da=this.g=0;var c=arguments.length;if(12*this.g&&sd(this),!0):!1};var sd=function(a){if(a.g!=a.c.length){for(var b=0,c=0;b=d.c.length)throw nd;var e=d.c[b++];return a?e:d.j[e]};return e};var td=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)};var ud=function(a){if("function"==typeof a.w)return a.w();if(p(a))return a.split("");if(da(a)){for(var b=[],c=a.length,d=0;d=Fd(this).value)for(r(b)&&(b=b()),a=new yd(a,String(b),this.kd),c&&(a.bc=c),c="log:"+a.jd,k.console&&(k.console.timeStamp?k.console.timeStamp(c):k.console.markTimeline&&k.console.markTimeline(c)),k.msWriteProfilerMark&&k.msWriteProfilerMark(c),c=this;c;){b=c;var d=a;if(b.fc)for(var e=0,f=void 0;f=b.fc[e];e++)f(d);c=c.getParent()}}; -var Gd={},Hd=null,Id=function(a){Hd||(Hd=new Ad(""),Gd[""]=Hd,Hd.vc(Dd));var b;if(!(b=Gd[a])){b=new Ad(a);var c=a.lastIndexOf("."),d=a.substr(c+1),c=Id(a.substr(0,c));c.dc()[d]=b;b.f=c;Gd[a]=b}return b};var Jd=function(a,b){a&&a.log(Ed,b,void 0)};var Kd=function(a,b,c){if(r(a))c&&(a=u(a,c));else if(a&&"function"==typeof a.handleEvent)a=u(a.handleEvent,a);else throw Error("Invalid listener argument");return 2147483647=Fd(this).value)for(r(b)&&(b=b()),a=new yd(a,String(b),this.kd),c&&(a.bc=c),c="log:"+a.jd,l.console&&(l.console.timeStamp?l.console.timeStamp(c):l.console.markTimeline&&l.console.markTimeline(c)),l.msWriteProfilerMark&&l.msWriteProfilerMark(c),c=this;c;){b=c;var d=a;if(b.fc)for(var e=0,f=void 0;f=b.fc[e];e++)f(d);c=c.getParent()}}; +var Gd={},Hd=null,Id=function(a){Hd||(Hd=new Ad(""),Gd[""]=Hd,Hd.vc(Dd));var b;if(!(b=Gd[a])){b=new Ad(a);var c=a.lastIndexOf("."),d=a.substr(c+1),c=Id(a.substr(0,c));c.dc()[d]=b;b.f=c;Gd[a]=b}return b};var Jd=function(a,b){a&&a.log(Ed,b,void 0)};var Kd=function(a,b,c){if(r(a))c&&(a=u(a,c));else if(a&&"function"==typeof a.handleEvent)a=u(a.handleEvent,a);else throw Error("Invalid listener argument");return 2147483647b&&(b+=e);return[me,"map",a.mapid,d,b,c].join("/")+"?token="+a.token});var ye=function(a,b){a=ta(a);return G("/value",H(a),b)};v("ee.data.getValue",ye);var ze=function(a,b){a=ta(a);n(a.size)&&(a.size=a.size.join("x"));var c=H(a).add("getid","1");return G("/thumb",c,b)};v("ee.data.getThumbId",ze);var Ae=function(a){return me+"/api/thumb?thumbid="+a.thumbid+"&token="+a.token};v("ee.data.makeThumbUrl",Ae); -var Be=function(a,b){a=ta(a);return G("/download",H(a),b)};v("ee.data.getDownloadId",Be);var Ce=function(a){return me+"/api/download?docid="+a.docid+"&token="+a.token};v("ee.data.makeDownloadUrl",Ce);var De=function(a,b){a=ta(a);return G("/table",H(a),b)};v("ee.data.getTableDownloadId",De);var Ee=function(a){return me+"/api/table?docid="+a.docid+"&token="+a.token};v("ee.data.makeTableDownloadUrl",Ee); -v("ee.data.createAsset",function(a,b,c,d){a={value:a};void 0!==b&&(a.id=b);a.force=c||!1;return G("/create",H(a),d)});v("ee.data.createFolder",function(a,b,c){return G("/createfolder",H({id:a,force:b||!1}),c)});v("ee.data.newTaskId",function(a,b){var c={};q(a)&&(c.count=a);return G("/newtaskid",H(c),b)});v("ee.data.getTaskStatus",function(a,b){if(p(a))a=[a];else if(!n(a))throw Error("Invalid taskId: expected a string or an array of strings.");return G("/taskstatus?q="+a.join(),null,b,"GET")}); -v("ee.data.getTaskList",function(a){return G("/tasklist",null,a,"GET")});v("ee.data.cancelTask",function(a,b){return Fe(a,"CANCEL",b)});var Fe=function(a,b,c){if(p(a))a=[a];else if(!n(a))throw Error("Invalid taskId: expected a string or an array of strings.");var d;a:{for(d in se)if(se[d]==b){d=!0;break a}d=!1}if(!d)throw Error("Invalid action: "+b);return G("/updatetask",H({id:a,action:b}),c,"POST")};v("ee.data.updateTask",Fe); -v("ee.data.prepareValue",function(a,b,c){b=ta(b);b.tid=a;return G("/prepare",H(b),c)});v("ee.data.startProcessing",function(a,b,c){b=ta(b);b.id=a;return G("/processingrequest",H(b),c)});v("ee.data.startIngestion",function(a,b,c){a={id:a,request:ib(b)};return G("/ingestionrequest",H(a),c)});v("ee.data.createAssetHome",function(a,b){var c=H({id:a});G("/createbucket",c,b)});v("ee.data.getAssetRoots",function(a){return G("/buckets",null,a,"GET")}); -v("ee.data.getAssetAcl",function(a,b){return G("/getacl",H({id:a}),b,"GET")});v("ee.data.setAssetAcl",function(a,b,c){a={id:a,value:ib(b)};G("/setacl",H(a),c)}); -var G=function(a,b,c,d){te();d=d||"POST";var e={"Content-Type":"application/x-www-form-urlencoded"};null!=oe&&(e.Authorization=oe);null!=ne&&(e["X-XSRF-Token"]=ne);var f=function(a,b,c,d){var e,f,g;b=b?b.replace(/;.*/,""):"application/json";if("application/json"==b||"text/json"==b)try{e=gb(c),f=e.data}catch(m){g="Invalid JSON: "+c}else g="Response was unexpectedly not JSON, but "+b;if(t(e))"error"in e&&"message"in e.error?g=e.error.message:"data"in e||(g="Malformed response: "+c);else if(200>a||300<= -a)g="Server returned HTTP code: "+a;if(d)return d(f,g),null;if(!g)return f;throw Error(g);};b=b?b.toString():"";"GET"!=d||/^[\s\xa0]*$/.test(b)||(a+=-1!=a.indexOf("?")?"&":"?",a+=b,b=null);a=le+a;if(c)return $d(a,function(a){a=a.target;var b=a.getStatus(),d=a.getResponseHeader("Content-Type"),e;try{e=a.a?a.a.responseText:""}catch(g){Jd(a.G,"Can not get responseText: "+g.message),e=""}return f(b,d,e,c)},d,b,e),null;var g=Sd();g.open(d,a,!1);ka(e,function(a,b){g.setRequestHeader(b,a)});g.send(b);var m; -try{m=g.getResponseHeader("Content-Type")}catch(F){m="application/json"}return f(g.status,m,g.responseText,null)},ve=function(a,b,c){k.gapi.auth.authorize({client_id:pe,immediate:!0,scope:qe.join(" ")},function(d){"immediate_failed"==d.error&&c?c():we(a,b,d)})},we=function(a,b,c){c.access_token?(oe=c.token_type+" "+c.access_token,setTimeout(ve,1E3*c.expires_in/2),a&&a()):b&&b(c.error||"Unknown error.")},H=function(a){var b=new ie,c;for(c in a)b.set(c,a[c]);return b};var Ge=function(){};var He=function(){this.O=-1};var Ie=function(){this.O=-1;this.O=64;this.o=Array(4);this.Ec=Array(this.O);this.gb=this.qa=0;this.reset()};w(Ie,He);Ie.prototype.reset=function(){this.o[0]=1732584193;this.o[1]=4023233417;this.o[2]=2562383102;this.o[3]=271733878;this.gb=this.qa=0}; -var Je=function(a,b,c){c||(c=0);var d=Array(16);if(p(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.o[0];c=a.o[1];var e=a.o[2],f=a.o[3],g=0,g=b+(f^c&(e^f))+d[0]+3614090360&4294967295;b=c+(g<<7&4294967295|g>>>25);g=f+(e^b&(c^e))+d[1]+3905402710&4294967295;f=b+(g<<12&4294967295|g>>>20);g=e+(c^f&(b^c))+d[2]+606105819&4294967295;e=f+(g<<17&4294967295|g>>>15);g=c+(b^e& +h.clone=function(){var a=new ie;a.I=this.I;this.i&&(a.i=this.i.clone(),a.g=this.g);return a};var ke=function(a,b){var c=String(b);a.$c&&(c=c.toLowerCase());return c};var le=null,me=null,ne=null,oe=fb,pe=null,qe=null,re=[],se=!1,Zd=0,te={Ed:"CANCEL",Hd:"UPDATE"},ue=function(a,b,c){null!=a?le=a:se||(le="https://earthengine.googleapis.com/api");null!=b?me=b:se||(me="https://earthengine.googleapis.com");m(c)&&(ne=c);se=!0};v("ee.data.authenticate",function(a,b,c,d,e){var f=["https://www.googleapis.com/auth/earthengine.readonly"];d&&(cb(f,d),eb(f));qe=a;re=f;null===a?pe=null:ve(function(){we(b,c,e||ia(xe,b,c))})}); +var xe=function(a,b){l.gapi.auth.authorize({client_id:qe,immediate:!1,scope:re.join(" ")},ia(ye,a,b))};v("ee.data.authenticateViaPopup",xe);v("ee.data.setAuthToken",function(a,b,c,d,e,f,g){var k=["https://www.googleapis.com/auth/earthengine.readonly"];e&&(cb(k,e),eb(k));qe=a;re=k;var y={token_type:b,access_token:c,state:k.join(" "),expires_in:d};ye(void 0,void 0,y);!1===g?f&&f():ve(function(){l.gapi.auth.setToken(y);f&&f()})});v("ee.data.setDeadline",function(a){Zd=a}); +v("ee.data.setParamAugmenter",function(a){oe=a||fb});v("ee.data.getApiBaseUrl",function(){return le});v("ee.data.getTileBaseUrl",function(){return me});v("ee.data.getXsrfToken",function(){return ne});v("ee.data.getAuthToken",function(){return pe});v("ee.data.getAuthClientId",function(){return qe});v("ee.data.getAuthScopes",function(){return re});v("ee.data.getInfo",function(a,b){return F("/info",(new ie).add("id",a),b)});v("ee.data.getList",function(a,b){var c=G(a);return F("/list",c,b)}); +var ze=function(a,b){a=ta(a);return F("/mapid",G(a),b)};v("ee.data.getMapId",ze);v("ee.data.getTileUrl",function(a,b,c,d){var e=Math.pow(2,d);b%=e;0>b&&(b+=e);return[me,"map",a.mapid,d,b,c].join("/")+"?token="+a.token});var Ae=function(a,b){a=ta(a);return F("/value",G(a),b)};v("ee.data.getValue",Ae);var Be=function(a,b){a=ta(a);n(a.size)&&(a.size=a.size.join("x"));var c=G(a).add("getid","1");return F("/thumb",c,b)};v("ee.data.getThumbId",Be); +var Ce=function(a){return me+"/api/thumb?thumbid="+a.thumbid+"&token="+a.token};v("ee.data.makeThumbUrl",Ce);var De=function(a,b){a=ta(a);return F("/download",G(a),b)};v("ee.data.getDownloadId",De);var Ee=function(a){return me+"/api/download?docid="+a.docid+"&token="+a.token};v("ee.data.makeDownloadUrl",Ee);var Fe=function(a,b){a=ta(a);return F("/table",G(a),b)};v("ee.data.getTableDownloadId",Fe);var Ge=function(a){return me+"/api/table?docid="+a.docid+"&token="+a.token}; +v("ee.data.makeTableDownloadUrl",Ge);v("ee.data.createAsset",function(a,b,c,d){a={value:a};void 0!==b&&(a.id=b);a.force=c||!1;return F("/create",G(a),d)});v("ee.data.createFolder",function(a,b,c){return F("/createfolder",G({id:a,force:b||!1}),c)});v("ee.data.newTaskId",function(a,b){var c={};q(a)&&(c.count=a);return F("/newtaskid",G(c),b)}); +v("ee.data.getTaskStatus",function(a,b){if(p(a))a=[a];else if(!n(a))throw Error("Invalid taskId: expected a string or an array of strings.");return F("/taskstatus?q="+a.join(),null,b,"GET")});v("ee.data.getTaskList",function(a){return F("/tasklist",null,a,"GET")});v("ee.data.cancelTask",function(a,b){return He(a,"CANCEL",b)}); +var He=function(a,b,c){if(p(a))a=[a];else if(!n(a))throw Error("Invalid taskId: expected a string or an array of strings.");var d;a:{for(d in te)if(te[d]==b){d=!0;break a}d=!1}if(!d)throw Error("Invalid action: "+b);return F("/updatetask",G({id:a,action:b}),c,"POST")};v("ee.data.updateTask",He);v("ee.data.prepareValue",function(a,b,c){b=ta(b);b.tid=a;return F("/prepare",G(b),c)});v("ee.data.startProcessing",function(a,b,c){b=ta(b);b.id=a;return F("/processingrequest",G(b),c)}); +v("ee.data.startIngestion",function(a,b,c){a={id:a,request:jb(b)};return F("/ingestionrequest",G(a),c)});v("ee.data.createAssetHome",function(a,b){var c=G({id:a});F("/createbucket",c,b)});v("ee.data.getAssetRoots",function(a){return F("/buckets",null,a,"GET")});v("ee.data.getAssetAcl",function(a,b){return F("/getacl",G({id:a}),b,"GET")});v("ee.data.setAssetAcl",function(a,b,c){a={id:a,value:jb(b)};F("/setacl",G(a),c)}); +var F=function(a,b,c,d){ue();d=d||"POST";var e={"Content-Type":"application/x-www-form-urlencoded"};null!=pe&&(e.Authorization=pe);b=oe(b||new ie,a);null!=ne&&(e["X-XSRF-Token"]=ne);var f=function(a,b,c,d){var e,f,g;b=b?b.replace(/;.*/,""):"application/json";if("application/json"==b||"text/json"==b)try{e=hb(c),f=e.data}catch(k){g="Invalid JSON: "+c}else g="Response was unexpectedly not JSON, but "+b;if(t(e))"error"in e&&"message"in e.error?g=e.error.message:"data"in e||(g="Malformed response: "+c); +else if(200>a||300<=a)g="Server returned HTTP code: "+a;if(d)return d(f,g),null;if(!g)return f;throw Error(g);};b=b?b.toString():"";"GET"!=d||/^[\s\xa0]*$/.test(b)||(a+=-1!=a.indexOf("?")?"&":"?",a+=b,b=null);a=le+a;if(c)return $d(a,function(a){a=a.target;var b=a.getStatus(),d=a.getResponseHeader("Content-Type"),e;try{e=a.a?a.a.responseText:""}catch(g){Jd(a.G,"Can not get responseText: "+g.message),e=""}return f(b,d,e,c)},d,b,e),null;var g=Sd();g.open(d,a,!1);ka(e,function(a,b){g.setRequestHeader(b, +a)});g.send(b);var k;try{k=g.getResponseHeader("Content-Type")}catch(y){k="application/json"}return f(g.status,k,g.responseText,null)},ve=function(a){var b=function(){l.gapi.config.update("client/cors",!0);a()};if(t(l.gapi)&&t(l.gapi.auth)&&r(l.gapi.auth.authorize))b();else{for(var c=ja().toString(36);c in l;)c+="_";l[c]=function(){delete l[c];b()};Gc("https://apis.google.com/js/client.js?onload="+c)}},we=function(a,b,c){l.gapi.auth.authorize({client_id:qe,immediate:!0,scope:re.join(" ")},function(d){"immediate_failed"== +d.error&&c?c():ye(a,b,d)})},ye=function(a,b,c){c.access_token?(pe=c.token_type+" "+c.access_token,isFinite(c.expires_in)&&setTimeout(we,1E3*c.expires_in/2),a&&a()):b&&b(c.error||"Unknown error.")},G=function(a){var b=new ie,c;for(c in a)b.set(c,a[c]);return b};var Ie=function(){};var Je=function(){this.O=-1};var Ke=function(){this.O=-1;this.O=64;this.o=Array(4);this.Ec=Array(this.O);this.gb=this.qa=0;this.reset()};w(Ke,Je);Ke.prototype.reset=function(){this.o[0]=1732584193;this.o[1]=4023233417;this.o[2]=2562383102;this.o[3]=271733878;this.gb=this.qa=0}; +var Le=function(a,b,c){c||(c=0);var d=Array(16);if(p(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.o[0];c=a.o[1];var e=a.o[2],f=a.o[3],g=0,g=b+(f^c&(e^f))+d[0]+3614090360&4294967295;b=c+(g<<7&4294967295|g>>>25);g=f+(e^b&(c^e))+d[1]+3905402710&4294967295;f=b+(g<<12&4294967295|g>>>20);g=e+(c^f&(b^c))+d[2]+606105819&4294967295;e=f+(g<<17&4294967295|g>>>15);g=c+(b^e& (f^b))+d[3]+3250441966&4294967295;c=e+(g<<22&4294967295|g>>>10);g=b+(f^c&(e^f))+d[4]+4118548399&4294967295;b=c+(g<<7&4294967295|g>>>25);g=f+(e^b&(c^e))+d[5]+1200080426&4294967295;f=b+(g<<12&4294967295|g>>>20);g=e+(c^f&(b^c))+d[6]+2821735955&4294967295;e=f+(g<<17&4294967295|g>>>15);g=c+(b^e&(f^b))+d[7]+4249261313&4294967295;c=e+(g<<22&4294967295|g>>>10);g=b+(f^c&(e^f))+d[8]+1770035416&4294967295;b=c+(g<<7&4294967295|g>>>25);g=f+(e^b&(c^e))+d[9]+2336552879&4294967295;f=b+(g<<12&4294967295|g>>>20);g= e+(c^f&(b^c))+d[10]+4294925233&4294967295;e=f+(g<<17&4294967295|g>>>15);g=c+(b^e&(f^b))+d[11]+2304563134&4294967295;c=e+(g<<22&4294967295|g>>>10);g=b+(f^c&(e^f))+d[12]+1804603682&4294967295;b=c+(g<<7&4294967295|g>>>25);g=f+(e^b&(c^e))+d[13]+4254626195&4294967295;f=b+(g<<12&4294967295|g>>>20);g=e+(c^f&(b^c))+d[14]+2792965006&4294967295;e=f+(g<<17&4294967295|g>>>15);g=c+(b^e&(f^b))+d[15]+1236535329&4294967295;c=e+(g<<22&4294967295|g>>>10);g=b+(e^f&(c^e))+d[1]+4129170786&4294967295;b=c+(g<<5&4294967295| g>>>27);g=f+(c^e&(b^c))+d[6]+3225465664&4294967295;f=b+(g<<9&4294967295|g>>>23);g=e+(b^c&(f^b))+d[11]+643717713&4294967295;e=f+(g<<14&4294967295|g>>>18);g=c+(f^b&(e^f))+d[0]+3921069994&4294967295;c=e+(g<<20&4294967295|g>>>12);g=b+(e^f&(c^e))+d[5]+3593408605&4294967295;b=c+(g<<5&4294967295|g>>>27);g=f+(c^e&(b^c))+d[10]+38016083&4294967295;f=b+(g<<9&4294967295|g>>>23);g=e+(b^c&(f^b))+d[15]+3634488961&4294967295;e=f+(g<<14&4294967295|g>>>18);g=c+(f^b&(e^f))+d[4]+3889429448&4294967295;c=e+(g<<20&4294967295| @@ -94,90 +93,91 @@ b^c)+d[7]+4139469664&4294967295;e=f+(g<<16&4294967295|g>>>16);g=c+(e^f^b)+d[10]+ f=b+(g<<11&4294967295|g>>>21);g=e+(f^b^c)+d[15]+530742520&4294967295;e=f+(g<<16&4294967295|g>>>16);g=c+(e^f^b)+d[2]+3299628645&4294967295;c=e+(g<<23&4294967295|g>>>9);g=b+(e^(c|~f))+d[0]+4096336452&4294967295;b=c+(g<<6&4294967295|g>>>26);g=f+(c^(b|~e))+d[7]+1126891415&4294967295;f=b+(g<<10&4294967295|g>>>22);g=e+(b^(f|~c))+d[14]+2878612391&4294967295;e=f+(g<<15&4294967295|g>>>17);g=c+(f^(e|~b))+d[5]+4237533241&4294967295;c=e+(g<<21&4294967295|g>>>11);g=b+(e^(c|~f))+d[12]+1700485571&4294967295;b=c+ (g<<6&4294967295|g>>>26);g=f+(c^(b|~e))+d[3]+2399980690&4294967295;f=b+(g<<10&4294967295|g>>>22);g=e+(b^(f|~c))+d[10]+4293915773&4294967295;e=f+(g<<15&4294967295|g>>>17);g=c+(f^(e|~b))+d[1]+2240044497&4294967295;c=e+(g<<21&4294967295|g>>>11);g=b+(e^(c|~f))+d[8]+1873313359&4294967295;b=c+(g<<6&4294967295|g>>>26);g=f+(c^(b|~e))+d[15]+4264355552&4294967295;f=b+(g<<10&4294967295|g>>>22);g=e+(b^(f|~c))+d[6]+2734768916&4294967295;e=f+(g<<15&4294967295|g>>>17);g=c+(f^(e|~b))+d[13]+1309151649&4294967295; c=e+(g<<21&4294967295|g>>>11);g=b+(e^(c|~f))+d[4]+4149444226&4294967295;b=c+(g<<6&4294967295|g>>>26);g=f+(c^(b|~e))+d[11]+3174756917&4294967295;f=b+(g<<10&4294967295|g>>>22);g=e+(b^(f|~c))+d[2]+718787259&4294967295;e=f+(g<<15&4294967295|g>>>17);g=c+(f^(e|~b))+d[9]+3951481745&4294967295;a.o[0]=a.o[0]+b&4294967295;a.o[1]=a.o[1]+(e+(g<<21&4294967295|g>>>11))&4294967295;a.o[2]=a.o[2]+e&4294967295;a.o[3]=a.o[3]+f&4294967295}; -Ie.prototype.update=function(a,b){l(b)||(b=a.length);for(var c=b-this.O,d=this.Ec,e=this.qa,f=0;fthis.qa?this.O:2*this.O)-this.qa);a[0]=128;for(var b=1;bb;++b)for(var d=0;32>d;d+=8)a[c++]=this.o[b]>>>d&255;return a};var Ke=function(a){this.Ea="__ee_hash__";this.Fb=!1!==a;this.na=[];this.ga={};this.Tb=[]};v("ee.Serializer",Ke);var Le=new hb,Me=new Ie,Oe=function(a,b){return Ne(new Ke(l(b)?b:!0),a)};v("ee.Serializer.encode",Oe);var Pe=function(a){return Le.v(Oe(a))};v("ee.Serializer.toJSON",Pe);var Qe=function(a){a=Ne(new Ke(!1),a);return"JSON"in k?k.JSON.stringify(a,null," "):Le.v(a)};v("ee.Serializer.toReadableJSON",Qe); -var Ne=function(a,b){var c=a.La(b);a.Fb&&(c=t(c)&&"ValueRef"==c.type&&1==a.na.length?a.na[0][1]:{type:"CompoundValue",scope:a.na,value:c},a.na=[],Qa(a.Tb,u(function(a){delete a[this.Ea]},a)),a.Tb=[],a.ga={});return c}; -Ke.prototype.La=function(a){if(!l(a))throw Error("Can't encode an undefined value.");var b,c=t(a)?a[this.Ea]:null;if(this.Fb&&null!=c&&this.ga[c])return{type:"ValueRef",value:this.ga[c]};if(null===a||"boolean"==typeof a||q(a)||p(a))return a;if(t(a)&&"function"==typeof a.getFullYear)return{type:"Invocation",functionName:"Date",arguments:{value:Math.floor(a.getTime())}};if(a instanceof Ge){if(b=a.encode(u(this.La,this)),!(n(b)||t(b)&&"ArgumentRef"!=b.type))return b}else if(n(a))b=Sa(a,function(a){return this.La(a)}, -this);else if(t(a)&&!r(a))b=oa(a,function(a){if(!r(a))return this.La(a)},this),sa(b,this.Ea),b={type:"Dictionary",value:b};else throw Error("Can't encode object: "+a);if(this.Fb){Me.reset();Me.update(Le.v(b));var c=Me.digest().toString(),d;this.ga[c]?d=this.ga[c]:(d=String(this.na.length),this.na.push([d,b]),this.ga[c]=d);a[this.Ea]=c;this.Tb.push(a);return{type:"ValueRef",value:d}}return b};var I=function(a,b,c){if(!(this instanceof I))return J(I,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.b=a;this.h=b;this.m=c||null};w(I,Ge);v("ee.ComputedObject",I);I.prototype.C=function(a){return ye({json:this.v()},a)};v("ee.ComputedObject.prototype.getInfo",I.prototype.C); -I.prototype.encode=function(a){if(null===this.b&&null===this.h)return{type:"ArgumentRef",value:this.m};var b={},c;for(c in this.h)l(this.h[c])&&(b[c]=a(this.h[c]));b={type:"Invocation",arguments:b};a=a(this.b);b[p(a)?"functionName":"function"]=a;return b};I.prototype.v=function(){return Pe(this)};v("ee.ComputedObject.prototype.serialize",I.prototype.v);I.prototype.toString=function(){return"ee."+this.name()+"("+Qe(this)+")"};v("ee.ComputedObject.prototype.toString",I.prototype.toString); -I.prototype.name=function(){return"ComputedObject"};var Re=function(a,b){if(b instanceof a.constructor)return b;var c=function(){};c.prototype=a.constructor.prototype;c=new c;c.b=b.b;c.h=b.h;c.m=b.m;return c},J=function(a,b){function c(){return a.apply(this,b)}c.prototype=a.prototype;return new c};var Se={},Te=function(a){return a.prototype instanceof I?a.prototype.name.call(null):a==Number?"Number":a==String?"String":a==Array?"Array":a==Date?"Date":"Object"},Ue=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}},Ve=function(a){return q(a)|| -a instanceof I&&"Number"==a.name()},We=function(a){return p(a)||a instanceof I&&"String"==a.name()},Xe=function(a){return t(a)&&!r(a)?(a=Object.getPrototypeOf(a),null!==a&&null===Object.getPrototypeOf(a)):!1};var K=function(){if(!(this instanceof K))return new K};w(K,Ge);v("ee.Function",K);var Ye=function(a){return a};K.prototype.call=function(a){return this.apply(Ze(this,Array.prototype.slice.call(arguments,0)))};v("ee.Function.prototype.call",K.prototype.call);K.prototype.apply=function(a){a=new I(this,$e(this,a));return Ye(a,this.F().returns)};v("ee.Function.prototype.apply",K.prototype.apply); -var af=function(a,b,c){var d=l(b),e=a.F(),f=!1;if(1==c.length&&Xe(c[0])){var g=e.args;d&&(g=g.slice(1));g.length&&(f=!((1==g.length||g[1].optional)&&"Dictionary"==g[0].type))}if(f){if(c=ta(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=Ze(a,d?[b].concat(c):c);return a.apply(c)},$e=function(a,b){for(var c=a.F().args,d={},e={},f=0;f/,"");for(var c=0;c/,"");return new L(b,a)}),a&&a())};a?G("/algorithms",null,c,"GET"):c(G("/algorithms",null,void 0,"GET"))}},N=function(a,b,c,d){ef();var e=d||"";ka(ma,function(d,g){var m=g.split(".");if(2==m.length&&m[0]==b){var m=e+m[1],F=d.F();df[g]=!0;var la=!1; -if(F.args.length)var X=F.args[0].type,la="Object"!=X&&Ue(X,c);X=la?a.prototype:a;m in X||(X[m]=function(a){return af(d,la?this:void 0,Array.prototype.slice.call(arguments,0))},X[m].toString=u(d.toString,d,m,la),X[m].signature=F)}})},O=function(a){var b=function(a){for(var b in a)r(a[b])&&a[b].signature&&delete a[b]};b(a);b(a.prototype)};var P=function(a,b,c){I.call(this,a,b,c);jf()};w(P,I);v("ee.Element",P);var kf=!1,jf=function(){kf||(N(P,"Element","Element"),kf=!0)};P.prototype.name=function(){return"Element"}; -P.prototype.set=function(a){var b;if(1>=arguments.length){var c=arguments[0];if(b=Xe(c))a:{b=qa(c);var d=["properties"];if(da(b)&&da(d)&&b.length==d.length){for(var e=b.length,f=0;fe.length)throw Error("Geometry constructor given extra arguments.");for(var f=0;fb||4/,"");c[e]=!0}var f=k.ee,g;for(g in b)g in c&&!(g in f)&&(f[g]=Hg(g),Fg.push(g),a[g]?(f[g].signature=a[g],f[g].signature.isConstructor=!0,df[g]=!0):f[g].signature={});Se=f;Ig()}catch(m){Eg(m);return}Ag="ready";for(Bg=[];0b)return Za(this.K,a);Ya(this.D,b);return!0}; -h.w=function(){for(var a=[],b=this.D.length-1;0<=b;--b)a.push(this.D[b]);for(var c=this.K.length,b=0;bthis.Wa)throw Error("[goog.structs.Pool] Min can not be greater than max");this.M=new Qg;this.V=new wd;this.ub=0;this.Gb=null;this.Ga()};w(Rg,Ic);h=Rg.prototype;h.Oa=function(){var a=ja();if(!(null!=this.Gb&&a-this.Gbthis.qa?this.O:2*this.O)-this.qa);a[0]=128;for(var b=1;bb;++b)for(var d=0;32>d;d+=8)a[c++]=this.o[b]>>>d&255;return a};var Me=function(a){this.Ea="__ee_hash__";this.Fb=!1!==a;this.na=[];this.ga={};this.Tb=[]};v("ee.Serializer",Me);var Ne=new ib,Oe=new Ke,Qe=function(a,b){return Pe(new Me(m(b)?b:!0),a)};v("ee.Serializer.encode",Qe);var Re=function(a){return Ne.v(Qe(a))};v("ee.Serializer.toJSON",Re);var Se=function(a){a=Pe(new Me(!1),a);return"JSON"in l?l.JSON.stringify(a,null," "):Ne.v(a)};v("ee.Serializer.toReadableJSON",Se); +var Pe=function(a,b){var c=a.La(b);a.Fb&&(c=t(c)&&"ValueRef"==c.type&&1==a.na.length?a.na[0][1]:{type:"CompoundValue",scope:a.na,value:c},a.na=[],Qa(a.Tb,u(function(a){delete a[this.Ea]},a)),a.Tb=[],a.ga={});return c}; +Me.prototype.La=function(a){if(!m(a))throw Error("Can't encode an undefined value.");var b,c=t(a)?a[this.Ea]:null;if(this.Fb&&null!=c&&this.ga[c])return{type:"ValueRef",value:this.ga[c]};if(null===a||"boolean"==typeof a||q(a)||p(a))return a;if(t(a)&&"function"==typeof a.getFullYear)return{type:"Invocation",functionName:"Date",arguments:{value:Math.floor(a.getTime())}};if(a instanceof Ie){if(b=a.encode(u(this.La,this)),!(n(b)||t(b)&&"ArgumentRef"!=b.type))return b}else if(n(a))b=Sa(a,function(a){return this.La(a)}, +this);else if(t(a)&&!r(a))b=oa(a,function(a){if(!r(a))return this.La(a)},this),sa(b,this.Ea),b={type:"Dictionary",value:b};else throw Error("Can't encode object: "+a);if(this.Fb){Oe.reset();Oe.update(Ne.v(b));var c=Oe.digest().toString(),d;this.ga[c]?d=this.ga[c]:(d=String(this.na.length),this.na.push([d,b]),this.ga[c]=d);a[this.Ea]=c;this.Tb.push(a);return{type:"ValueRef",value:d}}return b};var H=function(a,b,c){if(!(this instanceof H))return I(H,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.b=a;this.h=b;this.m=c||null};w(H,Ie);v("ee.ComputedObject",H);H.prototype.B=function(a){return Ae({json:this.v()},a)};H.prototype.getInfo=H.prototype.B; +H.prototype.encode=function(a){if(null===this.b&&null===this.h)return{type:"ArgumentRef",value:this.m};var b={},c;for(c in this.h)m(this.h[c])&&(b[c]=a(this.h[c]));b={type:"Invocation",arguments:b};a=a(this.b);b[p(a)?"functionName":"function"]=a;return b};H.prototype.v=function(){return Re(this)};H.prototype.serialize=H.prototype.v;H.prototype.toString=function(){return"ee."+this.name()+"("+Se(this)+")"};H.prototype.toString=H.prototype.toString;H.prototype.name=function(){return"ComputedObject"}; +H.prototype.Dc=function(a,b){var c=bb(arguments);c[0]=this;a.apply(l,c);return this};H.prototype.aside=H.prototype.Dc;var Te=function(a,b){if(b instanceof a.constructor)return b;var c=function(){};c.prototype=a.constructor.prototype;c=new c;c.b=b.b;c.h=b.h;c.m=b.m;return c},I=function(a,b){function c(){return a.apply(this,b)}c.prototype=a.prototype;return new c};var Ue={},Ve=function(a){return a.prototype instanceof H?a.prototype.name.call(null):a==Number?"Number":a==String?"String":a==Array?"Array":a==Date?"Date":"Object"},We=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}},Xe=function(a){return q(a)|| +a instanceof H&&"Number"==a.name()},Ye=function(a){return p(a)||a instanceof H&&"String"==a.name()},Ze=function(a){return t(a)&&!r(a)?(a=Object.getPrototypeOf(a),null!==a&&null===Object.getPrototypeOf(a)):!1};var J=function(){if(!(this instanceof J))return new J};w(J,Ie);v("ee.Function",J);var $e=fb;J.prototype.call=function(a){return this.apply(af(this,Array.prototype.slice.call(arguments,0)))};J.prototype.call=J.prototype.call;J.prototype.apply=function(a){a=new H(this,bf(this,a));return $e(a,this.F().returns)};J.prototype.apply=J.prototype.apply; +var cf=function(a,b,c){var d=m(b),e=a.F(),f=!1;if(1==c.length&&Ze(c[0])){var g=e.args;d&&(g=g.slice(1));g.length&&(f=!((1==g.length||g[1].optional)&&"Dictionary"==g[0].type))}if(f){if(c=ta(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=af(a,d?[b].concat(c):c);return a.apply(c)},bf=function(a,b){for(var c=a.F().args,d={},e={},f=0;f/,"");for(var c=0;c/,"");return new K(b,a)}),a&&a())};a?F("/algorithms",null,c,"GET"):c(F("/algorithms",null,void 0,"GET"))}},M=function(a,b,c,d){gf();var e=d||"";ka(la,function(d,g){var k=g.split(".");if(2==k.length&&k[0]==b){var k=e+k[1],y=d.F();ff[g]=!0;var ma=!1; +if(y.args.length)var Y=y.args[0].type,ma="Object"!=Y&&We(Y,c);Y=ma?a.prototype:a;k in Y||(Y[k]=function(a){return cf(d,ma?this:void 0,Array.prototype.slice.call(arguments,0))},Y[k].toString=u(d.toString,d,k,ma),Y[k].signature=y)}})},N=function(a){var b=function(a){for(var b in a)r(a[b])&&a[b].signature&&delete a[b]};b(a);b(a.prototype)};var O=function(a,b,c){H.call(this,a,b,c);lf()};w(O,H);v("ee.Element",O);var mf=!1,lf=function(){mf||(M(O,"Element","Element"),mf=!0)};O.prototype.name=function(){return"Element"}; +O.prototype.set=function(a){var b;if(1>=arguments.length){var c=arguments[0];if(b=Ze(c))a:{b=qa(c);var d=["properties"];if(da(b)&&da(d)&&b.length==d.length){for(var e=b.length,f=0;fe.length)throw Error("Geometry constructor given extra arguments.");for(var f=0;fb||4/,"");c[e]=!0}var f=l.ee,g;for(g in b)g in c&&!(g in f)&&(f[g]=Jg(g),Hg.push(g),a[g]?(f[g].signature=a[g],f[g].signature.isConstructor=!0,ff[g]=!0):f[g].signature={});Ue=f;Kg()}catch(k){Gg(k);return}Cg="ready";for(Dg=[];0b)return Za(this.K,a);Ya(this.D,b);return!0}; +h.w=function(){for(var a=[],b=this.D.length-1;0<=b;--b)a.push(this.D[b]);for(var c=this.K.length,b=0;bthis.Wa)throw Error("[goog.structs.Pool] Min can not be greater than max");this.M=new Sg;this.V=new wd;this.ub=0;this.Gb=null;this.Ga()};w(Tg,Hc);h=Tg.prototype;h.Oa=function(){var a=ja();if(!(null!=this.Gb&&a-this.Gbthis.Wa&&0=a.u()){a=this.J;for(var d=0;d>1,a[d].getKey()>c.getKey())a[b]=a[d],b=d;else break;a[b]=c};h=Tg.prototype; +h.isEmpty=function(){return this.M.isEmpty()&&this.V.isEmpty()};h.s=function(){Tg.l.s.call(this);if(0=a.u()){a=this.J;for(var d=0;d>1,a[d].getKey()>c.getKey())a[b]=a[d],b=d;else break;a[b]=c};h=Vg.prototype; h.remove=function(){var a=this.J,b=a.length,c=a[0];if(!(0>=b)){if(1==b)Xa(a);else{a[0]=a.pop();for(var a=0,b=this.J,d=b.length,e=b[a];a>1;){var f=2*a+1,g=2*a+2,f=ge.getKey())break;b[a]=b[f];a=f}b[a]=e}return c.Sb}};h.w=function(){for(var a=this.J,b=[],c=a.length,d=0;da.hd)return!1;a.Ub++;Pg(a.$,a.S);setTimeout(u(a.sd,a),0);return!0}; -Yg.prototype.sd=function(){if(!this.Fa){var a=this.getUrl();(a=p(a)?a:a.src)&&(this.$.xa[this.S]={src:a,Zb:l(void 0)?void 0:null});fd(this.$,ah,u(this.Zc,this));this.$.start()}};var bh=function(){this.H=!1};w(bh,Ic);bh.prototype.setActive=function(a){this.H=a};bh.prototype.ic=function(){return this.H};var Xg=function(a,b){Wg.call(this,a,b)};w(Xg,Wg);Xg.prototype.tb=function(){return new bh};Xg.prototype.Ka=function(a){a.R()};Xg.prototype.Kb=function(a){return!a.U&&!a.ic()};var ch=function(a,b,c,d){D.call(this);this.gd=b;this.vd=c;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.jc=l(d.jc)?d.jc:!0;this.name=d.name;this.Aa=[];this.Rb=new wd;this.wc=new wd;this.Qb=0;this.url=a;this.Lb=1};w(ch,D);v("ee.MapLayerOverlay",ch); -ch.prototype.getTile=function(a,b,c){var d=1<a.y||a.y>=d)return a=c.createElement("IMG"),a.style.width="0px",a.style.height="0px",a;c=a.x%d;0>c&&(c+=d);b=[this.gd,b,c,a.y].join("/");a=[this.url,b].join("/")+"?token="+this.vd;b=b+"/"+this.Qb;this.Qb+=1;d=Gb("div",{id:b});c=(new Date).getTime()/1E3;this.Aa.push(b);Z.ec().send(b,a,c,u(this.Yc,this,d,b));this.dispatchEvent(new dh(this.Aa.length));return d}; -ch.prototype.releaseTile=function(a){Z.ec().abort(a.id);this.Rb.remove(void 0!=a.firstElementChild?a.firstElementChild:Jb(a.firstChild));this.wc.remove(a.id)};ch.prototype.setOpacity=function(a){this.Lb=a;var b=this.Rb.ea();qd(b,function(b){Jg(b,a)})};ch.prototype.getTile=ch.prototype.getTile;ch.prototype.setOpacity=ch.prototype.setOpacity;ch.prototype.releaseTile=ch.prototype.releaseTile; -ch.prototype.Yc=function(a,b,c){"error"==c.type?(Za(this.Aa,b),this.wc.add(b),this.dispatchEvent(c)):(Za(this.Aa,b),c.target&&"load"==c.type&&(b=c.target,this.Rb.add(b),1!=this.Lb&&Jg(b,this.Lb),a.appendChild(b)),this.dispatchEvent(new dh(this.Aa.length)))};var dh=function(a){Jc.call(this,"tileevent");this.count=a};w(dh,Jc);})(); +h.Y=function(a){return Ta(this.J,function(b){return b.getKey()==a})};h.clone=function(){return new Vg(this)};h.u=function(){return this.J.length};h.isEmpty=function(){return 0==this.J.length};h.clear=function(){Xa(this.J)};var Xg=function(){Vg.call(this)};w(Xg,Vg);Xg.prototype.Ma=function(a,b){Wg(this,a,b)};Xg.prototype.ra=function(){return this.remove()};var Yg=function(a,b){this.ac=void 0;this.bb=new Xg;Tg.call(this,a,b)};w(Yg,Tg);h=Yg.prototype;h.Oa=function(a,b){if(!a){var c=Yg.l.Oa.call(this);c&&this.ub&&(this.ac=l.setTimeout(u(this.Qa,this),this.ub));return c}this.bb.Ma(m(b)?b:100,a);this.Qa()};h.Qa=function(){for(var a=this.bb;0a.hd)return!1;a.Ub++;Rg(a.$,a.S);setTimeout(u(a.ud,a),0);return!0}; +$g.prototype.ud=function(){if(!this.Fa){var a=this.getUrl();(a=p(a)?a:a.src)&&(this.$.xa[this.S]={src:a,Zb:m(void 0)?void 0:null});ed(this.$,ch,u(this.Zc,this));this.$.start()}};var dh=function(){this.H=!1};w(dh,Hc);dh.prototype.setActive=function(a){this.H=a};dh.prototype.ic=function(){return this.H};var Zg=function(a,b){Yg.call(this,a,b)};w(Zg,Yg);Zg.prototype.tb=function(){return new dh};Zg.prototype.Ka=function(a){a.R()};Zg.prototype.Kb=function(a){return!a.U&&!a.ic()};var Z=function(a,b,c,d){kd.call(this);this.gd=b;this.xd=c;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.jc=m(d.jc)?d.jc:!0;this.name=d.name;this.Aa=[];this.Rb=new wd;this.wc=new wd;this.Qb=0;this.url=a;this.Lb=1};w(Z,kd);v("ee.MapLayerOverlay",Z);Z.prototype.Bc=function(a){return Yc(this,"tileevent",a)};Z.prototype.addTileCallback=Z.prototype.Bc; +Z.prototype.rd=function(a){gd(a)};Z.prototype.removeTileCallback=Z.prototype.rd;Z.prototype.getTile=function(a,b,c){var d=1<a.y||a.y>=d)return a=c.createElement("IMG"),a.style.width="0px",a.style.height="0px",a;c=a.x%d;0>c&&(c+=d);b=[this.gd,b,c,a.y].join("/");a=[this.url,b].join("/")+"?token="+this.xd;b=b+"/"+this.Qb;this.Qb+=1;d=Gb("div",{id:b});c=(new Date).getTime()/1E3;this.Aa.push(b);X.ec().send(b,a,c,u(this.Yc,this,d,b));this.dispatchEvent(new eh(this.Aa.length));return d}; +Z.prototype.releaseTile=function(a){X.ec().abort(a.id);this.Rb.remove(m(a.firstElementChild)?a.firstElementChild:Jb(a.firstChild));this.wc.remove(a.id)};Z.prototype.setOpacity=function(a){this.Lb=a;var b=this.Rb.ea();qd(b,function(b){Lg(b,a)})};Z.prototype.getTile=Z.prototype.getTile;Z.prototype.setOpacity=Z.prototype.setOpacity;Z.prototype.releaseTile=Z.prototype.releaseTile; +Z.prototype.Yc=function(a,b,c){"error"==c.type?(Za(this.Aa,b),this.wc.add(b),this.dispatchEvent(c)):(Za(this.Aa,b),c.target&&"load"==c.type&&(b=c.target,this.Rb.add(b),1!=this.Lb&&Lg(b,this.Lb),a.appendChild(b)),this.dispatchEvent(new eh(this.Aa.length)))};var eh=function(a){Ic.call(this,"tileevent");this.count=a};w(eh,Ic);})(); //@ sourceMappingURL=ee_api_js.sourcemap diff --git a/javascript/build/ee_api_js_debug.js b/javascript/build/ee_api_js_debug.js index f30b613c3..1de9b0cb6 100644 --- a/javascript/build/ee_api_js_debug.js +++ b/javascript/build/ee_api_js_debug.js @@ -431,7 +431,18 @@ goog.globalEval = function(script) { goog.global.execScript(script, "JavaScript"); } else { if (goog.global.eval) { - if (null == goog.evalWorksForGlobals_ && (goog.global.eval("var _et_ = 1;"), "undefined" != typeof goog.global._et_ ? (delete goog.global._et_, goog.evalWorksForGlobals_ = !0) : goog.evalWorksForGlobals_ = !1), goog.evalWorksForGlobals_) { + if (null == goog.evalWorksForGlobals_) { + if (goog.global.eval("var _evalTest_ = 1;"), "undefined" != typeof goog.global._evalTest_) { + try { + delete goog.global._evalTest_; + } catch (ignore) { + } + goog.evalWorksForGlobals_ = !0; + } else { + goog.evalWorksForGlobals_ = !1; + } + } + if (goog.evalWorksForGlobals_) { goog.global.eval(script); } else { var doc = goog.global.document, scriptElt = doc.createElement("SCRIPT"); @@ -537,10 +548,6 @@ goog.MODIFY_FUNCTION_PROTOTYPES && (Function.prototype.bind = Function.prototype var args = Array.prototype.slice.call(arguments); args.unshift(this, null); return goog.bind.apply(null, args); -}, Function.prototype.inherits = function(parentCtor) { - goog.inherits(this, parentCtor); -}, Function.prototype.mixin = function(source) { - goog.mixin(this.prototype, source); }); goog.defineClass = function(superClass, def) { var constructor = def.constructor, statics = def.statics; @@ -1710,6 +1717,113 @@ goog.array.copyByIndex = function(arr, index_arr) { }); return result; }; +goog.functions = {}; +goog.functions.constant = function(retValue) { + return function() { + return retValue; + }; +}; +goog.functions.FALSE = goog.functions.constant(!1); +goog.functions.TRUE = goog.functions.constant(!0); +goog.functions.NULL = goog.functions.constant(null); +goog.functions.identity = function(opt_returnValue, var_args) { + return opt_returnValue; +}; +goog.functions.error = function(message) { + return function() { + throw Error(message); + }; +}; +goog.functions.fail = function(err) { + return function() { + throw err; + }; +}; +goog.functions.lock = function(f, opt_numArgs) { + opt_numArgs = opt_numArgs || 0; + return function() { + return f.apply(this, Array.prototype.slice.call(arguments, 0, opt_numArgs)); + }; +}; +goog.functions.nth = function(n) { + return function() { + return arguments[n]; + }; +}; +goog.functions.withReturnValue = function(f, retValue) { + return goog.functions.sequence(f, goog.functions.constant(retValue)); +}; +goog.functions.equalTo = function(value, opt_useLooseComparison) { + return function(other) { + return opt_useLooseComparison ? value == other : value === other; + }; +}; +goog.functions.compose = function(fn, var_args) { + var functions = arguments, length = functions.length; + return function() { + var result; + length && (result = functions[length - 1].apply(this, arguments)); + for (var i = length - 2;0 <= i;i--) { + result = functions[i].call(this, result); + } + return result; + }; +}; +goog.functions.sequence = function(var_args) { + var functions = arguments, length = functions.length; + return function() { + for (var result, i = 0;i < length;i++) { + result = functions[i].apply(this, arguments); + } + return result; + }; +}; +goog.functions.and = function(var_args) { + var functions = arguments, length = functions.length; + return function() { + for (var i = 0;i < length;i++) { + if (!functions[i].apply(this, arguments)) { + return !1; + } + } + return !0; + }; +}; +goog.functions.or = function(var_args) { + var functions = arguments, length = functions.length; + return function() { + for (var i = 0;i < length;i++) { + if (functions[i].apply(this, arguments)) { + return !0; + } + } + return !1; + }; +}; +goog.functions.not = function(f) { + return function() { + return !f.apply(this, arguments); + }; +}; +goog.functions.create = function(constructor, var_args) { + var temp = function() { + }; + temp.prototype = constructor.prototype; + var obj = new temp; + constructor.apply(obj, Array.prototype.slice.call(arguments, 1)); + return obj; +}; +goog.functions.CACHE_RETURN_VALUE = !0; +goog.functions.cacheReturnValue = function(fn) { + var called = !1, value; + return function() { + if (!goog.functions.CACHE_RETURN_VALUE) { + return fn(); + } + called || (value = fn(), called = !0); + return value; + }; +}; goog.json = {}; goog.json.USE_NATIVE_JSON = !1; goog.json.isValid = function(s) { @@ -2138,11 +2252,16 @@ goog.html.SafeUrl.unwrap = function(safeUrl) { goog.html.SafeUrl.fromConstant = function(url) { return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(goog.string.Const.unwrap(url)); }; -goog.html.SAFE_BLOB_TYPE_PATTERN_ = /^image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)$/i; +goog.html.SAFE_MIME_TYPE_PATTERN_ = /^(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm))$/i; goog.html.SafeUrl.fromBlob = function(blob) { - var url = goog.html.SAFE_BLOB_TYPE_PATTERN_.test(blob.type) ? goog.fs.url.createObjectUrl(blob) : goog.html.SafeUrl.INNOCUOUS_STRING; + var url = goog.html.SAFE_MIME_TYPE_PATTERN_.test(blob.type) ? goog.fs.url.createObjectUrl(blob) : goog.html.SafeUrl.INNOCUOUS_STRING; return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url); }; +goog.html.DATA_URL_PATTERN_ = /^data:([^;,]*);base64,[a-z0-9+\/]+=*$/i; +goog.html.SafeUrl.fromDataUrl = function(dataUrl) { + var match = dataUrl.match(goog.html.DATA_URL_PATTERN_), valid = match && goog.html.SAFE_MIME_TYPE_PATTERN_.test(match[1]); + return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(valid ? dataUrl : goog.html.SafeUrl.INNOCUOUS_STRING); +}; goog.html.SAFE_URL_PATTERN_ = /^(?:(?:https?|mailto|ftp):|[^&:/?#]*(?:[/?#]|$))/i; goog.html.SafeUrl.sanitize = function(url) { if (url instanceof goog.html.SafeUrl) { @@ -2666,13 +2785,16 @@ goog.labs.userAgent.browser.matchOpera_ = function() { return goog.labs.userAgent.util.matchUserAgent("Opera") || goog.labs.userAgent.util.matchUserAgent("OPR"); }; goog.labs.userAgent.browser.matchIE_ = function() { - return goog.labs.userAgent.util.matchUserAgent("Edge") || goog.labs.userAgent.util.matchUserAgent("Trident") || goog.labs.userAgent.util.matchUserAgent("MSIE"); + return goog.labs.userAgent.util.matchUserAgent("Trident") || goog.labs.userAgent.util.matchUserAgent("MSIE"); +}; +goog.labs.userAgent.browser.matchEdge_ = function() { + return goog.labs.userAgent.util.matchUserAgent("Edge"); }; goog.labs.userAgent.browser.matchFirefox_ = function() { return goog.labs.userAgent.util.matchUserAgent("Firefox"); }; goog.labs.userAgent.browser.matchSafari_ = function() { - return goog.labs.userAgent.util.matchUserAgent("Safari") && !(goog.labs.userAgent.browser.matchChrome_() || goog.labs.userAgent.browser.matchCoast_() || goog.labs.userAgent.browser.matchOpera_() || goog.labs.userAgent.browser.matchIE_() || goog.labs.userAgent.browser.isSilk() || goog.labs.userAgent.util.matchUserAgent("Android")); + return goog.labs.userAgent.util.matchUserAgent("Safari") && !(goog.labs.userAgent.browser.matchChrome_() || goog.labs.userAgent.browser.matchCoast_() || goog.labs.userAgent.browser.matchOpera_() || goog.labs.userAgent.browser.matchEdge_() || goog.labs.userAgent.browser.isSilk() || goog.labs.userAgent.util.matchUserAgent("Android")); }; goog.labs.userAgent.browser.matchCoast_ = function() { return goog.labs.userAgent.util.matchUserAgent("Coast"); @@ -2681,13 +2803,14 @@ goog.labs.userAgent.browser.matchIosWebview_ = function() { return (goog.labs.userAgent.util.matchUserAgent("iPad") || goog.labs.userAgent.util.matchUserAgent("iPhone")) && !goog.labs.userAgent.browser.matchSafari_() && !goog.labs.userAgent.browser.matchChrome_() && !goog.labs.userAgent.browser.matchCoast_() && goog.labs.userAgent.util.matchUserAgent("AppleWebKit"); }; goog.labs.userAgent.browser.matchChrome_ = function() { - return (goog.labs.userAgent.util.matchUserAgent("Chrome") || goog.labs.userAgent.util.matchUserAgent("CriOS")) && !goog.labs.userAgent.browser.matchOpera_() && !goog.labs.userAgent.browser.matchIE_(); + return (goog.labs.userAgent.util.matchUserAgent("Chrome") || goog.labs.userAgent.util.matchUserAgent("CriOS")) && !goog.labs.userAgent.browser.matchOpera_() && !goog.labs.userAgent.browser.matchEdge_(); }; goog.labs.userAgent.browser.matchAndroidBrowser_ = function() { return goog.labs.userAgent.util.matchUserAgent("Android") && !(goog.labs.userAgent.browser.isChrome() || goog.labs.userAgent.browser.isFirefox() || goog.labs.userAgent.browser.isOpera() || goog.labs.userAgent.browser.isSilk()); }; goog.labs.userAgent.browser.isOpera = goog.labs.userAgent.browser.matchOpera_; goog.labs.userAgent.browser.isIE = goog.labs.userAgent.browser.matchIE_; +goog.labs.userAgent.browser.isEdge = goog.labs.userAgent.browser.matchEdge_; goog.labs.userAgent.browser.isFirefox = goog.labs.userAgent.browser.matchFirefox_; goog.labs.userAgent.browser.isSafari = goog.labs.userAgent.browser.matchSafari_; goog.labs.userAgent.browser.isCoast = goog.labs.userAgent.browser.matchCoast_; @@ -2714,6 +2837,9 @@ goog.labs.userAgent.browser.getVersion = function() { if (goog.labs.userAgent.browser.isOpera()) { return lookUpValueWithKeys(["Version", "Opera", "OPR"]); } + if (goog.labs.userAgent.browser.isEdge()) { + return lookUpValueWithKeys(["Edge"]); + } if (goog.labs.userAgent.browser.isChrome()) { return lookUpValueWithKeys(["Chrome", "CriOS"]); } @@ -2728,10 +2854,6 @@ goog.labs.userAgent.browser.getIEVersion_ = function(userAgent) { if (rv && rv[1]) { return rv[1]; } - var edge = /Edge\/([\d\.]+)/.exec(userAgent); - if (edge) { - return edge[1]; - } var version = "", msie = /MSIE +([\d\.]+)/.exec(userAgent); if (msie && msie[1]) { var tridentVersion = /Trident\/(\d.\d)/.exec(userAgent); @@ -2857,12 +2979,13 @@ goog.labs.userAgent.platform.isVersionOrHigher = function(version) { }; goog.userAgent = {}; goog.userAgent.ASSUME_IE = !1; +goog.userAgent.ASSUME_EDGE = !1; goog.userAgent.ASSUME_GECKO = !1; goog.userAgent.ASSUME_WEBKIT = !1; goog.userAgent.ASSUME_MOBILE_WEBKIT = !1; goog.userAgent.ASSUME_OPERA = !1; goog.userAgent.ASSUME_ANY_VERSION = !1; -goog.userAgent.BROWSER_KNOWN_ = goog.userAgent.ASSUME_IE || goog.userAgent.ASSUME_GECKO || goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_OPERA; +goog.userAgent.BROWSER_KNOWN_ = goog.userAgent.ASSUME_IE || goog.userAgent.ASSUME_EDGE || goog.userAgent.ASSUME_GECKO || goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_OPERA; goog.userAgent.getUserAgentString = function() { return goog.labs.userAgent.util.getUserAgent(); }; @@ -2871,6 +2994,8 @@ goog.userAgent.getNavigator = function() { }; goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ? goog.userAgent.ASSUME_OPERA : goog.labs.userAgent.browser.isOpera(); goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ? goog.userAgent.ASSUME_IE : goog.labs.userAgent.browser.isIE(); +goog.userAgent.EDGE = goog.userAgent.BROWSER_KNOWN_ ? goog.userAgent.ASSUME_EDGE : goog.labs.userAgent.engine.isEdge(); +goog.userAgent.EDGE_OR_IE = goog.userAgent.EDGE || goog.userAgent.IE; goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ? goog.userAgent.ASSUME_GECKO : goog.labs.userAgent.engine.isGecko(); goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ? goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT : goog.labs.userAgent.engine.isWebKit(); goog.userAgent.isMobile_ = function() { @@ -2912,7 +3037,7 @@ goog.userAgent.determineVersion_ = function() { } var version = "", arr = goog.userAgent.getVersionRegexResult_(); arr && (version = arr ? arr[1] : ""); - if (goog.userAgent.IE && !goog.labs.userAgent.engine.isEdge()) { + if (goog.userAgent.IE) { var docMode = goog.userAgent.getDocumentMode_(); if (docMode > parseFloat(version)) { return String(docMode); @@ -2925,7 +3050,7 @@ goog.userAgent.getVersionRegexResult_ = function() { if (goog.userAgent.GECKO) { return /rv\:([^\);]+)(\)|;)/.exec(userAgent); } - if (goog.userAgent.IE && goog.labs.userAgent.engine.isEdge()) { + if (goog.userAgent.EDGE) { return /Edge\/([\d\.]+)/.exec(userAgent); } if (goog.userAgent.IE) { @@ -2949,12 +3074,12 @@ goog.userAgent.isVersionOrHigher = function(version) { }; goog.userAgent.isVersion = goog.userAgent.isVersionOrHigher; goog.userAgent.isDocumentModeOrHigher = function(documentMode) { - return goog.userAgent.IE && (goog.labs.userAgent.engine.isEdge() || goog.userAgent.DOCUMENT_MODE >= documentMode); + return goog.userAgent.DOCUMENT_MODE >= documentMode; }; goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher; goog.userAgent.DOCUMENT_MODE = function() { - var doc = goog.global.document, mode = goog.userAgent.getDocumentMode_(); - return !doc || !goog.userAgent.IE || !mode && goog.labs.userAgent.engine.isEdge() ? void 0 : mode || ("CSS1Compat" == doc.compatMode ? parseInt(goog.userAgent.VERSION, 10) : 5); + var doc = goog.global.document; + return doc && goog.userAgent.IE ? goog.userAgent.getDocumentMode_() || ("CSS1Compat" == doc.compatMode ? parseInt(goog.userAgent.VERSION, 10) : 5) : void 0; }(); goog.dom.BrowserFeature = {CAN_ADD_NAME_OR_TYPE_ATTRIBUTES:!goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(9), CAN_USE_CHILDREN_ATTRIBUTE:!goog.userAgent.GECKO && !goog.userAgent.IE || goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(9) || goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher("1.9.1"), CAN_USE_INNER_TEXT:goog.userAgent.IE && !goog.userAgent.isVersionOrHigher("9"), CAN_USE_PARENT_ELEMENT_PROPERTY:goog.userAgent.IE || goog.userAgent.OPERA || goog.userAgent.WEBKIT, INNER_HTML_NEEDS_SCOPED_ELEMENT:goog.userAgent.IE, LEGACY_IE_RANGES:goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)}; @@ -3079,7 +3204,7 @@ goog.dom.getElementsByTagNameAndClass_ = function(doc, opt_tag, opt_class, opt_e goog.dom.$$ = goog.dom.getElementsByTagNameAndClass; goog.dom.setProperties = function(element, properties) { goog.object.forEach(properties, function(val, key) { - "style" == key ? element.style.cssText = val : "class" == key ? element.className = val : "for" == key ? element.htmlFor = val : key in goog.dom.DIRECT_ATTRIBUTE_MAP_ ? element.setAttribute(goog.dom.DIRECT_ATTRIBUTE_MAP_[key], val) : goog.string.startsWith(key, "aria-") || goog.string.startsWith(key, "data-") ? element.setAttribute(key, val) : element[key] = val; + "style" == key ? element.style.cssText = val : "class" == key ? element.className = val : "for" == key ? element.htmlFor = val : goog.dom.DIRECT_ATTRIBUTE_MAP_.hasOwnProperty(key) ? element.setAttribute(goog.dom.DIRECT_ATTRIBUTE_MAP_[key], val) : goog.string.startsWith(key, "aria-") || goog.string.startsWith(key, "data-") ? element.setAttribute(key, val) : element[key] = val; }); }; goog.dom.DIRECT_ATTRIBUTE_MAP_ = {cellpadding:"cellPadding", cellspacing:"cellSpacing", colspan:"colSpan", frameborder:"frameBorder", height:"height", maxlength:"maxLength", role:"role", rowspan:"rowSpan", type:"type", usemap:"useMap", valign:"vAlign", width:"width"}; @@ -3320,16 +3445,16 @@ goog.dom.getChildren = function(element) { }); }; goog.dom.getFirstElementChild = function(node) { - return void 0 != node.firstElementChild ? node.firstElementChild : goog.dom.getNextElementNode_(node.firstChild, !0); + return goog.isDef(node.firstElementChild) ? node.firstElementChild : goog.dom.getNextElementNode_(node.firstChild, !0); }; goog.dom.getLastElementChild = function(node) { - return void 0 != node.lastElementChild ? node.lastElementChild : goog.dom.getNextElementNode_(node.lastChild, !1); + return goog.isDef(node.lastElementChild) ? node.lastElementChild : goog.dom.getNextElementNode_(node.lastChild, !1); }; goog.dom.getNextElementSibling = function(node) { - return void 0 != node.nextElementSibling ? node.nextElementSibling : goog.dom.getNextElementNode_(node.nextSibling, !0); + return goog.isDef(node.nextElementSibling) ? node.nextElementSibling : goog.dom.getNextElementNode_(node.nextSibling, !0); }; goog.dom.getPreviousElementSibling = function(node) { - return void 0 != node.previousElementSibling ? node.previousElementSibling : goog.dom.getNextElementNode_(node.previousSibling, !1); + return goog.isDef(node.previousElementSibling) ? node.previousElementSibling : goog.dom.getNextElementNode_(node.previousSibling, !1); }; goog.dom.getNextElementNode_ = function(node, forward) { for (;node && node.nodeType != goog.dom.NodeType.ELEMENT;) { @@ -3830,113 +3955,6 @@ goog.debug.entryPointRegistry.unmonitorAllIfPossible = function(monitor) { } monitors.length--; }; -goog.functions = {}; -goog.functions.constant = function(retValue) { - return function() { - return retValue; - }; -}; -goog.functions.FALSE = goog.functions.constant(!1); -goog.functions.TRUE = goog.functions.constant(!0); -goog.functions.NULL = goog.functions.constant(null); -goog.functions.identity = function(opt_returnValue, var_args) { - return opt_returnValue; -}; -goog.functions.error = function(message) { - return function() { - throw Error(message); - }; -}; -goog.functions.fail = function(err) { - return function() { - throw err; - }; -}; -goog.functions.lock = function(f, opt_numArgs) { - opt_numArgs = opt_numArgs || 0; - return function() { - return f.apply(this, Array.prototype.slice.call(arguments, 0, opt_numArgs)); - }; -}; -goog.functions.nth = function(n) { - return function() { - return arguments[n]; - }; -}; -goog.functions.withReturnValue = function(f, retValue) { - return goog.functions.sequence(f, goog.functions.constant(retValue)); -}; -goog.functions.equalTo = function(value, opt_useLooseComparison) { - return function(other) { - return opt_useLooseComparison ? value == other : value === other; - }; -}; -goog.functions.compose = function(fn, var_args) { - var functions = arguments, length = functions.length; - return function() { - var result; - length && (result = functions[length - 1].apply(this, arguments)); - for (var i = length - 2;0 <= i;i--) { - result = functions[i].call(this, result); - } - return result; - }; -}; -goog.functions.sequence = function(var_args) { - var functions = arguments, length = functions.length; - return function() { - for (var result, i = 0;i < length;i++) { - result = functions[i].apply(this, arguments); - } - return result; - }; -}; -goog.functions.and = function(var_args) { - var functions = arguments, length = functions.length; - return function() { - for (var i = 0;i < length;i++) { - if (!functions[i].apply(this, arguments)) { - return !1; - } - } - return !0; - }; -}; -goog.functions.or = function(var_args) { - var functions = arguments, length = functions.length; - return function() { - for (var i = 0;i < length;i++) { - if (functions[i].apply(this, arguments)) { - return !0; - } - } - return !1; - }; -}; -goog.functions.not = function(f) { - return function() { - return !f.apply(this, arguments); - }; -}; -goog.functions.create = function(constructor, var_args) { - var temp = function() { - }; - temp.prototype = constructor.prototype; - var obj = new temp; - constructor.apply(obj, Array.prototype.slice.call(arguments, 1)); - return obj; -}; -goog.functions.CACHE_RETURN_VALUE = !0; -goog.functions.cacheReturnValue = function(fn) { - var called = !1, value; - return function() { - if (!goog.functions.CACHE_RETURN_VALUE) { - return fn(); - } - called || (value = fn(), called = !0); - return value; - }; -}; goog.async.throwException = function(exception) { goog.global.setTimeout(function() { throw exception; @@ -4122,9 +4140,7 @@ goog.Promise = function(resolver, opt_context) { this.executing_ = !1; 0 < goog.Promise.UNHANDLED_REJECTION_DELAY ? this.unhandledRejectionId_ = 0 : 0 == goog.Promise.UNHANDLED_REJECTION_DELAY && (this.hadUnhandledRejection_ = !1); goog.Promise.LONG_STACK_TRACES && (this.stack_ = [], this.addStackTrace_(Error("created")), this.currentStep_ = 0); - if (resolver == goog.Promise.RESOLVE_FAST_PATH_) { - this.resolve_(goog.Promise.State_.FULFILLED, opt_context); - } else { + if (resolver != goog.nullFunction) { try { var self = this; resolver.call(opt_context, function(value) { @@ -4173,21 +4189,27 @@ goog.Promise.getCallbackEntry_ = function(onFulfilled, onRejected, context) { goog.Promise.returnEntry_ = function(entry) { goog.Promise.freelist_.put(entry); }; -goog.Promise.RESOLVE_FAST_PATH_ = function() { -}; goog.Promise.resolve = function(opt_value) { - return opt_value instanceof goog.Promise ? opt_value : new goog.Promise(goog.Promise.RESOLVE_FAST_PATH_, opt_value); + if (opt_value instanceof goog.Promise) { + return opt_value; + } + var promise = new goog.Promise(goog.nullFunction); + promise.resolve_(goog.Promise.State_.FULFILLED, opt_value); + return promise; }; goog.Promise.reject = function(opt_reason) { return new goog.Promise(function(resolve, reject) { reject(opt_reason); }); }; +goog.Promise.resolveThen_ = function(value, onFulfilled, onRejected) { + goog.Promise.maybeThen_(value, onFulfilled, onRejected, null) || goog.async.run(goog.partial(onFulfilled, value)); +}; goog.Promise.race = function(promises) { return new goog.Promise(function(resolve, reject) { promises.length || resolve(void 0); for (var i = 0, promise;promise = promises[i];i++) { - goog.Promise.maybeThenVoid_(promise, resolve, reject); + goog.Promise.resolveThen_(promise, resolve, reject); } }); }; @@ -4202,7 +4224,7 @@ goog.Promise.all = function(promises) { }, onReject = function(reason) { reject(reason); }, i = 0, promise;promise = promises[i];i++) { - goog.Promise.maybeThenVoid_(promise, goog.partial(onFulfill, i), onReject); + goog.Promise.resolveThen_(promise, goog.partial(onFulfill, i), onReject); } } else { resolve(values); @@ -4218,7 +4240,7 @@ goog.Promise.allSettled = function(promises) { results[index] = fulfilled ? {fulfilled:!0, value:result} : {fulfilled:!1, reason:result}; 0 == toSettle && resolve(results); }, i = 0, promise;promise = promises[i];i++) { - goog.Promise.maybeThenVoid_(promise, goog.partial(onSettled, i, !0), goog.partial(onSettled, i, !1)); + goog.Promise.resolveThen_(promise, goog.partial(onSettled, i, !0), goog.partial(onSettled, i, !1)); } } else { resolve(results); @@ -4236,7 +4258,7 @@ goog.Promise.firstFulfilled = function(promises) { reasons[index] = reason; 0 == toReject && reject(reasons); }, i = 0, promise;promise = promises[i];i++) { - goog.Promise.maybeThenVoid_(promise, onFulfill, goog.partial(onReject, i)); + goog.Promise.resolveThen_(promise, onFulfill, goog.partial(onReject, i)); } } else { resolve(void 0); @@ -4263,9 +4285,6 @@ goog.Promise.prototype.thenVoid = function(opt_onFulfilled, opt_onRejected, opt_ goog.Promise.LONG_STACK_TRACES && this.addStackTrace_(Error("then")); this.addCallbackEntry_(goog.Promise.getCallbackEntry_(opt_onFulfilled || goog.nullFunction, opt_onRejected || null, opt_context)); }; -goog.Promise.maybeThenVoid_ = function(promise, onFulfilled, onRejected, opt_context) { - promise instanceof goog.Promise ? promise.thenVoid(onFulfilled, onRejected, opt_context) : promise.then(onFulfilled, onRejected, opt_context); -}; goog.Promise.prototype.thenAlways = function(onSettled, opt_context) { goog.Promise.LONG_STACK_TRACES && this.addStackTrace_(Error("thenAlways")); var entry = goog.Promise.getCallbackEntry_(onSettled, onSettled, opt_context); @@ -4333,40 +4352,33 @@ goog.Promise.prototype.unblockAndReject_ = function(reason) { this.resolve_(goog.Promise.State_.REJECTED, reason); }; goog.Promise.prototype.resolve_ = function(state, x) { - if (this.state_ == goog.Promise.State_.PENDING) { - if (this == x) { - state = goog.Promise.State_.REJECTED, x = new TypeError("Promise cannot resolve to itself"); - } else { - if (goog.Thenable.isImplementedBy(x)) { - this.state_ = goog.Promise.State_.BLOCKED; - goog.Promise.maybeThenVoid_(x, this.unblockAndFulfill_, this.unblockAndReject_, this); - return; - } - if (goog.isObject(x)) { - try { - var then = x.then; - if (goog.isFunction(then)) { - this.tryThen_(x, then); - return; - } - } catch (e) { - state = goog.Promise.State_.REJECTED, x = e; - } + this.state_ == goog.Promise.State_.PENDING && (this == x && (state = goog.Promise.State_.REJECTED, x = new TypeError("Promise cannot resolve to itself")), this.state_ = goog.Promise.State_.BLOCKED, goog.Promise.maybeThen_(x, this.unblockAndFulfill_, this.unblockAndReject_, this) || (this.result_ = x, this.state_ = state, this.parent_ = null, this.scheduleCallbacks_(), state != goog.Promise.State_.REJECTED || x instanceof goog.Promise.CancellationError || goog.Promise.addUnhandledRejection_(this, + x))); +}; +goog.Promise.maybeThen_ = function(value, onFulfilled, onRejected, context) { + if (value instanceof goog.Promise) { + return value.thenVoid(onFulfilled, onRejected, context), !0; + } + if (goog.Thenable.isImplementedBy(value)) { + return value.then(onFulfilled, onRejected, context), !0; + } + if (goog.isObject(value)) { + try { + var then = value.then; + if (goog.isFunction(then)) { + return goog.Promise.tryThen_(value, then, onFulfilled, onRejected, context), !0; } + } catch (e) { + return onRejected.call(context, e), !0; } - this.result_ = x; - this.state_ = state; - this.parent_ = null; - this.scheduleCallbacks_(); - state != goog.Promise.State_.REJECTED || x instanceof goog.Promise.CancellationError || goog.Promise.addUnhandledRejection_(this, x); } + return !1; }; -goog.Promise.prototype.tryThen_ = function(thenable, then) { - this.state_ = goog.Promise.State_.BLOCKED; - var promise = this, called = !1, resolve = function(value) { - called || (called = !0, promise.unblockAndFulfill_(value)); +goog.Promise.tryThen_ = function(thenable, then, onFulfilled, onRejected, context) { + var called = !1, resolve = function(value) { + called || (called = !0, onFulfilled.call(context, value)); }, reject = function(reason) { - called || (called = !0, promise.unblockAndReject_(reason)); + called || (called = !0, onRejected.call(context, reason)); }; try { then.call(thenable, resolve, reject); @@ -4737,7 +4749,9 @@ goog.net.jsloader.load = function(uri, opt_options) { goog.net.jsloader.cleanup_(script, !0, timeout); deferred.errback(new goog.net.jsloader.Error(goog.net.jsloader.ErrorCode.LOAD_ERROR, "Error while loading script " + uri)); }; - goog.dom.setProperties(script, {type:"text/javascript", charset:"UTF-8", src:uri}); + var properties = options.attributes || {}; + goog.object.extend(properties, {type:"text/javascript", charset:"UTF-8", src:uri}); + goog.dom.setProperties(script, properties); goog.net.jsloader.getScriptParentElement_(doc).appendChild(script); return deferred; }; @@ -7059,10 +7073,12 @@ goog.uri.utils.assertNoFragmentsOrQueries_ = function(uri) { } }; goog.uri.utils.parseQueryData = function(encodedQuery, callback) { - for (var pairs = encodedQuery.split("&"), i = 0;i < pairs.length;i++) { - var indexOfEquals = pairs[i].indexOf("="), name = null, value = null; - 0 <= indexOfEquals ? (name = pairs[i].substring(0, indexOfEquals), value = pairs[i].substring(indexOfEquals + 1)) : name = pairs[i]; - callback(name, value ? goog.string.urlDecode(value) : ""); + if (encodedQuery) { + for (var pairs = encodedQuery.split("&"), i = 0;i < pairs.length;i++) { + var indexOfEquals = pairs[i].indexOf("="), name = null, value = null; + 0 <= indexOfEquals ? (name = pairs[i].substring(0, indexOfEquals), value = pairs[i].substring(indexOfEquals + 1)) : name = pairs[i]; + callback(name, value ? goog.string.urlDecode(value) : ""); + } } }; goog.uri.utils.appendQueryData_ = function(buffer) { @@ -8047,6 +8063,7 @@ var ee = {data:{}}; ee.data.apiBaseUrl_ = null; ee.data.tileBaseUrl_ = null; ee.data.xsrfToken_ = null; +ee.data.paramAugmenter_ = goog.functions.identity; ee.data.authToken_ = null; ee.data.authClientId_ = null; ee.data.authScopes_ = []; @@ -8054,7 +8071,7 @@ ee.data.AUTH_SCOPE_ = "https://www.googleapis.com/auth/earthengine.readonly"; ee.data.AUTH_LIBRARY_URL_ = "https://apis.google.com/js/client.js"; ee.data.initialized_ = !1; ee.data.deadlineMs_ = 0; -ee.data.DEFAULT_API_BASE_URL_ = "/api"; +ee.data.DEFAULT_API_BASE_URL_ = "https://earthengine.googleapis.com/api"; ee.data.DEFAULT_TILE_BASE_URL_ = "https://earthengine.googleapis.com"; ee.data.TaskUpdateActions = {CANCEL:"CANCEL", UPDATE:"UPDATE"}; ee.data.initialize = function(opt_apiBaseUrl, opt_tileBaseUrl, opt_xsrfToken) { @@ -8067,9 +8084,6 @@ ee.data.reset = function() { ee.data.apiBaseUrl_ = null; ee.data.tileBaseUrl_ = null; ee.data.xsrfToken_ = null; - ee.data.authClientId_ = null; - ee.data.authScopes_ = []; - ee.data.authToken_ = null; ee.data.initialized_ = !1; }; ee.data.authenticate = function(clientId, success, opt_error, opt_extraScopes, opt_onImmediateFailed) { @@ -8077,26 +8091,33 @@ ee.data.authenticate = function(clientId, success, opt_error, opt_extraScopes, o opt_extraScopes && (goog.array.extend(scopes, opt_extraScopes), goog.array.removeDuplicates(scopes)); ee.data.authClientId_ = clientId; ee.data.authScopes_ = scopes; - var onImmediateFailed = opt_onImmediateFailed || goog.partial(ee.data.authenticateViaPopup, success, opt_error); - if (goog.isObject(goog.global.gapi) && goog.isObject(goog.global.gapi.auth) && goog.isFunction(goog.global.gapi.auth.authorize)) { + goog.isNull(clientId) ? ee.data.authToken_ = null : ee.data.ensureAuthLibLoaded_(function() { + var onImmediateFailed = opt_onImmediateFailed || goog.partial(ee.data.authenticateViaPopup, success, opt_error); ee.data.refreshAuthToken_(success, opt_error, onImmediateFailed); - } else { - for (var callbackName = goog.now().toString(36);callbackName in goog.global;) { - callbackName += "_"; - } - goog.global[callbackName] = function() { - delete goog.global[callbackName]; - ee.data.refreshAuthToken_(success, opt_error, onImmediateFailed); - }; - goog.net.jsloader.load(ee.data.AUTH_LIBRARY_URL_ + "?onload=" + callbackName); - } + }); }; ee.data.authenticateViaPopup = function(opt_success, opt_error) { goog.global.gapi.auth.authorize({client_id:ee.data.authClientId_, immediate:!1, scope:ee.data.authScopes_.join(" ")}, goog.partial(ee.data.handleAuthResult_, opt_success, opt_error)); }; +ee.data.setAuthToken = function(clientId, tokenType, accessToken, expiresIn, opt_extraScopes, opt_callback, opt_updateAuthLibrary) { + var scopes = [ee.data.AUTH_SCOPE_]; + opt_extraScopes && (goog.array.extend(scopes, opt_extraScopes), goog.array.removeDuplicates(scopes)); + ee.data.authClientId_ = clientId; + ee.data.authScopes_ = scopes; + var tokenObject = {token_type:tokenType, access_token:accessToken, state:scopes.join(" "), expires_in:expiresIn}; + ee.data.handleAuthResult_(void 0, void 0, tokenObject); + !1 === opt_updateAuthLibrary ? opt_callback && opt_callback() : ee.data.ensureAuthLibLoaded_(function() { + goog.global.gapi.auth.setToken(tokenObject); + opt_callback && opt_callback(); + }); +}; ee.data.setDeadline = function(milliseconds) { ee.data.deadlineMs_ = milliseconds; }; +ee.data.setParamAugmenter = function(augmenter) { + ee.data.paramAugmenter_ = augmenter || goog.functions.identity; +}; +goog.exportSymbol("ee.data.setParamAugmenter", ee.data.setParamAugmenter); ee.data.getApiBaseUrl = function() { return ee.data.apiBaseUrl_; }; @@ -8106,6 +8127,15 @@ ee.data.getTileBaseUrl = function() { ee.data.getXsrfToken = function() { return ee.data.xsrfToken_; }; +ee.data.getAuthToken = function() { + return ee.data.authToken_; +}; +ee.data.getAuthClientId = function() { + return ee.data.authClientId_; +}; +ee.data.getAuthScopes = function() { + return ee.data.authScopes_; +}; ee.data.getInfo = function(id, opt_callback) { return ee.data.send_("/info", (new goog.Uri.QueryData).add("id", id), opt_callback); }; @@ -8249,6 +8279,7 @@ ee.data.send_ = function(path, params, opt_callback$$0, opt_method) { ee.data.initialize(); var method = opt_method || "POST", headers = {"Content-Type":"application/x-www-form-urlencoded"}; goog.isDefAndNotNull(ee.data.authToken_) && (headers.Authorization = ee.data.authToken_); + params = ee.data.paramAugmenter_(params || new goog.Uri.QueryData, path); goog.isDefAndNotNull(ee.data.xsrfToken_) && (headers["X-XSRF-Token"] = ee.data.xsrfToken_); var handleResponse = function(status, contentType, responseText, opt_callback) { var response, data, errorMessage; @@ -8299,6 +8330,24 @@ ee.data.send_ = function(path, params, opt_callback$$0, opt_method) { } return handleResponse(xmlHttp.status, contentType$$0, xmlHttp.responseText, null); }; +ee.data.ensureAuthLibLoaded_ = function(callback) { + var done = function() { + goog.global.gapi.config.update("client/cors", !0); + callback(); + }; + if (goog.isObject(goog.global.gapi) && goog.isObject(goog.global.gapi.auth) && goog.isFunction(goog.global.gapi.auth.authorize)) { + done(); + } else { + for (var callbackName = goog.now().toString(36);callbackName in goog.global;) { + callbackName += "_"; + } + goog.global[callbackName] = function() { + delete goog.global[callbackName]; + done(); + }; + goog.net.jsloader.load(ee.data.AUTH_LIBRARY_URL_ + "?onload=" + callbackName); + } +}; ee.data.refreshAuthToken_ = function(opt_success, opt_error, opt_onImmediateFailed) { var authArgs = {client_id:ee.data.authClientId_, immediate:!0, scope:ee.data.authScopes_.join(" ")}; goog.global.gapi.auth.authorize(authArgs, function(result) { @@ -8306,7 +8355,7 @@ ee.data.refreshAuthToken_ = function(opt_success, opt_error, opt_onImmediateFail }); }; ee.data.handleAuthResult_ = function(success, error, result) { - result.access_token ? (ee.data.authToken_ = result.token_type + " " + result.access_token, setTimeout(ee.data.refreshAuthToken_, 1E3 * result.expires_in / 2), success && success()) : error && error(result.error || "Unknown error."); + result.access_token ? (ee.data.authToken_ = result.token_type + " " + result.access_token, isFinite(result.expires_in) && setTimeout(ee.data.refreshAuthToken_, 1E3 * result.expires_in / 2), success && success()) : error && error(result.error || "Unknown error."); }; ee.data.makeRequest_ = function(params) { var request = new goog.Uri.QueryData, item; @@ -8317,6 +8366,7 @@ ee.data.makeRequest_ = function(params) { }; ee.data.setupMockSend = function(opt_calls) { function getResponse(url, method, data) { + url = url.replace(ee.data.apiBaseUrl_, ""); var response; if (url in calls) { response = calls[url]; @@ -8589,6 +8639,12 @@ ee.ComputedObject.prototype.isVariable = function() { ee.ComputedObject.prototype.name = function() { return "ComputedObject"; }; +ee.ComputedObject.prototype.aside = function(func, var_args) { + var args = goog.array.clone(arguments); + args[0] = this; + func.apply(goog.global, args); + return this; +}; ee.ComputedObject.prototype.castInternal = function(obj) { if (obj instanceof this.constructor) { return obj; @@ -9382,7 +9438,7 @@ ee.Geometry.Rectangle = function(coords, opt_proj, opt_geodesic, opt_maxError) { if (!(this instanceof ee.Geometry.Rectangle)) { return ee.Geometry.createInstance_(ee.Geometry.Rectangle, arguments); } - var init = ee.Geometry.parseArgs_("Polygon", 2, arguments); + var init = ee.Geometry.parseArgs_("Rectangle", 2, arguments); if (!(init instanceof ee.ComputedObject)) { var xy = init.coordinates; if (2 != xy.length) { @@ -9390,6 +9446,7 @@ ee.Geometry.Rectangle = function(coords, opt_proj, opt_geodesic, opt_maxError) { } var x1 = xy[0][0], y1 = xy[0][1], x2 = xy[1][0], y2 = xy[1][1]; init.coordinates = [[[x1, y2], [x1, y1], [x2, y1], [x2, y2]]]; + init.type = "Polygon"; } ee.Geometry.call(this, init); }; @@ -9872,7 +9929,7 @@ ee.FeatureCollection.prototype.getDownloadURL = function(opt_format, opt_selecto request.table = this.serialize(); opt_format && (request.format = opt_format.toUpperCase()); opt_filename && (request.filename = opt_filename); - opt_selectors && (request.selectors = opt_selectors); + opt_selectors && (goog.isArrayLike(opt_selectors) && (opt_selectors = opt_selectors.join(",")), request.selectors = opt_selectors); if (opt_callback) { ee.data.getTableDownloadId(request, function(downloadId, error) { downloadId ? opt_callback(ee.data.makeTableDownloadUrl(downloadId)) : opt_callback(null, error); @@ -10059,6 +10116,11 @@ ee.Image.prototype.clip = function(geometry) { } return ee.ApiFunction._call("Image.clip", this, geometry); }; +ee.Image.prototype.rename = function(var_args) { + var names; + names = 1 != arguments.length || ee.Types.isString(arguments[0]) ? goog.array.clone(arguments) : arguments[0]; + return ee.ApiFunction._call("Image.rename", this, names); +}; ee.Image.prototype.name = function() { return "Image"; }; @@ -11281,7 +11343,9 @@ goog.events.EventHandler.prototype.unlistenWithWrapper = function(src, wrapper, return this; }; goog.events.EventHandler.prototype.removeAll = function() { - goog.object.forEach(this.keys_, goog.events.unlistenByKey); + goog.object.forEach(this.keys_, function(listenerObj, key) { + this.keys_.hasOwnProperty(key) && goog.events.unlistenByKey(listenerObj); + }, this); this.keys_ = {}; }; goog.events.EventHandler.prototype.disposeInternal = function() { @@ -11729,12 +11793,6 @@ ee.MapTileManager.prototype.disposeInternal = function() { requests.clear(); this.requests_ = null; }; -ee.MapTileManager.Event_ = function(type, target, id, imageLoader) { - goog.events.Event.call(this, type, target); - this.id = id; - this.imageLoader = imageLoader; -}; -goog.inherits(ee.MapTileManager.Event_, goog.events.Event); ee.MapTileManager.Request_ = function(id, url, opt_imageEventCallback, opt_requestCompleteCallback, opt_maxRetries) { goog.Disposable.call(this); this.id_ = id; @@ -11886,8 +11944,14 @@ ee.MapLayerOverlay = function(url, mapId, token, init) { }; goog.inherits(ee.MapLayerOverlay, goog.events.EventTarget); ee.MapLayerOverlay.EventType = {TILE_LOADED:"tileevent"}; +ee.MapLayerOverlay.prototype.addTileCallback = function(callback) { + return goog.events.listen(this, ee.MapLayerOverlay.EventType.TILE_LOADED, callback); +}; +ee.MapLayerOverlay.prototype.removeTileCallback = function(callbackId) { + goog.events.unlistenByKey(callbackId); +}; ee.MapLayerOverlay.prototype.dispatchTileEvent_ = function() { - this.dispatchEvent(new ee.TileEvent_(this.tilesLoading_.length)); + this.dispatchEvent(new ee.TileEvent(this.tilesLoading_.length)); }; ee.MapLayerOverlay.prototype.getTile = function(coord, zoom, ownerDocument) { var maxCoord = 1 << zoom; @@ -11942,11 +12006,11 @@ ee.MapLayerOverlay.prototype.handleImageCompleted_ = function(div, tileId, e) { this.dispatchTileEvent_(); } }; -ee.TileEvent_ = function(count) { +ee.TileEvent = function(count) { goog.events.Event.call(this, ee.MapLayerOverlay.EventType.TILE_LOADED); this.count = count; }; -goog.inherits(ee.TileEvent_, goog.events.Event); +goog.inherits(ee.TileEvent, goog.events.Event); ee.SavedFunction = function(path, signature) { if (!(this instanceof ee.SavedFunction)) { return new ee.SavedFunction(path, signature); diff --git a/javascript/src/computedobject.js b/javascript/src/computedobject.js index 45029945a..fcef8e199 100644 --- a/javascript/src/computedobject.js +++ b/javascript/src/computedobject.js @@ -8,6 +8,7 @@ goog.provide('ee.ComputedObject'); goog.require('ee.Encodable'); goog.require('ee.Serializer'); goog.require('ee.data'); +goog.require('goog.array'); @@ -158,6 +159,29 @@ ee.ComputedObject.prototype.name = function() { }; +/** + * Calls a function passing this object as the first argument, and returning + * itself. Convenient e.g. when debugging: + * + * var c = ee.ImageCollection('foo').aside(print) + * .filterDate('2001-01-01', '2002-01-01').aside(print, 'In 2001') + * .filterBounds(geom).aside(print, 'In region') + * .aside(Map.addLayer, {min: 0, max: 142}, 'Filtered') + * .select('a', 'b'); + * + * @param {Function} func The function to call. + * @param {...*} var_args Any extra arguments to pass to the function. + * @return {ee.ComputedObject} The same object, for chaining. + * @export + */ +ee.ComputedObject.prototype.aside = function(func, var_args) { + var args = goog.array.clone(arguments); + args[0] = this; + func.apply(goog.global, args); + return this; +}; + + /** * Cast a ComputedObject to a new instance of the same class as this. * @param {ee.ComputedObject} obj The object to cast. diff --git a/javascript/src/data.js b/javascript/src/data.js index ac5b02ed8..de782e577 100644 --- a/javascript/src/data.js +++ b/javascript/src/data.js @@ -43,6 +43,7 @@ goog.provide('ee.data.VideoTaskConfig'); goog.require('goog.Uri'); goog.require('goog.array'); +goog.require('goog.functions'); goog.require('goog.json'); goog.require('goog.net.XhrIo'); goog.require('goog.net.XmlHttp'); @@ -74,6 +75,15 @@ ee.data.tileBaseUrl_ = null; ee.data.xsrfToken_ = null; +/** + * @type {function(!goog.Uri.QueryData, string): !goog.Uri.QueryData} A function + * used to transform parameters right before they are sent to the server. + * Takes the URL of the request as the second argument. + * @private + */ +ee.data.paramAugmenter_ = goog.functions.identity; + + /** * @private {string?} An OAuth2 token to use for authenticating EE API calls. */ @@ -125,7 +135,7 @@ ee.data.deadlineMs_ = 0; * @private * @const */ -ee.data.DEFAULT_API_BASE_URL_ = '/api'; +ee.data.DEFAULT_API_BASE_URL_ = 'https://earthengine.googleapis.com/api'; /** @@ -183,9 +193,6 @@ ee.data.reset = function() { ee.data.apiBaseUrl_ = null; ee.data.tileBaseUrl_ = null; ee.data.xsrfToken_ = null; - ee.data.authClientId_ = null; - ee.data.authScopes_ = []; - ee.data.authToken_ = null; ee.data.initialized_ = false; }; @@ -234,24 +241,17 @@ ee.data.authenticate = function( ee.data.authClientId_ = clientId; ee.data.authScopes_ = scopes; + if (goog.isNull(clientId)) { + ee.data.authToken_ = null; + return; + } + // Start the authentication flow as soon as we have the auth library. - var onImmediateFailed = opt_onImmediateFailed || goog.partial( - ee.data.authenticateViaPopup, success, opt_error); - if (goog.isObject(goog.global['gapi']) && - goog.isObject(goog.global['gapi']['auth']) && - goog.isFunction(goog.global['gapi']['auth']['authorize'])) { + ee.data.ensureAuthLibLoaded_(function() { + var onImmediateFailed = opt_onImmediateFailed || goog.partial( + ee.data.authenticateViaPopup, success, opt_error); ee.data.refreshAuthToken_(success, opt_error, onImmediateFailed); - } else { - // The library is not loaded; load it now. - var callbackName = goog.now().toString(36); - while (callbackName in goog.global) callbackName += '_'; - goog.global[callbackName] = function() { - delete goog.global[callbackName]; - ee.data.refreshAuthToken_(success, opt_error, onImmediateFailed); - }; - goog.net.jsloader.load( - ee.data.AUTH_LIBRARY_URL_ + '?onload=' + callbackName); - } + }); }; @@ -265,6 +265,7 @@ ee.data.authenticate = function( * succeeds. * @param {function(string)=} opt_error The function to call if authentication * fails, passing the error message. + * @export */ ee.data.authenticateViaPopup = function(opt_success, opt_error) { goog.global['gapi']['auth']['authorize']({ @@ -275,6 +276,53 @@ ee.data.authenticateViaPopup = function(opt_success, opt_error) { }; +/** + * Configures client-side authentication of EE API calls by providing a + * current OAuth2 token to use. This is a replacement for expected + * ee.data.authenticate() when a token is already available. + * @param {string} clientId The OAuth client ID associated with the token. + * @param {string} tokenType The OAuth2 token type, e.g. "Bearer". + * @param {string} accessToken The token string, typically looking something + * like "ya29.hgGGO...OtA". + * @param {number} expiresIn The number of seconds after which this token + * expires. + * @param {!Array=} opt_extraScopes Extra OAuth scopes associated with + * the token. + * @param {function()=} opt_callback A function to call when the token is set. + * @param {boolean=} opt_updateAuthLibrary Whether to also update the token + * set in the Google API Client Library for JavaScript. Defaults to true. + * @export + */ +ee.data.setAuthToken = function(clientId, tokenType, accessToken, + expiresIn, opt_extraScopes, opt_callback, + opt_updateAuthLibrary) { + var scopes = [ee.data.AUTH_SCOPE_]; + if (opt_extraScopes) { + goog.array.extend(scopes, opt_extraScopes); + goog.array.removeDuplicates(scopes); + } + ee.data.authClientId_ = clientId; + ee.data.authScopes_ = scopes; + + var tokenObject = { + 'token_type': tokenType, + 'access_token': accessToken, + 'state': scopes.join(' '), + 'expires_in': expiresIn + }; + ee.data.handleAuthResult_(undefined, undefined, tokenObject); + + if (opt_updateAuthLibrary === false) { + if (opt_callback) opt_callback(); + } else { + ee.data.ensureAuthLibLoaded_(function() { + goog.global['gapi']['auth']['setToken'](tokenObject); + if (opt_callback) opt_callback(); + }); + } +}; + + /** * Sets the timeout length for asynchronous API requests. * @@ -287,6 +335,20 @@ ee.data.setDeadline = function(milliseconds) { }; +/** + * Sets a function used to transform request parameters. + * + * @param {?function(!goog.Uri.QueryData, string): !goog.Uri.QueryData} + * augmenter A function used to transform request parameters right + * before they are sent to the server. Takes the URL of the request + * as the second argument. + */ +ee.data.setParamAugmenter = function(augmenter) { + ee.data.paramAugmenter_ = augmenter || goog.functions.identity; +}; +goog.exportSymbol('ee.data.setParamAugmenter', ee.data.setParamAugmenter); + + /** * Returns the base URL used for API calls. * @@ -320,6 +382,42 @@ ee.data.getXsrfToken = function() { }; +/** + * Returns the current OAuth token; null unless ee.data.setAuthToken() or + * ee.data.authorize() previously suceeded. + * + * @return {?string} The string to pass in the Authorization header of XHRs. + * @export + */ +ee.data.getAuthToken = function() { + return ee.data.authToken_; +}; + + +/** + * Returns the current OAuth client ID; null unless ee.data.setAuthToken() or + * ee.data.authorize() previously suceeded. + * + * @return {?string} The OAuth2 client ID for client-side authentication. + * @export + */ +ee.data.getAuthClientId = function() { + return ee.data.authClientId_; +}; + + +/** + * Returns the current OAuth scopes; empty unless ee.data.setAuthToken() or + * ee.data.authorize() previously suceeded. + * + * @return {!Array} The OAuth2 scopes for client-side authentication. + * @export + */ +ee.data.getAuthScopes = function() { + return ee.data.authScopes_; +}; + + /** * Load info for an asset, given an asset id. * @@ -936,6 +1034,9 @@ ee.data.send_ = function(path, params, opt_callback, opt_method) { headers['Authorization'] = ee.data.authToken_; } + // Apply any custom param augmentation. + params = ee.data.paramAugmenter_(params || new goog.Uri.QueryData(), path); + // XSRF protection for a server-side API proxy. if (goog.isDefAndNotNull(ee.data.xsrfToken_)) { headers['X-XSRF-Token'] = ee.data.xsrfToken_; @@ -1033,6 +1134,35 @@ ee.data.send_ = function(path, params, opt_callback, opt_method) { }; +/** + * Ensures that the Google API Client Library for JavaScript is loaded. + * @param {function()} callback The function to call when the library is ready. + * @private + */ +ee.data.ensureAuthLibLoaded_ = function(callback) { + var done = function() { + // Speed up auth request by using CORS instead of an iframe. + goog.global['gapi']['config']['update']('client/cors', true); + callback(); + }; + if (goog.isObject(goog.global['gapi']) && + goog.isObject(goog.global['gapi']['auth']) && + goog.isFunction(goog.global['gapi']['auth']['authorize'])) { + done(); + } else { + // The library is not loaded; load it now. + var callbackName = goog.now().toString(36); + while (callbackName in goog.global) callbackName += '_'; + goog.global[callbackName] = function() { + delete goog.global[callbackName]; + done(); + }; + goog.net.jsloader.load( + ee.data.AUTH_LIBRARY_URL_ + '?onload=' + callbackName); + } +}; + + /** * Retrieves a new OAuth2 token for the currently configured ID and scopes. * @@ -1082,7 +1212,9 @@ ee.data.handleAuthResult_ = function(success, error, result) { // Set up a refresh timer. This is necessary because we cannot refresh // synchronously, but since we want to allow synchronous API requests, // something must ensure that the auth token is always valid. - setTimeout(ee.data.refreshAuthToken_, result['expires_in'] * 1000 / 2); + if (isFinite(result['expires_in'])) { + setTimeout(ee.data.refreshAuthToken_, result['expires_in'] * 1000 / 2); + } if (success) success(); } else if (error) { error(result['error'] || 'Unknown error.'); @@ -1122,6 +1254,7 @@ ee.data.setupMockSend = function(opt_calls) { // If it's an object it has fields specifying more details. // If there's nothing set for this url, throw. function getResponse(url, method, data) { + url = url.replace(ee.data.apiBaseUrl_, ''); var response; if (url in calls) { response = calls[url]; diff --git a/javascript/src/deps.js b/javascript/src/deps.js index bb824ad78..f03784322 100644 --- a/javascript/src/deps.js +++ b/javascript/src/deps.js @@ -6,9 +6,9 @@ goog.addDependency('../../geo/gestalt/client/javascript/apifunction.js', ['ee.ApiFunction'], ['ee.ComputedObject', 'ee.Function', 'ee.Types', 'ee.data', 'goog.object'], false); goog.addDependency('../../geo/gestalt/client/javascript/apitestcase.js', ['ee.ApiTestCase', 'ee.ApiTestCase.BUILTIN_FUNCTIONS'], ['ee', 'ee.data', 'goog.json', 'goog.string', 'goog.testing.TestCase'], false); goog.addDependency('../../geo/gestalt/client/javascript/collection.js', ['ee.Collection'], ['ee.ApiFunction', 'ee.Element', 'ee.Filter'], false); -goog.addDependency('../../geo/gestalt/client/javascript/computedobject.js', ['ee.ComputedObject'], ['ee.Encodable', 'ee.Serializer', 'ee.data'], false); +goog.addDependency('../../geo/gestalt/client/javascript/computedobject.js', ['ee.ComputedObject'], ['ee.Encodable', 'ee.Serializer', 'ee.data', 'goog.array'], false); goog.addDependency('../../geo/gestalt/client/javascript/customfunction.js', ['ee.CustomFunction'], ['ee.ComputedObject', 'ee.Function', 'ee.Number', 'ee.Serializer', 'ee.String', 'ee.Types', 'goog.array', 'goog.object'], false); -goog.addDependency('../../geo/gestalt/client/javascript/data.js', ['ee.data', 'ee.data.AbstractTaskConfig', 'ee.data.AlgorithmArgument', 'ee.data.AlgorithmSignature', 'ee.data.AlgorithmsRegistry', 'ee.data.AssetAcl', 'ee.data.AssetAclUpdate', 'ee.data.AssetDescription', 'ee.data.AssetList', 'ee.data.AssetType', 'ee.data.Band', 'ee.data.BandDescription', 'ee.data.DownloadId', 'ee.data.FeatureCollectionDescription', 'ee.data.FeatureVisualizationParameters', 'ee.data.FileSource', 'ee.data.Fileset', 'ee.data.FolderDescription', 'ee.data.GMEProject', 'ee.data.GeoJSONFeature', 'ee.data.GeoJSONGeometry', 'ee.data.ImageCollectionDescription', 'ee.data.ImageDescription', 'ee.data.ImageTaskConfig', 'ee.data.ImageVisualizationParameters', 'ee.data.IngestionRequest', 'ee.data.MapId', 'ee.data.PixelTypeDescription', 'ee.data.ProcessingResponse', 'ee.data.RawMapId', 'ee.data.ShortAssetDescription', 'ee.data.TableTaskConfig', 'ee.data.TaskListResponse', 'ee.data.TaskStatus', 'ee.data.TaskUpdateActions', 'ee.data.ThumbnailId', 'ee.data.VideoTaskConfig'], ['goog.Uri', 'goog.array', 'goog.json', 'goog.net.XhrIo', 'goog.net.XmlHttp', 'goog.net.jsloader', 'goog.object', 'goog.string'], false); +goog.addDependency('../../geo/gestalt/client/javascript/data.js', ['ee.data', 'ee.data.AbstractTaskConfig', 'ee.data.AlgorithmArgument', 'ee.data.AlgorithmSignature', 'ee.data.AlgorithmsRegistry', 'ee.data.AssetAcl', 'ee.data.AssetAclUpdate', 'ee.data.AssetDescription', 'ee.data.AssetList', 'ee.data.AssetType', 'ee.data.Band', 'ee.data.BandDescription', 'ee.data.DownloadId', 'ee.data.FeatureCollectionDescription', 'ee.data.FeatureVisualizationParameters', 'ee.data.FileSource', 'ee.data.Fileset', 'ee.data.FolderDescription', 'ee.data.GMEProject', 'ee.data.GeoJSONFeature', 'ee.data.GeoJSONGeometry', 'ee.data.ImageCollectionDescription', 'ee.data.ImageDescription', 'ee.data.ImageTaskConfig', 'ee.data.ImageVisualizationParameters', 'ee.data.IngestionRequest', 'ee.data.MapId', 'ee.data.PixelTypeDescription', 'ee.data.ProcessingResponse', 'ee.data.RawMapId', 'ee.data.ShortAssetDescription', 'ee.data.TableTaskConfig', 'ee.data.TaskListResponse', 'ee.data.TaskStatus', 'ee.data.TaskUpdateActions', 'ee.data.ThumbnailId', 'ee.data.VideoTaskConfig'], ['goog.Uri', 'goog.array', 'goog.functions', 'goog.json', 'goog.net.XhrIo', 'goog.net.XmlHttp', 'goog.net.jsloader', 'goog.object', 'goog.string'], false); goog.addDependency('../../geo/gestalt/client/javascript/date.js', ['ee.Date'], ['ee.ApiFunction', 'ee.ComputedObject', 'ee.Types'], false); goog.addDependency('../../geo/gestalt/client/javascript/deserializer.js', ['ee.Deserializer'], ['ee.ApiFunction', 'ee.ComputedObject', 'ee.CustomFunction', 'ee.Date', 'ee.Encodable', 'ee.Function', 'ee.Geometry', 'goog.array', 'goog.json', 'goog.object'], false); goog.addDependency('../../geo/gestalt/client/javascript/dictionary.js', ['ee.Dictionary'], ['ee.ApiFunction', 'ee.ComputedObject', 'ee.Types'], false); @@ -23,7 +23,7 @@ goog.addDependency('../../geo/gestalt/client/javascript/geometry.js', ['ee.Geome goog.addDependency('../../geo/gestalt/client/javascript/image.js', ['ee.Image'], ['ee.ApiFunction', 'ee.ComputedObject', 'ee.Element', 'ee.Function', 'ee.Geometry', 'ee.Types', 'ee.data', 'goog.array', 'goog.json', 'goog.object'], false); goog.addDependency('../../geo/gestalt/client/javascript/imagecollection.js', ['ee.ImageCollection'], ['ee.ApiFunction', 'ee.Collection', 'ee.ComputedObject', 'ee.Image', 'ee.List', 'ee.Types', 'goog.array'], false); goog.addDependency('../../geo/gestalt/client/javascript/list.js', ['ee.List'], ['ee.ApiFunction', 'ee.ComputedObject', 'goog.array'], false); -goog.addDependency('../../geo/gestalt/client/javascript/maplayeroverlay.js', ['ee.MapLayerOverlay'], ['ee.MapTileManager', 'goog.array', 'goog.dom', 'goog.events.Event', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.iter', 'goog.net.EventType', 'goog.structs.Set', 'goog.style'], false); +goog.addDependency('../../geo/gestalt/client/javascript/maplayeroverlay.js', ['ee.MapLayerOverlay', 'ee.TileEvent'], ['ee.MapTileManager', 'goog.array', 'goog.dom', 'goog.events', 'goog.events.Event', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.iter', 'goog.net.EventType', 'goog.structs.Set', 'goog.style'], false); goog.addDependency('../../geo/gestalt/client/javascript/maptilemanager.js', ['ee.MapTileManager'], ['goog.Disposable', 'goog.array', 'goog.events', 'goog.events.Event', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.net.EventType', 'goog.net.ImageLoader', 'goog.structs.Map', 'goog.structs.PriorityPool'], false); goog.addDependency('../../geo/gestalt/client/javascript/number.js', ['ee.Number'], ['ee.ApiFunction', 'ee.ComputedObject'], false); goog.addDependency('../../geo/gestalt/client/javascript/package.js', ['ee.Package'], ['ee.ApiFunction', 'ee.CustomFunction', 'ee.SavedFunction', 'ee.data'], false); diff --git a/javascript/src/ee_api_js.externs.js b/javascript/src/ee_api_js.externs.js index 35bc61d1b..bbfdce60a 100644 --- a/javascript/src/ee_api_js.externs.js +++ b/javascript/src/ee_api_js.externs.js @@ -98,6 +98,13 @@ ee.Collection.prototype.map = function(algorithm, opt_dropNulls) { ee.Collection.prototype.sort = function(property, opt_ascending) { }; ee.ComputedObject; +/** + * @param {(Function|null)} func + * @param {...*} var_args + * @return {(ee.ComputedObject|null)} + */ +ee.ComputedObject.prototype.aside = function(func, var_args) { +}; /** * @param {function (?, string=): ?=} opt_callback * @return {*} @@ -707,6 +714,12 @@ ee.Image.prototype.getMap = function(opt_visParams, opt_callback) { */ ee.Image.prototype.getThumbURL = function(params, opt_callback) { }; +/** + * @param {...(Object|null|string)} var_args + * @return {(ee.Image|null)} + */ +ee.Image.prototype.rename = function(var_args) { +}; /** * @param {...*} var_args * @return {(ee.Image|null)} @@ -774,6 +787,12 @@ ee.List = function(list) { */ ee.MapLayerOverlay = function(url, mapId, token, init) { }; +/** + * @param {function ((ee.TileEvent|null)): ?} callback + * @return {Object} + */ +ee.MapLayerOverlay.prototype.addTileCallback = function(callback) { +}; /** * @param {(google.maps.Point|null)} coord * @param {number} zoom @@ -788,6 +807,12 @@ ee.MapLayerOverlay.prototype.getTile = function(coord, zoom, ownerDocument) { */ ee.MapLayerOverlay.prototype.releaseTile = function(tileDiv) { }; +/** + * @param {Object} callbackId + * @return {undefined} + */ +ee.MapLayerOverlay.prototype.removeTileCallback = function(callbackId) { +}; /** * @param {number} opacity * @return {undefined} @@ -857,6 +882,13 @@ ee.data; */ ee.data.authenticate = function(clientId, success, opt_error, opt_extraScopes, opt_onImmediateFailed) { }; +/** + * @param {function (): ?=} opt_success + * @param {function (string): ?=} opt_error + * @return {undefined} + */ +ee.data.authenticateViaPopup = function(opt_success, opt_error) { +}; /** * @param {string} taskId * @param {function ({note: (string|undefined), started: string}, string=): ?=} opt_callback @@ -906,6 +938,21 @@ ee.data.getAssetAcl = function(assetId, opt_callback) { */ ee.data.getAssetRoots = function(opt_callback) { }; +/** + * @return {(null|string)} + */ +ee.data.getAuthClientId = function() { +}; +/** + * @return {Array} + */ +ee.data.getAuthScopes = function() { +}; +/** + * @return {(null|string)} + */ +ee.data.getAuthToken = function() { +}; /** * @param {(Object|null)} params * @param {function ({docid: string, token: string}, string=): ?=} opt_callback @@ -1028,12 +1075,30 @@ ee.data.prepareValue = function(taskId, params, opt_callback) { */ ee.data.setAssetAcl = function(assetId, aclUpdate, opt_callback) { }; +/** + * @param {string} clientId + * @param {string} tokenType + * @param {string} accessToken + * @param {number} expiresIn + * @param {Array=} opt_extraScopes + * @param {function (): ?=} opt_callback + * @param {boolean=} opt_updateAuthLibrary + * @return {undefined} + */ +ee.data.setAuthToken = function(clientId, tokenType, accessToken, expiresIn, opt_extraScopes, opt_callback, opt_updateAuthLibrary) { +}; /** * @param {number} milliseconds * @return {undefined} */ ee.data.setDeadline = function(milliseconds) { }; +/** + * @param {(function (goog.Uri.QueryData, string): goog.Uri.QueryData|null)} augmenter + * @return {undefined} + */ +ee.data.setParamAugmenter = function(augmenter) { +}; /** * @param {string} taskId * @param {{bands: (Array|undefined), filesets: Array, name: string}} request diff --git a/javascript/src/examples/faq/gettingStarted01.js b/javascript/src/examples/faq/gettingStarted01.js deleted file mode 100644 index 1c527746f..000000000 --- a/javascript/src/examples/faq/gettingStarted01.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @fileoverview Earth Engine Developer's Guide examples - * from 'Earth Engine Algorithms' section. - */ - -// placeholded imports -var dem = ee.Image('USGS/SRTMGL1_003'); -var collection1 = ee.ImageCollection('NOAA/DMSP-OLS/NIGHTTIME_LIGHTS'); - -// dummy variables -var image1 = ee.Image(1); -var image2 = ee.Image(2); - -// [START image_add_method] -var image3 = image1.add(image2); -// [END image_add_method] - -// [START ee_algo] -var terrainImage = ee.Algorithms.Terrain(dem); -// [END ee_algo] - -// [START user_function] -var myFunction = function(args) { - // do something - return something; -}; -// [END user_function] - -// dummy function -var aFunction = function(image) { - return image; -}; - -// [START collection_map] -var collection2 = collection1.map(aFunction); -// [END collection_map] diff --git a/javascript/src/examples/faq/gettingStarted02.js b/javascript/src/examples/faq/gettingStarted02.js deleted file mode 100644 index 7d021c5d1..000000000 --- a/javascript/src/examples/faq/gettingStarted02.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @fileoverview Earth Engine Developer's Guide examples - * from '"Hello world!" JavaScript' section - */ - -// [START js_hello] -print('Hello world!'); -// [END js_hello] - -// [START ee_hello] -print(ee.Image('LANDSAT/LC8_L1T/LC80440342014077LGN00')); -// [END ee_hello] diff --git a/javascript/src/examples/faq/gettingStarted03.js b/javascript/src/examples/faq/gettingStarted03.js deleted file mode 100644 index b01d7ec60..000000000 --- a/javascript/src/examples/faq/gettingStarted03.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @fileoverview Earth Engine Developer's Guide examples - * from 'Adding data to the map' section - */ - -// [START image_display] -var image = ee.Image('LANDSAT/LC8_L1T/LC80440342014077LGN00'); -Map.centerObject(image, 9); -Map.addLayer(image); -// [END image_display] - -// [START image_visualization] -var image = ee.Image('LANDSAT/LC8_L1T/LC80440342014077LGN00'); -// define visualization parameters in an object literal -var vizParams = {bands: ['B5', 'B4', 'B3'], min: 5000, max: 15000, gamma: 1.3}; -Map.centerObject(image, 9); -Map.addLayer(image, vizParams, 'Landsat 8 false color'); -// [END image_visualization] - -// [START fc_display] -var counties = ee.FeatureCollection('ft:1S4EB6319wWW2sWQDPhDvmSBIVrD3iEmCLYB7nMM'); -Map.addLayer(counties, {}, 'counties'); -// [END fc_display] diff --git a/javascript/src/examples/faq/gettingStarted04.js b/javascript/src/examples/faq/gettingStarted04.js deleted file mode 100644 index 10432b8d6..000000000 --- a/javascript/src/examples/faq/gettingStarted04.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @fileoverview Earth Engine Developer's Guide examples - * from 'Finding ImageCollections and FeatureCollections' and - * 'Filtering and Sorting' sections - */ - -// [START collection_load] -var collection = ee.ImageCollection('LANDSAT/LC8_L1T'); -// [END collection_load] -print(collection.first()); - -// [START make_point] -var point = ee.Geometry.Point(-122.262, 37.8719); -// [END make_point] - -// [START date_range] -var start = ee.Date('2014-06-01'); -var finish = ee.Date('2014-10-01'); -// [END date_range] - -// [START filter_ic] -var filteredCollection = ee.ImageCollection('LANDSAT/LC8_L1T') - .filterBounds(point) - .filterDate(start, finish) - .sort('CLOUD_COVER', true); -// [END filter_ic] -print(filteredCollection); - -// [START get_first] -var first = filteredCollection.first(); -// [END get_first] -print(first); - -// [START filter_fc] -var featureCollection = ee.FeatureCollection('ft:1fRY18cjsHzDgGiJiS2nnpUU3v9JPDc2HNaR7Xk8'); -var filteredFC = featureCollection.filter(ee.Filter.eq('Name', 'California')); -Map.addLayer(filteredFC, {}, 'California'); -// [END filter_fc] diff --git a/javascript/src/examples/faq/gettingStarted05.js b/javascript/src/examples/faq/gettingStarted05.js deleted file mode 100644 index 1fb6680ea..000000000 --- a/javascript/src/examples/faq/gettingStarted05.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @fileoverview Earth Engine Developer's Guide examples - * from 'Band Math' and 'Masking' sections - */ - -// [START band_math] -// function to get NDVI from Landsat 5 imagery -var getNDVI = function(image) { - return image.normalizedDifference(['B4', 'B3']); -}; - -// two Landsat 5 images, 20 years apart -var image1 = ee.Image('LT5_L1T_TOA/LT50440341990155XXX03'); -var image2 = ee.Image('LT5_L1T_TOA/LT50440342010162EDC00'); - -// compute NDVI from the scenes -var ndvi1 = getNDVI(image1); -var ndvi2 = getNDVI(image2); - -// compute the difference in NDVI -var ndviDifference = ndvi2.subtract(ndvi1); -// [END band_math] - -// [START masking] - -// land mask from the SRTM DEM -var landMask = ee.Image('CGIAR/SRTM90_V4').mask(); -// combine the land mask with the original image mask -var mask = ndviDifference.mask().and(landMask); -// apply the mask to the NDVI difference -var maskedDifference = ndviDifference.mask(mask); - -// display the masked result -var vizParams = {min: -0.5, max: 0.5, palette: ['FF0000', 'FFFFFF', '0000FF']}; -Map.setCenter(-122.2531, 37.6295, 9); -Map.addLayer(maskedDifference, vizParams, 'NDVI difference'); -// [END masking] diff --git a/javascript/src/examples/faq/gettingStarted06.js b/javascript/src/examples/faq/gettingStarted06.js deleted file mode 100644 index 4f03eac1b..000000000 --- a/javascript/src/examples/faq/gettingStarted06.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @fileoverview Earth Engine Developer's Guide examples - * from 'Mapping (what to do instead of a for-loop)' section - */ - -// [START map_function] -// function to get NDVI from Landsat 8 imagery -var addNDVI = function(image) { - return image.addBands(image.normalizedDifference(['B5', 'B4'])); -}; - -// load the Landsat 8 raw data, filter by location and date -var collection = ee.ImageCollection('LANDSAT/LC8_L1T') - .filterBounds(ee.Geometry.Point(-122.262, 37.8719)) - .filterDate('2014-06-01', '2014-10-01'); - -// map the function over the collection -var ndviCollection = collection.map(addNDVI); -// [END map_function] - -// display the result -var vizParams = {bands: ['nd'], min: -0.5, max: 1, palette: ['FF0000', '00FF00']}; -var image = ee.Image(ndviCollection.mosaic()); -Map.addLayer(image, vizParams, 'NDVI'); diff --git a/javascript/src/examples/faq/gettingStarted07.js b/javascript/src/examples/faq/gettingStarted07.js deleted file mode 100644 index 93da1ae28..000000000 --- a/javascript/src/examples/faq/gettingStarted07.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @fileoverview Earth Engine Developer's Guide examples - * from 'Mapping (what to do instead of a for-loop)' section - */ - -// [START property_compute] -// function to compute a new property from existing properties -var addField = function(feature) { - var p3 = ee.Number(feature.get('property1')).add(feature.get('property2')); - return feature.set({'property3': p3}); -}; - -// create a FeatureCollection from a list of Features -var features = ee.FeatureCollection([ - ee.Feature(ee.Geometry.Point(-122.4536, 37.7403), - {property1: 100, property2: 100}), - ee.Feature(ee.Geometry.Point(-118.2294, 34.039), - {property1: 200, property2: 300}), -]); - -// map the function over the collection -var featureCollection = features.map(addField); -// [END property_compute] - -// print the FeatureCollection -print(featureCollection); diff --git a/javascript/src/examples/faq/gettingStarted08.js b/javascript/src/examples/faq/gettingStarted08.js deleted file mode 100644 index c6d0d5e77..000000000 --- a/javascript/src/examples/faq/gettingStarted08.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @fileoverview Earth Engine Developer's Guide examples - * from 'Mapping (what to do instead of a for-loop)' section - */ - -// [START convert_collection] -// function to get the image centroid -var getGeom = function(image) { - return ee.Feature(image.geometry().centroid(), {foo: 1}); -}; - -// Landsat 8 collection -var collection = ee.ImageCollection('LANDSAT/LC8_L1T') - .filterBounds(ee.Geometry.Point(-122.262, 37.8719)) - .filterDate('2014-06-01', '2014-10-01'); - -// map the function over the ImageCollection -var featureCollection = ee.FeatureCollection(collection.map(getGeom)); -// [END convert_collection] - -print(featureCollection); diff --git a/javascript/src/examples/faq/gettingStarted09.js b/javascript/src/examples/faq/gettingStarted09.js deleted file mode 100644 index 12dadade2..000000000 --- a/javascript/src/examples/faq/gettingStarted09.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @fileoverview Earth Engine Developer's Guide examples - * from 'Reducing' section - */ - -// [START temporal_reduce] -// load a Landsat 8 collection, sort by increasing cloudiness -var collection = ee.ImageCollection('LANDSAT/LC8_L1T') - .filterBounds(ee.Geometry.Point(-122.262, 37.8719)) - .filterDate('2014-01-01', '2014-12-31') - .sort('CLOUD_COVER'); - -// median in each pixel, each band of the 5 least cloudy scenes -var median = collection.limit(5).reduce(ee.Reducer.median()); -// [END temporal_reduce] - -// display -var vizParams = { - bands: ['B5_median', 'B4_median', 'B3_median'], min: 5000, max: 15000 -}; -Map.addLayer(median, vizParams, 'median false color'); diff --git a/javascript/src/examples/faq/gettingStarted10.js b/javascript/src/examples/faq/gettingStarted10.js deleted file mode 100644 index a484e6988..000000000 --- a/javascript/src/examples/faq/gettingStarted10.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @fileoverview Earth Engine Developer's Guide examples - * from 'Reducing' section - */ - -// [START spatial_reduce] -// load and display a precomputed NDVI composite -var ndviImage = ee.Image('LANDSAT/LC8_L1T_ANNUAL_NDVI/2014'); -Map.addLayer(ndviImage); - -// create an arbitrary rectangle as a region, display -var region = ee.Geometry.Rectangle(-122.0828, 36.9631, -121.7807, 37.2303); -Map.addLayer(region); - -// get a dictionary of means in the region, keys are bandnames -var mean = ndviImage.reduceRegion({ - reducer: ee.Reducer.mean(), - geometry: region, - scale: 30 -}); -// [END spatial_reduce] - -print('mean NDVI:', mean); diff --git a/javascript/src/examples/faq/gettingStarted11.js b/javascript/src/examples/faq/gettingStarted11.js deleted file mode 100644 index 2fbca9101..000000000 --- a/javascript/src/examples/faq/gettingStarted11.js +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @fileoverview Earth Engine Developer's Guide examples - * from 'Masking' section - */ - -// [START cloud_masking] -// function to get NDVI from a Landsat 8 image -var addNDVI = function(image) { - return image.addBands(image.normalizedDifference(['B5', 'B4'])); -}; - -// function to mask cloudy pixels -var cloudMask = function(image) { - var clouds = ee.Algorithms.Landsat.simpleCloudScore(image).select(['cloud']); - return image.mask(clouds.lt(10)); -}; - -// load a Landsat collection, map the NDVI and cloud masking functions over it -var collection = ee.ImageCollection('LANDSAT/LC8_L1T_TOA') - .filterBounds(ee.Geometry.Point(-122.262, 37.8719)) - .filterDate('2014-03-01', '2014-05-31') - .map(addNDVI) - .map(cloudMask); - -// reduce the collection to the mean in each pixel, display -var meanImage = collection.reduce(ee.Reducer.mean()); -var vizParams = {bands: ['B5_mean', 'B4_mean', 'B3_mean'], min: 0, max: 0.5}; -Map.addLayer(meanImage, vizParams, 'mean'); - -// load a region in which to compute the mean, display -var counties = ee.FeatureCollection ('ft:1S4EB6319wWW2sWQDPhDvmSBIVrD3iEmCLYB7nMM'); -var santacruz = ee.Feature(counties.filterMetadata('FIPS', 'equals', 6087).first()); -Map.addLayer(santacruz); - -// get the mean of NDVI in the region -var mean = meanImage.select(['nd_mean']).reduceRegion({ - reducer: ee.Reducer.mean(), - geometry: santacruz.geometry(), - scale: 30 -}); -// [END cloud_masking] - -print('Santa Cruz spring mean NDVI:', mean); diff --git a/javascript/src/examples/faq/overlay.js b/javascript/src/examples/faq/overlay.js deleted file mode 100644 index a4ed384f6..000000000 --- a/javascript/src/examples/faq/overlay.js +++ /dev/null @@ -1,57 +0,0 @@ -// Visualize example - -/** - * A Landsat 8 image is used to derive spectral indices NDVI and NDWI. - * The true-color bands of the image are linearly stretched to the - * 5th and 95th percentiles to create a visualization image. A palettized - * NDWI layer is visualized and combined with the true-color image - * for exporting a multi-layer overlay as a single RGB image. - */ - -var image = ee.Image('LANDSAT/LC8_L1T_TOA/LC80440342014077LGN00'); -Map.setCenter(-122.1899, 37.5010, 10); // SF Bay - -// Return the percentile values for each band in an image. -var getPercentile = function(image, percentile) { - return image.reduceRegion({ - reducer: ee.Reducer.percentile([percentile]), - bestEffort: true - }); -}; - -// Get the percentile values for each band. -var p05 = getPercentile(image, 5); -var p95 = getPercentile(image, 95); - -// define the visualization bands (true color) -var bands = ['B4', 'B3', 'B2']; -// linear stretch between the 5th and 95th percentiles -var vizParams = { - bands: bands, - min: p05.values(bands), - max: p95.values(bands), -}; -// create a true-color visualization image -var rgb = image.visualize(vizParams); -Map.addLayer(rgb, {}, 'vis'); - -// create an NDVI image -var ndvi = image.normalizedDifference(['B5', 'B4']); -Map.addLayer(ndvi, {min: -1, max: 1, palette: ['FF0000', '00FF00']}, 'NDVI', false); - -// create an NDWI image, see: -// http://www.tandfonline.com/doi/abs/10.1080/01431169608948714#.VMpommR4oeE -var ndwi = image.normalizedDifference(['B3', 'B5']); -var ndwiViz = {min: 0.5, max: 1, palette: ['00FFFF', '0000FF']}; -Map.addLayer(ndwi, ndwiViz, 'NDWI', false); -// mask the non-watery parts of the NDWI image, create a visualization layer -var ndwiRGB = ndwi.mask(ndwi.gte(0.4)).visualize(ndwiViz); -Map.addLayer(ndwiRGB, {}, 'NDWI-vis'); - -// Mosaic the true-color visualization and the NDWI visualization. -// The order of images in the collection determines rendering order. -// Specifically, the latter images in the collection will be "on top." -// Masks can be used to control which parts of an image are visible. -var flattenedImage = ee.ImageCollection([rgb, ndwiRGB]).mosaic(); - -Map.addLayer(flattenedImage, {}, 'flattened'); diff --git a/javascript/src/examples/faq/pca.js b/javascript/src/examples/faq/pca.js deleted file mode 100644 index df6ca949b..000000000 --- a/javascript/src/examples/faq/pca.js +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Compute the Principal Components of a Landsat 8 image. - */ - -// Load a landsat 8 image, select the bands of interest. -var image = ee.Image('LANDSAT/LC8_L1T/LC80440342014077LGN00') - .select(['B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B10', 'B11']); - -// Display the input imagery and the region in which to do the PCA. -var region = image.geometry(); -Map.centerObject(region, 10); -Map.addLayer(ee.Image().paint(region, 0, 2), {}, 'Region'); -Map.addLayer(image, {bands: ['B5', 'B4', 'B2'], min: 0, max: 20000}, 'Original Image'); - -// Get some information about the input to be used later. -var scale = image.projection().nominalScale(); -var bandNames = image.bandNames(); - -// Mean center the data to enable a faster covariance reducer -// and an SD stretch of the principal components. -var meanDict = image.reduceRegion({ - reducer: ee.Reducer.mean(), - geometry: region, - scale: scale, - maxPixels: 1e9 -}); -var means = ee.Image.constant(meanDict.values(bandNames)); -var centered = image.subtract(means); - -// This helper function returns a list of new band names. -var getNewBandNames = function(prefix) { - var seq = ee.List.sequence(1, bandNames.length()); - return seq.map(function(b) { - return ee.String(prefix).cat(ee.Number(b).int()); - }); -}; - -// This function accepts mean centered imagery, a scale and -// a region in which to perform the analysis. It returns the -// Principal Components (PC) in the region as a new image. -var getPrincipalComponents = function(centered, scale, region) { - // Collapse the bands of the image into a 1D array per pixel. - var arrays = centered.toArray(); - - // Compute the covariance of the bands within the region. - var covar = arrays.reduceRegion({ - reducer: ee.Reducer.centeredCovariance(), - geometry: region, - scale: scale, - maxPixels: 1e9 - }); - - // Get the 'array' covariance result and cast to an array. - // This represents the band-to-band covariance within the region. - var covarArray = ee.Array(covar.get('array')); - - // Perform an eigen analysis and slice apart the values and vectors. - var eigens = covarArray.eigen(); - - // This is a P-length vector of Eigenvalues. - var eigenValues = eigens.slice(1, 0, 1); - // This is a PxP matrix with eigenvectors in rows. - var eigenVectors = eigens.slice(1, 1); - - // Convert the array image to 2D arrays for matrix computations. - var arrayImage = arrays.toArray(1); - - // Left multiply the image array by the matrix of eigenvectors. - var principalComponents = ee.Image(eigenVectors).matrixMultiply(arrayImage); - - // Turn the square roots of the Eigenvalues into a P-band image. - var sdImage = ee.Image(eigenValues.sqrt()) - .arrayProject([0]).arrayFlatten([getNewBandNames('sd')]); - - // Turn the PCs into a P-band image, normalized by SD. - return principalComponents - // Throw out an an unneeded dimension, [[]] -> []. - .arrayProject([0]) - // Make the one band array image a multi-band image, [] -> image. - .arrayFlatten([getNewBandNames('pc')]) - // Normalize the PCs by their SDs. - .divide(sdImage); -}; - -// Get the PCs at the specified scale and in the specified region -var pcImage = getPrincipalComponents(centered, scale, region); - -// Plot each PC as a new layer -for (var i = 0; i < bandNames.length().getInfo(); i++) { - var band = pcImage.bandNames().get(i).getInfo(); - Map.addLayer(pcImage.select([band]), {min: -2, max: 2}, band); -} diff --git a/javascript/src/featurecollection.js b/javascript/src/featurecollection.js index c95a114a3..2dde75e47 100644 --- a/javascript/src/featurecollection.js +++ b/javascript/src/featurecollection.js @@ -190,6 +190,9 @@ ee.FeatureCollection.prototype.getDownloadURL = function( request['filename'] = opt_filename; } if (opt_selectors) { + if (goog.isArrayLike(opt_selectors)) { + opt_selectors = opt_selectors.join(','); + } request['selectors'] = opt_selectors; } diff --git a/javascript/src/geometry.js b/javascript/src/geometry.js index e80bde442..f7dbf426f 100644 --- a/javascript/src/geometry.js +++ b/javascript/src/geometry.js @@ -259,8 +259,9 @@ ee.Geometry.Rectangle = function( if (!(this instanceof ee.Geometry.Rectangle)) { return ee.Geometry.createInstance_(ee.Geometry.Rectangle, arguments); } - var init = ee.Geometry.parseArgs_('Polygon', 2, arguments); + var init = ee.Geometry.parseArgs_('Rectangle', 2, arguments); if (!(init instanceof ee.ComputedObject)) { + // GeoJSON does not have a 'Rectangle' type, so expand it into a Polygon. var xy = init['coordinates']; if (xy.length != 2) { throw Error('The Geometry.Rectangle constructor requires 2 points or 4 ' + @@ -271,6 +272,7 @@ ee.Geometry.Rectangle = function( var x2 = xy[1][0]; var y2 = xy[1][1]; init['coordinates'] = [[[x1, y2], [x1, y1], [x2, y1], [x2, y2]]]; + init['type'] = 'Polygon'; } goog.base(this, init); }; diff --git a/javascript/src/image.js b/javascript/src/image.js index 9303940f9..ec70e379f 100644 --- a/javascript/src/image.js +++ b/javascript/src/image.js @@ -475,6 +475,28 @@ ee.Image.prototype.clip = function(geometry) { }; +/** + * Rename the bands of an image. + * + * @param {...string|Object|Array} var_args The new names for the bands. + * Must match the number of bands in the Image. + * @return {ee.Image} The renamed image. + * @export + */ +ee.Image.prototype.rename = function(var_args) { + var names; + if (arguments.length == 1 && !ee.Types.isString(arguments[0])) { + // An array. + names = arguments[0]; + } else { + // Varargs list of strings. + names = goog.array.clone(arguments); + } + return /** @type {ee.Image} */( + ee.ApiFunction._call('Image.rename', this, names)); +}; + + /** @override */ ee.Image.prototype.name = function() { return 'Image'; diff --git a/javascript/src/maplayeroverlay.js b/javascript/src/maplayeroverlay.js index 3daa8b770..07c093f7e 100644 --- a/javascript/src/maplayeroverlay.js +++ b/javascript/src/maplayeroverlay.js @@ -1,8 +1,10 @@ goog.provide('ee.MapLayerOverlay'); +goog.provide('ee.TileEvent'); goog.require('ee.MapTileManager'); goog.require('goog.array'); goog.require('goog.dom'); +goog.require('goog.events'); goog.require('goog.events.Event'); goog.require('goog.events.EventTarget'); goog.require('goog.events.EventType'); @@ -82,12 +84,37 @@ ee.MapLayerOverlay.EventType = { }; +/** + * Adds a callback to be fired each time a tile is loaded. + * @param {function(ee.TileEvent)} callback The function to call when a + * tile has loaded. + * @return {!Object} An ID which can be passed to removeTileCallback() to + * remove the callback. + * @export + */ +ee.MapLayerOverlay.prototype.addTileCallback = function(callback) { + return goog.events.listen( + this, ee.MapLayerOverlay.EventType.TILE_LOADED, callback); +}; + + +/** + * Removes the callback with the given ID. + * @param {!Object} callbackId The ID returned by addTileLoaded() + * when the callback was registered. + * @export + */ +ee.MapLayerOverlay.prototype.removeTileCallback = function(callbackId) { + goog.events.unlistenByKey(/** @type {goog.events.Key} */ (callbackId)); +}; + + /** * Send an event about a change in the number of outstanding tiles. * @private */ ee.MapLayerOverlay.prototype.dispatchTileEvent_ = function() { - this.dispatchEvent(new ee.TileEvent_(this.tilesLoading_.length)); + this.dispatchEvent(new ee.TileEvent(this.tilesLoading_.length)); }; @@ -242,11 +269,10 @@ ee.MapLayerOverlay.prototype.handleImageCompleted_ = function( * An event contaning the information about the tile status * @param {number} count The number of outstanding tile requests. * @constructor - * @private * @extends {goog.events.Event} */ -ee.TileEvent_ = function(count) { +ee.TileEvent = function(count) { goog.events.Event.call(this, ee.MapLayerOverlay.EventType.TILE_LOADED); this.count = count; }; -goog.inherits(ee.TileEvent_, goog.events.Event); +goog.inherits(ee.TileEvent, goog.events.Event); diff --git a/javascript/src/maptilemanager.js b/javascript/src/maptilemanager.js index e55e05a41..ead02936a 100644 --- a/javascript/src/maptilemanager.js +++ b/javascript/src/maptilemanager.js @@ -227,38 +227,6 @@ ee.MapTileManager.prototype.disposeInternal = function() { -/** - * An event dispatched by MapTileManager. - * - * @param {goog.net.EventType} type Event Type. - * @param {ee.MapTileManager} target Reference to the object that is the - * target of this event. - * @param {string} id The id of the request this event is for. - * @param {goog.net.ImageLoader} imageLoader The ImageLoader object - of the request. - * @constructor - * @private - * @extends {goog.events.Event} - */ -ee.MapTileManager.Event_ = function(type, target, id, imageLoader) { - goog.events.Event.call(this, type, target); - - /** - * The id of the request this event is for. - * @type {string} - */ - this.id = id; - - /** - * The ImageLoader object of the request. - * @type {goog.net.ImageLoader} - */ - this.imageLoader = imageLoader; -}; -goog.inherits(ee.MapTileManager.Event_, goog.events.Event); - - - /** * An encapsulation of everything needed to make a Xhr request. * NOTE: This is used internal to the MapTileManager. diff --git a/INSTALL.md b/python/README.md similarity index 78% rename from INSTALL.md rename to python/README.md index 95a8fbcaa..624fa0b22 100644 --- a/INSTALL.md +++ b/python/README.md @@ -92,19 +92,30 @@ cd earthengine-api-VERSION python setup.py install ``` -# Mac OSX Installation # +# Mac OS X Installation # -The installation instructions assume that you are using the -[Fink](http://www.finkproject.org/) package manager and the +The installation instructions assume that you are using Mac OS X 10.9+, the +[Homebrew](http://brew.sh/) Mac OS package manager, and the [PIP](http://pip.readthedocs.org/en/latest/) Python package manager. -Pip can be installed with the following command: +Feel free to use a different package manager such as [Fink](www.finkproject.org) +or [MacPorts](https://www.macports.org/) if you prefer. -`fink install pip` +Homebrew can be installed with: + +`ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"` + +Then Pip (and Python) can be installed with the following command: + +`brew install python` + +Note: Mac OS X ships with a default version of Python 2. Homebrew will install +its own Python 2.7, which we'll be using to avoid interfering with +the system-level configuration. ## Python 2.6+ ## -Mac OSX users will already have Python installed. You can check the version of -Python that is installed by running the following command: +You can check the version of Python that is installed by running the +following command: `python --version` @@ -113,14 +124,12 @@ Python 2.6 or 2.7. ## Google APIs Client Library for Python ## -The Google APIs Client Library for Python provides support for authenticating -to the Earth Engine servers. The library can be installed from the Python -Package index by running the following command: +The [Google APIs Client Library](https://github.com/google/google-api-python-client) +for Python provides support for authenticating to the Earth Engine servers. +The library can be installed from the Python Package index by running the +following command: -`sudo pip install google-api-python-client` - -Alternatively, the library can be built from the source code, -[available on GitHub](https://github.com/google/google-api-python-client). +`pip install google-api-python-client` ## OpenSSL ## @@ -130,21 +139,18 @@ which will print the version of the library: `openssl version` -On Ubuntu/Debian systems, the library can be installed by running the following -command: +The library can be installed by running the following command: -`fink install openssl` +`brew install openssl` ## pyOpenSSL ## -pyOpenSSL is a Python wrapper for the OpenSSL library. The pyOpenSSL library -can be installed from the Python Package Index by running the following command: +[pyOpenSSL](https://github.com/pyca/pyopenssl) is a Python wrapper for the +OpenSSL library. The pyOpenSSL library can be installed from the Python Package +Index by running the following command: -`sudo pip install 'pyOpenSSL>=0.11'` - -Alternatively, the library can be built from the source code, -[available on GitHub](https://github.com/pyca/pyopenssl) +`pip install 'pyOpenSSL>=0.11'` ## pyCrypto ## @@ -152,7 +158,7 @@ Alternatively, the library can be built from the source code, The pyCrypto library can be installed from the Python Package Index by running the following command: -`sudo pip install pyCrypto` +`pip install pyCrypto` To verify that the pyCrypto library has been installed correctly, run the following command: @@ -166,7 +172,7 @@ If no error is returned, the library has been correctly installed. The Earth Engine Python library can be installed from the Python Package Index by running the following command: -`sudo pip install earthengine-api` +`pip install earthengine-api` # Windows Installation # diff --git a/python/ee/__init__.py b/python/ee/__init__.py index 74ee9bdc9..30557d2a3 100644 --- a/python/ee/__init__.py +++ b/python/ee/__init__.py @@ -2,7 +2,7 @@ """The EE Javascript library.""" -__version__ = '0.1.56' +__version__ = '0.1.57' # Using lowercase function naming to match the JavaScript names. # pylint: disable=g-bad-name diff --git a/python/ee/apitestcase.py b/python/ee/apitestcase.py index 7ef87ed65..a855ee337 100644 --- a/python/ee/apitestcase.py +++ b/python/ee/apitestcase.py @@ -1330,8 +1330,26 @@ def MockSend(path, params, unused_method=None, unused_raw=None): 'description': '', 'returns': 'Object', }, + 'Image.rename': { + 'type': 'Algorithm', + 'args': [ + { + 'description': '', + 'name': 'input', + 'type': 'Image' + }, + { + 'description': '', + 'name': 'names', + 'type': 'List' + } + ], + 'description': '', + 'returns': 'Image' + }, } + # A sample of encoded EE API JSON, used by SerializerTest and DeserializerTest. ENCODED_JSON_SAMPLE = { 'type': 'CompoundValue', diff --git a/python/ee/computedobject.py b/python/ee/computedobject.py index 49166637c..1e6adc8c9 100644 --- a/python/ee/computedobject.py +++ b/python/ee/computedobject.py @@ -134,6 +134,27 @@ def isVariable(self): # to remain null until for CustomFunction.resolveNamelessArgs_(). return self.func is None and self.args is None + def aside(self, func, *var_args): + """Calls a function passing this object as the first argument. + + Returns the object itself for chaining. Convenient e.g. when debugging: + + c = (ee.ImageCollection('foo').aside(logging.info) + .filterDate('2001-01-01', '2002-01-01').aside(logging.info) + .filterBounds(geom).aside(logging.info) + .aside(addToMap, {'min': 0, 'max': 142}) + .select('a', 'b')) + + Args: + func: The function to call. + *var_args: Any extra arguments to pass to the function. + + Returns: + The same object, for chaining. + """ + func(self, *var_args) + return self + @classmethod def name(cls): """Returns the name of the object, used in __str__().""" diff --git a/python/ee/featurecollection.py b/python/ee/featurecollection.py index 3f6f514cd..6010edaf1 100644 --- a/python/ee/featurecollection.py +++ b/python/ee/featurecollection.py @@ -129,6 +129,8 @@ def getDownloadURL(self, filetype=None, selectors=None, filename=None): if filename is not None: request['filename'] = filename if selectors is not None: + if isinstance(selectors, (list, tuple)): + selectors = ','.join(selectors) request['selectors'] = selectors return data.makeTableDownloadUrl(data.getTableDownloadId(request)) diff --git a/python/ee/geometry.py b/python/ee/geometry.py index 744e21dd6..3aee197c3 100644 --- a/python/ee/geometry.py +++ b/python/ee/geometry.py @@ -198,10 +198,11 @@ def Rectangle(coords=_UNSPECIFIED, proj=_UNSPECIFIED, Returns: An ee.Geometry describing a rectangular polygon. """ - init = Geometry._parseArgs('Polygon', 2, Geometry._GetSpecifiedArgs( + init = Geometry._parseArgs('Rectangle', 2, Geometry._GetSpecifiedArgs( (coords, proj, geodesic, maxError) + args, ('xlo', 'ylo', 'xhi', 'yhi'), **kwargs)) if not isinstance(init, computedobject.ComputedObject): + # GeoJSON does not have a Rectangle type, so expand to a Polygon. xy = init['coordinates'] if not isinstance(xy, (list, tuple)) or len(xy) != 2: raise ee_exception.EEException( @@ -212,6 +213,7 @@ def Rectangle(coords=_UNSPECIFIED, proj=_UNSPECIFIED, x2 = xy[1][0] y2 = xy[1][1] init['coordinates'] = [[[x1, y2], [x1, y1], [x2, y1], [x2, y2]]] + init['type'] = 'Polygon' return Geometry(init) @staticmethod diff --git a/python/ee/image.py b/python/ee/image.py index 9850bcfc3..dd6bff5e0 100644 --- a/python/ee/image.py +++ b/python/ee/image.py @@ -370,6 +370,31 @@ def clip(self, clip_geometry): pass # Not an ee.Geometry or GeoJSON. Just pass it along. return apifunction.ApiFunction.call_('Image.clip', self, clip_geometry) + def rename(self, names, *args): + """Rename the bands of an image. + + Can be called with either a list of strings or any number of strings. + + Args: + names: An array of strings specifying the new names for the + bands. Must exactly match the number of bands in the image. + *args: Band names as varargs. + + Returns: + An image with the renamed bands. + """ + if args: + # Handle varargs; everything else we let the server handle. + args = list(args) + args.insert(0, names) + names = args + + algorithm_args = { + 'input': self, + 'names': names + } + return apifunction.ApiFunction.apply_('Image.rename', algorithm_args) + @staticmethod def name(): return 'Image' diff --git a/python/ee/tests/image_test.py b/python/ee/tests/image_test.py index cd8f3e8af..9617956b6 100644 --- a/python/ee/tests/image_test.py +++ b/python/ee/tests/image_test.py @@ -88,6 +88,16 @@ def testSelect(self): self.assertEquals(ee.ApiFunction.lookup('Image.select'), image.func) self.assertEquals(ee.List([]), image.args['bandSelectors']) + def testRename(self): + """Verifies image.rename varargs handling.""" + image = ee.Image([1, 2]).rename('a', 'b') + self.assertEquals(ee.ApiFunction.lookup('Image.rename'), image.func) + self.assertEquals(ee.List(['a', 'b']), image.args['names']) + + image = ee.Image([1, 2]).rename(['a', 'b']) + self.assertEquals(ee.ApiFunction.lookup('Image.rename'), image.func) + self.assertEquals(ee.List(['a', 'b']), image.args['names']) + def testExpression(self): """Verifies the behavior of ee.Image.expression().""" image = ee.Image([1, 2]).expression('a', {'b': 'c'}) diff --git a/python/examples/AppEngine/hello-world/README.md b/python/examples/AppEngine/hello-world/README.md new file mode 100644 index 000000000..dbf5ffad7 --- /dev/null +++ b/python/examples/AppEngine/hello-world/README.md @@ -0,0 +1,66 @@ +Hello World EE Demo App +======================= + +This example shows how to build a simple Google App Engine web application that +communicates with Google Earth Engine. Upon successful deployment, you will see +a webpage with a Google map showing the SRTM DEM zoomed into the east coast of +Australia. + +Download Hello World +-------------------- + +Download the Earth Engine API repository from GitHub: + + git clone https://github.com/google/earthengine-api.git + +Navigate to the Hello World example code: + + cd ./python/examples/AppEngine/hello-world/ + + +Create your own project +----------------------- + +Each App Engine app needs its own project. The project ID for +the default instance of this app is `hello-earth-engine`. To deploy +your own instance, you'll need to create a new project with a +different ID. + +To create an App Engine project for your app: + +1. Open the [Google Developers Console](https://console.developers.google.com). +2. Click **Create Project**. +3. Enter a project name and a project ID. +4. Click **Create**. + +Once you've selected a project ID, update your `app.yaml` file. + + +Set up a service account +------------------------ + +- [Create a service account](https://sites.google.com/site/earthengineapidocs/creating-oauth2-service-account). +- Download a P12 private key from the Google Developers Console. +- Convert the private key of that service account to a `.pem` file: + `openssl pkcs12 -in downloaded-privatekey.p12 -nodes -nocerts > privatekey.pem` +- Copy the `.pem` file into the directory that has your app.yaml file. +- Update `config.py` file with your service account email address. + + +Build the app +------------- + +From within the hello-world folder, run: + + . ./build.sh + +This script will build the app and fetch all its dependencies. It will also +install the [Google Cloud SDK](https://cloud.google.com/sdk/) if necessary. + + +Run the app! +------------ + +From within the hello-world folder, run: + + dev_appserver.py ./ diff --git a/python/examples/AppEngine/hello_world/app.yaml b/python/examples/AppEngine/hello-world/app.yaml similarity index 100% rename from python/examples/AppEngine/hello_world/app.yaml rename to python/examples/AppEngine/hello-world/app.yaml diff --git a/python/examples/AppEngine/hello-world/build.sh b/python/examples/AppEngine/hello-world/build.sh new file mode 100644 index 000000000..9e5710495 --- /dev/null +++ b/python/examples/AppEngine/hello-world/build.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# This Bash script (1) builds Python dependencies needed to run and deploy +# the Trendy Lights application and (2) installs the Google App Engine +# developer tools if they aren't found on the system. + +# Builds the specified dependency if it hasn't been built. Takes 3 parameters: +# 1. The URL of the git repo. +# 2. The tag name or commit SHA at which to checkout the repo. +# 3. The path within the repo to the library folder. +BuildDep () { + DST_FOLDER=`basename "$3"` + echo "Building $DST_FOLDER..." + if [ ! -d "$DST_FOLDER" ]; then + # See: http://unix.stackexchange.com/a/84980 + TEMP_DIR=`mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir'` + cd $TEMP_DIR + git clone "$1" . + git checkout "$2" . + cd - + mv "$TEMP_DIR/$3" ./ + rm -rf $TEMP_DIR + fi +} + +# Build oauth2client. +BuildDep https://github.com/google/oauth2client.git tags/v1.3.2 oauth2client + +# Build the Earth Engine Python client library. +SHA_v0153="92b1b5f" # v0.1.53 +BuildDep https://github.com/google/earthengine-api.git $SHA_v0153 python/ee + +# Build httplib2. +BuildDep https://github.com/jcgregorio/httplib2.git tags/v0.9.1 python2/httplib2 + +# Build PyCrypto. +BuildDep https://github.com/dlitz/pycrypto.git tags/v2.6.1 lib/Crypto + +# Install the Google App Engine command line tools. +if ! hash dev_appserver.py 2>/dev/null; then + # Install the `gcloud` command line tool. + curl https://sdk.cloud.google.com/ | bash + # Ensure the `gcloud` command is in our path. + if [ -f ~/.bashrc ]; then + source ~/.bashrc + elif [ -f ~/.bash_profile ]; then + source ~/.bash_profile + fi + # Install the Google App Engine command line tools. + gcloud components update gae-python +fi diff --git a/python/examples/AppEngine/hello_world/config.py b/python/examples/AppEngine/hello-world/config.py similarity index 64% rename from python/examples/AppEngine/hello_world/config.py rename to python/examples/AppEngine/hello-world/config.py index 6c502a97c..3c0a1b58c 100644 --- a/python/examples/AppEngine/hello_world/config.py +++ b/python/examples/AppEngine/hello-world/config.py @@ -6,8 +6,6 @@ import os import ee -from ee.oauthinfo import OAuthInfo -from oauth2client.appengine import AppAssertionCredentials # The URL of the Earth Engine API. EE_URL = 'https://earthengine.googleapis.com' @@ -27,13 +25,5 @@ DEBUG_MODE = ('SERVER_SOFTWARE' in os.environ and os.environ['SERVER_SOFTWARE'].startswith('Dev')) -# Set up the appropriate credentials depending on where we're running. -if DEBUG_MODE: - EE_CREDENTIALS = ee.ServiceAccountCredentials(EE_ACCOUNT, EE_PRIVATE_KEY_FILE) -else: - EE_CREDENTIALS = AppAssertionCredentials(OAuthInfo.SCOPE) - # Change the above line to the below to use your private credentials in - # an App Engine instance. - # EE_CREDENTIALS = - # ee.ServiceAccountCredentials(EE_ACCOUNT, EE_PRIVATE_KEY_FILE) +EE_CREDENTIALS = ee.ServiceAccountCredentials(EE_ACCOUNT, EE_PRIVATE_KEY_FILE) diff --git a/python/examples/AppEngine/hello_world/ee_appengine.py b/python/examples/AppEngine/hello-world/ee_appengine.py similarity index 99% rename from python/examples/AppEngine/hello_world/ee_appengine.py rename to python/examples/AppEngine/hello-world/ee_appengine.py index 222226ca9..07d02bbd3 100644 --- a/python/examples/AppEngine/hello_world/ee_appengine.py +++ b/python/examples/AppEngine/hello-world/ee_appengine.py @@ -20,6 +20,7 @@ class MainPage(webapp2.RequestHandler): + def get(self): # pylint: disable=g-bad-name """Request an image from Earth Engine and render it to a web page.""" ee.Initialize(config.EE_CREDENTIALS, config.EE_URL) diff --git a/python/examples/AppEngine/hello_world/index.html b/python/examples/AppEngine/hello-world/index.html similarity index 100% rename from python/examples/AppEngine/hello_world/index.html rename to python/examples/AppEngine/hello-world/index.html diff --git a/python/examples/AppEngine/hello_world/README.txt b/python/examples/AppEngine/hello_world/README.txt deleted file mode 100644 index fcd613741..000000000 --- a/python/examples/AppEngine/hello_world/README.txt +++ /dev/null @@ -1,73 +0,0 @@ -This example shows how to build a simple Google App Engine web application that -communicates with Google Earth Engine. Upon successful deployment, you will see -a webpage with a Google map showing the SRTM DEM zoomed into the east coast of -Australia. - -You can deploy this application to an App Engine instance, or run it in your -local App Engine development environment. Instructions for how to configure -each are below. - -In all cases, the Python environment running the code will need to access -the oauth2client, httplib2, pycrypto, and ee libraries. When running locally, -make sure your Python environment contains these libraries (yolk is a useful -tool for this). When running in App Engine, you will need to include the -oauth2client, httplib2, and ee libraries in the directory containing the -app.yaml file. Instructions for downloading the libraries are at the end of -this file. - -For local development using a personal service account: -* Set up a service account as described here: - https://sites.google.com/site/earthengineapidocs/creating-oauth2-service-account -* Email the service account email address to your google contact. -* Convert the private key of that service account to a pem file: - openssl pkcs12 -in downloaded-privatekey.p12 -nodes -nocerts > privatekey.pem -* Old versions of oauth2client will require you to delete the first few - lines of the of the .pem file so it begins with - ---BEGIN - We recommend you update to the most recent version of the libraries. -* Copy the pem file into the directory that has your app.yaml file. -* Update the included config.py file with your service account email - address. -* Use appcfg.py or the App Engine Launcher to run in your local App Engine - development environment. - -To give your App Engine account access using a personal service account: -* Follow the instructions for local development. -* Create an App Engine instance. -* Update the end of the config.py file to use your private credentials. -* Update the included app.yaml file with the id of your App Engine instance. - If your instance is at my-app.appspot.com, the id of the instance is my-app. -* Use appcfg.py or the App Engine Launcher to deploy your application to - App Engine. - -To give your App Engine account access using an App Engine service account: -* Go to your App Engine console (http://appengine.google.com) and choose the - instance you want to authenticate. -* Look under application settings - you'll find a link under 'Administration' - on the left hand side of the screen. -* Email the Service Account Name to your Google contact, who will whitelist - your application. -* Update the included app.yaml file with the id of your App Engine instance. -* Use appcfg.py or the App Engine Launcher to deploy your application to - App Engine. - -Dependencies - -oauth2client: -* hg clone https://code.google.com/p/google-api-python-client/ -* mv google-api-python-client/oauth2client/ into the directory - containing app.yaml - -Earth Engine -* follow the instructions at: - https://code.google.com/p/earthengine-api/source/checkout -* move ee into the directory containing app.yaml - -httplib2 -* hg clone https://code.google.com/p/httplib2/ -* move httplib2/python2/httplib2 into the directory containing app.yaml - -pycrypto: -* download from https://www.dlitz.net/software/pycrypto/ -* python setup.py build -* python setup.py install diff --git a/python/examples/AppEngine/trendy-lights/README.md b/python/examples/AppEngine/trendy-lights/README.md index 30103cb87..54c0d8be2 100644 --- a/python/examples/AppEngine/trendy-lights/README.md +++ b/python/examples/AppEngine/trendy-lights/README.md @@ -15,7 +15,7 @@ Download the Earth Engine API repository from GitHub: Navigate to the Trendy Lights example code: - cd ./python/examples/AppEngine/trendy-lights/ + cd ./earthengine-api/python/examples/AppEngine/trendy-lights/ Create your own project diff --git a/python/examples/AppEngine/trendy-lights/build.sh b/python/examples/AppEngine/trendy-lights/build.sh new file mode 100644 index 000000000..9e5710495 --- /dev/null +++ b/python/examples/AppEngine/trendy-lights/build.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# This Bash script (1) builds Python dependencies needed to run and deploy +# the Trendy Lights application and (2) installs the Google App Engine +# developer tools if they aren't found on the system. + +# Builds the specified dependency if it hasn't been built. Takes 3 parameters: +# 1. The URL of the git repo. +# 2. The tag name or commit SHA at which to checkout the repo. +# 3. The path within the repo to the library folder. +BuildDep () { + DST_FOLDER=`basename "$3"` + echo "Building $DST_FOLDER..." + if [ ! -d "$DST_FOLDER" ]; then + # See: http://unix.stackexchange.com/a/84980 + TEMP_DIR=`mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir'` + cd $TEMP_DIR + git clone "$1" . + git checkout "$2" . + cd - + mv "$TEMP_DIR/$3" ./ + rm -rf $TEMP_DIR + fi +} + +# Build oauth2client. +BuildDep https://github.com/google/oauth2client.git tags/v1.3.2 oauth2client + +# Build the Earth Engine Python client library. +SHA_v0153="92b1b5f" # v0.1.53 +BuildDep https://github.com/google/earthengine-api.git $SHA_v0153 python/ee + +# Build httplib2. +BuildDep https://github.com/jcgregorio/httplib2.git tags/v0.9.1 python2/httplib2 + +# Build PyCrypto. +BuildDep https://github.com/dlitz/pycrypto.git tags/v2.6.1 lib/Crypto + +# Install the Google App Engine command line tools. +if ! hash dev_appserver.py 2>/dev/null; then + # Install the `gcloud` command line tool. + curl https://sdk.cloud.google.com/ | bash + # Ensure the `gcloud` command is in our path. + if [ -f ~/.bashrc ]; then + source ~/.bashrc + elif [ -f ~/.bash_profile ]; then + source ~/.bash_profile + fi + # Install the Google App Engine command line tools. + gcloud components update gae-python +fi diff --git a/python/examples/AppEngine/trendy-lights/index.html b/python/examples/AppEngine/trendy-lights/index.html index 8aa1745c3..80fdb0336 100644 --- a/python/examples/AppEngine/trendy-lights/index.html +++ b/python/examples/AppEngine/trendy-lights/index.html @@ -62,6 +62,14 @@

+ + + Fork me on GitHub + +