"""Toolbox for the `ee.FeatureCollection` class."""
from __future__ import annotations
from typing import Union
import ee
from geetools.accessors import register_class_accessor
from geetools.types import ee_int, ee_str
@register_class_accessor(ee.FeatureCollection, "geetools")
[docs]
class FeatureCollectionAccessor:
"""Toolbox for the `ee.FeatureCollection` class."""
def __init__(self, obj: ee.FeatureCollection):
"""Initialize the FeatureCollection class."""
self._obj = obj
[docs]
def toImage(
self,
color: Union[ee_str, ee_int] = 0,
width: Union[ee_str, ee_int] = "",
) -> ee.Image:
"""Paint the current FeatureCollection to an Image.
It's simply a wrapper on Image.paint() method
Args:
color: The pixel value to paint into every band of the input image, either as a number which will be used for all features, or the name of a numeric property to take from each feature in the collection.
width: Line width, either as a number which will be the line width for all geometries, or the name of a numeric property to take from each feature in the collection. If unspecified, the geometries will be filled instead of outlined.
"""
params = {"color": color}
width == "" or params.update(width=width)
return ee.Image().paint(self._obj, **params)
[docs]
def addId(self, name: ee_str = "id", start: ee_int = 1) -> ee.FeatureCollection:
"""Add a unique numeric identifier, starting from parameter ``start``.
Returns:
The parsed collection with a new id property
Example:
.. code-block:: python
import ee
import geetools
ee.Initialize()
fc = ee.FeatureCollection('FAO/GAUL/2015/level0')
fc = fc.geetools.addId()
print(fc.first().get('id').getInfo())
"""
start, name = ee.Number(start).toInt(), ee.String(name)
indexes = ee.List(self._obj.aggregate_array("system:index"))
ids = ee.List.sequence(start, start.add(self._obj.size()).subtract(1))
idByIndex = ee.Dictionary.fromLists(indexes, ids)
return self._obj.map(lambda f: f.set(name, idByIndex.get(f.get("system:index"))))
[docs]
def mergeGeometries(self) -> ee.Geometry:
"""Merge the geometries the included features.
Returns:
the dissolved geometry
Example:
.. code-block:: python
import ee
import geetools
ee.Initialize()
fc = ee.FeatureCollection("FAO/GAUL/2015/level0")
fc =fc.filter(ee.Filter.inList("ADM0_CODE", [122, 237, 85]))
geom = fc.geetools.mergeGeometries()
print(geom.getInfo())
"""
first = self._obj.first().geometry()
union = self._obj.iterate(lambda f, g: f.geometry().union(g), first)
return ee.Geometry(union).dissolve()
[docs]
def toPolygons(self) -> ee.FeatureCollection:
"""Drop any geometry that is not a Polygon or a multipolygon.
This method is made to avoid errors when performing zonal statistics and/or other surfaces operations. These operations won't work on geometries that are Lines or points. The methods remove these geometry types from GEometryCollections and rremove features that don't have any polygon geometry
Returns:
The parsed collection with only polygon/MultiPolygon geometries
Example:
.. code-block:: python
import ee
import geetools
ee.Initialize()
point0 = ee.Geometry.Point([0,0], proj="EPSG:4326")
point1 = ee.Geometry.Point([0,1], proj="EPSG:4326")
poly0 = point0.buffer(1, proj="EPSG:4326")
poly1 = point1.buffer(1, proj="EPSG:4326").bounds(proj="EPSG:4326")
line = ee.Geometry.LineString([point1, point0], proj="EPSG:4326")
multiPoly = ee.Geometry.MultiPolygon([poly0, poly1], proj="EPSG:4326")
geometryCol = ee.Algorithms.GeometryConstructors.MultiGeometry([multiPoly, poly0, poly1, point0, line], crs="EPSG:4326", geodesic=True, maxError=1)
fc = ee.FeatureCollection([geometryCol])
fc = fc.geetools.toPolygons()
print(fc.getInfo())
"""
def filterGeom(geom):
geom = ee.Geometry(geom)
return ee.Algorithms.If(geom.type().compareTo("Polygon"), None, geom)
def removeNonPoly(feat):
filteredGeoms = feat.geometry().geometries().map(filterGeom, True)
proj = feat.geometry().projection()
return feat.setGeometry(ee.Geometry.MultiPolygon(filteredGeoms, proj))
return self._obj.map(removeNonPoly)