diff --git a/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACEncoder.java b/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACEncoder.java index 78893b4ca..da0eca5f9 100644 --- a/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACEncoder.java +++ b/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACEncoder.java @@ -40,6 +40,7 @@ import org.integratedmodelling.klab.Authentication; import org.integratedmodelling.klab.Observables; +import org.integratedmodelling.klab.api.auth.IUserIdentity; import org.integratedmodelling.klab.api.data.IGeometry; import org.integratedmodelling.klab.api.data.IGeometry.Dimension.Type; import org.integratedmodelling.klab.api.data.IResource; @@ -78,6 +79,8 @@ import org.integratedmodelling.klab.stac.extensions.COGAssetExtension; import org.integratedmodelling.klab.stac.extensions.STACFeatureExtension; import org.integratedmodelling.klab.stac.extensions.STACIIASAExtension; +import org.integratedmodelling.klab.stac.extensions.WEEDModelSTACExtension; + import org.integratedmodelling.klab.utils.JsonUtils; import org.integratedmodelling.klab.utils.s3.S3URLUtils; import org.locationtech.jts.geom.Envelope; @@ -328,11 +331,9 @@ public boolean test(HMStacAsset asset) { // Assuming for now that "eo:bands" wou return true; } } - } else { // meaning eo:bands is not present like Microsoft Planetary, in this - // case this would be like the asset key i.e. Id - return asset.getId().equals(assetId); } - return false; + + return asset.getId().equals(assetId); // case this would be like the asset key i.e. Id } }; } @@ -576,7 +577,6 @@ public boolean test(HMStacAsset asset) { // Assuming for now that "eo:bands" wou + "The transformation process could affect the data."); } - // Specific Implementation for the Slow Requests flow in WEED if (collection.getId().contains("EU_modelV2-1-MECE") && resource.getUrn().contains("im.resources-main")) { @@ -588,8 +588,19 @@ public boolean test(HMStacAsset asset) { // Assuming for now that "eo:bands" wou ); if (!unionMLStacInference.contains(poly)) { - scope.getMonitor().warn("The requested extend for ML inferences is not completely contained in STAC, Starting ML Inference Request"); + scope.getMonitor().info("Fetching Model IDs to pass to the Slow Request UDP"); + ListmodelIds = null; + try { + var modelIDs = WEEDModelSTACExtension.GetONNXModelIDs(bbox, scope.getMonitor(), targetSemantics); + if (modelIDs == null || modelIDs.size() == 0) { + throw new Exception("No ONNX Models were found over the specified context"); + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + throw new KlabIllegalStateException("Error occured while getting ONNX Model info over the specified context"); + } OpenEO service = OpenEOAdapter.getClient("openeo_weed.dataspace.copernicus.eu"); List processes = new ArrayList<>(); String processNamespace = "https://raw.githubusercontent.com/ESA-WEED-project/OpenEO-UDP-UDF-catalogue/refs/heads/main/UDP/json/udp_starter.json"; @@ -598,31 +609,35 @@ public boolean test(HMStacAsset asset) { // Assuming for now that "eo:bands" wou Process process = JsonUtils.load(new URL(processNamespace), Process.class); process.encodeSelf(processNamespace); + + scope.getMonitor().warn("The requested extend for ML inferences is not completely contained in STAC, Starting ML Inference Request"); processes.add(process); - JSONObject arguments = new JSONObject() - .put("bbox", new JSONObject() - .put("crs", 4326) - .put("west", bbox.get(0)) - .put("south", bbox.get(3)) - .put("east", bbox.get(1)) - .put("north", bbox.get(2))) - .put("digitalId", "AM1729") // Forms the STAC coordinate later - .put("scenarioId", "DT_SLOW_FLOW") // Forms the STAC coordinate later - .put("year", ctxTime.getEnd().getYear()) - .put("onnx_model", "EUNIS2021plus_panEU_v201_2024_OneZone") // Hardcoding for now only for Europe, until the "BEST" model is decided! - .put("dt_url", "https://services.integratedmodelling.org/runtime/main/api/v1/dt/ESA_INSTITUTIONAL.3vh554o6h6c"); + for (var modelId:modelIds) { // triggering multiple UDPs parallely + JSONObject arguments = new JSONObject() + .put("bbox", new JSONObject() // convert this to a geojson + .put("crs", 4326) + .put("west", bbox.get(0)) + .put("south", bbox.get(3)) + .put("east", bbox.get(1)) + .put("north", bbox.get(2))) + .put("digitalId", "AM1729") // Forms the STAC coordinate later + .put("scenarioId", "DT_SLOW_FLOW") // Forms the STAC coordinate later + .put("year", ctxTime.getEnd().getYear()) + .put("onnx_model", modelId) // Hardcoding for now only for Europe, until the "BEST" model is decided! + .put("userId", Authentication.INSTANCE.getAuthenticatedIdentity(IUserIdentity.class).getUsername()) + .put("dt_url", "https://services.integratedmodelling.org/runtime/main/api/v1/dt/ESA_INSTITUTIONAL.510zsaubjxr"); - - OpenEOFuture job = service.submit(processID, arguments, - scope.getMonitor(), processes.toArray(new Process[processes.size()])); - - if (job.isCancelled()) { - scope.getMonitor().warn("job canceled"); - } else if (job.getError() != null) { - scope.getMonitor().error(job.getError()); - } else { - scope.getMonitor().info("Inference Request has been submitted to the ML Workflows"); + OpenEOFuture job = service.submit(processID, arguments, + scope.getMonitor(), processes.toArray(new Process[processes.size()])); + + if (job.isCancelled()) { + scope.getMonitor().warn("job canceled"); + } else if (job.getError() != null) { + scope.getMonitor().error(job.getError()); + } else { + scope.getMonitor().info("Inference Request has been submitted to the ML Workflows"); + } } } } diff --git a/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/extensions/WEEDModelSTACExtension.java b/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/extensions/WEEDModelSTACExtension.java new file mode 100644 index 000000000..5ca14424d --- /dev/null +++ b/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/extensions/WEEDModelSTACExtension.java @@ -0,0 +1,92 @@ +package org.integratedmodelling.klab.stac.extensions; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.ArrayList; + +import org.geotools.api.feature.simple.SimpleFeature; +import org.geotools.data.geojson.GeoJSONReader; +import org.hortonmachine.gears.io.stac.HMStacManager; +import org.hortonmachine.gears.libs.monitor.LogProgressMonitor; +import org.integratedmodelling.klab.api.knowledge.IObservable; +import org.integratedmodelling.klab.api.runtime.monitoring.IMonitor; + +import kong.unirest.HttpResponse; +import kong.unirest.JsonNode; +import kong.unirest.Unirest; +import kong.unirest.json.JSONArray; +import kong.unirest.json.JSONObject; + +public class WEEDModelSTACExtension { + + String MODEL_STAC_API = "https://catalogue.weed.apex.esa.int/collections/model-STAC"; + + public static List GetONNXModelIDs(List bbox, IMonitor monitor, IObservable targetSemantics) { + + + monitor.debug("Making a Search Query to the Model STAC"); + try { + + JSONObject searchPayload = new JSONObject() + .put("limit", 1000) + .put("bbox", new ArrayList<>(List.of( + bbox.get(0), + bbox.get(2), + bbox.get(1), + bbox.get(3) + ))) + + .put("collections", new JSONArray().put("model-STAC")); + + HttpResponse response = Unirest + .post("https://catalogue.weed.apex.esa.int/search") + .header("Content-Type", "application/json") + .body(searchPayload) + .asJson(); + + JSONObject body = response.getBody().getObject(); + JSONArray features = body.getJSONArray("features"); + + List> pairs = new ArrayList<>(); + + float highestModelVersion = Float.MIN_VALUE; + + Iterator featureIterator = features.iterator(); + while (featureIterator.hasNext()) { + JSONObject feature = (JSONObject) featureIterator.next(); + SimpleFeature feat = GeoJSONReader.parseFeature(feature.toString()); + if (targetSemantics.toString().toLowerCase().contains("eunis")) { + if (feat.getAttribute("topology") != null && + feat.getAttribute("topology").toString().toLowerCase().startsWith("eunis")) { // it should've been "typology" instead of "topology" + pairs.add(Map.entry(feat.getID(), ((Number) feat.getAttribute("model_version")).floatValue())); + highestModelVersion = Math.max(highestModelVersion, ((Number) feat.getAttribute("model_version")).floatValue()); + } + } + + if (targetSemantics.toString().toLowerCase().contains("iucn")) { + if (feat.getAttribute("topology") != null && + feat.getAttribute("topology").toString().toLowerCase().startsWith("iucn")) { // it should've been "typology" instead of "topology" + pairs.add(Map.entry(feat.getID(), ((Number) feat.getAttribute("model_version")).floatValue())); + highestModelVersion = Math.max(highestModelVersion, ((Number) feat.getAttribute("model_version")).floatValue()); + } + } + } + + var filterModelVersion = highestModelVersion; + pairs = pairs.stream() + .filter(item -> item.getValue().equals(filterModelVersion)) + .toList(); + + return pairs.stream() + .map(Map.Entry::getKey) + .toList(); + + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return null; + } + +}