earthengine-api/python/ee/collection.py
2013-09-13 12:19:21 -07:00

290 lines
10 KiB
Python

"""Common representation for ImageCollection and FeatureCollection.
This class is never intended to be instantiated by the user.
"""
# Using lowercase function naming to match the JavaScript names.
# pylint: disable=g-bad-name
import apifunction
import computedobject
import customfunction
import ee_exception
import ee_types
import filter # pylint: disable=redefined-builtin
import function
class Collection(computedobject.ComputedObject):
"""Base class for ImageCollection and FeatureCollection."""
_initialized = False
# The serial number of the next mapping variable.
_serialMappingId = 0
def __init__(self, func, args):
"""Constructs a collection by initializing its ComputedObject."""
super(Collection, self).__init__(func, args)
@classmethod
def initialize(cls):
"""Imports API functions to this class."""
if not cls._initialized:
apifunction.ApiFunction.importApi(cls, 'Collection', 'Collection')
apifunction.ApiFunction.importApi(
cls, 'AggregateFeatureCollection', 'Collection', 'aggregate_')
cls._initialized = True
@classmethod
def reset(cls):
"""Removes imported API functions from this class.
Also resets the serial ID used for mapping Python functions to 0.
"""
apifunction.ApiFunction.clearApi(cls)
cls._initialized = False
cls._serialMappingId = 0
def filter(self, new_filter):
"""Apply a filter to this collection.
Args:
new_filter: Filter to add to this collection.
Returns:
The filtered collection object.
"""
if not new_filter:
raise ee_exception.EEException('Empty filters.')
return self._cast(apifunction.ApiFunction.call_(
'Collection.filter', self, new_filter))
def filterMetadata(self, name, operator, value):
"""Shortcut to add a metadata filter to a collection.
This is equivalent to self.filter(Filter().metadata(...)).
Args:
name: Name of a property to filter.
operator: Name of a comparison operator as defined
by FilterCollection. Possible values are: "equals", "less_than",
"greater_than", "not_equals", "not_less_than", "not_greater_than",
"starts_with", "ends_with", "not_starts_with", "not_ends_with",
"contains", "not_contains".
value: The value to compare against.
Returns:
The filtered collection.
"""
return self.filter(filter.Filter.metadata_(name, operator, value))
def filterBounds(self, geometry):
"""Shortcut to add a geometry filter to a collection.
Items in the collection with a footprint that fails to intersect
the given geometry will be excluded when the collection is evaluated.
This is equivalent to self.filter(Filter().geometry(...)).
Args:
geometry: The boundary to filter to either as a GeoJSON geometry,
or a FeatureCollection, from which a geometry will be extracted.
Returns:
The filter object.
"""
return self.filter(filter.Filter.geometry(geometry))
def filterDate(self, start, opt_end=None):
"""Shortcut to filter a collection with a date range.
Items in the collection with a time_start property that doesn't
fall between the start and end dates will be excluded.
This is equivalent to self.filter(Filter().date(...)).
Args:
start: The start date as a Date object, a string representation of
a date, or milliseconds since epoch.
opt_end: The end date as a Date object, a string representation of
a date, or milliseconds since epoch.
Returns:
The filter object.
"""
return self.filter(filter.Filter.date(start, opt_end))
def getInfo(self):
"""Returns all the known information about this collection.
This function makes an REST call to to retrieve all the known information
about this collection.
Returns:
The return contents vary but will include at least:
features: an array containing metadata about the items in the
collection that passed all filters.
properties: a dictionary containing the collection's metadata
properties.
"""
return super(Collection, self).getInfo()
def limit(self, maximum, opt_property=None, opt_ascending=None):
"""Limit a collection to the specified number of elements.
This limits a collection to the specified number of elements, optionally
sorting them by a specified property first.
Args:
maximum: The number to limit the collection to.
opt_property: The property to sort by, if sorting.
opt_ascending: Whether to sort in ascending or descending order.
The default is true (ascending).
Returns:
The collection.
"""
args = {'collection': self, 'limit': maximum}
if opt_property is not None:
args['key'] = opt_property
if opt_ascending is not None:
args['ascending'] = opt_ascending
return self._cast(
apifunction.ApiFunction.apply_('Collection.limit', args))
def sort(self, prop, opt_ascending=None):
"""Sort a collection by the specified property.
Args:
prop: The property to sort by.
opt_ascending: Whether to sort in ascending or descending
order. The default is true (ascending).
Returns:
The collection.
"""
args = {'collection': self, 'key': prop}
if opt_ascending is not None:
args['ascending'] = opt_ascending
return self._cast(
apifunction.ApiFunction.apply_('Collection.limit', args))
@classmethod
def _cast(cls, obj):
"""Cast a ComputedObject to a new instance of the same class as this.
Args:
obj: The object to cast.
Returns:
The cast object, and instance of the class on which this method is called.
"""
if isinstance(obj, cls):
return obj
else:
# Assumes all subclass constructors can be called with a
# ComputedObject as their first parameter.
return cls(obj)
@staticmethod
def name():
return 'Collection'
def mapInternal(self,
cls,
algorithm,
opt_dynamicArgs=None,
opt_constantArgs=None,
opt_destination=None):
"""Maps an algorithm over a collection.
Args:
cls: The collection elements' type (class).
algorithm: The operation to map over the images or features of the
collection. Either an algorithm name as a string, or a Python
function that receives an image or features and returns one. If a
function is passed, it is called only once and the result is captured
as a description, so it cannot perform imperative operations or rely
on external state.
opt_dynamicArgs: A map specifying which properties of the input objects
to pass to each argument of the algorithm. This maps from argument
names to selector strings. Selector strings are property names,
optionally concatenated into chains separated by a period to access
properties-of-properties. To pass the whole object, use the special
selector string '.all', and to pass the geometry, use '.geo'. If
this argument is not specified, the names of the arguments will be
matched exactly to the properties of the input object. If algorithm
is a Python function, this must be null or undefined as the
image will always be the only dynamic argument.
opt_constantArgs: A map from argument names to constant values to be
passed to the algorithm on every invocation.
opt_destination: The property where the result of the algorithm will be
put. If this is null or undefined, the result of the algorithm will
replace the input, as is the usual behavior of a mapping opeartion.
Returns:
The mapped collection.
Raises:
RuntimeError: if algorithm is a Python function and opt_dynamicArgs is
specified.
"""
if callable(algorithm):
if opt_dynamicArgs:
# TODO(user): Remove this once we have a getProperty() algorithm.
raise ee_exception.EEException(
'Can\'t use dynamicArgs with a mapped Python function.')
varName = '_MAPPING_VAR_%d' % Collection._serialMappingId
Collection._serialMappingId += 1
typeName = ee_types.classToName(cls)
sig = {'returns': typeName, 'args': [{'name': varName, 'type': typeName}]}
algorithm = customfunction.CustomFunction(sig, algorithm)
elif isinstance(algorithm, basestring):
algorithm = apifunction.ApiFunction.lookup(algorithm)
elif not isinstance(algorithm, function.Function):
raise ee_exception.EEException(
'Can\'t map non-callable object: %s' % algorithm)
args = {
'collection': self,
'baseAlgorithm': algorithm
}
if opt_dynamicArgs:
args['dynamicArgs'] = opt_dynamicArgs
else:
# Use the function's first argument.
varName = algorithm.getSignature()['args'][0]['name']
args['dynamicArgs'] = {varName: '.all'}
if opt_constantArgs: args['constantArgs'] = opt_constantArgs
if opt_destination: args['destination'] = opt_destination
return self._cast(apifunction.ApiFunction.apply_('Collection.map', args))
@classmethod
def createAutoMapFunctions(cls, elementClass):
"""Creates map_*() methods on collectionClass.
Creates a map_* method on collectionClass for each generated instance
method on elementClass that maps that method over the collection.
TODO(user): Deprecate these.
Args:
elementClass: The collection elements' type.
"""
for name in dir(elementClass):
method = getattr(elementClass, name)
if callable(method) and hasattr(method, 'signature'):
def MakeMapFunction(name, method): # Capture scope.
def MapFunction(self, *args, **kwargs):
method_caller = lambda elem: method(elem, *args, **kwargs)
destination = None
if not ee_types.isSubtype('EEObject', method.signature['returns']):
destination = name
return self.mapInternal(
elementClass, method_caller, None, None, destination)
return MapFunction
setattr(cls, 'map_' + name, MakeMapFunction(name, method))