Skip to content

Commit

Permalink
v0.1.396
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 617697857
  • Loading branch information
Google Earth Engine Authors authored and schwehr committed Mar 27, 2024
1 parent 7c525ff commit 387e451
Show file tree
Hide file tree
Showing 26 changed files with 2,572 additions and 690 deletions.
293 changes: 147 additions & 146 deletions javascript/build/ee_api_js.js

Large diffs are not rendered by default.

27 changes: 20 additions & 7 deletions javascript/build/ee_api_js_debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -1378,7 +1378,6 @@ goog.LOAD_MODULE_USING_EVAL = !0;
goog.SEAL_MODULE_EXPORTS = goog.DEBUG;
goog.loadedModules_ = {};
goog.DEPENDENCIES_ENABLED = !1;
goog.TRANSPILE = "detect";
goog.ASSUME_ES_MODULES_TRANSPILED = !1;
goog.TRUSTED_TYPES_POLICY_NAME = "goog";
goog.hasBadLetScoping = null;
Expand Down Expand Up @@ -19089,7 +19088,7 @@ var $jscomp$templatelit$294235699$96 = $jscomp.createTemplateTagFirstArg(["https
ee.apiclient = {};
var module$contents$ee$apiclient_apiclient = {};
ee.apiclient.VERSION = module$exports$ee$apiVersion.V1;
ee.apiclient.API_CLIENT_VERSION = "0.1.395";
ee.apiclient.API_CLIENT_VERSION = "0.1.396";
ee.apiclient.NULL_VALUE = module$exports$eeapiclient$domain_object.NULL_VALUE;
ee.apiclient.PromiseRequestService = module$exports$eeapiclient$promise_request_service.PromiseRequestService;
ee.apiclient.MakeRequestParams = module$contents$eeapiclient$request_params_MakeRequestParams;
Expand Down Expand Up @@ -19379,8 +19378,8 @@ module$contents$ee$apiclient_apiclient.send = function(path, params, callback, m
var profileHookAtCallTime = module$contents$ee$apiclient_apiclient.profileHook_, contentType = "application/x-www-form-urlencoded";
body && (contentType = "application/json", method && method.startsWith("multipart") && (contentType = method, method = "POST"));
method = method || "POST";
var headers = {"Content-Type":contentType}, version = "0.1.395";
"0.1.395" === version && (version = "latest");
var headers = {"Content-Type":contentType}, version = "0.1.396";
"0.1.396" === version && (version = "latest");
headers[module$contents$ee$apiclient_apiclient.API_CLIENT_VERSION_HEADER] = "ee-js/" + version;
var authToken = module$contents$ee$apiclient_apiclient.getAuthToken();
if (null != authToken) {
Expand Down Expand Up @@ -21534,12 +21533,26 @@ goog.exportSymbol("ee.data.getInfo", ee.data.getInfo);
ee.data.makeListAssetsCall_ = function(parent, opt_params, opt_callback, opt_postProcessing) {
opt_params = void 0 === opt_params ? {} : opt_params;
opt_postProcessing = void 0 === opt_postProcessing ? goog.functions.identity : opt_postProcessing;
var isProjectAssetRoot = ee.rpc_convert.CLOUD_ASSET_ROOT_RE.test(parent), call = new module$contents$ee$apiclient_Call(opt_callback), methodRoot = isProjectAssetRoot ? call.projects() : call.assets();
var params = Object.assign({}, opt_params), call = new module$contents$ee$apiclient_Call(opt_callback), isProjectAssetRoot = ee.rpc_convert.CLOUD_ASSET_ROOT_RE.test(parent), methodRoot = isProjectAssetRoot ? call.projects() : call.assets();
parent = isProjectAssetRoot ? ee.rpc_convert.projectParentFromPath(parent) : ee.rpc_convert.assetIdToAssetName(parent);
return call.handle(methodRoot.listAssets(parent, opt_params).then(opt_postProcessing));
var getNextPageIfNeeded = function(response) {
if (null != params.pageSize || !response.nextPageToken) {
return response;
}
var previousAssets = response.assets || [];
params.pageToken = response.nextPageToken;
var nextResponse = methodRoot.listAssets(parent, params).then(function(response) {
response.assets = previousAssets.concat(response.assets);
return response;
}).then(getNextPageIfNeeded);
return opt_callback ? nextResponse : call.handle(nextResponse);
};
return call.handle(methodRoot.listAssets(parent, params).then(getNextPageIfNeeded).then(opt_postProcessing));
};
ee.data.getList = function(params, opt_callback) {
return ee.data.makeListAssetsCall_(params.id, ee.rpc_convert.getListToListAssets(params), opt_callback, function(r) {
var convertedParams = ee.rpc_convert.getListToListAssets(params);
convertedParams.pageSize || (convertedParams.pageSize = 1E3);
return ee.data.makeListAssetsCall_(params.id, convertedParams, opt_callback, function(r) {
return null == r ? null : ee.rpc_convert.listAssetsToGetList(r);
});
};
Expand Down
70 changes: 42 additions & 28 deletions javascript/build/ee_api_js_npm.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion javascript/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@google/earthengine",
"version": "0.1.395",
"version": "0.1.396",
"description": "JavaScript client for Google Earth Engine API.",
"author": "Google LLC",
"license": "Apache-2.0",
Expand Down
2 changes: 1 addition & 1 deletion javascript/src/apiclient.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const {trustedResourceUrl} = goog.require('safevalues');
/** @namespace */
const apiclient = {};

const API_CLIENT_VERSION = '0.1.395';
const API_CLIENT_VERSION = '0.1.396';

exports.VERSION = apiVersion.VERSION;
exports.API_CLIENT_VERSION = API_CLIENT_VERSION;
Expand Down
47 changes: 40 additions & 7 deletions javascript/src/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -1659,14 +1659,41 @@ ee.data.getInfo = ee.data.getAsset;
ee.data.makeListAssetsCall_ = function(
parent, opt_params = {}, opt_callback = undefined,
opt_postProcessing = goog.functions.identity) {
/** @type {!ee.api.ProjectsAssetsListAssetsNamedParameters} */
const params = Object.assign({}, opt_params);
const call = new ee.apiclient.Call(opt_callback);
// Detect project asset root call.
const isProjectAssetRoot = ee.rpc_convert.CLOUD_ASSET_ROOT_RE.test(parent);
const call = new ee.apiclient.Call(opt_callback);
const methodRoot = isProjectAssetRoot ? call.projects() : call.assets();
parent = isProjectAssetRoot ? ee.rpc_convert.projectParentFromPath(parent) :
ee.rpc_convert.assetIdToAssetName(parent);
return call.handle(
methodRoot.listAssets(parent, opt_params).then(opt_postProcessing));
const getNextPageIfNeeded = (response) => {
// We currently treat pageSize as a cap on the results, if this param was
// provided we should break fast and not return more than the asked for
// amount.
if (params.pageSize != null || !response.nextPageToken) {
return response;
}
const previousAssets = response.assets || [];
params.pageToken = response.nextPageToken;
const nextResponse = methodRoot.listAssets(parent, params)
.then((response) => {
// Add previous assets to front.
response.assets = previousAssets.concat(response.assets);
return response;
})
.then(getNextPageIfNeeded);
if (opt_callback) {
// For async, make sure we have only a single chained `call.handle` call.
return nextResponse;
}
// For sync mode, the response data needs to be uplifted from the fake
// Promise object to be returned immediately.
return call.handle(nextResponse);
};
return call.handle(methodRoot.listAssets(parent, params)
.then(getNextPageIfNeeded)
.then(opt_postProcessing));
};


Expand All @@ -1686,9 +1713,15 @@ ee.data.makeListAssetsCall_ = function(
* @export
*/
ee.data.getList = function(params, opt_callback) {
const convertedParams = ee.rpc_convert.getListToListAssets(params);
// Force a single page of results by explicitly specifying a page size.
// This maintains backward compatibility, as well as compatibility with the
// Python implementation.
if (!convertedParams.pageSize) {
convertedParams.pageSize = 1000;
}
return ee.data.makeListAssetsCall_(
params['id'], ee.rpc_convert.getListToListAssets(params), opt_callback,
(r) => {
params['id'], convertedParams, opt_callback, (r) => {
if (r == null) {
return null;
}
Expand All @@ -1709,7 +1742,7 @@ ee.data.getList = function(params, opt_callback) {
* <table>
* <tr>
* <td><code> pageSize </code> (string) The number of results to
* return. Defaults to 1000.</td>
* return. If not specified, all results are returned.</td>
* </tr>
* <tr>
* <td><code> pageToken </code> (string) The token for the page of
Expand Down Expand Up @@ -1753,7 +1786,7 @@ ee.data.listAssets = function(
* <table>
* <tr>
* <td><code> pageSize </code> (string) The number of results to return.
* Defaults to 1000.</td>
* If not specified, all results are returned.</td>
* </tr>
* <tr>
* <td><code> pageToken </code> (string) The token page of results to
Expand Down
4 changes: 3 additions & 1 deletion javascript/src/examples/Demos/Landsat8HarmonicModeling.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@ var addHarmonics = function(freqs) {
};
};

// Filter to the area of interest, mask clouds, add variables.
// Filter to the desired date range and area of interest, mask clouds,
// and add variables.
var harmonicLandsat = landsatCollection
.filterDate('2015-01-01', '2020-01-01')
.filterBounds(roi)
.map(maskClouds)
.map(addNDVI)
Expand Down
2 changes: 1 addition & 1 deletion python/ee/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""The EE Python library."""

__version__ = '0.1.395'
__version__ = '0.1.396'

# Using lowercase function naming to match the JavaScript names.
# pylint: disable=g-bad-name
Expand Down
16 changes: 13 additions & 3 deletions python/ee/_cloud_api_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,19 @@ def request( # pylint: disable=invalid-name
del connection_type # Ignored
del redirections # Ignored

response = self._session.request(
method, uri, data=body, headers=headers, timeout=self._timeout
)
try:
# googleapiclient is expecting an httplib2 object, and doesn't include
# requests error in the list of transient errors. Therefore, transient
# requests errors should be converted to kinds that googleapiclient
# consider transient.
response = self._session.request(
method, uri, data=body, headers=headers, timeout=self._timeout
)
except requests.exceptions.ConnectionError as connection_error:
raise ConnectionError(connection_error) from connection_error
except requests.exceptions.ChunkedEncodingError as encoding_error:
# This is not a one-to-one match, but it's close enough.
raise ConnectionError(encoding_error) from encoding_error
headers = dict(response.headers)
headers['status'] = response.status_code
content = response.content
Expand Down
37 changes: 25 additions & 12 deletions python/ee/batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@
from ee import geometry


def _transform_operation_to_task(operation: Dict[str, Any]) -> Task:
"""Converts an operation to a task."""
status = _cloud_api_utils.convert_operation_to_task(operation)
return Task(
status['id'],
status.get('task_type'),
status.get('state'),
{'description': status.get('description')},
status.get('name'),
)


class Task:
"""A batch task that can be run on the EE batch processing system."""

Expand Down Expand Up @@ -108,6 +120,14 @@ def __init__(
self.state = state
self.name = name

@property
def operation_name(self) -> Optional[str]:
if self.name:
return self.name
if self.id:
return _cloud_api_utils.convert_task_id_to_operation_name(self.id)
return None

def start(self) -> None:
"""Starts the task. No-op for started tasks."""
if not self.config:
Expand Down Expand Up @@ -152,8 +172,9 @@ def status(self) -> Dict[str, Any]:
- error_message: Failure reason. Appears only if state is FAILED.
May also include other fields.
"""
if self.id:
result = data.getTaskStatus(self.id)[0]
if self.operation_name:
operation = data.getOperation(self.operation_name)
result = _cloud_api_utils.convert_operation_to_task(operation)
if result['state'] == 'UNKNOWN':
result['state'] = Task.State.UNSUBMITTED
else:
Expand All @@ -166,7 +187,7 @@ def active(self) -> bool:

def cancel(self) -> None:
"""Cancels the task."""
data.cancelTask(self.id)
data.cancelOperation(self.operation_name)

@staticmethod
def list() -> List[Task]:
Expand All @@ -178,15 +199,7 @@ def list() -> List[Task]:
Returns:
A list of Tasks.
"""
statuses = data.getTaskList()
tasks = []
for status in statuses:
tasks.append(Task(status['id'],
status.get('task_type'),
status.get('state'),
{'description': status.get('description')},
status.get('name')))
return tasks
return list(map(_transform_operation_to_task, data.listOperations()))

def __repr__(self) -> str:
"""Returns a string representation of the task."""
Expand Down
18 changes: 11 additions & 7 deletions python/ee/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,14 +556,16 @@ def getList(params: Dict[str, Any]) -> Any:
return result


def listImages(params: Dict[str, Any]) -> Dict[str, Optional[List[int]]]:
def listImages(params: str|Dict[str, Any]) -> Dict[str, Optional[List[int]]]:
"""Returns the images in an image collection or folder.
Args:
params: An object containing request parameters with the following possible
params: Either a string representing the ID of the image collection to list,
or an object containing request parameters with the following possible
values, all but 'parent` are optional:
parent - (string) The ID of the image collection to list, required.
pageSize - (string) The number of results to return. Defaults to 1000.
pageSize - (string) The number of results to return. If not specified, all
results are returned.
pageToken - (string) The token page of results to return.
startTime - (ISO 8601 string): The minimum start time (inclusive).
endTime - (ISO 8601 string): The maximum end time (exclusive).
Expand Down Expand Up @@ -591,14 +593,16 @@ def listImages(params: Dict[str, Any]) -> Dict[str, Optional[List[int]]]:
return images


def listAssets(params: Dict[str, Any]) -> Dict[str, List[Any]]:
def listAssets(params: str|Dict[str, Any]) -> Dict[str, List[Any]]:
"""Returns the assets in a folder.
Args:
params: An object containing request parameters with the following possible
values, all but 'parent` are optional:
params: Either a string representing the ID of the collection or folder to
list, or an object containing request parameters with the following
possible values, all but 'parent` are optional:
parent - (string) The ID of the collection or folder to list, required.
pageSize - (string) The number of results to return. Defaults to 1000.
pageSize - (string) The number of results to return. If not specified, all
results are returned.
pageToken - (string) The token page of results to return.
filter - (string) An additional filter query to apply. Example query:
'''properties.my_property>=1 AND properties.my_property<2 AND
Expand Down
81 changes: 81 additions & 0 deletions python/ee/daterange.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,84 @@ def reset(cls) -> None:
@staticmethod
def name() -> str:
return 'DateRange'

def contains(
self, other: Union[_DateType, _DateRangeType]
) -> computedobject.ComputedObject:
"""Returns true if the given Date or DateRange is within this DateRange.
Args:
other: The Date or DateRange to check if it is inside the DateRange.
Returns:
A Boolean ComputedObject.
"""

return apifunction.ApiFunction.call_(self.name() + '.contains', self, other)

def end(self) -> ee_date.Date:
"""Returns the (exclusive) end of this DateRange."""

return apifunction.ApiFunction.call_(self.name() + '.end', self)

def intersection(
self, other: Union[_DateType, _DateRangeType]
) -> 'DateRange':
"""Returns a DateRange that contains all the timespan of this and other.
Args:
other: The other DateRange to include in the intersection.
Raises:
EEException if the result is an empty DateRange.
Returns:
An ee.DateRange.
"""

return apifunction.ApiFunction.call_(
self.name() + '.intersection', self, other
)

def intersects(
self, other: Union[_DateType, _DateRangeType]
) -> computedobject.ComputedObject:
"""Returns true if the other DateRange has at least one time in common.
Args:
other: The other DateRange to check against.
Returns:
A Boolean ComputedObject.
"""

return apifunction.ApiFunction.call_(
self.name() + '.intersects', self, other
)

def isEmpty(self) -> computedobject.ComputedObject:
"""Returns true if this DateRange contains no dates, i.e. start >= end."""

return apifunction.ApiFunction.call_(self.name() + '.isEmpty', self)

def isUnbounded(self) -> computedobject.ComputedObject:
"""Returns true if this DateRange contains all dates."""

return apifunction.ApiFunction.call_(self.name() + '.isUnbounded', self)

def start(self) -> ee_date.Date:
"""Returns the (inclusive) start of this DateRange."""

return apifunction.ApiFunction.call_(self.name() + '.start', self)

def union(self, other: Union[_DateType, _DateRangeType]) -> DateRange:
"""Returns a DateRange that contains all points in this and other.
Args:
other: The DateRange to union with.
Returns:
An ee.DateRange.
"""

return apifunction.ApiFunction.call_(self.name() + '.union', self, other)
Loading

5 comments on commit 387e451

@schwehr
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yanked this release as it is broken for python 3.7-3.9 https://pypi.org/project/earthengine-api/0.1.396/

@12rambau
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guys this release has broken all our pipelines please avoid the use of pipes in the python hints and rollback to the Union from typing lib

@schwehr
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you pulling the code from pypi, conda forge, github, or somewhere else?

@12rambau
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pypi but I see the 0.1.396 is already yanked

@schwehr
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've also backed github master up to 0.1.395.

Please sign in to comment.