View Javadoc
1   /**
2    * This file is part of Indicators.
3    *
4    * Indicators is free software: you can redistribute it and/or modify
5    * it under the terms of the GNU General Public License as published by
6    * the Free Software Foundation, either version 3 of the License, or
7    * (at your option) any later version.
8    *
9    * Indicators is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with Indicators. If not, see <https://www.gnu.org/licenses/>.
16   */
17  package fr.inrae.agroclim.indicators.model.data.climate;
18  
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.List;
22  import java.util.StringJoiner;
23  
24  import fr.inrae.agroclim.indicators.model.TimeScale;
25  import fr.inrae.agroclim.indicators.model.data.DailyData;
26  import fr.inrae.agroclim.indicators.model.data.Variable;
27  import fr.inrae.agroclim.indicators.model.data.Variable.Type;
28  import fr.inrae.agroclim.indicators.resources.Messages;
29  import lombok.Getter;
30  import lombok.NonNull;
31  import lombok.Setter;
32  
33  /**
34   * Class to handle reading climatic daily data.
35   *
36   * @author $Author$
37   */
38  public final class ClimaticDailyData extends DailyData {
39  
40      /**
41       * UUID for Serializable.
42       */
43      private static final long serialVersionUID = 4872847709393688042L;
44  
45      /**
46       * @param timescale related timescale
47       * @return Names of all columns, according to timescale.
48       */
49      public static List<String> getAllColumnNames(@NonNull final TimeScale timescale) {
50          switch (timescale) {
51          case DAILY:
52              return Arrays.asList("year", "month", "day", "tmin", "tmax", "tmean", "radiation", "rain", "wind", "etp",
53                      "rh", "soilwatercontent");
54          case HOURLY:
55              return Arrays.asList("year", "month", "day", "hour", "th", "radiation", "rain", "wind", "etp", "rh");
56          default:
57              throw new UnsupportedOperationException("Not implemented: " + timescale);
58          }
59      }
60  
61      /**
62       * @param timescale related timescale
63       * @return Names of all columns, according to timescale.
64       */
65      public static List<String> getOptionalColumnNames(final TimeScale timescale) {
66          switch (timescale) {
67          case DAILY:
68              return Arrays.asList("rh", "soilwatercontent");
69          case HOURLY:
70              return Arrays.asList("rh");
71          default:
72              throw new UnsupportedOperationException("Not implemented: " + timescale);
73          }
74      }
75  
76      /**
77       * @param timescale related timescale
78       * @return Names of required columns, according to timescale, used by GUI.
79       */
80      public static List<String> getRequiredColumnNames(final TimeScale timescale) {
81          switch (timescale) {
82          case DAILY:
83              return Arrays.asList("year", "month", "day");
84          case HOURLY:
85              return Arrays.asList("year", "month", "hour");
86          default:
87              throw new UnsupportedOperationException("Not implemented: " + timescale);
88          }
89      }
90  
91      /**
92       * Errors for the data.
93       */
94      private final List<String> errors = new ArrayList<>();
95  
96      /**
97       * Compute ETP from climatic daily data.
98       */
99      @Getter
100     @Setter
101     private EtpCalculator etpCalcultator;
102 
103     /**
104      * Related time scale.
105      */
106     @Setter
107     private TimeScale timescale;
108 
109     /**
110      * Warnings for the data.
111      */
112     private final List<String> warnings = new ArrayList<>();
113 
114     /**
115      * Constructor.
116      */
117     public ClimaticDailyData() {
118         //
119     }
120 
121     /**
122      * Copy constructor.
123      *
124      * @param data instance to copy
125      */
126     public ClimaticDailyData(final ClimaticDailyData data) {
127         super(data);
128         this.timescale = data.timescale;
129         this.etpCalcultator = data.etpCalcultator;
130     }
131 
132     @Override
133     public void check(final int line, final String path) {
134         if (getTmin() != null && getTmax() != null && getTmin() > getTmax()) {
135             warnings.add(Messages.format("warning.tmax.inferiorto.tmin", path, line));
136         }
137         final double rhMin = 0;
138         final double rhMax = 100;
139         if (getRh() != null && (getRh() < rhMin || getRh() > rhMax)) {
140             warnings.add(Messages.format("error.rh.outofrange", path, line));
141         }
142         if (timescale == TimeScale.DAILY && getRawValue(Variable.ETP) == null) {
143             boolean missing = false;
144             for (final Variable variable : etpCalcultator.getVariables()) {
145                 if (getRawValue(variable) == null) {
146                     missing = true;
147                 }
148             }
149             if (missing) {
150                 warnings.add(Messages.format("warning.missing", path, line, "ETP"));
151             }
152         }
153         for (final Variable variable : Variable.getByTimeScaleAndType(timescale, Type.CLIMATIC)) {
154             if (variable != Variable.ETP && getValue(variable) == null) {
155                 warnings.add(Messages.format("warning.missing", path, line, variable.getName()));
156             }
157         }
158         if (getMonth() == null || getMonth() != null && getMonth() == 0) {
159             errors.add(Messages.format("error.month.null", path, line));
160         }
161         if (getDay() == null || getDay() != null && getDay() == 0) {
162             errors.add(Messages.format("error.day.null", path, line));
163         }
164     }
165 
166     @Override
167     public ClimaticDailyData clone() throws CloneNotSupportedException {
168         final ClimaticDailyData clone = (ClimaticDailyData) super.clone();
169         if (etpCalcultator != null) {
170             clone.etpCalcultator = etpCalcultator.clone();
171         }
172         clone.errors.addAll(errors);
173         clone.warnings.addAll(warnings);
174         return clone;
175     }
176 
177     @Override
178     public List<String> getErrors() {
179         return errors;
180     }
181 
182     /**
183      * @return stored ETP value or compute it.
184      */
185     public Double getEtp() {
186         Double etp = getRawValue(Variable.ETP);
187         if (etp == null && etpCalcultator != null) {
188             try {
189                 etp = etpCalcultator.compute(this);
190                 setEtp(etp);
191             } catch (IllegalArgumentException e) {
192                 etp = null;
193             }
194         }
195         return etp;
196     }
197 
198     /**
199      * @return Global radiation [W/m²].
200      */
201     public Double getRadiation() {
202         return getRawValue(Variable.RADIATION);
203     }
204 
205     /**
206      * @return Rain precipitation [mm/d].
207      */
208     public Double getRain() {
209         return getRawValue(Variable.RAIN);
210     }
211 
212     /**
213      * @return relative humidity (%)
214      */
215     public Double getRh() {
216         return getRawValue(Variable.RH);
217     }
218 
219     /**
220      * @return Soil water content [% mass]
221      */
222     public Double getSoilwatercontent() {
223         return getRawValue(Variable.SOILWATERCONTENT);
224     }
225 
226     /**
227      * @return Instantaneous hourly air temperature [°C].
228      */
229     public Double getTh() {
230         return getRawValue(Variable.TH);
231     }
232 
233     /**
234      * @return Maximal air temperature [°C]
235      */
236     public Double getTmax() {
237         return getRawValue(Variable.TMAX);
238     }
239 
240     /**
241      * @return Average air temperature [°C]
242      */
243     public Double getTmean() {
244         return getRawValue(Variable.TMEAN);
245     }
246 
247     /**
248      * @return Minimal air temperature [°C]
249      */
250     public Double getTmin() {
251         return getRawValue(Variable.TMIN);
252     }
253 
254     @Override
255     public Double getValue(final Variable variable) {
256         if (variable == Variable.ETP) {
257             return getEtp();
258         }
259         return super.getRawValue(variable);
260     }
261 
262     @Override
263     public List<String> getWarnings() {
264         return warnings;
265     }
266 
267     /**
268      * @return Wind speed [m/s]
269      */
270     public Double getWind() {
271         return getValue(Variable.WIND);
272     }
273 
274     /**
275      * @param value Evapotranspiration [mm/d].
276      */
277     public void setEtp(final Double value) {
278         setValue(Variable.ETP, value);
279     }
280 
281     /**
282      * @param value Global radiation [W/m²]
283      */
284     public void setRadiation(final Double value) {
285         setValue(Variable.RADIATION, value);
286     }
287 
288     /**
289      * @param value Rain precipitation [mm]
290      */
291     public void setRain(final Double value) {
292         setValue(Variable.RAIN, value);
293     }
294 
295     /**
296      * @param value Relative humidity.
297      */
298     public void setRh(final Double value) {
299         setValue(Variable.RH, value);
300     }
301 
302     /**
303      * @param value Soil water content [% mass]
304      */
305     public void setSoilwatercontent(final Double value) {
306         setValue(Variable.SOILWATERCONTENT, value);
307     }
308 
309     /**
310      * @param value Instantaneous hourly air temperature [°C]
311      */
312     public void setTh(final Double value) {
313         setValue(Variable.TH, value);
314     }
315 
316     /**
317      * @param value Maximal air temperature [°C]
318      */
319     public void setTmax(final Double value) {
320         setValue(Variable.TMAX, value);
321     }
322 
323     /**
324      * @param value Average air temperature [°C]
325      */
326     public void setTmean(final Double value) {
327         setValue(Variable.TMEAN, value);
328     }
329 
330     /**
331      * @param value Minimal air temperature [°C]
332      */
333     public void setTmin(final Double value) {
334         setValue(Variable.TMIN, value);
335     }
336 
337     /**
338      * @param value Wind speed [m/s]
339      */
340     public void setWind(final Double value) {
341         setValue(Variable.WIND, value);
342     }
343 
344     @Override
345     public String toString() {
346         final StringJoiner sj = new StringJoiner(", ");
347         for (final Variable variable : Variable.values()) {
348             sj.add(variable.name() + "=" + getRawValue(variable));
349         }
350         return "ClimaticDailyData [year=" + getYear() + ", month=" + getMonth()
351         + ", day=" + getDay() + ", hour=" + getHour() + ", " + sj.toString() + "]";
352     }
353 }