Something went wrong on our end
-
Tor-Einar Skog authoredTor-Einar Skog authored
DataTransformer.java 10.67 KiB
/*
* 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.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import net.ipmdecisions.model.entity.LocationResult;
import net.ipmdecisions.model.entity.ModelOutput;
import net.ipmdecisions.weather.entity.LocationWeatherData;
import net.ipmdecisions.weather.entity.WeatherData;
import no.nibio.vips.entity.Result;
import no.nibio.vips.entity.WeatherObservation;
import no.nibio.vips.observation.Observation;
import no.nibio.vips.observation.ObservationImpl;
/**
* Util methods to transform data between IPM Decisions and VIPS formats
* @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) throws DataTransformerException
{
IPMDecisionsWeatherUtil ipmUtil = new IPMDecisionsWeatherUtil();
// Input check: Are we able to transform all the parameters?
List<Integer> missingParamCodes = new ArrayList<>();
for(Integer paramCode: IPMWeatherData.getWeatherParameters())
{
if(ipmUtil.getVIPSParameterId(paramCode) == null)
{
missingParamCodes.add(paramCode);
}
}
if(missingParamCodes.size() > 0)
{
String paramStr = missingParamCodes.stream().map(p->String.valueOf(p)).collect(Collectors.joining(","));
throw new DataTransformerException("Could not find matching internal parameter codes for: " + paramStr);
}
List<WeatherObservation> retVal = new ArrayList<>();
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;
}
/**
* Convert VIPS Result list to a IPM Decisions model output object
* @param VIPSResults
* @return
*/
public ModelOutput getIPMDecisionsModelOutput(List<Result> VIPSResults){
// Loop through once first to
// a) Detect the max frequency between results and
// b) check all possible result parameters
Collections.sort(VIPSResults);
Set<String> resultParameters = new HashSet<>();
//resultParameters.add("WARNING_STATUS");
Long minSecondsBetween = null;
Long lastTime = null;
for(Result r:VIPSResults)
{
resultParameters.addAll(r.getKeys());
if(lastTime != null)
{
minSecondsBetween = minSecondsBetween != null ? Math.min(minSecondsBetween, (r.getValidTimeStart().getTime() - lastTime)/1000)
:(r.getValidTimeStart().getTime() - lastTime)/1000;
}
lastTime = r.getValidTimeStart().getTime();
}
String[] VIPSResultParameters = resultParameters.stream().toArray(String[] ::new);
String[] IPMResultParameters = new String[VIPSResultParameters.length];
for(int i=0;i<VIPSResultParameters.length;i++)
{
String[] parts = VIPSResultParameters[i].split("\\.");
IPMResultParameters[i] = parts[parts.length-1];
}
// Build the result
ModelOutput retVal = new ModelOutput();
retVal.setInterval(minSecondsBetween.intValue());
retVal.setResultParameters(IPMResultParameters);
retVal.setTimeStart(VIPSResults.get(0).getValidTimeStart().toInstant());
retVal.setTimeEnd(VIPSResults.get(VIPSResults.size()-1).getValidTimeStart().toInstant());
LocationResult locationResult = new LocationResult();
Long rows = 1 + (retVal.getTimeEnd().getEpochSecond() - retVal.getTimeStart().getEpochSecond()) / retVal.getInterval();
Double[][] data = new Double[rows.intValue()][retVal.getResultParameters().length];
Integer[] warningStatus = new Integer[rows.intValue()];
for(Result r:VIPSResults)
{
// Calculate which row, based on
Long row = (r.getValidTimeStart().getTime()/1000 - retVal.getTimeStart().getEpochSecond()) / retVal.getInterval();
warningStatus[row.intValue()] = r.getWarningStatus();
// Using the ordering in the resultParameters
for(int i=0;i<retVal.getResultParameters().length;i++)
{
data[row.intValue()][i] = null;
if(VIPSResultParameters[i] != null)
{
if(r.getAllValues().get(VIPSResultParameters[i]) != null && ! r.getAllValues().get(VIPSResultParameters[i]).toLowerCase().equals("null"))
{
data[row.intValue()][i] = Double.valueOf(r.getAllValues().get(VIPSResultParameters[i]));
}
}
}
}
locationResult.setWarningStatus(warningStatus);
locationResult.setData(data);
retVal.addLocationResult(locationResult);
return retVal;
}
/**
* Transform a field observation from IPM Decisions to VIPS entity
*
* Example schema for an IPM Decisions DSS/model:
*
* {
"type": "object",
"properties": {
"modelId": {
"type": "string",
"pattern": "^PSILAROBSE$",
"title": "Model Id",
"default": "PSILAROBSE",
"description": "Must be PSILAROBSE"
},
"configParameters": {
"title": "Configuration parameters",
"type": "object",
"properties": {
"timeZone": {
"type": "string",
"title": "Time zone (e.g. Europe/Oslo)",
"default": "Europe/Oslo"
},
"startDateCalculation": {
"type": "string",
"format": "date",
"default": "{CURRENT_YEAR}-03-01",
"title": "Start date of calculation (YYYY-MM-DD)"
},
"endDateCalculation": {
"type": "string",
"format": "date",
"default": "{CURRENT_YEAR}-09-01",
"title": "End date of calculation (YYYY-MM-DD)"
},
"fieldObservations": {
"title": "Field observations",
"type": "array",
"items": {
"type": "object",
"title": "Field observation",
"properties": {
"fieldObservation": {
"title": "Generic field observation information",
"$ref": "https://platform.ipmdecisions.net/api/dss/rest/schema/fieldobservation"
},
"quantification": {
"$ref": "#/definitions/fieldObs_PSILRO"
}
}
}
}
},
"required": [
"timeZone",
"startDateCalculation",
"endDateCalculation"
]
}
},
"required": [
"modelId",
"configParameters"
],
"definitions": {
"fieldObs_PSILRO": {
"title": "Psila rosae quantification",
"properties": {
"trapCountCropEdge": {
"title": "Insect trap count at the edge of the field",
"type": "integer"
},
"trapCountCropInside": {
"title": "Insect trap count inside the field",
"type": "integer"
}
},
"required": [
"trapCountCropEdge",
"trapCountCropInside"
]
}
}
}
*
* @param fieldObservations
* @return
*/
public List<Observation> getVIPSPestObservations(List<Map> fieldObservations) {
List<Observation> retVal = new ArrayList<>();
ObjectMapper om = new ObjectMapper();
for(int i=0;i<fieldObservations.size();i++)
{
Map fieldObservation = fieldObservations.get(i);
Map commonInfo = (Map) fieldObservation.get("fieldObservation");
Map quantification = (Map) fieldObservation.get("quantification");
ObservationImpl VIPSObs = new ObservationImpl();
VIPSObs.setGeoinfo(commonInfo.get("location").toString());
try
{
VIPSObs.setObservationData(om.writeValueAsString(quantification));
}catch(JsonProcessingException ex)
{
ex.printStackTrace();
}
VIPSObs.setTimeOfObservation(om.convertValue(commonInfo.get("time"), Date.class));
retVal.add(VIPSObs);
}
return retVal;
}
}