1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package fr.inrae.agroclim.indicators.model;
18
19 import java.time.LocalDate;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.Date;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.LinkedHashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Objects;
30 import java.util.Set;
31 import java.util.stream.Collectors;
32
33 import fr.inrae.agroclim.indicators.exception.IndicatorsException;
34 import fr.inrae.agroclim.indicators.exception.type.ResourceErrorType;
35 import fr.inrae.agroclim.indicators.model.data.DailyData;
36 import fr.inrae.agroclim.indicators.model.data.ResourceManager;
37 import fr.inrae.agroclim.indicators.model.data.Variable;
38 import fr.inrae.agroclim.indicators.model.data.Variable.Type;
39 import fr.inrae.agroclim.indicators.model.data.climate.ClimaticDailyData;
40 import fr.inrae.agroclim.indicators.model.data.climate.ClimaticResource;
41 import fr.inrae.agroclim.indicators.model.data.phenology.AnnualStageData;
42 import fr.inrae.agroclim.indicators.model.data.phenology.PhenologicalResource;
43 import fr.inrae.agroclim.indicators.model.data.phenology.PhenologyCalculator;
44 import fr.inrae.agroclim.indicators.model.data.phenology.Stage;
45 import fr.inrae.agroclim.indicators.model.data.soil.SoilDailyData;
46 import fr.inrae.agroclim.indicators.model.data.soil.SoilLoaderProxy;
47 import fr.inrae.agroclim.indicators.model.function.aggregation.JEXLFunction;
48 import fr.inrae.agroclim.indicators.model.indicator.CompositeIndicator;
49 import fr.inrae.agroclim.indicators.model.indicator.Indicator;
50 import fr.inrae.agroclim.indicators.model.indicator.IndicatorCategory;
51 import fr.inrae.agroclim.indicators.model.indicator.listener.IndicatorEvent;
52 import fr.inrae.agroclim.indicators.model.result.EvaluationResult;
53 import fr.inrae.agroclim.indicators.model.result.IndicatorResult;
54 import fr.inrae.agroclim.indicators.model.result.PhaseResult;
55 import fr.inrae.agroclim.indicators.resources.Messages;
56 import fr.inrae.agroclim.indicators.util.DateUtils;
57 import fr.inrae.agroclim.indicators.util.StageUtils;
58 import lombok.EqualsAndHashCode;
59 import lombok.Getter;
60 import lombok.NonNull;
61 import lombok.Setter;
62 import lombok.extern.log4j.Log4j2;
63
64
65
66
67
68
69
70
71
72 @EqualsAndHashCode(callSuper = true, of = {"isTranscient", "settings"})
73 @Log4j2
74 public final class Evaluation extends CompositeIndicator {
75
76
77
78 private static final long serialVersionUID = 9205643160821888597L;
79
80
81
82
83
84
85
86 private static void fillIndicatorResults(final CompositeIndicator indicator,
87 final List<IndicatorResult> indicatorResults) {
88 indicator.getIndicators().forEach(ind -> {
89 IndicatorCategory cat;
90 cat = IndicatorCategory.getByTag(ind.getCategory());
91 if (cat == IndicatorCategory.PHENO_PHASES) {
92 return;
93 }
94
95 final IndicatorResult result = new IndicatorResult();
96 result.setIndicatorCategory(cat);
97 result.setIndicatorId(ind.getId());
98 result.setNormalizedValue(ind.getValue());
99 result.setRawValue(ind.getNotNormalizedValue());
100 indicatorResults.add(result);
101
102 if (ind instanceof CompositeIndicator compositeIndicator) {
103 fillIndicatorResults(compositeIndicator, result.getIndicatorResults());
104 }
105 });
106 }
107
108
109
110
111
112
113 @Getter
114 private final ResourceManager resourceManager;
115
116
117
118
119 private boolean ignoreEmptyClimaticData = false;
120
121
122
123
124 @Getter
125 private boolean isTranscient;
126
127
128
129
130 @Getter
131 private EvaluationSettings settings;
132
133
134
135
136 @Getter
137 @Setter
138 private transient EvaluationState state;
139
140
141
142
143 public Evaluation() {
144 super();
145 resourceManager = new ResourceManager();
146 if (getAggregationFunction() == null) {
147 setAggregationFunction(new JEXLFunction());
148 }
149 setState(EvaluationState.NEW);
150 }
151
152
153
154
155
156
157
158 public Evaluation(final CompositeIndicator indicator) {
159 super(indicator);
160 resourceManager = new ResourceManager();
161 if (getAggregationFunction() == null) {
162 setAggregationFunction(new JEXLFunction());
163 }
164 setState(EvaluationState.NEW);
165 }
166
167
168
169
170
171
172
173 public Evaluation(final Evaluation evaluation) {
174 super(evaluation);
175 if (getAggregationFunction() == null) {
176 setAggregationFunction(new JEXLFunction());
177 }
178 try {
179 resourceManager = evaluation.resourceManager.clone();
180 } catch (final CloneNotSupportedException ex) {
181 throw new RuntimeException("This should never occur!", ex);
182 }
183 if (evaluation.settings != null) {
184 try {
185 settings = evaluation.settings.clone();
186 } catch (final CloneNotSupportedException ex) {
187 throw new RuntimeException("This should never occur!", ex);
188 }
189 }
190 isTranscient = evaluation.isTranscient;
191 state = evaluation.state;
192 }
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207 public Indicator add(final IndicatorCategory category,
208 final CompositeIndicator parent, final Indicator indicator)
209 throws CloneNotSupportedException {
210 final Indicator newIndicator = indicator.clone();
211 newIndicator.setParent(parent);
212 if (!category.equals(IndicatorCategory.PHENO_PHASES)
213 && newIndicator instanceof CompositeIndicator) {
214 ((CompositeIndicator) newIndicator).clearIndicators();
215 }
216
217 if (category.equals(IndicatorCategory.PHENO_PHASES)) {
218
219 final String endStageId = newIndicator.getId();
220 final String firstStageId = ((CompositeIndicator) newIndicator)
221 .getIndicators().iterator().next().getId();
222
223 if (!isStagePresent(firstStageId, endStageId)) {
224 add(newIndicator);
225 fireIndicatorEvent(IndicatorEvent.Type.ADD.event(newIndicator));
226 } else {
227 LOGGER.warn("Phase ({},{}) already exists for evaluation.",
228 firstStageId, endStageId);
229 }
230 } else if (!parent.isPresent(newIndicator.getId())) {
231 parent.add(newIndicator);
232 newIndicator.setComputable(newIndicator
233 .isComputable(resourceManager.getClimaticResource()));
234 fireIndicatorEvent(IndicatorEvent.Type.ADD.event(newIndicator));
235 } else {
236 LOGGER.warn("Indicator {} already exists for {}.",
237 newIndicator.getId(), parent.getId());
238 return newIndicator;
239 }
240
241 fireIndicatorEvent(IndicatorEvent.Type.CHANGE.event(this));
242
243 return newIndicator;
244 }
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259 public Indicator add(final String categoryTag,
260 final CompositeIndicator parent, final Indicator indicator)
261 throws CloneNotSupportedException {
262
263 final IndicatorCategory category = IndicatorCategory.getByTag(categoryTag);
264 if (category == null) {
265 throw new RuntimeException("Unknown category: " + categoryTag);
266 }
267
268 return add(category, parent, indicator);
269 }
270
271
272
273
274
275
276
277
278 private void checkBeforeCompute(final List<CompositeIndicator> phases, final ClimaticResource climaticResource)
279 throws IndicatorsException {
280 if (phases == null) {
281 throw new RuntimeException("Phase list is null!");
282 }
283 if (phases.isEmpty()) {
284 throw new RuntimeException("Phase list is empty!");
285 }
286 if (climaticResource.isEmpty()) {
287 throw new IndicatorsException(ResourceErrorType.CLIMATE_EMPTY);
288 }
289 if (resourceManager.getPhenologicalResource().isEmpty()) {
290 throw new IndicatorsException(ResourceErrorType.PHENO_EMPTY);
291 }
292 }
293
294 @Override
295 public Evaluation clone() {
296 return new Evaluation(this);
297 }
298
299
300
301
302
303
304
305
306 public Map<Integer, EvaluationResult> compute() throws IndicatorsException {
307 LOGGER.trace("start computing evaluation \"" + getName() + "\"");
308 this.ignoreEmptyClimaticData = false;
309 fireIndicatorEvent(IndicatorEvent.Type.COMPUTE_START.event(this));
310
311 final List<CompositeIndicator> phases = getPhases();
312 final ClimaticResource climaticResource = resourceManager.getClimaticResource();
313 checkBeforeCompute(phases, climaticResource);
314
315 var results = compute(climaticResource, phases);
316 fireIndicatorEvent(IndicatorEvent.Type.COMPUTE_SUCCESS.event(this));
317 return results;
318 }
319
320 private Map<Integer, EvaluationResult> compute(final ClimaticResource climaticResource,
321 final List<CompositeIndicator> phases) throws IndicatorsException {
322 final Map<Integer, EvaluationResult> results = new LinkedHashMap<>();
323
324
325 final List<AnnualStageData> stageDatas = getResourceManager().getPhenologicalResource().getData();
326 for (final CompositeIndicator phase : phases) {
327 final String phaseId = phase.getId();
328 if (phaseId == null) {
329 throw new RuntimeException("Id of phase is null!");
330 }
331
332
333 final String startStageName = phase.getFirstIndicator().getName();
334
335 final String endStageName = phase.getName();
336
337
338 for (final AnnualStageData stageData : stageDatas) {
339
340 final Integer year = stageData.getYear();
341
342
343 int dateYear = year;
344 if (stageData.existWinterCrop()) {
345 dateYear -= 1;
346 }
347
348 if (!climaticResource.getYears().contains(dateYear)) {
349 continue;
350 }
351
352 if (!results.containsKey(year)) {
353 results.put(year, new EvaluationResult());
354 }
355
356
357 final Integer startStage = stageData.getStageValue(startStageName);
358
359
360 final Integer endStage = stageData.getStageValue(endStageName);
361
362 final AnnualPhase annualPhase = new AnnualPhase();
363 annualPhase.setHarvestYear(year);
364 annualPhase.setEndStage(endStageName);
365 annualPhase.setStartStage(startStageName);
366 annualPhase.setUid(phaseId);
367 final PhaseResult phaseResult = new PhaseResult();
368 phaseResult.setAnnualPhase(annualPhase);
369 results.get(year).getPhaseResults().add(phaseResult);
370
371 if (startStage == null) {
372 LOGGER.info(String.format("No start stage for %s/%s : %s", startStageName, endStageName,
373 stageData.toString()));
374 continue;
375 }
376
377
378 if (endStage == null || endStage == 0) {
379 LOGGER.info(String.format("No end stage for %s/%s : %s", startStageName, endStageName,
380 stageData.toShortString()));
381 continue;
382 }
383
384
385 final Date endDate = DateUtils.getDate(dateYear, endStage);
386 annualPhase.setEnd(endDate);
387 final Date startDate = DateUtils.getDate(dateYear, startStage);
388 annualPhase.setStart(startDate);
389
390
391 if (!stageData.isComplete()) {
392 LOGGER.info("In {}, missing stages {}, do not compute", year, stageData);
393 continue;
394 }
395
396
397 final ClimaticResource climaticData = climaticResource
398 .getClimaticDataByPhaseAndYear(startDate, endDate);
399 if (climaticData.isEmpty()) {
400 if (climaticResource.isEmpty()) {
401 throw new IndicatorsException(ResourceErrorType.CLIMATE_EMPTY);
402 }
403 if (ignoreEmptyClimaticData) {
404 continue;
405 }
406 final int yearToSearch = dateYear;
407 final List<ClimaticDailyData> ddataList = climaticResource.getData().stream()
408 .filter(f -> f.getYear() == yearToSearch)
409 .collect(Collectors.toList());
410
411 final ClimaticDailyData startData = ddataList.get(0);
412 final ClimaticDailyData endData = ddataList.get(ddataList.size() - 1);
413
414 throw new IndicatorsException(ResourceErrorType.CLIMATE_EMPTY_FOR_PHASE,
415 startStageName, endStageName, startStage, endStage, dateYear,
416 startData.getYear() + "-" + startData.getMonth() + "-" + startData.getDay(),
417 endData.getYear() + "-" + endData.getMonth() + "-" + endData.getDay()
418 );
419 }
420
421
422
423
424 Double value = null;
425 if (climaticData.hasCompleteDataInYear(settings.getClimateLoader().getProvidedVariables())) {
426 value = phase.compute(climaticData);
427 }
428 phaseResult.setNormalizedValue(value);
429
430 fillIndicatorResults(phase, phaseResult.getIndicatorResults());
431 phase.fireValueUpdated();
432 }
433 }
434
435 computeFaisability(results);
436 LOGGER.trace("end of computing evaluation \"{}\"", getName());
437 return results;
438 }
439
440
441
442
443
444
445
446
447
448 public Map<LocalDate, Map<Integer, EvaluationResult>> computeEachDate() throws IndicatorsException {
449 this.ignoreEmptyClimaticData = true;
450 final List<CompositeIndicator> phases = getPhases();
451 final ClimaticResource climaticResource = resourceManager.getClimaticResource();
452 checkBeforeCompute(phases, climaticResource);
453
454 final List<ClimaticDailyData> dailyData = climaticResource.getData();
455
456 final Map<LocalDate, Map<Integer, EvaluationResult>> allResults = new LinkedHashMap<>();
457 dailyData.stream().map(DailyData::getLocalDate).forEach(date -> allResults.put(date, new LinkedHashMap<>()));
458
459 final ClimaticResource resource = new ClimaticResource();
460 resource.setMissingVariables(climaticResource.getMissingVariables());
461 for (int i = 0; i < dailyData.size(); i++) {
462 final List<ClimaticDailyData> data = dailyData.subList(0, i);
463 resource.setData(data);
464 final Map<Integer, EvaluationResult> results = compute(resource, phases);
465 final LocalDate date = dailyData.get(i).getLocalDate();
466 allResults.put(date, results);
467 }
468 return allResults;
469 }
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484 private void computeFaisability(final Map<Integer, EvaluationResult> results) throws IndicatorsException {
485 LOGGER.traceEntry();
486 if (getType() == EvaluationType.WITHOUT_AGGREGATION) {
487 return;
488 }
489 if (getPhases().size() == 1) {
490
491
492 results.values().forEach(result ->
493 result.setNormalizedValue(result.getPhaseResults().get(0).getNormalizedValue()));
494 return;
495 } else if (getAggregationFunction().getExpression() == null) {
496 throw new IllegalStateException("An evaluation with more than 1 "
497 + "phase must have a defined expression for aggregation!");
498 }
499
500 for (final Map.Entry<Integer, EvaluationResult> entry : results.entrySet()) {
501 final EvaluationResult evaluationResult = entry.getValue();
502 final int year = entry.getKey();
503 if (evaluationResult == null) {
504 LOGGER.warn(
505 "Strange, null value for EvaluationResult for year {}",
506 year);
507 continue;
508 }
509 if (evaluationResult.getPhaseResults().isEmpty()) {
510 LOGGER.warn("Strange, empty results for phases for year {}",
511 year);
512 continue;
513 }
514 final Map<String, Double> values = new HashMap<>();
515 boolean failedPhase = false;
516 for (final PhaseResult phase : evaluationResult.getPhaseResults()) {
517 if (phase.getNormalizedValue() == null) {
518 failedPhase = true;
519 break;
520 }
521 values.put(phase.getEncodedPhaseId(),
522 phase.getNormalizedValue());
523 }
524 if (failedPhase) {
525 evaluationResult.setNormalizedValue(0.);
526 continue;
527 }
528 final Double normalizedValue = getAggregationFunction().aggregate(values);
529 evaluationResult.setNormalizedValue(normalizedValue);
530 }
531 }
532
533
534
535
536
537
538 @Override
539 public boolean containsClimaticIndicator() {
540 boolean contains = true;
541 for (final CompositeIndicator phase : getPhases()) {
542 if (!phase.containsClimaticIndicator()) {
543
544 fireIndicatorEvent(
545 IndicatorEvent.Type.CLIMATIC_MISSING.event(phase));
546 contains = false;
547 }
548 }
549
550 if (getPhases().isEmpty()) {
551 LOGGER.trace("Evaluation {} does not contain any phase!",
552 getName());
553 fireIndicatorEvent(IndicatorEvent.Type.PHASE_MISSING.event(this));
554 }
555 return contains;
556 }
557
558
559
560
561
562 public boolean containsClimaticIndicator(final boolean fire) {
563 if (fire) {
564 return containsClimaticIndicator();
565 }
566 boolean contains = true;
567 for (final CompositeIndicator phase : getPhases()) {
568 if (!phase.containsClimaticIndicator()) {
569
570 contains = false;
571 }
572 }
573
574 return contains;
575 }
576
577
578
579
580 public ClimaticResource getClimaticResource() {
581 return resourceManager.getClimaticResource();
582 }
583
584 @Override
585 public String getId() {
586 return "root-evaluation";
587 }
588
589
590
591
592
593
594 @Override
595 public Indicator getParent() {
596 return null;
597 }
598
599
600
601
602 public List<CompositeIndicator> getPhases() {
603 final List<CompositeIndicator> phases = new ArrayList<>();
604 for (final Indicator indicator : getIndicators()) {
605 if (indicator instanceof CompositeIndicator compositeIndicator && compositeIndicator.isPhase()) {
606 phases.add(compositeIndicator);
607 }
608 }
609 return phases;
610 }
611
612
613
614
615 public List<String> getStages() {
616 final List<String> stages = new ArrayList<>();
617 for (final CompositeIndicator phase : getPhases()) {
618 if (phase == null) {
619 throw new RuntimeException("phase in getPhases() must not be null!");
620 }
621 if (phase.getFirstIndicator() != null) {
622 final String startStage = phase.getFirstIndicator().getName();
623 if (startStage == null) {
624 throw new RuntimeException("Name of first indicator in phase must not be null!");
625 }
626 if (!stages.contains(startStage)) {
627 stages.add(startStage);
628 }
629 }
630 final String endStage = phase.getName();
631 if (endStage == null) {
632 throw new RuntimeException("Name of phase must not be null!");
633 }
634 if (!stages.contains(endStage)) {
635 stages.add(endStage);
636 }
637 }
638 Collections.sort(stages);
639 return stages;
640 }
641
642
643
644
645 private List<Date> getStagesForSoil() {
646 LOGGER.traceEntry();
647
648 PhenologyCalculator soilPhenoCalc = settings.getSoilPhenologyCalculator();
649 if (soilPhenoCalc != null) {
650 final List<AnnualStageData> dates = soilPhenoCalc.load();
651 return PhenologicalResource.asDates(
652 StageUtils.sanitizeStagesForSoil(
653 dates,
654 soilPhenoCalc.getSowingDate()));
655 }
656
657
658 final List<AnnualStageData> data = settings.getPhenologyLoader().load();
659 final int nbStages = data.get(0).getStages().size();
660 if (nbStages == Stage.FOUR) {
661 return PhenologicalResource.asDates(data);
662 }
663 throw new RuntimeException(Messages.format(nbStages, "warning.soilcalculator.4stages", nbStages));
664 }
665
666
667
668
669
670
671 @Override
672 public Set<Variable> getVariables() {
673 final Set<Variable> variables = new HashSet<>(super.getVariables());
674 if (settings == null) {
675 LOGGER.error("Evaluation.settings is null!");
676 return variables;
677 }
678 if (settings.getPhenologyLoader() != null) {
679 if (settings.getPhenologyLoader().getVariables() != null) {
680 variables.addAll(settings.getPhenologyLoader().getVariables());
681 } else {
682 LOGGER.warn("No variable in PhenologyLoader().getVariables())");
683 }
684 }
685 if (settings.getSoilLoader() != null) {
686 variables.addAll(settings.getSoilLoader().getVariables());
687 }
688 variables.remove(null);
689 return variables;
690 }
691
692
693
694
695 private void initializeClimaticResource() {
696 LOGGER.traceEntry();
697 if (settings == null) {
698 throw new RuntimeException("settings should not be null!");
699 }
700 if (settings.getClimateLoader() == null) {
701 throw new RuntimeException(
702 "settings.getClimateLoader() should not be null!");
703 }
704 settings.getClimateLoader().addDataLoadingListener(this);
705 settings.getClimateLoader().setTimeScale(settings.getTimescale());
706 final List<ClimaticDailyData> data = settings.getClimateLoader().load();
707 resourceManager.getClimaticResource().setData(data);
708 resourceManager.getClimaticResource().setMissingVariables(
709 settings.getClimateLoader().getMissingVariables());
710 }
711
712
713
714
715 private void initializePhenologicalResource() {
716 LOGGER.trace("start");
717 if (settings == null) {
718 throw new RuntimeException("settings should not be null!");
719 }
720 if (settings.getPhenologyLoader() == null) {
721 throw new RuntimeException("settings.getPhenologyLoader() should not be null!");
722 }
723 if (settings.getPhenologyLoader().getCalculator() != null) {
724 List<ClimaticDailyData> climaticData = settings.getPhenologyLoader().getCalculator().getClimaticDailyData();
725 if (climaticData == null || climaticData.isEmpty()) {
726 climaticData = resourceManager.getClimaticResource().getData();
727 settings.getPhenologyLoader().getCalculator().setClimaticDailyData(climaticData);
728 }
729 }
730 final List<AnnualStageData> data = settings.getPhenologyLoader().load();
731 LOGGER.trace("{} stages", data.size());
732 resourceManager.getPhenologicalResource().setData(data);
733 LOGGER.trace("{} stages", resourceManager.getPhenologicalResource().getData().size());
734 if (settings.getPhenologyLoader().getFile() != null) {
735 resourceManager.getPhenologicalResource().setUserHeader(
736 settings.getPhenologyLoader().getFile().getHeaders());
737 }
738 LOGGER.trace("end");
739 }
740
741
742
743
744 public void initializeResources() {
745 initializeResources(false, false);
746 }
747
748
749
750
751
752
753
754
755 public void initializeResources(final boolean climatic, final boolean soil) {
756 LOGGER.trace("start");
757
758
759 resourceManager.setVariables(getVariables());
760 LOGGER.trace(getVariables());
761
762 if (climatic || resourceManager.hasClimaticVariables()) {
763 LOGGER.trace("hasClimaticVariables!");
764 initializeClimaticResource();
765 }
766
767 initializePhenologicalResource();
768
769
770 final Set<Variable> neededSoilVariables = getVariables().stream()
771 .filter(v -> v.getType() == Type.SOIL)
772 .collect(Collectors.toSet());
773 if (soil || !neededSoilVariables.isEmpty()) {
774 final Set<Variable> providedSoilVariables = settings.getClimateLoader().getProvidedVariables().stream()
775 .filter(v -> v.getType() == Type.SOIL)
776 .collect(Collectors.toSet());
777 final boolean compute = !providedSoilVariables.containsAll(neededSoilVariables);
778 initializeSoilResource(compute);
779 }
780 }
781
782
783
784
785
786 private void initializeSoilResource(final boolean compute) {
787 if (compute) {
788 if (settings == null) {
789 throw new RuntimeException("settings should not be null!");
790 }
791 final SoilLoaderProxy soilLoader = settings.getSoilLoader();
792 if (soilLoader == null) {
793 throw new RuntimeException(Messages.get("warning.soilloader.missing"));
794 }
795 soilLoader.addDataLoadingListener(this);
796 final List<ClimaticDailyData> data = resourceManager.getClimaticResource().getData();
797 if (settings.getSoilPhenologyCalculator() != null) {
798 final List<ClimaticDailyData> filled = new ArrayList<>();
799
800 int nbOfYears = settings.getSoilPhenologyCalculator().getNbOfYears();
801 if (nbOfYears > 1) {
802 int firstYear = data.get(0).getYear();
803 for (int i = nbOfYears - 1; i > 0; i--) {
804 int year = firstYear - i;
805 int nbOfDays = DateUtils.nbOfDays(year);
806 for (int d = 0; d < nbOfDays; d++) {
807 ClimaticDailyData aData = new ClimaticDailyData(data.get(d));
808 LocalDate date = DateUtils.asLocalDate(aData.getDate()).minusYears(i);
809 aData.setDay(date.getDayOfMonth());
810 aData.setMonth(date.getMonthValue());
811 aData.setYear(date.getYear());
812 filled.add(aData);
813 }
814 }
815 }
816 filled.addAll(data);
817 settings.getSoilPhenologyCalculator().setClimaticDailyData(filled);
818 }
819 soilLoader.setClimaticDailyData(data);
820 soilLoader.setStages(getStagesForSoil());
821 final Iterator<SoilDailyData> soilDataIterator = soilLoader.load().iterator();
822
823
824 for (final ClimaticDailyData aClimaticData : data) {
825 final SoilDailyData aSoilData = soilDataIterator.next();
826 aClimaticData.setValue(Variable.WATER_RESERVE, aSoilData.getWaterReserve());
827 aClimaticData.setValue(Variable.SOILWATERCONTENT, aSoilData.getSwc());
828 }
829 }
830 final List<String> missingVariables = resourceManager.getClimaticResource().getMissingVariables();
831 missingVariables.remove(Variable.WATER_RESERVE.getName());
832 missingVariables.remove(Variable.SOILWATERCONTENT.getName());
833 resourceManager.getClimaticResource().setMissingVariables(missingVariables);
834 }
835
836
837
838
839
840
841
842 public boolean isAggregationValid() {
843 if (getType() == EvaluationType.WITHOUT_AGGREGATION || getPhases().size() < 2) {
844 return true;
845 }
846 final Map<String, Double> values = new HashMap<>();
847 getPhases().forEach(phase -> values.put(phase.getId(), 1.));
848 try {
849 getAggregationFunction().aggregate(values);
850 } catch (final IndicatorsException ex) {
851 LOGGER.info("Invalid aggregation: {}", ex.getLocalizedMessage());
852 return false;
853 }
854 return true;
855 }
856
857
858
859
860 public boolean isNew() {
861 return getSettings().getFilePath() == null;
862 }
863
864
865
866
867
868 public boolean isOnErrorOrIncomplete(final boolean fire) {
869 final boolean hasClimaticIndicator = containsClimaticIndicator(fire);
870 final boolean isToAggregate = isAggregationMissing(fire);
871 final boolean isComputable = isComputable();
872
873 return !hasClimaticIndicator || isToAggregate || !isComputable;
874 }
875
876
877
878
879
880
881
882
883
884
885 protected boolean isStagePresent(final String firstId, final String endId) {
886 LOGGER.trace("firstId: {}, endId: {}", firstId, endId);
887 boolean result = false;
888 for (final Indicator i : getIndicators()) {
889 final Indicator firstIndicator = ((CompositeIndicator) i).getIndicators()
890 .get(0);
891 LOGGER.trace("indicator id={}", i.getId());
892 LOGGER.trace("first child indicator id={}", firstIndicator.getId());
893 if (i.getId().equals(endId)
894 && firstIndicator.getId().equals(firstId)) {
895 result = true;
896 break;
897 }
898 }
899 return result;
900 }
901
902
903
904
905
906 public void setParametersFromKnowledge() {
907 if (getSettings() == null) {
908 throw new IllegalStateException("Settings are not set!");
909 }
910 if (getSettings().getKnowledge() == null) {
911 throw new IllegalStateException(
912 "Knowledge is not set in settings!");
913 }
914 setParametersFromKnowledge(getSettings().getKnowledge());
915 }
916
917
918
919
920
921 public void setSettings(final EvaluationSettings value) {
922 Objects.requireNonNull(value);
923 settings = value;
924 if (settings.getEvaluation().getAggregationFunction() != null) {
925 setAggregationFunction(settings.getEvaluation()
926 .getAggregationFunction());
927 } else {
928 setAggregationFunction(new JEXLFunction());
929 }
930 setName("en", settings.getName());
931 setTimescale(settings.getTimescale());
932 resourceManager.setVariables(this.getVariables());
933 }
934
935
936
937
938
939
940 public void setTranscient(final boolean value) {
941 LOGGER.traceEntry();
942 this.setTranscient(value, false);
943 }
944
945
946
947
948
949
950 public void setTranscient(final boolean value, final boolean fromSave) {
951 LOGGER.traceEntry();
952 if (isTranscient == value) {
953 return;
954 }
955 this.isTranscient = value;
956 if (!fromSave) {
957 fireIndicatorEvent(IndicatorEvent.Type.CHANGE.event(this));
958 }
959 }
960
961
962
963
964 public void setType(@NonNull final EvaluationType type) {
965 Objects.requireNonNull(settings);
966 settings.setType(type);
967 }
968
969
970
971
972 public String toStringTree() {
973 final StringBuilder sb = new StringBuilder();
974 getIndicators().forEach(phase -> sb.append(phase.toStringTree("")).append("\n"));
975 return sb.toString();
976 }
977
978
979
980
981 public void validate() {
982 state.onValidate(this);
983 }
984
985 }