Skip to content

Commit 1fba8f8

Browse files
committed
EP-3981 #114 add tests for aggregate_spatial with vector cubes
1 parent 2dbc599 commit 1fba8f8

File tree

3 files changed

+143
-12
lines changed

3 files changed

+143
-12
lines changed

openeo_driver/datacube.py

+12-6
Original file line numberDiff line numberDiff line change
@@ -186,14 +186,20 @@ def _as_geopandas_df(self) -> gpd.GeoDataFrame:
186186
# TODO: avoid copy?
187187
df = self._geometries.copy(deep=True)
188188
if self._cube is not None:
189+
assert self._cube.dims[0] == self.DIM_GEOMETRIES
189190
# TODO: better way to combine cube with geometries
190191
# Flatten multiple (non-geometry) dimensions from cube to new properties in geopandas dataframe
191-
stacked = self._cube.stack(prop=self._cube.dims[1:])
192-
log.info(f"Flattened cube component of vector cube to {stacked.shape[1]} properties")
193-
for p in stacked.indexes["prop"]:
194-
name = "~".join(str(x) for x in p)
195-
# TODO: avoid column collisions?
196-
df[name] = stacked.sel(prop=p)
192+
prefix = self._cube.attrs.get("prefix", "cube")
193+
if self._cube.dims[1:]:
194+
stacked = self._cube.stack(prop=self._cube.dims[1:])
195+
log.info(f"Flattened cube component of vector cube to {stacked.shape[1]} properties")
196+
for p in stacked.indexes["prop"]:
197+
name = "~".join(str(x) for x in [prefix] + list(p))
198+
# TODO: avoid column collisions?
199+
df[name] = stacked.sel(prop=p)
200+
else:
201+
df[prefix] = self._cube
202+
197203
return df
198204

199205
def to_geojson(self):

openeo_driver/dummy/dummy_backend.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,9 @@ def assert_polygon_sequence(geometries: Union[Sequence, BaseMultipartGeometry]):
233233
coords[self.metadata.band_dimension.name] = self.metadata.band_names
234234
shape = [len(coords[d]) for d in dims]
235235
data = numpy.arange(numpy.prod(shape)).reshape(shape)
236-
cube = xarray.DataArray(data=data, dims=dims, coords=coords, name="aggregate_spatial")
236+
cube = xarray.DataArray(
237+
data=data, dims=dims, coords=coords, name="aggregate_spatial", attrs={"prefix": "agg"}
238+
)
237239
return geometries.with_cube(cube=cube)
238240
elif isinstance(geometries, str):
239241
geometries = [geometry for geometry in DelayedVector(geometries).geometries]

tests/test_views_execute.py

+128-5
Original file line numberDiff line numberDiff line change
@@ -727,7 +727,7 @@ def test_aggregate_spatial_invalid_geometry(api100, geometries, expected):
727727
_ = api100.result(pg).assert_error(400, "ProcessParameterInvalid", expected)
728728

729729

730-
def test_aggregate_spatial_vector_cube(api100):
730+
def test_aggregate_spatial_vector_cube_basic(api100):
731731
path = get_path("geojson/FeatureCollection02.json")
732732
pg = {
733733
"lc": {"process_id": "load_collection", "arguments": {"id": "S2_FOOBAR", "bands": ["B02", "B03", "B04"]}},
@@ -761,23 +761,146 @@ def test_aggregate_spatial_vector_cube(api100):
761761
"geometry": {"type": "Polygon", "coordinates": [[[1, 1], [3, 1], [2, 3], [1, 1]]]},
762762
"properties": {
763763
"id": "first", "pop": 1234,
764-
"2015-07-06T00:00:00~B02": 0, "2015-07-06T00:00:00~B03": 1, "2015-07-06T00:00:00~B04": 2,
765-
"2015-08-22T00:00:00~B02": 3, "2015-08-22T00:00:00~B03": 4, "2015-08-22T00:00:00~B04": 5,
764+
"agg~2015-07-06T00:00:00~B02": 0,
765+
"agg~2015-07-06T00:00:00~B03": 1,
766+
"agg~2015-07-06T00:00:00~B04": 2,
767+
"agg~2015-08-22T00:00:00~B02": 3,
768+
"agg~2015-08-22T00:00:00~B03": 4,
769+
"agg~2015-08-22T00:00:00~B04": 5,
766770
},
767771
}),
768772
DictSubSet({
769773
"type": "Feature",
770774
"geometry": {"type": "Polygon", "coordinates": [[[4, 2], [5, 4], [3, 4], [4, 2]]]},
771775
"properties": {
772776
"id": "second", "pop": 5678,
773-
"2015-07-06T00:00:00~B02": 6, "2015-07-06T00:00:00~B03": 7, "2015-07-06T00:00:00~B04": 8,
774-
"2015-08-22T00:00:00~B02": 9, "2015-08-22T00:00:00~B03": 10, "2015-08-22T00:00:00~B04": 11,
777+
"agg~2015-07-06T00:00:00~B02": 6,
778+
"agg~2015-07-06T00:00:00~B03": 7,
779+
"agg~2015-07-06T00:00:00~B04": 8,
780+
"agg~2015-08-22T00:00:00~B02": 9,
781+
"agg~2015-08-22T00:00:00~B03": 10,
782+
"agg~2015-08-22T00:00:00~B04": 11,
775783
},
776784
}),
777785
]
778786
})
779787

780788

789+
@pytest.mark.parametrize(["info", "preprocess_pg", "aggregate_data", "p1_properties", "p2_properties"], [
790+
(
791+
"time-and-bands",
792+
{},
793+
"lc",
794+
{
795+
"id": "first", "pop": 1234,
796+
"agg~2015-07-06T00:00:00~B02": 0, "agg~2015-07-06T00:00:00~B03": 1, "agg~2015-07-06T00:00:00~B04": 2,
797+
"agg~2015-08-22T00:00:00~B02": 3, "agg~2015-08-22T00:00:00~B03": 4, "agg~2015-08-22T00:00:00~B04": 5,
798+
},
799+
{
800+
"id": "second", "pop": 5678,
801+
"agg~2015-07-06T00:00:00~B02": 6, "agg~2015-07-06T00:00:00~B03": 7, "agg~2015-07-06T00:00:00~B04": 8,
802+
"agg~2015-08-22T00:00:00~B02": 9, "agg~2015-08-22T00:00:00~B03": 10, "agg~2015-08-22T00:00:00~B04": 11,
803+
},
804+
),
805+
(
806+
"no-time",
807+
{
808+
"r": {"process_id": "reduce_dimension", "arguments": {
809+
"data": {"from_node": "lc"},
810+
"dimension": "t",
811+
"reducer": {"process_graph": {"mean": {
812+
"process_id": "mean", "arguments": {"data": {"from_parameter": "data"}}, "result": True,
813+
}}},
814+
}},
815+
},
816+
"r",
817+
{"id": "first", "pop": 1234, "agg~B02": 0, "agg~B03": 1, "agg~B04": 2},
818+
{"id": "second", "pop": 5678, "agg~B02": 3, "agg~B03": 4, "agg~B04": 5},
819+
),
820+
(
821+
"no-bands",
822+
{
823+
"r": {"process_id": "reduce_dimension", "arguments": {
824+
"data": {"from_node": "lc"},
825+
"dimension": "bands",
826+
"reducer": {"process_graph": {"mean": {
827+
"process_id": "mean", "arguments": {"data": {"from_parameter": "data"}}, "result": True,
828+
}}},
829+
}}
830+
},
831+
"r",
832+
{"id": "first", "pop": 1234, "agg~2015-07-06T00:00:00": 0, "agg~2015-08-22T00:00:00": 1},
833+
{"id": "second", "pop": 5678, "agg~2015-07-06T00:00:00": 2, "agg~2015-08-22T00:00:00": 3},
834+
),
835+
(
836+
"no-time-nor-bands",
837+
{
838+
"r1": {"process_id": "reduce_dimension", "arguments": {
839+
"data": {"from_node": "lc"},
840+
"dimension": "t",
841+
"reducer": {"process_graph": {"mean": {
842+
"process_id": "mean", "arguments": {"data": {"from_parameter": "data"}}, "result": True,
843+
}}},
844+
}},
845+
"r2": {"process_id": "reduce_dimension", "arguments": {
846+
"data": {"from_node": "r1"},
847+
"dimension": "bands",
848+
"reducer": {"process_graph": {"mean": {
849+
"process_id": "mean", "arguments": {"data": {"from_parameter": "data"}}, "result": True,
850+
}}},
851+
}},
852+
},
853+
"r2",
854+
{"id": "first", "pop": 1234, "agg": 0},
855+
{"id": "second", "pop": 5678, "agg": 1},
856+
),
857+
])
858+
def test_aggregate_spatial_vector_cube_dimensions(
859+
api100, info, preprocess_pg, aggregate_data, p1_properties, p2_properties
860+
):
861+
path = get_path("geojson/FeatureCollection02.json")
862+
pg = {
863+
"lc": {"process_id": "load_collection", "arguments": {"id": "S2_FOOBAR", "bands": ["B02", "B03", "B04"]}},
864+
"lf": {
865+
"process_id": "load_uploaded_files",
866+
"arguments": {"paths": [str(path)], "format": "GeoJSON"},
867+
},
868+
"ag": {
869+
"process_id": "aggregate_spatial",
870+
"arguments": {
871+
"data": {"from_node": aggregate_data},
872+
"geometries": {"from_node": "lf"},
873+
"reducer": {"process_graph": {
874+
"mean": {"process_id": "mean", "arguments": {"data": {"from_parameter": "data"}}, "result": True}}
875+
}
876+
},
877+
"result": True
878+
}
879+
}
880+
pg.update(preprocess_pg)
881+
res = api100.check_result(pg)
882+
883+
params = dummy_backend.last_load_collection_call("S2_FOOBAR")
884+
assert params["spatial_extent"] == {"west": 1, "south": 1, "east": 5, "north": 4, "crs": "EPSG:4326"}
885+
assert isinstance(params["aggregate_spatial_geometries"], DriverVectorCube)
886+
887+
assert res.json == DictSubSet({
888+
"type": "FeatureCollection",
889+
"features": [
890+
DictSubSet({
891+
"type": "Feature",
892+
"geometry": {"type": "Polygon", "coordinates": [[[1, 1], [3, 1], [2, 3], [1, 1]]]},
893+
"properties": p1_properties,
894+
}),
895+
DictSubSet({
896+
"type": "Feature",
897+
"geometry": {"type": "Polygon", "coordinates": [[[4, 2], [5, 4], [3, 4], [4, 2]]]},
898+
"properties": p2_properties,
899+
}),
900+
]
901+
})
902+
903+
781904
def test_create_wmts_040(api040):
782905
api040.set_auth_bearer_token(TEST_USER_BEARER_TOKEN)
783906
process_graph = api040.load_json("filter_temporal.json")

0 commit comments

Comments
 (0)