Skip to content

Commit a27cc9b

Browse files
committed
fixup! Issue #114/#141 convert inline GeoJSON in aggregate_spatial to VectorCube
1 parent c8fcd91 commit a27cc9b

File tree

3 files changed

+210
-98
lines changed

3 files changed

+210
-98
lines changed

openeo_driver/datacube.py

+1
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ def _as_geopandas_df(self) -> gpd.GeoDataFrame:
226226
return df
227227

228228
def to_geojson(self):
229+
"""Export as GeoJSON FeatureCollection."""
229230
return shapely.geometry.mapping(self._as_geopandas_df())
230231

231232
def to_wkt(self) -> List[str]:

openeo_driver/dry_run.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,10 @@ def get_source_constraints(self, merge=True) -> List[SourceConstraint]:
350350
return source_constraints
351351

352352
def get_geometries(
353-
self, operation="aggregate_spatial"
354-
) -> List[Union[shapely.geometry.base.BaseGeometry, DelayedVector]]:
353+
self, operation="aggregate_spatial"
354+
) -> List[
355+
Union[shapely.geometry.base.BaseGeometry, DelayedVector, DriverVectorCube]
356+
]:
355357
"""Get geometries (polygons or DelayedVector), as used by aggregate_spatial"""
356358
geometries_by_id = {}
357359
for leaf in self.get_trace_leaves():

tests/test_dry_run.py

+205-96
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
from openeo.rest.datacube import DataCube
66
from openeo_driver.ProcessGraphDeserializer import evaluate, ENV_DRY_RUN_TRACER, _extract_load_parameters, \
77
ENV_SOURCE_CONSTRAINTS, custom_process_from_process_graph, process_registry_100
8+
from openeo_driver.datacube import DriverVectorCube
89
from openeo_driver.datastructs import SarBackscatterArgs
910
from openeo_driver.delayed_vector import DelayedVector
1011
from openeo_driver.dry_run import DryRunDataTracer, DataSource, DataTrace, ProcessType
12+
from openeo_driver.testing import DictSubSet
1113
from openeo_driver.utils import EvalEnv
1214
from tests.data import get_path, load_json
1315

@@ -526,67 +528,148 @@ def test_aggregate_spatial_only(dry_run_env, dry_run_tracer):
526528
assert src == ("load_collection", ("S2_FOOBAR", ()))
527529
assert constraints == {
528530
"spatial_extent": {"west": 0.0, "south": 0.0, "east": 8.0, "north": 5.0, "crs": "EPSG:4326"},
529-
"aggregate_spatial": {"geometries": shapely.geometry.shape(polygon)},
531+
"aggregate_spatial": {"geometries": DriverVectorCube.from_geojson(polygon)},
530532
}
531533
geometries, = dry_run_tracer.get_geometries()
532-
assert isinstance(geometries, shapely.geometry.Polygon)
533-
assert shapely.geometry.mapping(geometries) == {
534-
"type": "Polygon",
535-
"coordinates": (((0.0, 0.0), (3.0, 5.0), (8.0, 2.0), (0.0, 0.0)),)
536-
}
534+
assert isinstance(geometries, DriverVectorCube)
535+
assert geometries.to_geojson() == DictSubSet(
536+
type="FeatureCollection",
537+
features=[
538+
DictSubSet(
539+
geometry={
540+
"type": "Polygon",
541+
"coordinates": (((0.0, 0.0), (3.0, 5.0), (8.0, 2.0), (0.0, 0.0)),),
542+
}
543+
),
544+
],
545+
)
537546

538547

539548
def test_aggregate_spatial_apply_dimension(dry_run_env, dry_run_tracer):
540549
polygon = {"type": "Polygon", "coordinates": [[(0, 0), (3, 5), (8, 2), (0, 0)]]}
541-
pg = {'loadcollection1': {'process_id': 'load_collection', 'arguments': {'bands': ['B04', 'B08', 'B11', 'SCL'],
542-
'id': 'S2_FOOBAR',
543-
'spatial_extent': None,
544-
'temporal_extent': ['2018-11-01',
545-
'2020-02-01']}},
546-
'maskscldilation1': {'process_id': 'mask_scl_dilation',
547-
'arguments': {'data': {'from_node': 'loadcollection1'}, 'scl_band_name': 'SCL'}},
548-
'aggregatetemporalperiod1': {'process_id': 'aggregate_temporal_period',
549-
'arguments': {'data': {'from_node': 'maskscldilation1'}, 'period': 'month',
550-
'reducer': {'process_graph': {'mean1': {'process_id': 'mean',
551-
'arguments': {'data': {
552-
'from_parameter': 'data'}},
553-
'result': True}}}}},
554-
'applydimension1': {'process_id': 'apply_dimension',
555-
'arguments': {'data': {'from_node': 'aggregatetemporalperiod1'}, 'dimension': 't',
556-
'process': {'process_graph': {
557-
'arrayinterpolatelinear1': {'process_id': 'array_interpolate_linear',
558-
'arguments': {
559-
'data': {'from_parameter': 'data'}},
560-
'result': True}}}}},
561-
'filtertemporal1': {'process_id': 'filter_temporal', 'arguments': {'data': {'from_node': 'applydimension1'},
562-
'extent': ['2019-01-01', '2020-01-01']}},
563-
'applydimension2': {'process_id': 'apply_dimension',
564-
'arguments': {'data': {'from_node': 'filtertemporal1'}, 'dimension': 'bands', 'process': {
565-
'process_graph': {'arrayelement1': {'process_id': 'array_element',
566-
'arguments': {'data': {'from_parameter': 'data'},
567-
'index': 1}},
568-
'arrayelement2': {'process_id': 'array_element',
569-
'arguments': {'data': {'from_parameter': 'data'},
570-
'index': 0}},
571-
'normalizeddifference1': {'process_id': 'normalized_difference',
572-
'arguments': {
573-
'x': {'from_node': 'arrayelement1'},
574-
'y': {'from_node': 'arrayelement2'}}},
575-
'arraymodify1': {'process_id': 'array_modify',
576-
'arguments': {'data': {'from_parameter': 'data'},
577-
'index': 0, 'values': {
578-
'from_node': 'normalizeddifference1'}},
579-
'result': True}}}}},
580-
'renamelabels1': {'process_id': 'rename_labels',
581-
'arguments': {'data': {'from_node': 'applydimension2'}, 'dimension': 'bands',
582-
'target': ['NDVI', 'B04', 'B08']}},
583-
'aggregatespatial1': {'process_id': 'aggregate_spatial',
584-
'arguments': {'data': {'from_node': 'renamelabels1'}, 'geometries': polygon,
585-
'reducer': {'process_graph': {'mean2': {'process_id': 'mean',
586-
'arguments': {'data': {
587-
'from_parameter': 'data'}},
588-
'result': True}}}},
589-
'result': True}}
550+
pg = {
551+
"loadcollection1": {
552+
"process_id": "load_collection",
553+
"arguments": {
554+
"bands": ["B04", "B08", "B11", "SCL"],
555+
"id": "S2_FOOBAR",
556+
"spatial_extent": None,
557+
"temporal_extent": ["2018-11-01", "2020-02-01"],
558+
},
559+
},
560+
"maskscldilation1": {
561+
"process_id": "mask_scl_dilation",
562+
"arguments": {
563+
"data": {"from_node": "loadcollection1"},
564+
"scl_band_name": "SCL",
565+
},
566+
},
567+
"aggregatetemporalperiod1": {
568+
"process_id": "aggregate_temporal_period",
569+
"arguments": {
570+
"data": {"from_node": "maskscldilation1"},
571+
"period": "month",
572+
"reducer": {
573+
"process_graph": {
574+
"mean1": {
575+
"process_id": "mean",
576+
"arguments": {"data": {"from_parameter": "data"}},
577+
"result": True,
578+
}
579+
}
580+
},
581+
},
582+
},
583+
"applydimension1": {
584+
"process_id": "apply_dimension",
585+
"arguments": {
586+
"data": {"from_node": "aggregatetemporalperiod1"},
587+
"dimension": "t",
588+
"process": {
589+
"process_graph": {
590+
"arrayinterpolatelinear1": {
591+
"process_id": "array_interpolate_linear",
592+
"arguments": {"data": {"from_parameter": "data"}},
593+
"result": True,
594+
}
595+
}
596+
},
597+
},
598+
},
599+
"filtertemporal1": {
600+
"process_id": "filter_temporal",
601+
"arguments": {
602+
"data": {"from_node": "applydimension1"},
603+
"extent": ["2019-01-01", "2020-01-01"],
604+
},
605+
},
606+
"applydimension2": {
607+
"process_id": "apply_dimension",
608+
"arguments": {
609+
"data": {"from_node": "filtertemporal1"},
610+
"dimension": "bands",
611+
"process": {
612+
"process_graph": {
613+
"arrayelement1": {
614+
"process_id": "array_element",
615+
"arguments": {
616+
"data": {"from_parameter": "data"},
617+
"index": 1,
618+
},
619+
},
620+
"arrayelement2": {
621+
"process_id": "array_element",
622+
"arguments": {
623+
"data": {"from_parameter": "data"},
624+
"index": 0,
625+
},
626+
},
627+
"normalizeddifference1": {
628+
"process_id": "normalized_difference",
629+
"arguments": {
630+
"x": {"from_node": "arrayelement1"},
631+
"y": {"from_node": "arrayelement2"},
632+
},
633+
},
634+
"arraymodify1": {
635+
"process_id": "array_modify",
636+
"arguments": {
637+
"data": {"from_parameter": "data"},
638+
"index": 0,
639+
"values": {"from_node": "normalizeddifference1"},
640+
},
641+
"result": True,
642+
},
643+
}
644+
},
645+
},
646+
},
647+
"renamelabels1": {
648+
"process_id": "rename_labels",
649+
"arguments": {
650+
"data": {"from_node": "applydimension2"},
651+
"dimension": "bands",
652+
"target": ["NDVI", "B04", "B08"],
653+
},
654+
},
655+
"aggregatespatial1": {
656+
"process_id": "aggregate_spatial",
657+
"arguments": {
658+
"data": {"from_node": "renamelabels1"},
659+
"geometries": polygon,
660+
"reducer": {
661+
"process_graph": {
662+
"mean2": {
663+
"process_id": "mean",
664+
"arguments": {"data": {"from_parameter": "data"}},
665+
"result": True,
666+
}
667+
}
668+
},
669+
},
670+
"result": True,
671+
},
672+
}
590673

591674
cube = evaluate(pg, env=dry_run_env)
592675

@@ -599,15 +682,22 @@ def test_aggregate_spatial_apply_dimension(dry_run_env, dry_run_tracer):
599682
"process_type": [ProcessType.GLOBAL_TIME],
600683
"bands": ["B04", "B08", "B11", "SCL"],
601684
"custom_cloud_mask": {"method": "mask_scl_dilation", 'scl_band_name': 'SCL'},
602-
"aggregate_spatial": {"geometries": shapely.geometry.shape(polygon)},
685+
"aggregate_spatial": {"geometries": DriverVectorCube.from_geojson(polygon)},
603686
"temporal_extent": ("2018-11-01", "2020-02-01")
604687
}
605688
geometries, = dry_run_tracer.get_geometries()
606-
assert isinstance(geometries, shapely.geometry.Polygon)
607-
assert shapely.geometry.mapping(geometries) == {
608-
"type": "Polygon",
609-
"coordinates": (((0.0, 0.0), (3.0, 5.0), (8.0, 2.0), (0.0, 0.0)),)
610-
}
689+
assert isinstance(geometries, DriverVectorCube)
690+
assert geometries.to_geojson() == DictSubSet(
691+
type="FeatureCollection",
692+
features=[
693+
DictSubSet(
694+
geometry={
695+
"type": "Polygon",
696+
"coordinates": (((0.0, 0.0), (3.0, 5.0), (8.0, 2.0), (0.0, 0.0)),),
697+
}
698+
),
699+
],
700+
)
611701

612702

613703
def test_aggregate_spatial_and_filter_bbox(dry_run_env, dry_run_tracer):
@@ -626,14 +716,21 @@ def test_aggregate_spatial_and_filter_bbox(dry_run_env, dry_run_tracer):
626716
assert src == ("load_collection", ("S2_FOOBAR", ()))
627717
assert constraints == {
628718
"spatial_extent": bbox,
629-
"aggregate_spatial": {"geometries": shapely.geometry.shape(polygon)},
719+
"aggregate_spatial": {"geometries": DriverVectorCube.from_geojson(polygon)},
630720
}
631721
geometries, = dry_run_tracer.get_geometries()
632-
assert isinstance(geometries, shapely.geometry.Polygon)
633-
assert shapely.geometry.mapping(geometries) == {
634-
"type": "Polygon",
635-
"coordinates": (((0.0, 0.0), (3.0, 5.0), (8.0, 2.0), (0.0, 0.0)),)
636-
}
722+
assert isinstance(geometries, DriverVectorCube)
723+
assert geometries.to_geojson() == DictSubSet(
724+
type="FeatureCollection",
725+
features=[
726+
DictSubSet(
727+
geometry={
728+
"type": "Polygon",
729+
"coordinates": (((0.0, 0.0), (3.0, 5.0), (8.0, 2.0), (0.0, 0.0)),),
730+
}
731+
),
732+
],
733+
)
637734

638735

639736
def test_resample_filter_spatial(dry_run_env, dry_run_tracer):
@@ -697,31 +794,34 @@ def test_aggregate_spatial_read_vector(dry_run_env, dry_run_tracer):
697794
assert isinstance(geometries, DelayedVector)
698795

699796

700-
def test_aggregate_spatial_get_geometries_feature_collection(dry_run_env, dry_run_tracer):
797+
def test_aggregate_spatial_get_geometries_feature_collection(
798+
dry_run_env, dry_run_tracer
799+
):
701800
pg = {
702801
"lc": {"process_id": "load_collection", "arguments": {"id": "S2_FOOBAR"}},
703-
"vector": {"process_id": "get_geometries", "arguments": {"feature_collection": {
704-
"type": "FeatureCollection",
705-
"name": "fields",
706-
"crs": {
707-
"type": "name",
708-
"properties": {
709-
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
710-
}
711-
},
712-
"features": [
713-
{
714-
"type": "Feature",
715-
"geometry": {
716-
"type": "Polygon",
717-
"coordinates": [[(0, 0), (3, 5), (8, 2), (0, 0)]]
802+
"vector": {
803+
"process_id": "get_geometries",
804+
"arguments": {
805+
"feature_collection": {
806+
"type": "FeatureCollection",
807+
"name": "fields",
808+
"crs": {
809+
"type": "name",
810+
"properties": {"name": "urn:ogc:def:crs:OGC:1.3:CRS84"},
718811
},
719-
"properties": {
720-
"CODE_OBJ": "0000000000000001"
721-
}
812+
"features": [
813+
{
814+
"type": "Feature",
815+
"geometry": {
816+
"type": "Polygon",
817+
"coordinates": [[(0, 0), (3, 5), (8, 2), (0, 0)]],
818+
},
819+
"properties": {"CODE_OBJ": "0000000000000001"},
820+
}
821+
],
722822
}
723-
]
724-
}}},
823+
},
824+
},
725825
"agg": {
726826
"process_id": "aggregate_spatial",
727827
"arguments": {
@@ -744,19 +844,28 @@ def test_aggregate_spatial_get_geometries_feature_collection(dry_run_env, dry_ru
744844
assert len(source_constraints) == 1
745845
src, constraints = source_constraints[0]
746846
assert src == ("load_collection", ("S2_FOOBAR", ()))
747-
expected_geometry_collection = shapely.geometry.GeometryCollection(
748-
[shapely.geometry.shape({"type": "Polygon", "coordinates": [[(0, 0), (3, 5), (8, 2), (0, 0)]]})]
847+
848+
expected_geometry_collection = DriverVectorCube.from_geojson(
849+
pg["vector"]["arguments"]["feature_collection"]
749850
)
750851
assert constraints == {
751-
"spatial_extent": {'west': 0.0, 'south': 0.0, 'east': 8.0, 'north': 5.0, 'crs': 'EPSG:4326'},
752-
"aggregate_spatial": {"geometries": expected_geometry_collection}
852+
"spatial_extent": {
853+
"west": 0.0,
854+
"south": 0.0,
855+
"east": 8.0,
856+
"north": 5.0,
857+
"crs": "EPSG:4326",
858+
},
859+
"aggregate_spatial": {"geometries": expected_geometry_collection},
753860
}
754-
geometries, = dry_run_tracer.get_geometries()
755-
assert isinstance(geometries, shapely.geometry.GeometryCollection)
861+
(geometries,) = dry_run_tracer.get_geometries()
862+
assert isinstance(geometries, DriverVectorCube)
756863

757864

758-
@pytest.mark.parametrize(["arguments", "expected"], [
759-
(
865+
@pytest.mark.parametrize(
866+
["arguments", "expected"],
867+
[
868+
(
760869
{},
761870
SarBackscatterArgs(coefficient="gamma0-terrain", elevation_model=None, mask=False, contributing_area=False,
762871
local_incidence_angle=False, ellipsoid_incidence_angle=False, noise_removal=True,

0 commit comments

Comments
 (0)