diff --git a/pom.xml b/pom.xml index dff56c8d524612165e1c3ab310de9e09e201db23..dc5527c1bea5a8d32b7d7d227015a3227524115e 100755 --- a/pom.xml +++ b/pom.xml @@ -100,19 +100,18 @@ <dependency> <groupId>org.geotools</groupId> <artifactId>gt-api</artifactId> - <version>20.3</version> - </dependency> - <dependency> + <version>20.5</version> +</dependency> +<dependency> <groupId>org.geotools</groupId> <artifactId>gt-epsg-hsql</artifactId> - <version>20.3</version> + <version>20.5</version> </dependency> - <dependency> - <groupId>org.renjin</groupId> - <artifactId>renjin-script-engine</artifactId> - <version>RELEASE</version> - </dependency> - + <dependency> + <groupId>com.kjetland</groupId> + <artifactId>mbknor-jackson-jsonschema_2.13</artifactId> + <version>1.0.36</version> + </dependency> <dependency> <groupId>com.bedatadriven</groupId> <artifactId>jackson-datatype-jts</artifactId> @@ -135,19 +134,32 @@ <artifactId>unit-ri</artifactId> <version>1.0.3</version> </dependency> - +<dependency> + <groupId>org.renjin</groupId> + <artifactId>renjin-script-engine</artifactId> + <version>RELEASE</version> + </dependency> </dependencies> <repositories> - <repository> + <repository> <id>bedatadriven</id> <name>bedatadriven public repo</name> <url>https://nexus.bedatadriven.com/content/groups/public/</url> </repository> - <repository> + <repository> <id>osgeo</id> - <name>OSGEO</name> - <url>https://download.osgeo.org/webdav/geotools/</url> - </repository> + <name>OSGeo Release Repository</name> + <url>https://repo.osgeo.org/repository/release/</url> + <snapshots><enabled>false</enabled></snapshots> + <releases><enabled>true</enabled></releases> + </repository> + <repository> + <id>osgeo-snapshot</id> + <name>OSGeo Snapshot Repository</name> + <url>https://repo.osgeo.org/repository/snapshot/</url> + <snapshots><enabled>true</enabled></snapshots> + <releases><enabled>false</enabled></releases> + </repository> <repository> <id>jitpack.io</id> <url>https://jitpack.io</url> diff --git a/src/main/java/net/ipmdecisions/weather/entity/LocationWeatherData.java b/src/main/java/net/ipmdecisions/weather/entity/LocationWeatherData.java new file mode 100644 index 0000000000000000000000000000000000000000..4009c3c57432a7ecce2da50a4d2d5a40758e2481 --- /dev/null +++ b/src/main/java/net/ipmdecisions/weather/entity/LocationWeatherData.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2020 NIBIO <http://www.nibio.no/>. + * + * This file is part of IPMDecisionsWeatherService. + * IPMDecisionsWeatherService is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * IPMDecisionsWeatherService is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with IPMDecisionsWeatherService. If not, see <http://www.gnu.org/licenses/>. + * + */ + +package net.ipmdecisions.weather.entity; + +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle; +import javax.validation.constraints.NotNull; + +/** + * @copyright 2020 <a href="http://www.nibio.no/">NIBIO</a> + * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + */ +public class LocationWeatherData { + @NotNull + @JsonSchemaTitle("Longitude (WGS84)") + @JsonPropertyDescription("The longitude of the location. Decimal degrees (WGS84)") + private Double longitude; + @NotNull + @JsonSchemaTitle("Latitude (WGS84)") + @JsonPropertyDescription("The latitude of the location. Decimal degrees (WGS84)") + private Double latitude; + @JsonSchemaTitle("Altitude (Meters)") + @JsonPropertyDescription("The altitude of the location. Measured in meters") + private Double altitude; + @JsonSchemaTitle("Weather data per location") + @JsonPropertyDescription("The data. In rows, ordered chronologically. Columns ordered as given in weatherParameters.") + private Double[][] data; + + public LocationWeatherData(Double longitude, Double latitude, Double altitude, int rows, int columns){ + this.longitude = longitude; + this.latitude = latitude; + this.altitude = altitude; + this.data = new Double[rows][columns]; + } + + public LocationWeatherData(){ + + } + + /** + * + * @return The number of rows in the dataset + */ + public Integer getLength() + { + return this.data.length; + } + + /** + * + * @return The number of parameters per rows in the dataset + */ + public Integer getWidth() + { + return this.data[0].length; + } + + public Double[][] getData() + { + return this.data; + } + + public void setData(Double[][] data) + { + this.data = data; + } + + public void setValue(Integer row, Integer column, Double value) + { + this.data[row][column] = value; + } + + public Double getValue(Integer row, Integer column) + { + return this.data[row][column]; + } + + public Double[] getRow(Integer row) + { + return this.data[row]; + } + + public Double[] getColumn(Integer column) + { + Double[] columnValues = new Double[this.data.length]; + for(Integer i=0; i < this.data.length; i++) + { + columnValues[i] = this.data[i][column]; + } + return columnValues; + } + + @Override + public String toString() + { + String retVal = ""; + for(Integer i=0;i<this.data.length;i++) + { + retVal += i + ""; + for(Integer j = 0; j < this.data[i].length; j++) + { + retVal += "\t" + this.data[i][j]; + } + retVal += "\n"; + } + return retVal; + } + + /** + * @return the longitude + */ + public Double getLongitude() { + return longitude; + } + + /** + * @param longitude the longitude to set + */ + public void setLongitude(Double longitude) { + this.longitude = longitude; + } + + /** + * @return the latitude + */ + public Double getLatitude() { + return latitude; + } + + /** + * @param latitude the latitude to set + */ + public void setLatitude(Double latitude) { + this.latitude = latitude; + } + + /** + * @return the altitude + */ + public Double getAltitude() { + return altitude; + } + + /** + * @param altitude the altitude to set + */ + public void setAltitude(Double altitude) { + this.altitude = altitude; + } +} diff --git a/src/main/java/net/ipmdecisions/weather/entity/WeatherData.java b/src/main/java/net/ipmdecisions/weather/entity/WeatherData.java new file mode 100644 index 0000000000000000000000000000000000000000..9b9f317881f905ed135a58d378e14e6e5c8c1674 --- /dev/null +++ b/src/main/java/net/ipmdecisions/weather/entity/WeatherData.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2020 NIBIO <http://www.nibio.no/>. + * + * This file is part of IPMDecisionsWeatherService. + * IPMDecisionsWeatherService is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * IPMDecisionsWeatherService is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with IPMDecisionsWeatherService. If not, see <http://www.gnu.org/licenses/>. + * + */ + +package net.ipmdecisions.weather.entity; + +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaDescription; +import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaExamples; +import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaInject; +import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaString; +import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Positive; +import javax.validation.constraints.Size; + +/** + * We are using this tool to serialize to Json Schema: https://github.com/mbknor/mbknor-jackson-jsonSchema + * + * @copyright 2020 <a href="http://www.nibio.no/">NIBIO</a> + * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + */ +@JsonSchemaInject(strings = { + @JsonSchemaString(path = "$id", value="https://ipmdecisions.nibio.no/WeatherService/rest/schema/weatherdata"), + @JsonSchemaString(path = "properties/timeStart/type", value="string") + } +) +@JsonSchemaTitle("Weather Data") +@JsonSchemaExamples("https://ipmdecisions.nibio.no/WeatherService/rest/forecasts/yr/?longitude=14.3711&latitude=67.2828&altitude=70") +@JsonSchemaDescription("Version 0.1. The schema describes the weather data format for the IPM Decisions platform. See an example here: https://ipmdecisions.nibio.no/WeatherService/rest/forecasts/yr/?longitude=14.3711&latitude=67.2828&altitude=70") +public class WeatherData { + @NotNull + @JsonSchemaTitle("Time start (yyyy-MM-dd'T'HH:mm:ssXXX)") + @JsonPropertyDescription("The timestamp of the first weather observation. Format: \"yyyy-MM-dd'T'HH:mm:ssXXX\", e.g. 2020-04-09T18:00:00+02:00") + private Instant timeStart; + @NotNull + @JsonSchemaTitle("Time end (yyyy-MM-dd'T'HH:mm:ssXXX)") + @JsonPropertyDescription("The timestamp of the last weather observation. Format: \"yyyy-MM-dd'T'HH:mm:ssXXX\", e.g. 2020-04-09T18:00:00+02:00") + private Instant timeEnd; + @NotNull + @Positive + @JsonSchemaTitle("Sampling frequency (seconds)") + @JsonPropertyDescription("The sampling frequency in seconds. E.g. 3600 = hourly values") + private Integer interval; + @NotNull + @Size(min=1) + @JsonSchemaTitle("Weather parameters") + @JsonPropertyDescription("The weather parameters. For reference, see https://ipmdecisions.nibio.no/WeatherService/rest/parameter/list") + private Integer[] weatherParameters; + @JsonSchemaTitle("Weather data") + @JsonPropertyDescription("The weather data per location.") + private List<LocationWeatherData> locationWeatherData; + + /** + * @return the timeStart + */ + public Instant getTimeStart() { + return timeStart; + } + + + /** + * @param timeStart the timeStart to set + */ + public void setTimeStart(Instant timeStart) { + this.timeStart = timeStart; + } + + /** + * @return the timeEnd + */ + public Instant getTimeEnd() { + return timeEnd; + } + + + /** + * @param timeEnd the timeEnd to set + */ + public void setTimeEnd(Instant timeEnd) { + this.timeEnd = timeEnd; + } + + /** + * @return the interval + */ + public Integer getInterval() { + return interval; + } + + /** + * @param interval the interval to set + */ + public void setInterval(Integer interval) { + this.interval = interval; + } + + /** + * @return the weatherParameters + */ + public Integer[] getWeatherParameters() { + return weatherParameters; + } + + /** + * @param weatherParameters the weatherParameters to set + */ + public void setWeatherParameters(Integer[] weatherParameters) { + this.weatherParameters = weatherParameters; + } + + /** + * @return the locationWeatherData + */ + public List<LocationWeatherData> getLocationWeatherData() { + return locationWeatherData; + } + + /** + * @param locationWeatherData the locationWeatherData to set + */ + public void setLocationWeatherData(List<LocationWeatherData> locationWeatherData) { + this.locationWeatherData = locationWeatherData; + } + + public void addLocationWeatherData(LocationWeatherData locationWeatherData) { + if (this.locationWeatherData == null) + { + this.locationWeatherData = new ArrayList<>(); + } + this.locationWeatherData.add(locationWeatherData); + } +} diff --git a/src/main/java/net/ipmdecisions/weather/entity/WeatherParameter.java b/src/main/java/net/ipmdecisions/weather/entity/WeatherParameter.java new file mode 100644 index 0000000000000000000000000000000000000000..bc4cec7232d30da0dbf578231f49e118331205de --- /dev/null +++ b/src/main/java/net/ipmdecisions/weather/entity/WeatherParameter.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2020 NIBIO <http://www.nibio.no/>. + * + * This file is part of IPMDecisionsWeatherService. + * IPMDecisionsWeatherService is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * IPMDecisionsWeatherService is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with IPMDecisionsWeatherService. If not, see <http://www.gnu.org/licenses/>. + * + */ + +package net.ipmdecisions.weather.entity; + +import javax.validation.constraints.NotNull; + +/** + * @copyright 2020 <a href="http://www.nibio.no/">NIBIO</a> + * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + */ +public class WeatherParameter { + @NotNull + private Integer parameterCode; + @NotNull + private String name; + private String description; + @NotNull + private String unit; + + /** + * @return the parameterCode + */ + public Integer getParameterCode() { + return parameterCode; + } + + /** + * @param parameterCode the parameterCode to set + */ + public void setParameterCode(Integer parameterCode) { + this.parameterCode = parameterCode; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * @param description the description to set + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * @return the unit + */ + public String getUnit() { + return unit; + } + + /** + * @param unit the unit to set + */ + public void setUnit(String unit) { + this.unit = unit; + } +} diff --git a/src/main/java/no/nibio/vips/core/service/ModelResource.java b/src/main/java/no/nibio/vips/core/service/ModelResource.java index 81e9eefb8e1f0461f3957508ccf6a4860f8f9fc1..8eb474bf89ac36f9fdf1f7d7036fe240fb939405 100755 --- a/src/main/java/no/nibio/vips/core/service/ModelResource.java +++ b/src/main/java/no/nibio/vips/core/service/ModelResource.java @@ -27,6 +27,7 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Response; import no.nibio.vips.entity.ModelConfiguration; +import no.nibio.vips.ipmdecisions.IPMDecisionsModelConfiguration; /** * Interface for the ModelResource. Implemented by ModelResourceImpl @@ -175,5 +176,9 @@ public interface ModelResource { @Produces("application/json") public Response runModel(@PathParam("modelId") String modelId, ModelConfiguration config); - + @POST + @Path("models/{modelId}/run/ipmd") + @Consumes("application/json") + @Produces("application/json") + public Response runModelForIPMDecisions(@PathParam("modelId") String modelId, IPMDecisionsModelConfiguration config); } diff --git a/src/main/java/no/nibio/vips/ipmdecisions/DataTransformer.java b/src/main/java/no/nibio/vips/ipmdecisions/DataTransformer.java new file mode 100644 index 0000000000000000000000000000000000000000..c4dceed1e594ef10cb8b177065258912215d8a45 --- /dev/null +++ b/src/main/java/no/nibio/vips/ipmdecisions/DataTransformer.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2020 NIBIO <http://www.nibio.no/>. + * + * This file is part of VIPSCommon. + * VIPSCommon is free software: you can redistribute it and/or modify + * it under the terms of the NIBIO Open Source License as published by + * NIBIO, either version 1 of the License, or (at your option) any + * later version. + * + * VIPSCommon is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * NIBIO Open Source License for more details. + * + * You should have received a copy of the NIBIO Open Source License + * along with VIPSCommon. If not, see <http://www.nibio.no/licenses/>. + * + */ + +package no.nibio.vips.ipmdecisions; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import net.ipmdecisions.weather.entity.LocationWeatherData; +import net.ipmdecisions.weather.entity.WeatherData; +import no.nibio.vips.entity.WeatherObservation; + +/** + * @copyright 2020 <a href="http://www.nibio.no/">NIBIO</a> + * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + */ +public class DataTransformer { + + public List<WeatherObservation> getVIPSWeatherData(WeatherData IPMWeatherData) + { + List<WeatherObservation> retVal = new ArrayList<>(); + IPMDecisionsWeatherUtil ipmUtil = new IPMDecisionsWeatherUtil(); + Integer logIntervalId = IPMWeatherData.getInterval().equals(3600) ? WeatherObservation.LOG_INTERVAL_ID_1H + : WeatherObservation.LOG_INTERVAL_ID_1D; + LocationWeatherData oneLocation = IPMWeatherData.getLocationWeatherData().get(0); + Double[][] data = oneLocation.getData(); + Instant currentTimeStamp = IPMWeatherData.getTimeStart(); + for(int i=0; i<data.length; i++) + { + // Calculate the timeMeasured for this row + + for(int j=0; j<data[i].length;j++) + { + WeatherObservation VIPSObs = new WeatherObservation(); + VIPSObs.setTimeMeasured(Date.from(currentTimeStamp)); + VIPSObs.setLogIntervalId(logIntervalId); + VIPSObs.setElementMeasurementTypeId(ipmUtil.getVIPSParameterId(IPMWeatherData.getWeatherParameters()[j])); + VIPSObs.setValue(data[i][j]); + retVal.add(VIPSObs); + } + currentTimeStamp = currentTimeStamp.plusSeconds(IPMWeatherData.getInterval()); + } + return retVal; + } + +} diff --git a/src/main/java/no/nibio/vips/ipmdecisions/IPMDecisionsModelConfiguration.java b/src/main/java/no/nibio/vips/ipmdecisions/IPMDecisionsModelConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..7ef0bbe232697ae985eebb3ae7ea01c89a97d9d4 --- /dev/null +++ b/src/main/java/no/nibio/vips/ipmdecisions/IPMDecisionsModelConfiguration.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020 NIBIO <http://www.nibio.no/>. + * + * This file is part of VIPSCommon. + * VIPSCommon is free software: you can redistribute it and/or modify + * it under the terms of the NIBIO Open Source License as published by + * NIBIO, either version 1 of the License, or (at your option) any + * later version. + * + * VIPSCommon is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * NIBIO Open Source License for more details. + * + * You should have received a copy of the NIBIO Open Source License + * along with VIPSCommon. If not, see <http://www.nibio.no/licenses/>. + * + */ + +package no.nibio.vips.ipmdecisions; + +import net.ipmdecisions.weather.entity.WeatherData; +import no.nibio.vips.entity.ModelConfiguration; + +/** + * @copyright 2020 <a href="http://www.nibio.no/">NIBIO</a> + * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + */ +public class IPMDecisionsModelConfiguration extends ModelConfiguration{ + private WeatherData IPMWeatherData; + public void setWeatherData(WeatherData weatherData) + { + this.IPMWeatherData = weatherData; + } + public WeatherData getWeatherData() + { + return this.IPMWeatherData; + } +} diff --git a/src/main/java/no/nibio/vips/ipmdecisions/IPMDecisionsWeatherUtil.java b/src/main/java/no/nibio/vips/ipmdecisions/IPMDecisionsWeatherUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..67b01eb43a5286909da73dba20c16d48ef235ddb --- /dev/null +++ b/src/main/java/no/nibio/vips/ipmdecisions/IPMDecisionsWeatherUtil.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020 NIBIO <http://www.nibio.no/>. + * + * This file is part of LMTServices. + * LMTServices is free software: you can redistribute it and/or modify + * it under the terms of the NIBIO Open Source License as published by + * NIBIO, either version 1 of the License, or (at your option) any + * later version. + * + * LMTServices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * NIBIO Open Source License for more details. + * + * You should have received a copy of the NIBIO Open Source License + * along with LMTServices. If not, see <http://www.nibio.no/licenses/>. + * + */ + +package no.nibio.vips.ipmdecisions; + +import java.util.Map; +import no.nibio.vips.util.WeatherElements; + +/** + * @copyright 2020 <a href="http://www.nibio.no/">NIBIO</a> + * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + */ +public class IPMDecisionsWeatherUtil { + private final Map<String, Integer> VIPSToIPM = Map.of( + WeatherElements.TEMPERATURE_MEAN,1002, // TM + WeatherElements.PRECIPITATION, 2001, // RR + WeatherElements.RELATIVE_HUMIDITY_MEAN, 3002, // UM + WeatherElements.GLOBAL_RADIATION, 5001,// Q0 + WeatherElements.LEAF_WETNESS_DURATION, 3101 // BT + ); + + private final Map<Integer, String> IPMToVIPS = Map.of( + 1002,WeatherElements.TEMPERATURE_MEAN, // TM + 2001, WeatherElements.PRECIPITATION, // RR + 3002, WeatherElements.RELATIVE_HUMIDITY_MEAN, // UM + 5001, WeatherElements.GLOBAL_RADIATION, // Q0 + 3101, WeatherElements.LEAF_WETNESS_DURATION // BT + ); + + public Integer getIPMParameterId(String VIPSParameterId) + { + return this.VIPSToIPM.get(VIPSParameterId); + } + + public String getVIPSParameterId(Integer IPMParameterId) + { + return this.IPMToVIPS.get(IPMParameterId); + } + + //public WeatherData deserializeWeatherData(Object) +} diff --git a/src/main/java/no/nibio/vips/ipmdecisions/LocationWeatherDataDeserializer.java b/src/main/java/no/nibio/vips/ipmdecisions/LocationWeatherDataDeserializer.java new file mode 100644 index 0000000000000000000000000000000000000000..e299ea9997372c5d373d8e72746bd1c0f226a5bc --- /dev/null +++ b/src/main/java/no/nibio/vips/ipmdecisions/LocationWeatherDataDeserializer.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 NIBIO <http://www.nibio.no/>. + * + * This file is part of VIPSCommon. + * VIPSCommon is free software: you can redistribute it and/or modify + * it under the terms of the NIBIO Open Source License as published by + * NIBIO, either version 1 of the License, or (at your option) any + * later version. + * + * VIPSCommon is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * NIBIO Open Source License for more details. + * + * You should have received a copy of the NIBIO Open Source License + * along with VIPSCommon. If not, see <http://www.nibio.no/licenses/>. + * + */ + +package no.nibio.vips.ipmdecisions; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import java.io.IOException; +import net.ipmdecisions.weather.entity.LocationWeatherData; + +/** + * @copyright 2020 <a href="http://www.nibio.no/">NIBIO</a> + * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + */ +public class LocationWeatherDataDeserializer extends JsonDeserializer<LocationWeatherData>{ + + + @Override + public LocationWeatherData deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException { + ObjectCodec oc = jp.getCodec(); + JsonNode oneLocation = oc.readTree(jp); + LocationWeatherData lwd = new LocationWeatherData( + oneLocation.get("longitude").asDouble(), + oneLocation.get("latitude").asDouble(), + oneLocation.get("altitude").asDouble(), + oneLocation.get("data").size(), + oneLocation.get("data").get(0).size()); + + for(int i=0; i< oneLocation.get("data").size();i++) + { + for(int j=0; j<oneLocation.get("data").get(0).size();j++) + { + lwd.setValue(i, j,oneLocation.get("data").get(i).get(j).asDouble()); + } + } + return lwd; + } +} diff --git a/src/main/java/no/nibio/vips/model/RenjinModel.java b/src/main/java/no/nibio/vips/model/RenjinModel.java index ce74dd77126f5a0ec614490e1e35acc50fb552ac..5858a04ed9d8c7c5bceed8b9092cc922bef68b70 100644 --- a/src/main/java/no/nibio/vips/model/RenjinModel.java +++ b/src/main/java/no/nibio/vips/model/RenjinModel.java @@ -1,20 +1,20 @@ -/* - * Copyright (c) 2017 NIBIO <http://www.nibio.no/>. - * - * This file is part of VIPSCommon. +/* + * Copyright (c) 2017 NIBIO <http://www.nibio.no/>. + * + * This file is part of VIPSCommon. * VIPSCommon is free software: you can redistribute it and/or modify - * it under the terms of the NIBIO Open Source License as published by + * it under the terms of the NIBIO Open Source License as published by * NIBIO, either version 1 of the License, or (at your option) any * later version. - * + * * VIPSCommon is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * NIBIO Open Source License for more details. - * + * * You should have received a copy of the NIBIO Open Source License * along with VIPSCommon. If not, see <http://www.nibio.no/licenses/>. - * + * */ package no.nibio.vips.model; @@ -26,51 +26,45 @@ import org.renjin.script.RenjinScriptEngineFactory; import org.renjin.sexp.ExternalPtr; /** - * When implementing a model using Renjin (http://www.renjin.org/ - R on the Java Virtual Machine), + * When implementing a model using Renjin (http://www.renjin.org/ - R on the + * Java Virtual Machine), * you must extend this class. - * - * @copyright 2017 <a href="http://www.nibio.no/">NIBIO</a> - * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + * @copyright 2017 + * <a href="http://www.nibio.no/">NIBIO</a> * @author Tor-Einar Skog + * <tor-einar.skog@nibio.no> */ public abstract class RenjinModel { - + protected ScriptEngine engine; - - public RenjinModel() - { - // create a Renjin engine: + + public RenjinModel() { + // create a Renjin engine: RenjinScriptEngineFactory factory = new RenjinScriptEngineFactory(); engine = factory.getScriptEngine(); } - + /** - * Run an R script file available on the classpath - * @param scriptPath - * @throws ScriptException + * Run an R script file available on the classpath * @param scriptPath * + * @throws ScriptException */ - public void runRScript(String scriptPath) throws ScriptException - { + public void runRScript(String scriptPath) throws ScriptException { InputStreamReader fr = new java.io.InputStreamReader(this.getClass().getResourceAsStream(scriptPath)); engine.eval(fr); } - + /** - * Place a Java object in the Renjin script engine - * @param objectName - * @param object + * Place a Java object in the Renjin script engine * @param objectName * + * @param object */ - public void placeObjectInScriptEngine(String objectName, Object object) - { + public void placeObjectInScriptEngine(String objectName, Object object) { engine.put(objectName, object); } - + /** - * - * @param objectName - * @return a Java object from the Renjin script engine + * @param objectName * @return a Java object from the Renjin script + * engine */ - public Object getObjectFromScriptEngine(String objectName) - { + public Object getObjectFromScriptEngine(String objectName) { ExternalPtr ptr = (ExternalPtr) engine.get(objectName); return ptr.getInstance(); } diff --git a/src/main/java/no/nibio/vips/util/ModelUtil.java b/src/main/java/no/nibio/vips/util/ModelUtil.java index bed787c891765b3c3bec67fc3f7d351909280060..4781caec68dd179311c4567adb3641db424e185c 100755 --- a/src/main/java/no/nibio/vips/util/ModelUtil.java +++ b/src/main/java/no/nibio/vips/util/ModelUtil.java @@ -19,12 +19,16 @@ package no.nibio.vips.util; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URLConnection; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import no.nibio.vips.entity.WeatherObservation; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; @@ -115,4 +119,30 @@ public class ModelUtil { return Base64.encodeBase64String(bytes); } + /** + * This solves the problem of the model not knowing if the ModelConfiguration + * contains a halfway serialized List of WeatherObservation (typically LinkedHashMap) + * OR a List of actual WeatherObservations. + * @param unknownClassList + * @return + */ + public List<WeatherObservation> extractWeatherObservationList(Object unknownClassList) + { + try + { + // These are real WeatherObservation classes + List<WeatherObservation> obsList = (List<WeatherObservation>) unknownClassList; + if(obsList != null && obsList.size() > 0) + { + WeatherObservation o = obsList.get(0); + } + return obsList; + } + catch(ClassCastException ex) + { + // These are semi serialized, tell ObjectMapper/Jackson how to do it + ObjectMapper mapper = new ObjectMapper(); + return mapper.convertValue(unknownClassList, new TypeReference<List<WeatherObservation>>(){}); + } + } } diff --git a/src/test/java/no/nibio/vips/ipmdecisions/DataTransformerTest.java b/src/test/java/no/nibio/vips/ipmdecisions/DataTransformerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..70f11569827476349d7573cb717219e0f11d43a9 --- /dev/null +++ b/src/test/java/no/nibio/vips/ipmdecisions/DataTransformerTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2020 NIBIO <http://www.nibio.no/>. + * + * This file is part of VIPSCommon. + * VIPSCommon is free software: you can redistribute it and/or modify + * it under the terms of the NIBIO Open Source License as published by + * NIBIO, either version 1 of the License, or (at your option) any + * later version. + * + * VIPSCommon is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * NIBIO Open Source License for more details. + * + * You should have received a copy of the NIBIO Open Source License + * along with VIPSCommon. If not, see <http://www.nibio.no/licenses/>. + * + */ +package no.nibio.vips.ipmdecisions; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.MappingJsonFactory; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import static junit.framework.Assert.fail; +import junit.framework.TestCase; +import net.ipmdecisions.weather.entity.LocationWeatherData; +import net.ipmdecisions.weather.entity.WeatherData; +import no.nibio.vips.entity.WeatherObservation; + +/** + * + * @author treinar + */ +public class DataTransformerTest extends TestCase { + + public DataTransformerTest(String testName) { + super(testName); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + /** + * Test of getVIPSWeatherData method, of class DataTransformer. + */ + public void testGetVIPSWeatherData() { + System.out.println("getVIPSWeatherData"); + WeatherData IPMWeatherData = this.getWeatherData("/IPMData_hourly.json"); + DataTransformer instance = new DataTransformer(); + + List<WeatherObservation> result = instance.getVIPSWeatherData(IPMWeatherData); + + result.forEach(obs->System.out.println(obs)); + + assertEquals(IPMWeatherData.getLocationWeatherData().get(0).getLength() * IPMWeatherData.getLocationWeatherData().get(0).getWidth(), result.size()); + + } + + private WeatherData getWeatherData(String fileName) + { + try + { + BufferedInputStream inputStream = new BufferedInputStream(this.getClass().getResourceAsStream(fileName)); + + JsonFactory f = new MappingJsonFactory(); + JsonParser jp = f.createParser(inputStream); + JsonNode all = jp.readValueAsTree(); + WeatherData weatherData = new WeatherData(); + ObjectMapper mapper = new ObjectMapper(); + //mapper.convertValue(all, new TypeReference<WeatherData>(){}); + + weatherData.setTimeStart(((Date)mapper.convertValue(all.get("timeStart"), new TypeReference<Date>(){})).toInstant()); + weatherData.setTimeEnd(((Date)mapper.convertValue(all.get("timeEnd"), new TypeReference<Date>(){})).toInstant()); + + weatherData.setInterval(all.get("interval").asInt()); + Integer[] params = new Integer[all.get("weatherParameters").size()]; + for(int i=0; i< all.get("weatherParameters").size();i++){ + params[i] = all.get("weatherParameters").get(i).asInt(); + } + weatherData.setWeatherParameters(params); + JsonNode oneLocation = all.get("locationWeatherData").get(0); + + LocationWeatherData lwd = new LocationWeatherData(0.0,0.0,0.0,oneLocation.get("data").size(),params.length); + for(int i=0; i< oneLocation.get("data").size();i++) + { + for(int j=0; j<params.length;j++) + { + lwd.setValue(i, j,oneLocation.get("data").get(i).get(j).asDouble()); + } + } + + weatherData.addLocationWeatherData(lwd); + + System.out.println(mapper.writeValueAsString(weatherData)); + + return weatherData; + } + catch(IOException ex) + { + return null; + } + } + +} diff --git a/src/test/resources/IPMData_daily.json b/src/test/resources/IPMData_daily.json new file mode 100644 index 0000000000000000000000000000000000000000..1d93ca338014091cda770e99a4502c20d2c6ddf8 --- /dev/null +++ b/src/test/resources/IPMData_daily.json @@ -0,0 +1,51 @@ +{ + "timeStart": "2020-04-13T22:00:00Z", + "timeEnd": "2020-04-18T22:00:00Z", + "interval": 86400, + "weatherParameters": [ + 1002, + 2001, + 3002 + ], + "locationWeatherData": [ + { + "longitude": 9.21189, + "latitude": 59.38223, + "altitude": 94.0, + "data": [ + [ + 3.3252083333333324, + 0.4, + 57.949999999999996 + ], + [ + 9.189458333333333, + 0.0, + 53.56249999999999 + ], + [ + 8.060041666666669, + 0.0, + 34.20750000000001 + ], + [ + 5.689166666666668, + 0.0, + 40.34875 + ], + [ + 6.208708333333333, + 0.0, + 43.70333333333334 + ], + [ + 8.549999999999999, + 0.0, + 49.01833333333334 + ] + ], + "length": 6, + "width": 3 + } + ] +} \ No newline at end of file diff --git a/src/test/resources/IPMData_hourly.json b/src/test/resources/IPMData_hourly.json new file mode 100644 index 0000000000000000000000000000000000000000..5c3fc8838c93e06d61f2a6ae35b571515c21d6ee --- /dev/null +++ b/src/test/resources/IPMData_hourly.json @@ -0,0 +1,626 @@ +{ + "timeStart": "2020-04-13T22:00:00Z", + "timeEnd": "2020-04-18T22:00:00Z", + "interval": 3600, + "weatherParameters": [ + 1002, + 2001, + 3002 + ], + "locationWeatherData": [ + { + "longitude": 9.21189, + "latitude": 59.38223, + "altitude": 94.0, + "data": [ + [ + 0.655, + 0.0, + 42.16 + ], + [ + 0.089, + 0.0, + 43.17 + ], + [ + -0.725, + 0.0, + 48.96 + ], + [ + -0.962, + 0.0, + 51.0 + ], + [ + -1.787, + 0.0, + 59.24 + ], + [ + -1.42, + 0.0, + 59.3 + ], + [ + -1.471, + 0.0, + 63.36 + ], + [ + -0.813, + 0.0, + 68.32 + ], + [ + 0.569, + 0.0, + 60.63 + ], + [ + 2.15, + 0.0, + 53.33 + ], + [ + 4.348, + 0.0, + 49.08 + ], + [ + 7.5, + 0.0, + 43.23 + ], + [ + 4.929, + 0.4, + 68.32 + ], + [ + 4.565, + 0.0, + 73.5 + ], + [ + 4.838, + 0.0, + 72.4 + ], + [ + 5.38, + 0.0, + 69.72 + ], + [ + 5.924, + 0.0, + 65.88 + ], + [ + 6.267, + 0.0, + 63.26 + ], + [ + 6.579, + 0.0, + 60.77 + ], + [ + 6.2, + 0.0, + 62.13 + ], + [ + 6.616, + 0.0, + 57.47 + ], + [ + 7.23, + 0.0, + 50.59 + ], + [ + 6.854, + 0.0, + 51.39 + ], + [ + 6.29, + 0.0, + 53.59 + ], + [ + 5.387, + 0.0, + 58.69 + ], + [ + 5.11, + 0.0, + 60.83 + ], + [ + 4.061, + 0.0, + 66.77 + ], + [ + 3.674, + 0.0, + 69.18 + ], + [ + 3.017, + 0.0, + 73.0 + ], + [ + 1.15, + 0.0, + 81.1 + ], + [ + 0.866, + 0.0, + 83.5 + ], + [ + 1.588, + 0.0, + 83.2 + ], + [ + 4.564, + 0.0, + 72.4 + ], + [ + 7.24, + 0.0, + 64.89 + ], + [ + 10.3, + 0.0, + 57.04 + ], + [ + 14.1, + 0.0, + 45.59 + ], + [ + 16.63, + 0.0, + 36.69 + ], + [ + 16.71, + 0.0, + 35.2 + ], + [ + 16.39, + 0.0, + 27.19 + ], + [ + 16.27, + 0.0, + 28.44 + ], + [ + 15.04, + 0.0, + 30.76 + ], + [ + 13.75, + 0.0, + 33.94 + ], + [ + 13.11, + 0.0, + 36.35 + ], + [ + 12.19, + 0.0, + 41.4 + ], + [ + 10.54, + 0.0, + 47.37 + ], + [ + 9.71, + 0.0, + 50.01 + ], + [ + 9.13, + 0.0, + 53.42 + ], + [ + 10.02, + 0.0, + 48.54 + ], + [ + 9.89, + 0.0, + 46.86 + ], + [ + 8.8, + 0.0, + 39.82 + ], + [ + 7.9, + 0.0, + 35.2 + ], + [ + 6.955, + 0.0, + 37.3 + ], + [ + 5.309, + 0.0, + 37.69 + ], + [ + 4.258, + 0.0, + 38.95 + ], + [ + 4.255, + 0.0, + 40.13 + ], + [ + 5.229, + 0.0, + 37.91 + ], + [ + 7.81, + 0.0, + 31.21 + ], + [ + 8.74, + 0.0, + 31.16 + ], + [ + 8.79, + 0.0, + 31.7 + ], + [ + 10.31, + 0.0, + 29.42 + ], + [ + 11.19, + 0.0, + 27.02 + ], + [ + 11.68, + 0.0, + 26.24 + ], + [ + 11.04, + 0.0, + 27.37 + ], + [ + 11.32, + 0.0, + 26.68 + ], + [ + 10.99, + 0.0, + 26.74 + ], + [ + 10.41, + 0.0, + 27.32 + ], + [ + 9.51, + 0.0, + 29.18 + ], + [ + 8.52, + 0.0, + 30.89 + ], + [ + 6.893, + 0.0, + 34.22 + ], + [ + 5.257, + 0.0, + 40.67 + ], + [ + 4.493, + 0.0, + 42.42 + ], + [ + 3.892, + 0.0, + 44.88 + ], + [ + 3.062, + 0.0, + 47.76 + ], + [ + 2.63, + 0.0, + 47.3 + ], + [ + 1.755, + 0.0, + 51.81 + ], + [ + 0.402, + 0.0, + 58.5 + ], + [ + -0.587, + 0.0, + 63.77 + ], + [ + -1.302, + 0.0, + 69.7 + ], + [ + -1.591, + 0.0, + 71.2 + ], + [ + -1.172, + 0.0, + 69.92 + ], + [ + 1.689, + 0.0, + 57.48 + ], + [ + 4.263, + 0.0, + 43.65 + ], + [ + 6.705, + 0.0, + 35.21 + ], + [ + 7.89, + 0.0, + 29.8 + ], + [ + 8.7, + 0.0, + 27.58 + ], + [ + 9.79, + 0.0, + 25.55 + ], + [ + 10.44, + 0.0, + 23.77 + ], + [ + 11.45, + 0.0, + 22.14 + ], + [ + 12.24, + 0.0, + 21.03 + ], + [ + 12.42, + 0.0, + 20.79 + ], + [ + 11.56, + 0.0, + 20.99 + ], + [ + 10.57, + 0.0, + 21.67 + ], + [ + 9.12, + 0.0, + 25.25 + ], + [ + 6.802, + 0.0, + 31.41 + ], + [ + 5.652, + 0.0, + 37.0 + ], + [ + 4.052, + 0.0, + 45.09 + ], + [ + 3.613, + 0.0, + 46.17 + ], + [ + 2.809, + 0.0, + 51.26 + ], + [ + 1.864, + 0.0, + 55.94 + ], + [ + 0.783, + 0.0, + 61.03 + ], + [ + 0.165, + 0.0, + 64.48 + ], + [ + -0.696, + 0.0, + 70.0 + ], + [ + -1.124, + 0.0, + 73.2 + ], + [ + -0.55, + 0.0, + 72.8 + ], + [ + 0.819, + 0.0, + 67.58 + ], + [ + 3.469, + 0.0, + 57.01 + ], + [ + 5.879, + 0.0, + 43.61 + ], + [ + 7.79, + 0.0, + 32.47 + ], + [ + 8.82, + 0.0, + 30.15 + ], + [ + 9.85, + 0.0, + 28.34 + ], + [ + 10.88, + 0.0, + 27.27 + ], + [ + 12.0, + 0.0, + 24.36 + ], + [ + 12.6, + 0.0, + 23.15 + ], + [ + 12.95, + 0.0, + 23.02 + ], + [ + 12.73, + 0.0, + 23.74 + ], + [ + 12.03, + 0.0, + 25.25 + ], + [ + 10.85, + 0.0, + 27.55 + ], + [ + 9.24, + 0.0, + 31.04 + ], + [ + 6.964, + 0.0, + 41.15 + ], + [ + 5.274, + 0.0, + 48.31 + ], + [ + 4.423, + 0.0, + 52.34 + ] + ], + "length": 121, + "width": 3 + } + ] +} \ No newline at end of file