Source code for openeo_udf.api.feature_collection

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""OpenEO Python UDF interface"""

import geopandas
import pandas
import json
from typing import Optional, Dict
from openeo_udf.api.collection_base import CollectionBase


__license__ = "Apache License, Version 2.0"
__author__     = "Soeren Gebbert"
__copyright__  = "Copyright 2018, Soeren Gebbert"
__maintainer__ = "Soeren Gebbert"
__email__      = "soerengebbert@googlemail.com"


[docs]class FeatureCollection(CollectionBase): """A feature collection that represents a subset or a whole feature collection where single vector features may have time stamps assigned. Some basic tests: >>> from shapely.geometry import Point >>> import geopandas >>> p1 = Point(0,0) >>> p2 = Point(100,100) >>> p3 = Point(100,0) >>> pseries = [p1, p2, p3] >>> data = geopandas.GeoDataFrame(geometry=pseries, columns=["a", "b"]) >>> data["a"] = [1,2,3] >>> data["b"] = ["a","b","c"] >>> fct = FeatureCollection(id="test", data=data) >>> print(fct) id: test start_times: None end_times: None data: a b geometry 0 1 a POINT (0 0) 1 2 b POINT (100 100) 2 3 c POINT (100 0) >>> import json >>> json.dumps(fct.to_dict()) # doctest: +ELLIPSIS ... # doctest: +NORMALIZE_WHITESPACE '{"id": "test", "data": {"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {"a": 1, "b": "a"}, "geometry": {"type": "Point", "coordinates": [0.0, 0.0]}}, {"id": "1", "type": "Feature", "properties": {"a": 2, "b": "b"}, "geometry": {"type": "Point", "coordinates": [100.0, 100.0]}}, {"id": "2", "type": "Feature", "properties": {"a": 3, "b": "c"}, "geometry": {"type": "Point", "coordinates": [100.0, 0.0]}}]}}' >>> p1 = Point(0,0) >>> pseries = [p1] >>> data = geopandas.GeoDataFrame(geometry=pseries, columns=["a", "b"]) >>> data["a"] = [1] >>> data["b"] = ["a"] >>> dates = [pandas.Timestamp('2012-05-01')] >>> starts = pandas.DatetimeIndex(dates) >>> dates = [pandas.Timestamp('2012-05-02')] >>> ends = pandas.DatetimeIndex(dates) >>> fct = FeatureCollection(id="test", start_times=starts, end_times=ends, data=data) >>> print(fct) id: test start_times: DatetimeIndex(['2012-05-01'], dtype='datetime64[ns]', freq=None) end_times: DatetimeIndex(['2012-05-02'], dtype='datetime64[ns]', freq=None) data: a b geometry 0 1 a POINT (0 0) >>> import json >>> json.dumps(fct.to_dict()) # doctest: +ELLIPSIS ... # doctest: +NORMALIZE_WHITESPACE '{"id": "test", "start_times": ["2012-05-01T00:00:00"], "end_times": ["2012-05-02T00:00:00"], "data": {"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {"a": 1, "b": "a"}, "geometry": {"type": "Point", "coordinates": [0.0, 0.0]}}]}}' >>> fct = FeatureCollection.from_dict(fct.to_dict()) >>> json.dumps(fct.to_dict()) # doctest: +ELLIPSIS ... # doctest: +NORMALIZE_WHITESPACE '{"id": "test", "start_times": ["2012-05-01T00:00:00"], "end_times": ["2012-05-02T00:00:00"], "data": {"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {"a": 1, "b": "a"}, "geometry": {"type": "Point", "coordinates": [0.0, 0.0]}}]}}' """ def __init__(self, id: str, data: geopandas.GeoDataFrame, start_times: Optional[pandas.DatetimeIndex]=None, end_times: Optional[pandas.DatetimeIndex]=None): """Constructor of the of a vector collection Args: id (str): The unique id of the vector collection data (geopandas.GeoDataFrame): A GeoDataFrame with geometry column and attribute data start_times (pandas.DateTimeIndex): The vector with start times for each spatial x,y slice end_times (pandas.DateTimeIndex): The pandas.DateTimeIndex vector with end times for each spatial x,y slice, if no end times are defined, then time instances are assumed not intervals """ CollectionBase.__init__(self, id=id, start_times=start_times, end_times=end_times) self.set_data(data) self.check_data_with_time() def __str__(self): return "id: %(id)s\n" \ "start_times: %(start_times)s\n" \ "end_times: %(end_times)s\n" \ "data: %(data)s"%{"id":self.id, "extent":self.extent, "start_times":self.start_times, "end_times":self.end_times, "data":self.data}
[docs] def get_data(self) -> geopandas.GeoDataFrame: """Return the geopandas.GeoDataFrame that contains the geometry column and any number of attribute columns Returns: geopandas.GeoDataFrame: A data frame that contains the geometry column and any number of attribute columns """ return self._data
[docs] def set_data(self, data: geopandas.GeoDataFrame): """Set the geopandas.GeoDataFrame that contains the geometry column and any number of attribute columns This function will check if the provided data is a geopandas.GeoDataFrame and raises an Exception Args: data (geopandas.GeoDataFrame): A GeoDataFrame with geometry column and attribute data """ if isinstance(data, geopandas.GeoDataFrame) is False: raise Exception("Argument data must be of type geopandas.GeoDataFrame") self._data = data
data = property(fget=get_data, fset=set_data)
[docs] def to_dict(self) -> Dict: """Convert this FeatureCollection into a dictionary that can be converted into a valid JSON representation Returns: dict: FeatureCollection as a dictionary """ d = {"id": self.id} if self._start_times is not None: d.update(self.start_times_to_dict()) if self._end_times is not None: d.update(self.end_times_to_dict()) if self._data is not None: d["data"] = json.loads(self._data.to_json()) return d
[docs] @staticmethod def from_dict(fct_dict: Dict): """Create a feature collection from a python dictionary that was created from the JSON definition of the FeatureCollection Args: fct_dict (dict): The dictionary that contains the feature collection definition Returns: FeatureCollection: A new FeatureCollection object """ if "id" not in fct_dict: raise Exception("Missing id in dictionary") if "data" not in fct_dict: raise Exception("Missing data in dictionary") fct = FeatureCollection(id =fct_dict["id"], data=geopandas.GeoDataFrame.from_features(fct_dict["data"])) if "start_times" in fct_dict: fct.set_start_times_from_list(fct_dict["start_times"]) if "end_times" in fct_dict: fct.set_end_times_from_list(fct_dict["end_times"]) return fct
if __name__ == "__main__": import doctest doctest.testmod()