From fe78f49c10dd983b73d4902d6b3f5ef20469a367 Mon Sep 17 00:00:00 2001 From: Jeroen Dries Date: Mon, 25 Sep 2023 12:16:57 +0200 Subject: [PATCH] add date_difference https://github.com/Open-EO/openeo-geopyspark-driver/issues/515 --- .../OpenEOProcessScriptBuilder.scala | 30 +++++++++- .../TestOpenEOProcessScriptBuilder.java | 56 +++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/openeo-geotrellis/src/main/scala/org/openeo/geotrellis/OpenEOProcessScriptBuilder.scala b/openeo-geotrellis/src/main/scala/org/openeo/geotrellis/OpenEOProcessScriptBuilder.scala index 93546c3e8..9f4f603e1 100644 --- a/openeo-geotrellis/src/main/scala/org/openeo/geotrellis/OpenEOProcessScriptBuilder.scala +++ b/openeo-geotrellis/src/main/scala/org/openeo/geotrellis/OpenEOProcessScriptBuilder.scala @@ -15,6 +15,7 @@ import org.slf4j.LoggerFactory import spire.math.UShort import spire.syntax.cfor.cfor +import java.time.{Duration, ZonedDateTime} import java.util import scala.Double.NaN import scala.collection.JavaConversions.mapAsScalaMap @@ -545,6 +546,28 @@ class OpenEOProcessScriptBuilder { ifElseProcess(value, accept, reject) } + private def dateDifferenceProcess(arguments: java.util.Map[String, Object]): OpenEOProcess = { + val date1 = arguments.get("date1") + val date2 = arguments.get("date2") + + + val dateDiffProcess = (context: Map[String, Any]) => { + def normalize(argument: Any): String = { + if( argument.isInstanceOf[String]) { + argument.asInstanceOf[String] + }else if(argument.isInstanceOf[util.Map[String,Any]] && argument.asInstanceOf[util.Map[String,Any]].containsKey("from_parameter")) { + val paramName = argument.asInstanceOf[util.Map[String,Any]].get("from_parameter").asInstanceOf[String] + context.getOrElse(paramName, throw new IllegalArgumentException(s"date_difference: Parameter $paramName not found in context: $context")).asInstanceOf[String] + }else{ + throw new IllegalArgumentException(s"date_difference got unexpected argument: $argument") + } + } + val diff = Duration.between(ZonedDateTime.parse(normalize(date1)),ZonedDateTime.parse(normalize(date2))).toDays + createConstantTileFunction(diff) + } + dateDiffProcess + } + private def xyFunction(operator:(Tile,Tile) => Tile, xArgName:String = "x", yArgName:String = "y" ,convertBitCells: Boolean = true): OpenEOProcess = { val x_function: OpenEOProcess = getProcessArg(xArgName) @@ -602,7 +625,11 @@ class OpenEOProcessScriptBuilder { val defaultName = defaultDataParameterName inputFunction = (context:Map[String,Any]) => (tiles: Seq[Tile]) => { if(context.contains(parameterName)) { - context.getOrElse(parameterName,tiles).asInstanceOf[Seq[Tile]] + if(context.get(parameterName).isInstanceOf[Seq[Tile]]) { + context.getOrElse(parameterName,tiles).asInstanceOf[Seq[Tile]] + }else{ + null + } }else if(parameterName == defaultName) { tiles } @@ -708,6 +735,7 @@ class OpenEOProcessScriptBuilder { val hasConditionExpression = arguments.get("condition") != null && !arguments.get("condition").isInstanceOf[Boolean] val operation: OpenEOProcess = operator match { + case "date_difference" => dateDifferenceProcess(arguments) case "if" => ifProcess(arguments) // Comparison operators case "gt" if hasXY => xyFunction(Greater.apply,convertBitCells = false) diff --git a/openeo-geotrellis/src/test/java/org/openeo/geotrellis/TestOpenEOProcessScriptBuilder.java b/openeo-geotrellis/src/test/java/org/openeo/geotrellis/TestOpenEOProcessScriptBuilder.java index bfc8c3975..a0367d049 100644 --- a/openeo-geotrellis/src/test/java/org/openeo/geotrellis/TestOpenEOProcessScriptBuilder.java +++ b/openeo-geotrellis/src/test/java/org/openeo/geotrellis/TestOpenEOProcessScriptBuilder.java @@ -1657,6 +1657,42 @@ static OpenEOProcessScriptBuilder createArrayApply() { return builder; } + static OpenEOProcessScriptBuilder createArrayApplyDateDifference() { + OpenEOProcessScriptBuilder builder = new OpenEOProcessScriptBuilder(); + + Map arguments = new HashMap<>(); + arguments.put("data", Collections.singletonMap("from_parameter", "x")); + arguments.put("process", "dummy"); + builder.expressionStart("array_apply", arguments); + + builder.argumentStart("data"); + builder.fromParameter("data"); + builder.argumentEnd(); + + builder.argumentStart("process"); + { + // scope block, just for nice indentation + Map argumentsCos = new HashMap<>(); + argumentsCos.put("date1", Collections.singletonMap("from_parameter", "label")); + argumentsCos.put("date2", "2022-01-01T00:00:00Z"); + builder.expressionStart("date_difference", argumentsCos); + + builder.argumentStart("date1"); + builder.fromParameter("label"); + builder.argumentEnd(); + builder.argumentStart("date2"); + //string constant is not yet transmitted + builder.argumentEnd(); + + + builder.expressionEnd("date_difference", argumentsCos); + } + builder.argumentEnd(); + + builder.expressionEnd("array_apply", arguments); + return builder; + } + static OpenEOProcessScriptBuilder createRankComposite() { OpenEOProcessScriptBuilder builder = new OpenEOProcessScriptBuilder(); @@ -1719,6 +1755,26 @@ public void testArrayApply() { } } + @DisplayName("Test array_apply with date difference process") + @Test + public void testArrayApplyDateDifference() { + for (OpenEOProcessScriptBuilder builder : Arrays.asList(createArrayApplyDateDifference())) { + Function1, Seq> transformation = builder.generateFunction(Collections.singletonMap("array_labels",JavaConversions.asScalaBuffer(Arrays.asList("2022-01-04T00:00:00Z","2022-01-05T00:00:00Z","2016-02-29T00:00:00Z","2019-06-30T00:00:00Z","2030-12-31T00:00:00Z")))); + Tile tile0 = FloatConstantNoDataArrayTile.fill(1, 4, 4); + Tile tile1 = FloatConstantNoDataArrayTile.fill(3, 4, 4); + Tile tile2 = FloatConstantNoDataArrayTile.fill(-1, 4, 4); + Tile tile3 = FloatConstantNoDataArrayTile.fill(1.9f, 4, 4); + Tile nodataTile = ByteConstantNoDataArrayTile.empty(4, 4); + + Seq result = transformation.apply(JavaConversions.asScalaBuffer(Arrays.asList(nodataTile, tile0, tile1, tile2, tile3))); + + assertEquals(-3, result.apply(0).get(0, 0)); + assertEquals(-4, result.apply(1).get(0, 0)); + assertEquals(2133, result.apply(2).get(0, 0)); + + } + } + static OpenEOProcessScriptBuilder createMedian(Boolean ignoreNoData) { OpenEOProcessScriptBuilder builder = new OpenEOProcessScriptBuilder(); Map arguments = ignoreNoData!=null? Collections.singletonMap("ignore_nodata",ignoreNoData.booleanValue()) : Collections.emptyMap();