StageUtils.java
/*
* This file is part of Indicators.
*
* Indicators is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Indicators 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Indicators. If not, see <https://www.gnu.org/licenses/>.
*/
package fr.inrae.agroclim.indicators.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import fr.inrae.agroclim.indicators.model.Evaluation;
import fr.inrae.agroclim.indicators.model.data.phenology.AnnualStageData;
import fr.inrae.agroclim.indicators.model.data.phenology.Stage;
import fr.inrae.agroclim.indicators.model.data.phenology.StageDelta;
import lombok.extern.log4j.Log4j2;
/**
* Helper class to handle Stages.
*/
@Log4j2
public final class StageUtils {
/**
* @param evaluation evaluation to check
* @return missing stage ⇒ phases in the evaluation where the stage is used
*/
public static Map<String, List<String>> check(final Evaluation evaluation) {
final Map<String, List<String>> errors = new HashMap<>();
Objects.requireNonNull(evaluation, "evaluation must not be null");
Objects.requireNonNull(evaluation.getResourceManager(), "resource manager must not be null");
Objects.requireNonNull(evaluation.getResourceManager().getPhenologicalResource(),
"pheno resource must not be null");
final List<String> stages = evaluation.getResourceManager().getPhenologicalResource().getStages();
Objects.requireNonNull(stages, "stages must not be null");
evaluation.getPhases().forEach(p -> {
/* Nom du stade de départ de la phase */
final String start = p.getFirstIndicator().getName();
if (!stages.contains(start)) {
if (!errors.containsKey(start)) {
errors.put(start, new ArrayList<>());
}
errors.get(start).add(p.getId());
}
/* Nom du stage de fin de la phase */
final String end = p.getName();
if (!stages.contains(end)) {
if (!errors.containsKey(end)) {
errors.put(end, new ArrayList<>());
}
errors.get(end).add(p.getId());
}
});
return errors;
}
/**
* Extract StageDelta from stage name.
*
* @param stage stage name
* @return stage delta
*/
public static StageDelta getDeltaFromStage(final String stage) {
if (stage.contains("p")) {
final int i = stage.indexOf("p");
final String name = stage.substring(0, i);
final String days = stage.substring(i + 1);
return new StageDelta(name, Integer.valueOf(days));
} else if (stage.contains("m")) {
final int i = stage.indexOf("m");
final String name = stage.substring(0, i);
final String days = stage.substring(i + 1);
return new StageDelta(name, -1 * Integer.parseInt(days));
}
return new StageDelta(stage, 0);
}
/**
* Extract StageDelta from stage name.
*
* @param stages list of stage names
* @return list of stage deltas
*/
public static List<StageDelta> getDeltasFromStages(final List<String> stages) {
return stages.stream() //
.filter(stage -> stage.startsWith("s")) //
.map(StageUtils::getDeltaFromStage) //
.filter(Objects::nonNull) //
.collect(Collectors.toList());
}
/**
* @param stage stage to represent
* @return representation of stage
*/
public static String repr(final StageDelta stage) {
final Integer days = stage.getDays();
if (days == null || days == 0) {
return stage.getStage();
} else if (days > 0) {
return stage.getStage() + "p" + Math.abs(days);
} else {
return stage.getStage() + "m" + Math.abs(days);
}
}
/**
* SoilCalculator does not need sowing date in stages.
*
* @param all stages with sowing date
* @param sowingDate day of year of sowing date
* @return stages without sowing date
*/
public static List<AnnualStageData> sanitizeStagesForSoil(final List<AnnualStageData> all, final int sowingDate) {
final List<AnnualStageData> newData = new ArrayList<>();
if (all.isEmpty()) {
LOGGER.error("Strange no stages with sowing date were given");
}
all.stream().map(data -> {
AnnualStageData adata;
try {
adata = data.clone();
newData.add(adata);
} catch (final CloneNotSupportedException ex) {
throw new RuntimeException("This should never occur!", ex);
}
return adata;
}).forEachOrdered(data -> {
Stage toRemove = null;
for (final Stage stage : data.getStages()) {
if (sowingDate == stage.getValue()) {
toRemove = stage;
}
}
if (toRemove != null) {
data.getStages().remove(toRemove);
}
});
// fill first year with second year
if (newData.get(0).getStages().size() != Stage.FOUR) {
final int year = newData.get(0).getYear();
try {
if (newData.size() == 1) {
// use the same year twice if only one year is given
newData.add(newData.get(0).clone());
newData.get(1).setYear(year + 1);
} else {
newData.set(0, newData.get(1).clone());
newData.get(0).setYear(year);
}
} catch (final CloneNotSupportedException ex) {
throw new RuntimeException("This should never occur!", ex);
}
}
// fill last year with previous year
final int lastIndex = newData.size() - 1;
if (newData.get(lastIndex).getStages().size() != Stage.FOUR) {
final int year = newData.get(lastIndex).getYear();
try {
newData.set(lastIndex, newData.get(lastIndex - 1).clone());
newData.get(lastIndex).setYear(year);
} catch (final CloneNotSupportedException ex) {
throw new RuntimeException("This should never occur!", ex);
}
}
return newData;
}
/**
* No constructor for helper class.
*/
private StageUtils() {
}
}