diff --git a/algorithm_catalog/meeo/flood_risk_service/benchmark_scenarios/flood_risk_service.json b/algorithm_catalog/meeo/flood_risk_service/benchmark_scenarios/flood_risk_service.json new file mode 100644 index 000000000..a8d8cc7c9 --- /dev/null +++ b/algorithm_catalog/meeo/flood_risk_service/benchmark_scenarios/flood_risk_service.json @@ -0,0 +1,46 @@ +{ + "id": "flood_risk_service", + "description": "Benchmark scenario for a coastal area near Ferrara, Italy. Tests inundation impact on population, built surface, and cereal cropland under SSP5-8.5 with 1.0m storm surge by 2040.", + "backend": "https://api.ideas.adamplatform.eu/", + "endpoint": "POST /", + "inputs": { + "confidence": "medium", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [12.146707532485465, 44.93552068972261], + [12.146707532485465, 44.832178039646664], + [12.389998034208674, 44.832178039646664], + [12.389998034208674, 44.93552068972261], + [12.146707532485465, 44.93552068972261] + ] + ] + }, + "ssp": "ssp585", + "storm_surge": "1_0", + "year": 2040 + }, + "outputs": { + "GHS_BUILT_S_E2020_GLOBE": 2560807.0, + "GHS_POP_E2020_GLOBE": 5385.0, + "cereals": 26.5, + "cereals_rice": 58.4 + }, + "tolerance": { + "type": "absolute", + "value": 0.0, + "note": "Results are deterministic given fixed inundation projection layers and static GHS/WorldCereal datasets. Exact match expected." + }, + "performance": { + "max_response_time_seconds": 30, + "note": "Measured at ~22s on 2026-04-13. Threshold rounded up to 30s." + }, + "links": [ + { + "rel": "about", + "href": "https://api.ideas.adamplatform.eu/docs", + "title": "ADAM Platform API documentation" + } + ] +} diff --git a/algorithm_catalog/meeo/flood_risk_service/openeo_udp/app.py b/algorithm_catalog/meeo/flood_risk_service/openeo_udp/app.py new file mode 100644 index 000000000..42ebe00c2 --- /dev/null +++ b/algorithm_catalog/meeo/flood_risk_service/openeo_udp/app.py @@ -0,0 +1,23 @@ +from flask import Flask, request, jsonify +from generate import generate_process # import from your original logic + +app = Flask(__name__) + +# create the process only once +process = generate_process() + +@app.route("/flood", methods=["POST"]) +def flood_endpoint(): + try: + data = request.get_json() + + # use the function from generate.py + result = process["apply"](data) + + return jsonify(result), 200 + + except Exception as e: + return jsonify({"error": str(e)}), 500 + +if __name__ == "__main__": + app.run(debug=True, port=5000) \ No newline at end of file diff --git a/algorithm_catalog/meeo/flood_risk_service/openeo_udp/flood_risk_service.json b/algorithm_catalog/meeo/flood_risk_service/openeo_udp/flood_risk_service.json new file mode 100644 index 000000000..583241ed8 --- /dev/null +++ b/algorithm_catalog/meeo/flood_risk_service/openeo_udp/flood_risk_service.json @@ -0,0 +1,111 @@ +{ + "process_graph": { + "flood_risk_service_node": { + "process_id": "flood_risk_service", + "arguments": { + "area": { + "from_parameter": "area" + }, + "year": { + "from_parameter": "year" + }, + "ssp": { + "from_parameter": "ssp" + }, + "storm_surge": { + "from_parameter": "storm_surge" + }, + "confidence": { + "from_parameter": "confidence" + } + }, + "result": true + } + }, + "parameters": { + "area": { + "description": "Bounding box of the area [west, south, east, north]", + "schema": { + "type": "object", + "properties": { + "west": { + "type": "number" + }, + "south": { + "type": "number" + }, + "east": { + "type": "number" + }, + "north": { + "type": "number" + } + }, + "required": [ + "west", + "south", + "east", + "north" + ] + } + }, + "year": { + "description": "Year of simulation", + "schema": { + "type": "string", + "enum": [ + "2020", + "2040", + "2060", + "2080", + "2100", + "2120", + "2150", + "all" + ] + }, + "default": 2150 + }, + "ssp": { + "description": "SSP scenario", + "schema": { + "type": "string", + "enum": [ + "ssp119", + "ssp126", + "ssp245", + "ssp370", + "ssp585", + "all" + ] + } + }, + "storm_surge": { + "description": "Storm surge level", + "schema": { + "type": "string", + "enum": [ + "0_0", + "0_5", + "1_0", + "1_5", + "2_0", + "3_0", + "5_0", + "all" + ] + } + }, + "confidence": { + "description": "Confidence level", + "schema": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ] + } + } + } +} \ No newline at end of file diff --git a/algorithm_catalog/meeo/flood_risk_service/openeo_udp/flood_service_description.md b/algorithm_catalog/meeo/flood_risk_service/openeo_udp/flood_service_description.md new file mode 100644 index 000000000..b652e4ac6 --- /dev/null +++ b/algorithm_catalog/meeo/flood_risk_service/openeo_udp/flood_service_description.md @@ -0,0 +1,183 @@ + +# openeo_udp — Flood Service Driver per OpenEO / APEX + +Custom OpenEO driver for [ADAM platform's](https://api.ideas.adamplatform.eu) flood‑risk prediction service. + +--- + +## Structure + +``` +openeo_udp/ +├── app.py # Flask API to expose the process as an endpoint +├── generate.py # Definition of the custom OpenEO process +├── flood_risk_service.json # OpenEO‑standard process graph (JSON) +├── test_flood.py # Local test script for the Flask endpoint +└── README.md +``` + +--- + +## Installation + +```bash +pip install requests flask openeo +``` + +--- + +## Start the Flask API + +```bash +python app.py +``` + +The API will listen on **port 5000** (configurable in `app.py`). + +### Endpoint + +POST /flood + +**Body JSON example:** + +```json +{ + "area": {"west": 0.6, "south": 40.7, "east": 0.7, "north": 40.8}, + "year": "2150", + "ssp": "ssp585", + "storm_surge": "5_0", + "confidence": "medium" +} +``` + +--- + +## Direct usage with Python (without Flask) + +```python +from generate import generate_process + +process = generate_process() + +context = { + "area": {"west": 0.6, "south": 40.7, "east": 0.7, "north": 40.8}, + "year": "2150", + "ssp": "ssp585", + "storm_surge": "5_0", + "confidence": "medium", +} + +result = process["apply"](context) +print(result) +``` + +--- + +## Local test of the Flask endpoint + +```bash +python test_flood.py +``` + +`test_flood.py` sends a POST request to the `/flood` endpoint and prints: + +- Status code +- JSON returned by the flood service + +--- + +## Registration in the APEX catalog + +The process must be registered **only once** in the back‑end catalog. + +### Option A — Via REST API (curl) + +```bash +curl -X PUT https:///processes/flood_service_request -H "Content-Type: application/json" -d @process_graph.json +``` + +### Option B — Via OpenEO Python SDK + +```python +import openeo, json + +conn = openeo.connect("https://") +# add here the chosen authentication method + +with open("process_graph.json") as f: + pg = json.load(f) + +conn.save_user_defined_process( + user_defined_process_id="flood_service_request", + process_graph=pg["process_graph"], + parameters=pg["parameters"], + summary=pg.get("summary", "Flood service request"), + description=pg.get("description", "Request flood service and return response"), +) +``` + +### Option C — Through the APEX UI + +1. Go to **Processes** → **User-defined** → **New**. +2. Paste the contents of `process_graph.json`. +3. Save and publish. + +--- + +## Parameters + +| Parameter | Type | Allowed values | Default | +|---------------|--------|----------------------------------------- -------|------------| +| `area` | object | `{west, south, east, north}` in decimal degrees | — | +| `year` | string | 2020, 2040, 2060, 2080, 2100, 2120, 2150, all | `"2150"` | +| `ssp` | string | ssp119, ssp126, ssp245, ssp370, ssp585, all | — | +| `storm_surge` | string | 0_0, 0_5, 1_0, 1_5, 2_0, 3_0, 5_0, all | — | +| `confidence` | string | low, medium, high | `"medium"` | + +> Nota: `area` must be an object with the keys "west", "south", "east", "north" in decimal degrees. Do not use coordinate lists. + +--- + +## Composition with other OpenEO processes + +```json +{ + "process_graph": { + "flood_node": { + "process_id": "flood_service_request", + "arguments": { + "area": { "from_parameter": "area" }, + "year": "2100", + "ssp": "ssp585", + "storm_surge": "1_0", + "confidence": "high" + } + }, + "save_node": { + "process_id": "save_result", + "arguments": { + "data": { "from_node": "flood_node" }, + "format": "JSON" + }, + "result": true + } + } +} +``` + +--- + +## Notes for APEX integration + +- **`process_id`**: `flood_service_request` +- The `"apply"` field in `generate_process()` is the callable invoked by the Python runtime; it is ignored in the JSON serialization. +- Il back-end must have network access to `https://api.ideas.adamplatform.eu/`. +- Timeout is set to 120 seconds: increase it in generate.py for very large areas or for requests using the `"all"` parameter. + +--- + +## References + +- [openEO API — Processes](https://api.openeo.org/#tag/Process-Discovery) +- [openEO Python Client](https://open-eo.github.io/openeo-python-client/) +- [ADAM Platform API](https://api.ideas.adamplatform.eu/docs) diff --git a/algorithm_catalog/meeo/flood_risk_service/openeo_udp/generate.py b/algorithm_catalog/meeo/flood_risk_service/openeo_udp/generate.py new file mode 100644 index 000000000..49666d87b --- /dev/null +++ b/algorithm_catalog/meeo/flood_risk_service/openeo_udp/generate.py @@ -0,0 +1,61 @@ +import requests + + +def generate_process(): + """ + OpenEO custom process for Flood Service. + """ + + FLOOD_SERVICE_URL = "https://api.ideas.adamplatform.eu/fixed_value" + + def flood_process(context): + # Convert the bounding box into a polygon + bbox = context["area"] + polygon = [ + [bbox["west"], bbox["north"]], + [bbox["west"], bbox["south"]], + [bbox["east"], bbox["south"]], + [bbox["east"], bbox["north"]], + [bbox["west"], bbox["north"]], + ] + + year = context["year"] + if year.isdigit(): + year = int(year) + + payload = { + "geometry": {"type": "Polygon", "coordinates": [polygon]}, + "year": year, + "ssp": context["ssp"], + "storm_surge": context["storm_surge"], + "confidence": context["confidence"], + } + + response = requests.post(FLOOD_SERVICE_URL, json=payload) + if response.status_code != 200: + raise RuntimeError(f"Flood service error: {response.status_code}") + return response.json() + + process_dict = { + "process_id": "flood_risk_service", + "description": "Request flood service and return response", + "apply": flood_process, + } + + return process_dict + + +if __name__ == "__main__": + process = generate_process() + + # Local test with bounding box + test_context = { + "area": { [0.6, 40.8], [0.6, 40.7], [0.7, 40.7], [0.7, 40.8]}, + "year": "2150", + "ssp": "ssp585", + "storm_surge": "5_0", + "confidence": "medium", + } + + result = process["apply"](test_context) + print(result) diff --git a/algorithm_catalog/meeo/flood_risk_service/openeo_udp/output_example.json b/algorithm_catalog/meeo/flood_risk_service/openeo_udp/output_example.json new file mode 100644 index 000000000..fc1160603 --- /dev/null +++ b/algorithm_catalog/meeo/flood_risk_service/openeo_udp/output_example.json @@ -0,0 +1,6 @@ +{ + "GHS_BUILT_S_E2020_GLOBE": 2560807.0, + "GHS_POP_E2020_GLOBE": 5385.0, + "cereals": 26.5, + "cereals_rice": 58.4 +} \ No newline at end of file diff --git a/algorithm_catalog/meeo/flood_risk_service/openeo_udp/requirements.txt b/algorithm_catalog/meeo/flood_risk_service/openeo_udp/requirements.txt new file mode 100644 index 000000000..61cc58642 --- /dev/null +++ b/algorithm_catalog/meeo/flood_risk_service/openeo_udp/requirements.txt @@ -0,0 +1,18 @@ +certifi==2026.2.25 +charset-normalizer==3.4.6 +Deprecated==1.3.1 +idna==3.11 +numpy==2.4.3 +openeo==0.48.0 +packaging==26.0 +pandas==2.3.3 +pystac==1.14.3 +python-dateutil==2.9.0.post0 +pytz==2026.1.post1 +requests==2.32.5 +shapely==2.1.2 +six==1.17.0 +tzdata==2025.3 +urllib3==2.6.3 +wrapt==2.1.2 +xarray==2025.1.1 diff --git a/algorithm_catalog/meeo/flood_risk_service/openeo_udp/test_flood.py b/algorithm_catalog/meeo/flood_risk_service/openeo_udp/test_flood.py new file mode 100644 index 000000000..43143e0cf --- /dev/null +++ b/algorithm_catalog/meeo/flood_risk_service/openeo_udp/test_flood.py @@ -0,0 +1,15 @@ +import requests + +url = "http://127.0.0.1:5000/flood" + +payload = { + "area": {"west": 0.6, "south": 40.7, "east": 0.7, "north": 40.8}, + "year": "2150", + "ssp": "ssp585", + "storm_surge": "5_0", + "confidence": "medium" +} + +response = requests.post(url, json=payload) +print(response.status_code) +print(response.json()) \ No newline at end of file diff --git a/algorithm_catalog/meeo/flood_risk_service/record.json b/algorithm_catalog/meeo/flood_risk_service/record.json new file mode 100644 index 000000000..bf2ac5c5a --- /dev/null +++ b/algorithm_catalog/meeo/flood_risk_service/record.json @@ -0,0 +1,56 @@ +{ + "id": "meeo", + "type": "Feature", + "conformsTo": [ + "http://www.opengis.net/spec/ogcapi-records-1/1.0/req/record-core" + ], + "properties": { + "created": "2026-03-02T13:00:00Z", + "updated": "2026-05-20T13:00:00Z", + "type": "service", + "title": "Flood Risk Assessment Service", + "description": "Benchmark scenario for a coastal area near Ferrara, Italy. Tests inundation impact on population, built surface, and cereal cropland under SSP5-8.5 with 1.0m storm surge by 2040.", + "keywords": [ + "natural hazards", + "hazards planning", + "data analysis" + ], + "language": { + "code": "en-US", + "name": "English (United States)" + }, + "languages": [ + { + "code": "en-US", + "name": "English (United States)" + } + ], + "contacts": [], + "themes": [], + "license": "other", + "acl": { + "admin": ["@meeo.it"] + } + }, + "linkTemplates": [], + "links": [ + { + "rel": "website", + "type": "text/html", + "title": "MEEO", + "href": "https://meeo.it" + }, + { + "rel": "logo-light", + "type": "image/png", + "title": "Logo", + "href": "https://raw.githubusercontent.com/ESA-APEx/apex_algorithms/refs/heads/main/algorithm_catalog/meeo/flood_risk_service/records/logo-meeo-light.svg" + }, + { + "rel": "logo-dark", + "type": "image/png", + "title": "Logo", + "href": "https://raw.githubusercontent.com/ESA-APEx/apex_algorithms/refs/heads/main/algorithm_catalog/meeo/flood_risk_service/records/logo-meeo-dark.svg" + } + ] +} diff --git a/algorithm_catalog/meeo/flood_risk_service/records/flood_risk_service.json b/algorithm_catalog/meeo/flood_risk_service/records/flood_risk_service.json new file mode 100644 index 000000000..9447f7b22 --- /dev/null +++ b/algorithm_catalog/meeo/flood_risk_service/records/flood_risk_service.json @@ -0,0 +1,127 @@ +{ + "id": "flood_risk_service", + "type": "Feature", + "conformsTo": [ + "http://www.opengis.net/spec/ogcapi-records-1/1.0/req/record-core", + "https://apex.esa.int/core/openeo-udp" + ], + "geometry": null, + "properties": { + "created": "2026-03-02T13:00:00Z", + "updated": "2026-05-20T13:00:00Z", + "type": "service", + "title": "Flood Risk Assessment Service", + "description": "Benchmark scenario for a coastal area near Ferrara, Italy. Tests inundation impact on population, built surface, and cereal cropland under SSP5-8.5 with 1.0m storm surge by 2040.", + "cost_estimate": 0.0, + "cost_unit": null, + "keywords": [ + "natural hazards", + "hazards planning", + "data analysis" + ], + "language": { + "code": "en-US", + "name": "English (United States)" + }, + "languages": [ + { + "code": "en-US", + "name": "English (United States)" + } + ], + "contacts": [ + { + "name": "Fabio Govoni", + "position": "technical manager", + "organization": "MEEO", + "links": [ + { + "rel": "website", + "type": "text/html", + "title": "MEEO", + "href": "https://meeo.it" + } + ], + "contactInstructions": "Contact via MEEO", + "roles": [ + "principal investigator" + ] + }, + { + "name": "Alessia Cattozzo", + "position": "project manager", + "organization": "MEEO", + "links": [ + { + "rel": "website", + "type": "text/html", + "title": "MEEO", + "href": "https://meeo.it" + } + ], + "contactInstructions": "Contact via MEEO", + "roles": [ + "support" + ] + } + ], + "themes": [ + { + "concepts": [ + { + "id": "flood-risk" + }, + { + "id": "land-use" + }, + { + "id": "population-exposure" + }, + { + "id": "agriculture" + } + ], + "scheme": "https://gcmd.earthdata.nasa.gov/kms/concepts/concept_scheme/sciencekeywords" + } + ], + "formats": [ + { + "name": "json" + } + ], + "license": "other" + }, + "linkTemplates": [], + "links": [ + { + "rel": "application", + "type": "application/vnd.openeo+json;type=process", + "title": "openEO Process Definition", + "href": "https://raw.githubusercontent.com/ESA-APEx/apex_algorithms/main/algorithm_catalog/meeo/flood_risk_service/openeo_udp/flood_risk_service.json" + }, + { + "rel": "service", + "type": "application/json", + "title": "Flood service", + "href": "https://api.ideas.adamplatform.eu/" + }, + { + "rel": "provider", + "type": "application/json", + "title": "MEEO", + "href": "../../record.json" + }, + { + "rel": "example-output", + "type": "application/json", + "title": "Example output", + "href": "../openeo_udp/output_example.json" + }, + { + "rel": "thumbnail", + "type": "image/png", + "title": "Thumbnail image", + "href": "https://raw.githubusercontent.com/ESA-APEx/apex_algorithms/refs/heads/main/algorithm_catalog/meeo/flood_riks_service/records/thumbnail.jpg" + } + ] + } diff --git a/algorithm_catalog/meeo/flood_risk_service/records/gda_app.json b/algorithm_catalog/meeo/flood_risk_service/records/gda_app.json new file mode 100644 index 000000000..af7ef4667 --- /dev/null +++ b/algorithm_catalog/meeo/flood_risk_service/records/gda_app.json @@ -0,0 +1,89 @@ +{ + "id": "gda_app", + "type": "Feature", + "conformsTo": [ + "http://www.opengis.net/spec/ogcapi-records-1/1.0/req/record-core" + ], + "properties": { + "created": "2026-03-02T13:00:00Z", + "updated": "2026-05-20T13:00:00Z", + "type": "ogc_backend", + "title": "Global Development Assistance - Analytics & Processing Platform", + "description": "The cloud-based environment developed under ESA GDA programme to harness Earth Observation for development aid operations", + "keywords": [], + "language": { + "code": "en-US", + "name": "English (United States)" + }, + "languages": [ + { + "code": "en-US", + "name": "English (United States)" + } + ], + "contacts": [ + { + "name": "Fabio Govoni", + "organization": "MEEO", + "position": "technical manager", + "roles": [ + "platform operations", + "technical support" + ], + "links": [ + { + "href": "https://app-gda.esa.int", + "title": "GDA APP", + "rel": "about", + "type": "text/html" + } + ], + "contactInstructions": "For matters concerning functional or analytical requirements, onboarding your services to the Platform, or enabling premium tier features, please reach out to the Platform's User Engagement Team, overseen by Alessia Cattozzo.", + "email": "helpdesk-gdaapp@meeo.it" + }, + { + "name": "Alessia", + "organization": "MEEO", + "position": "project manager", + "roles": [ + "user engagement", + "platform onboarding", + "platform premium-tier" + ], + "links": [ + { + "href": "https://app-gda.esa.int", + "title": "GDA APP", + "rel": "about", + "type": "text/html" + } + ], + "contactInstructions": "For inquiries on selecting Processing Services from the TAO Portfolio, and on budgeting the processing costs for your study areas, please email the Platform team at CS GROUP - ROMANIA", + "email": "engage-gdaapp@meeo.it" + } + ], + "themes": [], + "license": "other" + }, + "linkTemplates": [], + "links": [ + { + "rel": "website", + "type": "text/html", + "title": "MEEO", + "href": "https://meeo.it" + }, + { + "rel": "logo-dark", + "type": "image/png", + "title": "MEEO Logo", + "href": "https://raw.githubusercontent.com/ESA-APEx/apex_algorithms/refs/heads/main/algorithm_catalog/meeo/flood_risk_service/records/logo-meeo-dark.svg" + }, + { + "rel": "logo-light", + "type": "image/png", + "title": "MEEO logo", + "href": "https://raw.githubusercontent.com/ESA-APEx/apex_algorithms/refs/heads/main/algorithm_catalog/meeo/flood_risk_service/records/logo-meeo-light.svg" + } + ] +} diff --git a/algorithm_catalog/meeo/flood_risk_service/records/logo-meeo-dark.svg b/algorithm_catalog/meeo/flood_risk_service/records/logo-meeo-dark.svg new file mode 100644 index 000000000..252333e6c --- /dev/null +++ b/algorithm_catalog/meeo/flood_risk_service/records/logo-meeo-dark.svg @@ -0,0 +1,17 @@ + + meeo_logo-svg + + + + + + + + + + \ No newline at end of file diff --git a/algorithm_catalog/meeo/flood_risk_service/records/logo-meeo-light.svg b/algorithm_catalog/meeo/flood_risk_service/records/logo-meeo-light.svg new file mode 100644 index 000000000..5246b479c --- /dev/null +++ b/algorithm_catalog/meeo/flood_risk_service/records/logo-meeo-light.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + diff --git a/algorithm_catalog/meeo/flood_risk_service/records/thumbnail.jpg b/algorithm_catalog/meeo/flood_risk_service/records/thumbnail.jpg new file mode 100644 index 000000000..b78fc3965 Binary files /dev/null and b/algorithm_catalog/meeo/flood_risk_service/records/thumbnail.jpg differ