comparison src/main/java/de/intevation/lada/factory/ProbeFactory.java @ 1064:8ec6a2a31171

Generalize generation of Proben for (multiples of) months.
author Tom Gottfried <tom@intevation.de>
date Thu, 29 Sep 2016 16:15:38 +0200
parents 0fbcaefc565b
children 703b370c3b34
comparison
equal deleted inserted replaced
1063:7788a805a98d 1064:8ec6a2a31171
10 import java.sql.Timestamp; 10 import java.sql.Timestamp;
11 import java.util.ArrayList; 11 import java.util.ArrayList;
12 import java.util.Calendar; 12 import java.util.Calendar;
13 import java.util.Date; 13 import java.util.Date;
14 import java.util.List; 14 import java.util.List;
15 import java.util.Hashtable;
15 16
16 import javax.inject.Inject; 17 import javax.inject.Inject;
17 18
18 import org.apache.log4j.Logger; 19 import org.apache.log4j.Logger;
19 20
42 * 43 *
43 * @author <a href="mailto:rrenkert@intevation.de">Raimund Renkert</a> 44 * @author <a href="mailto:rrenkert@intevation.de">Raimund Renkert</a>
44 */ 45 */
45 public class ProbeFactory { 46 public class ProbeFactory {
46 47
48 private static Hashtable<String, int[]> fieldsTable;
49
50 public ProbeFactory() {
51 int[] M = { Calendar.MONTH, Calendar.DAY_OF_MONTH, 1 };
52 int[] Q = { Calendar.MONTH, Calendar.DAY_OF_MONTH, 3 };
53 int[] H = { Calendar.MONTH, Calendar.DAY_OF_MONTH, 6 };
54
55 this.fieldsTable = new Hashtable<String, int[]>();
56
57 this.fieldsTable.put("M", M);
58 this.fieldsTable.put("Q", Q);
59 this.fieldsTable.put("H", H);
60 }
61
62 private class Intervall {
63 private int teilVon;
64 private int teilBis;
65 private int offset;
66
67 private int intervallField;
68 private int subIntField;
69 private int intervallFactor;
70
71 private Calendar from;
72
73 public Intervall(
74 Messprogramm messprogramm,
75 Calendar start
76 ) {
77 this.teilVon = messprogramm.getTeilintervallVon();
78 this.teilBis = messprogramm.getTeilintervallBis();
79 this.offset = messprogramm.getIntervallOffset();
80
81 this.intervallField = fieldsTable
82 .get(messprogramm.getProbenintervall())[0];
83 this.subIntField = fieldsTable
84 .get(messprogramm.getProbenintervall())[1];
85 this.intervallFactor = fieldsTable
86 .get(messprogramm.getProbenintervall())[2];
87
88 /* Align with beginning of next interval
89 * like first day of next quarter.*/
90 // TODO: Avoid overflow e.g. into next quarter, if e.g. teilBis
91 // greater than sum of actual maxima of months in quarter.
92 int startIntField = start.get(intervallField);
93 this.from = (Calendar)start.clone();
94 from.set(
95 intervallField,
96 startIntField + startIntField % intervallFactor
97 );
98 from = adjustSubIntField(from, teilVon);
99 if (start.after(from)) {
100 // to next intervall if start not at first day of intervall
101 this.roll();
102 }
103 }
104
105 /**
106 * Return given calendar adjusted to start of intervall (e.g. first
107 * day in quarter) plus offset and given amount of days.
108 *
109 * @param cal Calendar to be adjusted
110 * @param int amount of days to be added (plus offset)
111 *
112 * @return the adjusted Calendar object.
113 */
114 private Calendar adjustSubIntField(Calendar cal, int teil) {
115 int intField = cal.get(intervallField);
116 cal.set(
117 intervallField,
118 intField - intField % intervallFactor
119 );
120 cal.set(subIntField, offset + Math.min(teil, getDuration()));
121 return cal;
122 }
123
124 /**
125 * Get sum of actual maxima for given field from actual field value
126 * for the next factor values of iterField.
127 *
128 * @param calendar Time to start from
129 *
130 * @return the sum of maxima for the field values specified with
131 * field and factor.
132 */
133 private int getDuration() {
134 logger.debug("## calculate maximum ##");
135 int duration = 0;
136 Calendar tmp = (Calendar)from.clone();
137 /* reset to beginning of intervall, e.g. first day of quarter
138 * to compensate possible overflow if
139 * teilVon > maximum of intervallField: */
140 int fromIntField = from.get(intervallField);
141 tmp.set(
142 intervallField,
143 fromIntField - fromIntField % intervallFactor
144 );
145 tmp.set(subIntField, tmp.getActualMinimum(subIntField));
146 logger.debug(tmp);
147 for (int i = 0; i < intervallFactor; i++) {
148 logger.debug(tmp.getActualMaximum(subIntField));
149 duration += tmp.getActualMaximum(subIntField);
150 tmp.add(intervallField, 1);
151 }
152 logger.debug(duration);
153 return duration;
154 }
155
156 public Date getFrom() {
157 logger.debug("getFrom() from: " + from);
158 return from.getTime();
159 }
160
161 public Date getTo() {
162 logger.debug("getTo() from: " + from);
163 Calendar to = (Calendar)from.clone();
164 to = adjustSubIntField(to, teilBis);
165 return to.getTime();
166 }
167
168 public boolean startInLeapYear() {
169 return from.getActualMaximum(Calendar.DAY_OF_YEAR) > 364;
170 }
171
172 public int getStartDOY() {
173 return from.get(Calendar.DAY_OF_YEAR);
174 }
175
176 public void roll() {
177 from.add(intervallField, intervallFactor);
178 from = adjustSubIntField(from, teilVon);
179 }
180
181 }
182 // end Intervall class
183
184
47 @Inject Logger logger; 185 @Inject Logger logger;
48 186
49 /** 187 /**
50 * The data repository 188 * The data repository
51 */ 189 */
78 Messprogramm messprogramm = messprogramme.get(0); 216 Messprogramm messprogramm = messprogramme.get(0);
79 Calendar start = Calendar.getInstance(); 217 Calendar start = Calendar.getInstance();
80 start.setTimeInMillis(from); 218 start.setTimeInMillis(from);
81 Calendar end = Calendar.getInstance(); 219 Calendar end = Calendar.getInstance();
82 end.setTimeInMillis(to); 220 end.setTimeInMillis(to);
221
83 List<LProbe> proben = new ArrayList<LProbe>(); 222 List<LProbe> proben = new ArrayList<LProbe>();
84 223
85 224 if (fieldsTable.keySet().contains(messprogramm.getProbenintervall())) {
86 if ("M".equals(messprogramm.getProbenintervall())) { 225 proben.addAll(generateMonthly(messprogramm, start, end));
87 Calendar realStart = getStart ("M", start);
88 proben.addAll(generateMonth(messprogramm, realStart, end));
89 }
90 else if ("Q".equals(messprogramm.getProbenintervall())) {
91 Calendar realStart = getStart ("Q", start);
92 proben.addAll(generateQuarter(messprogramm, realStart, end));
93 }
94 else if ("H".equals(messprogramm.getProbenintervall())) {
95 Calendar realStart = getStart ("H", start);
96 proben.addAll(generateHalf(messprogramm, realStart, end));
97 } 226 }
98 else { 227 else {
99 Date[][] intervals = calculateIntervals(start, end, messprogramm); 228 Date[][] intervals = calculateIntervals(start, end, messprogramm);
100 for (Date[] interval : intervals) { 229 for (Date[] interval : intervals) {
101 createProben(interval, messprogramm, proben); 230 createProben(interval, messprogramm, proben);
159 } 288 }
160 } 289 }
161 } 290 }
162 } 291 }
163 292
164 private List<LProbe> generateMonth( 293 private List<LProbe> generateMonthly(
165 Messprogramm messprogramm, 294 Messprogramm messprogramm,
166 Calendar start, 295 Calendar start,
167 Calendar end 296 Calendar end
168 ) { 297 ) {
298 logger.debug("start: " + start);
299
169 int gueltigVon = messprogramm.getGueltigVon(); 300 int gueltigVon = messprogramm.getGueltigVon();
170 int gueltigBis = messprogramm.getGueltigBis(); 301 int gueltigBis = messprogramm.getGueltigBis();
171 302
172 int offset = messprogramm.getIntervallOffset();
173 int teilVon = messprogramm.getTeilintervallVon();
174 int teilBis = messprogramm.getTeilintervallBis();
175
176 List<LProbe> proben = new ArrayList<LProbe>(); 303 List<LProbe> proben = new ArrayList<LProbe>();
177 304
178 // Create template Calendars to be recomputed/adapted to actual 305 for (Intervall intervall = new Intervall(messprogramm, start);
179 // maxima/minima of fields. 306 intervall.getFrom().before(end.getTime());
180 Calendar initSolldatumBeginn = (Calendar)start.clone(); 307 intervall.roll()
181 initSolldatumBeginn.set(Calendar.DAY_OF_MONTH, offset + teilVon); 308 ) {
182 Calendar initSolldatumEnde = (Calendar)start.clone();
183 initSolldatumEnde.set(Calendar.DAY_OF_MONTH, offset + teilBis);
184
185 // Initialize solldatum values for Probe objects, i.e. trigger
186 // recomputation of Calendar fields.
187 Calendar solldatumBeginn = (Calendar)initSolldatumBeginn.clone();
188 Calendar solldatumEnde = (Calendar)initSolldatumEnde.clone();
189 solldatumBeginn.add(Calendar.MONTH, 0);
190 solldatumEnde.add(Calendar.MONTH, 0);
191
192 for (int monthCount = 1; solldatumBeginn.before(end); monthCount++) {
193 /* Leap year adaption of validity period. 309 /* Leap year adaption of validity period.
194 * It is assumed here (and should be enforced by the data model) 310 * It is assumed here (and should be enforced by the data model)
195 * that gueltigVon and gueltigBis are always given relative to 311 * that gueltigVon and gueltigBis are always given relative to
196 * a non-leap year. E.g. a value of 59 is assumed to denote 312 * a non-leap year. E.g. a value of 59 is assumed to denote
197 * March 1 and thus has to be adapted in a leap year. */ 313 * March 1 and thus has to be adapted in a leap year. */
198 int leapDay = 314 int leapDay = intervall.startInLeapYear() ? 1 : 0;
199 solldatumBeginn.getActualMaximum(Calendar.DAY_OF_YEAR) > 364
200 ? 1
201 : 0;
202 int actualGueltigVon = 315 int actualGueltigVon =
203 gueltigVon > 58 316 gueltigVon > 58
204 ? gueltigVon + leapDay 317 ? gueltigVon + leapDay
205 : gueltigVon; 318 : gueltigVon;
206 int actualGueltigBis = 319 int actualGueltigBis =
207 gueltigBis > 58 320 gueltigBis > 58
208 ? gueltigBis + leapDay 321 ? gueltigBis + leapDay
209 : gueltigBis; 322 : gueltigBis;
210 323
211 int solldatumBeginnDOY = solldatumBeginn.get(Calendar.DAY_OF_YEAR); 324 int solldatumBeginnDOY = intervall.getStartDOY();
212 325
213 if (( 326 if ((
214 // Validity within one year 327 // Validity within one year
215 actualGueltigVon < actualGueltigBis 328 actualGueltigVon < actualGueltigBis
216 && solldatumBeginnDOY >= actualGueltigVon 329 && solldatumBeginnDOY >= actualGueltigVon
222 || solldatumBeginnDOY <= actualGueltigBis) 335 || solldatumBeginnDOY <= actualGueltigBis)
223 ) 336 )
224 ) { 337 ) {
225 LProbe probe = createObjects( 338 LProbe probe = createObjects(
226 messprogramm, 339 messprogramm,
227 solldatumBeginn.getTime(), 340 intervall.getFrom(),
228 solldatumEnde.getTime() 341 intervall.getTo()
229 ); 342 );
230 if (probe != null) { 343 if (probe != null) {
231 proben.add(probe); 344 proben.add(probe);
232 } 345 }
233 } 346 } else {
234 347 logger.debug(solldatumBeginnDOY + " not within validity "
235 // Reset solldatumBeginn to align DAY_OF_MONTH with initial value 348 + actualGueltigVon + " to " + actualGueltigBis);
236 solldatumBeginn = (Calendar)initSolldatumBeginn.clone(); 349 }
237 solldatumEnde = (Calendar)initSolldatumEnde.clone();
238
239 // Move to next month.
240 // This may change DAY_OF_MONTH due to actual maximum.
241 solldatumBeginn.add(Calendar.MONTH, monthCount);
242 solldatumEnde.add(Calendar.MONTH, monthCount);
243 }
244
245 return proben;
246 }
247
248 private List<LProbe> generateQuarter(
249 Messprogramm messprogramm,
250 Calendar start,
251 Calendar end
252 ) {
253 int offset = messprogramm.getIntervallOffset();
254 int teilVon = messprogramm.getTeilintervallVon() - 1;
255 int teilBis = messprogramm.getTeilintervallBis();
256 int manualDuration = teilBis - teilVon;
257
258 List<LProbe> proben = new ArrayList<LProbe>();
259
260 int currentLength = getDurationQuarter(start);
261 if (manualDuration > 0) {
262 currentLength = manualDuration;
263 }
264 Calendar quarterStart = (Calendar)start.clone();
265 Calendar quarterEnd = Calendar.getInstance();
266 quarterStart.add(Calendar.DAY_OF_YEAR, offset + teilVon);
267 quarterEnd.setTime(start.getTime());
268 quarterEnd.add(Calendar.DAY_OF_YEAR, currentLength);
269 for (;quarterStart.before(end);) {
270 int startDOY = quarterStart.get(Calendar.DAY_OF_YEAR);
271 if (startDOY > messprogramm.getGueltigVon()
272 && startDOY < messprogramm.getGueltigBis()) {
273 LProbe probe = createObjects(
274 messprogramm,
275 quarterStart.getTime(),
276 quarterEnd.getTime()
277 );
278 if (probe != null) {
279 proben.add(probe);
280 }
281 }
282 quarterStart.set(Calendar.DAY_OF_MONTH, 2);
283 quarterStart = getStart("Q", quarterStart);
284 quarterStart.add(Calendar.DAY_OF_YEAR, offset + teilVon);
285 if (manualDuration <= 0) {
286 currentLength = getDurationQuarter(quarterStart);
287 }
288 quarterEnd.setTime(quarterStart.getTime());
289 quarterEnd.add(Calendar.DAY_OF_YEAR, currentLength);
290 }
291
292 return proben;
293 }
294
295 private List<LProbe> generateHalf(
296 Messprogramm messprogramm,
297 Calendar start,
298 Calendar end
299 ) {
300 int offset = messprogramm.getIntervallOffset();
301 int teilVon = messprogramm.getTeilintervallVon() - 1;
302 int teilBis = messprogramm.getTeilintervallBis();
303 int manualDuration = teilBis - teilVon;
304
305 List<LProbe> proben = new ArrayList<LProbe>();
306
307 int currentLength = getDurationHalf(start);
308 if (manualDuration > 0) {
309 currentLength = manualDuration;
310 }
311 Calendar halfStart = (Calendar)start.clone();
312 Calendar halfEnd = Calendar.getInstance();
313 halfStart.add(Calendar.DAY_OF_YEAR, offset + teilVon);
314 halfEnd.setTime(halfStart.getTime());
315 halfEnd.add(Calendar.DAY_OF_YEAR, currentLength);
316 for (;halfStart.before(end);) {
317 int startDOY = halfStart.get(Calendar.DAY_OF_YEAR);
318 if (startDOY > messprogramm.getGueltigVon()
319 && startDOY < messprogramm.getGueltigBis()) {
320 LProbe probe = createObjects(
321 messprogramm, halfStart.getTime(), halfEnd.getTime());
322 if (probe != null) {
323 proben.add(probe);
324 }
325 }
326 halfStart.set(Calendar.DAY_OF_MONTH, 2);
327 halfStart = getStart("H", halfStart);
328 halfStart.add(Calendar.DAY_OF_YEAR, offset + teilVon);
329 if (manualDuration <= 0) {
330 currentLength = getDurationHalf(halfStart);
331 }
332 halfEnd.setTime(halfStart.getTime());
333 halfEnd.add(Calendar.DAY_OF_YEAR, currentLength);
334 } 350 }
335 351
336 return proben; 352 return proben;
337 } 353 }
338 354
364 if (probe != null) { 380 if (probe != null) {
365 proben.add(probe); 381 proben.add(probe);
366 } 382 }
367 } 383 }
368 return proben; 384 return proben;
369 }
370
371 private int getDurationMonth(Calendar month) {
372 return month.getActualMaximum(Calendar.DAY_OF_MONTH);
373 }
374
375 private int getDurationQuarter(Calendar month) {
376 if ((month.get(Calendar.MONTH)) % 3 != 0) {
377 logger.debug("not a valid month!");
378 return 91; // Fallback to not generate to much probe objects.
379 }
380 int duration = 0;
381 Calendar tmp = (Calendar)month.clone();
382 for (int i = 0; i < 3; i++) {
383 duration += tmp.getActualMaximum(Calendar.DAY_OF_MONTH);
384 tmp.set(Calendar.MONTH, tmp.get(Calendar.MONTH) + 1);
385 }
386 return duration;
387 }
388
389 private int getDurationHalf(Calendar month) {
390 if ((month.get(Calendar.MONTH)) % 6 != 0) {
391 logger.debug("not a valid month!");
392 return 183; // Fallback to not generate to much probe objects.
393 }
394 int duration = 0;
395 Calendar tmp = (Calendar)month.clone();
396 for (int i = 0; i < 6; i++) {
397 duration += tmp.getActualMaximum(Calendar.DAY_OF_MONTH);
398 tmp.set(Calendar.MONTH, tmp.get(Calendar.MONTH) + 1);
399 }
400 return duration - 1;
401 }
402
403 private Calendar getStart(String interval, Calendar date) {
404 Calendar start = Calendar.getInstance();
405 start.setTime(date.getTime());
406 if ("M".equals(interval)) {
407 if (start.get(Calendar.DAY_OF_MONTH) > 1) {
408 int month = start.get(Calendar.MONTH) + 1;
409 start.set(Calendar.MONTH, month);
410 start.set(Calendar.DAY_OF_MONTH, 1);
411 }
412 }
413 else if ("Q".equals(interval)) {
414 int month = start.get(Calendar.MONTH); /* 0 through 11 */
415 int quarter = (month / 3) + 1;
416 Calendar tmp = (Calendar)start.clone();
417 logger.debug("in " + quarter + ". quarter");
418
419 tmp.set(Calendar.MONTH, (quarter * 3));
420 logger.debug("its the " + tmp.get(Calendar.MONTH) + ". month");
421 tmp.set(Calendar.DAY_OF_MONTH, 1);
422 int firstDayOfQuarter = tmp.get(Calendar.DAY_OF_YEAR);
423 if (start.get(Calendar.DAY_OF_YEAR) > firstDayOfQuarter) {
424 if (quarter < 4) {
425 start.set(Calendar.MONTH, (quarter + 1) * 3 - 2);
426 start.set(Calendar.DAY_OF_MONTH, 1);
427 }
428 else {
429 // Next year...
430 start.set(Calendar.MONTH, 0);
431 start.set(Calendar.YEAR, start.get(Calendar.YEAR) + 1);
432 start.set(Calendar.DAY_OF_MONTH, 1);
433 }
434 }
435 else {
436 start = (Calendar)tmp.clone();
437 }
438 }
439 else if ("H".equals(interval)) {
440 int month = start.get(Calendar.MONTH);
441 int half = (month / 6) + 1;
442 Calendar tmp = (Calendar)start.clone();
443 tmp.set(Calendar.MONTH, half * 6 - 6);
444 tmp.set(Calendar.DAY_OF_MONTH, 1);
445 int firstDayOfHalf = tmp.get(Calendar.DAY_OF_YEAR);
446 if (start.get(Calendar.DAY_OF_YEAR) > firstDayOfHalf) {
447 if (half == 1) {
448 start.set(Calendar.MONTH, (half + 1) * 6 - 6);
449 start.set(Calendar.DAY_OF_MONTH, 1);
450 }
451 else {
452 // Next year...
453 start.set(Calendar.MONTH, 0);
454 start.set(Calendar.YEAR, start.get(Calendar.YEAR) + 1);
455 start.set(Calendar.DAY_OF_MONTH, 1);
456 }
457 }
458 }
459 return start;
460 } 385 }
461 386
462 private Calendar getMonday(Calendar week) { 387 private Calendar getMonday(Calendar week) {
463 if (week.get(Calendar.DAY_OF_WEEK) > 1) { 388 if (week.get(Calendar.DAY_OF_WEEK) > 1) {
464 week.set(Calendar.WEEK_OF_YEAR, 389 week.set(Calendar.WEEK_OF_YEAR,
649 } 574 }
650 575
651 return intervals; 576 return intervals;
652 } 577 }
653 578
654 /**
655 * Parse an interval string.
656 * Posible values are: J, H, Q, M, W4, W2, W, T
657 *
658 * @param interval the interval string.
659 *
660 * @return the amount of days for the given interval.
661 */
662 private int parseInterval(String interval) {
663 if ("J".equals(interval)) {
664 return 365;
665 }
666 else if ("H".equals(interval)) {
667 return 183;
668 }
669 else if ("Q".equals(interval)) {
670 return 91;
671 }
672 else if ("M".equals(interval)) {
673 return 30;
674 }
675 else if ("W4".equals(interval)) {
676 return 28;
677 }
678 else if ("W2".equals(interval)) {
679 return 14;
680 }
681 else if ("W".equals(interval)) {
682 return 7;
683 }
684 else if ("T".equals(interval)) {
685 return 1;
686 }
687 else {
688 return 0;
689 }
690 }
691 579
692 /** 580 /**
693 * Search for the umwelt id using the 'deskriptor'. 581 * Search for the umwelt id using the 'deskriptor'.
694 * 582 *
695 * @param probe The probe object. 583 * @param probe The probe object.
734 return messprogramm; 622 return messprogramm;
735 } 623 }
736 messprogramm.setUmwId(findUmwelt(mediaDesk)); 624 messprogramm.setUmwId(findUmwelt(mediaDesk));
737 return messprogramm; 625 return messprogramm;
738 } 626 }
627
739 628
740 /** 629 /**
741 * Find the umwelt id for a given deskriptor. 630 * Find the umwelt id for a given deskriptor.
742 * 631 *
743 * @param mediaDesk The deskriptor string 632 * @param mediaDesk The deskriptor string
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)