"""Representation of an Earth Engine FeatureCollection.""" # Using lowercase function naming to match the JavaScript names. # pylint: disable=g-bad-name from __future__ import annotations from typing import Any, Dict, List, Optional, Type, Union from ee import _arg_types from ee import _utils from ee import apifunction from ee import collection from ee import computedobject from ee import data from ee import deprecation from ee import ee_exception from ee import ee_list from ee import ee_types from ee import feature from ee import geometry from ee import image class FeatureCollection(collection.Collection): """A representation of a FeatureCollection.""" _initialized = False # Tell pytype to not complain about dynamic attributes. _HAS_DYNAMIC_ATTRIBUTES = True @_utils.accept_opt_prefix('opt_column') @deprecation.WarnForDeprecatedAsset('args') def __init__( self, args: Optional[ Union[ Dict[str, Any], List[Any], str, feature.Feature, geometry.Geometry, computedobject.ComputedObject, ] ], column: Optional[Any] = None, ): """Constructs a collection features. Args: args: constructor argument. One of: 1) A string - assumed to be the name of a collection. 2) A geometry. 3) A feature. 4) An array of features. 5) A GeoJSON FeatureCollection. 6) A computed object - reinterpreted as a collection. column: The name of the geometry column to use. Only useful with the string constructor. Raises: EEException: if passed something other than the above. """ self.initialize() # Wrap geometries with features. if isinstance(args, geometry.Geometry): args = feature.Feature(args) # Wrap single features in an array. if isinstance(args, feature.Feature): args = [args] if ee_types.isString(args): # An ID. actual_args = {'tableId': args} if column: actual_args['geometryColumn'] = column super().__init__( apifunction.ApiFunction.lookup('Collection.loadTable'), actual_args) elif isinstance(args, (list, tuple)): # A list of features. super().__init__( apifunction.ApiFunction.lookup('Collection'), { 'features': [feature.Feature(i) for i in args] }) elif isinstance(args, ee_list.List): # A computed list of features. super().__init__( apifunction.ApiFunction.lookup('Collection'), { 'features': args }) elif isinstance(args, dict) and args.get('type') == self.name(): # A GeoJSON FeatureCollection super().__init__( apifunction.ApiFunction.lookup('Collection'), {'features': [feature.Feature(i) for i in args.get('features', [])]}) elif isinstance(args, computedobject.ComputedObject): # A custom object to reinterpret as a FeatureCollection. super().__init__(args.func, args.args, args.varName) else: raise ee_exception.EEException( 'Unrecognized argument type to convert to a FeatureCollection: %s' % args) @classmethod def initialize(cls) -> None: """Imports API functions to this class.""" if not cls._initialized: super().initialize() apifunction.ApiFunction.importApi(cls, cls.name(), cls.name()) cls._initialized = True @classmethod def reset(cls) -> None: """Removes imported API functions from this class.""" apifunction.ApiFunction.clearApi(cls) cls._initialized = False def getMapId( self, vis_params: Optional[Dict[str, Any]] = None ) -> Dict[str, Any]: """Fetch and return a map id and token, suitable for use in a Map overlay. Args: vis_params: The visualization parameters. Currently only one parameter, 'color', containing a hex RGB color string is allowed. Returns: A map ID dictionary as described in ee.data.getMapId, including an additional 'image' field containing Collection.draw image wrapping a FeatureCollection containing this feature. """ painted = apifunction.ApiFunction.apply_('Collection.draw', { 'collection': self, 'color': (vis_params or {}).get('color', '000000') }) return painted.getMapId({}) def getDownloadURL( self, filetype: Optional[str] = None, selectors: Optional[Any] = None, filename: Optional[str] = None, ) -> str: """Gets a download URL. When the URL is accessed, the FeatureCollection is downloaded in one of several formats. Args: filetype: The format of download, one of: "csv", "json", "geojson", "kml", "kmz" ("json" outputs GeoJSON). If unspecified, defaults to "csv". selectors: Feature property names used to select the attributes to be downloaded. If unspecified, all properties are included. filename: Name of the file to be downloaded; extension is appended by default. If unspecified, defaults to "table". Returns: A URL to download this FeatureCollection. """ request = {} request['table'] = self if filetype is not None: request['format'] = filetype.upper() 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)) # Deprecated spelling to match the JS library. getDownloadUrl = deprecation.Deprecated('Use getDownloadURL().')( getDownloadURL) # TODO(user): How to handle type annotations for # feature_collection.select('a', 'b', 'c')? # pylint: disable-next=keyword-arg-before-vararg def select( self, propertySelectors: Any, newProperties: Optional[Any] = None, retainGeometry: Union[bool, str] = True, *args, ) -> FeatureCollection: """Select properties from each feature in a collection. Args: propertySelectors: An array of names or regexes specifying the properties to select. newProperties: An array of strings specifying the new names for the selected properties. If supplied, the length must match the number of properties selected. retainGeometry: A boolean. When false, the result will have no geometry. *args: Selector elements as varargs. Returns: The feature collection with selected properties. """ if len(args) or ee_types.isString(propertySelectors): args = list(args) if not isinstance(retainGeometry, bool): args.insert(0, retainGeometry) if newProperties is not None: args.insert(0, newProperties) args.insert(0, propertySelectors) return self.map(lambda feat: feat.select(args, None, True)) else: return self.map( # pylint: disable-next=g-long-lambda lambda feat: feat.select( propertySelectors, newProperties, retainGeometry ) ) @staticmethod def name() -> str: return 'FeatureCollection' @staticmethod def elementType() -> Type[feature.Feature]: return feature.Feature def classify( self, classifier: _arg_types.Classifier, # pylint: disable-next=invalid-name outputName: Optional[_arg_types.String] = None, ) -> FeatureCollection: """Returns the result of classifying each feature in a collection. Args: classifier: The classifier to use. outputName: The name of the output property to be added. This argument is ignored if the classifier has more than one output. Returns: An ee.FeatureCollection. """ return apifunction.ApiFunction.call_( self.name() + '.classify', self, classifier, outputName ) def cluster( self, clusterer: _arg_types.Clusterer, # pylint: disable-next=invalid-name outputName: Optional[_arg_types.String] = None, ) -> FeatureCollection: """Returns the results of clustering each feature in a collection. Clusters each feature in a collection, adding a new column to each feature containing the cluster number to which it has been assigned. Args: clusterer: The clusterer to use. outputName: The name of the output property to be added. """ return apifunction.ApiFunction.call_( self.name() + '.cluster', self, clusterer, outputName ) def inverseDistance( self, range: _arg_types.Number, # pylint: disable=redefined-builtin propertyName: _arg_types.String, # pylint: disable=invalid-name mean: _arg_types.Number, stdDev: _arg_types.Number, # pylint: disable=invalid-name gamma: Optional[_arg_types.Number] = None, reducer: Optional[_arg_types.Reducer] = None, ) -> image.Image: """Returns an inverse-distance weighted estimate of the value at each pixel. Args: range: Size of the interpolation window (in meters). propertyName: Name of the numeric property to be estimated. mean: Global expected mean. stdDev: Global standard deviation. gamma: Determines how quickly the estimates tend towards the global mean. reducer: Reducer used to collapse the 'propertyName' value of overlapping points into a single value. """ return apifunction.ApiFunction.call_( self.name() + '.inverseDistance', self, range, propertyName, mean, stdDev, gamma, reducer, ) def kriging( self, propertyName: _arg_types.String, # pylint: disable=invalid-name shape: _arg_types.String, range: _arg_types.Number, # pylint: disable=redefined-builtin sill: _arg_types.Number, nugget: _arg_types.Number, # pylint: disable-next=invalid-name maxDistance: Optional[_arg_types.Number] = None, reducer: Optional[_arg_types.Reducer] = None, ) -> image.Image: """Returns the results of sampling a Kriging estimator at each pixel. Args: propertyName: Property to be estimated (must be numeric). shape: Semivariogram shape (one of {exponential, gaussian, spherical}). range: Semivariogram range, in meters. sill: Semivariogram sill. nugget: Semivariogram nugget. maxDistance: Radius which determines which features are included in each pixel's computation, in meters. Defaults to the semivariogram's range. reducer: Reducer used to collapse the 'propertyName' value of overlapping points into a single value. """ return apifunction.ApiFunction.call_( self.name() + '.kriging', self, propertyName, shape, range, sill, nugget, maxDistance, reducer, ) def makeArray( self, properties: _arg_types.List, name: Optional[_arg_types.String] = None, ) -> FeatureCollection: """Returns a collection with a 1-D Array property for each feature. Add a 1-D Array to each feature in a collection by combining a list of properties for each feature into a 1-D Array. All of the properties must be numeric values. If a feature doesn't contain all of the named properties, or any of them aren't numeric, the feature will be dropped from the resulting collection. Args: properties: The properties to select. name: The name of the new array property. """ return apifunction.ApiFunction.call_( self.name() + '.makeArray', self, properties, name ) @staticmethod def randomPoints( region: geometry.Geometry, points: Optional[_arg_types.Integer] = None, seed: Optional[_arg_types.Integer] = None, # pylint: disable-next=invalid-name maxError: Optional[_arg_types.ErrorMargin] = None, ) -> FeatureCollection: """Returns a collection of random points. Generates points that are uniformly random in the given geometry. If the geometry is two-dimensional (polygon or multi-polygon) then the returned points are uniformly distributed on the given region of the sphere. If the geometry is one-dimensional (linestrings), the returned points are interpolated uniformly along the geometry's edges. If the geometry has dimension zero (points), the returned points are sampled uniformly from the input points. If a multi-geometry of mixed dimension is given, points are sampled from the component geometries with the highest dimension. Args: region: The region to generate points for. points: The number of points to generate. seed: A seed for the random number generator. maxError: The maximum amount of error tolerated when performing any necessary reprojection. """ return apifunction.ApiFunction.call_( 'FeatureCollection.randomPoints', region, points, seed, maxError )