comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java @ 3318:dbe2f85bf160

merged flys-artifacts/2.8
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:35 +0200
parents 31168ac9c7e7
children f903f1e5aed6
comparison
equal deleted inserted replaced
2987:98c7a46ec5ae 3318:dbe2f85bf160
1 package de.intevation.flys.artifacts;
2
3 import de.intevation.artifactdatabase.data.StateData;
4
5 import de.intevation.artifacts.CallContext;
6
7 import de.intevation.artifacts.common.utils.StringUtils;
8
9 import de.intevation.flys.artifacts.geom.Lines;
10
11 import de.intevation.flys.artifacts.model.Calculation1;
12 import de.intevation.flys.artifacts.model.Calculation2;
13 import de.intevation.flys.artifacts.model.Calculation3;
14 import de.intevation.flys.artifacts.model.Calculation4;
15 import de.intevation.flys.artifacts.model.Calculation5;
16 import de.intevation.flys.artifacts.model.Calculation6;
17 import de.intevation.flys.artifacts.model.Calculation;
18 import de.intevation.flys.artifacts.model.CalculationResult;
19 import de.intevation.flys.artifacts.model.DischargeTables;
20 import de.intevation.flys.artifacts.model.FacetTypes;
21 import de.intevation.flys.artifacts.model.MainValuesFactory;
22 import de.intevation.flys.artifacts.model.Segment;
23 import de.intevation.flys.artifacts.model.WQCKms;
24 import de.intevation.flys.artifacts.model.WQKms;
25 import de.intevation.flys.artifacts.model.WW;
26 import de.intevation.flys.artifacts.model.WstValueTable;
27 import de.intevation.flys.artifacts.model.WstValueTableFactory;
28
29 import de.intevation.flys.artifacts.states.LocationDistanceSelect;
30
31 import de.intevation.flys.model.DischargeTable;
32 import de.intevation.flys.model.FastCrossSectionLine;
33 import de.intevation.flys.model.Gauge;
34 import de.intevation.flys.model.River;
35
36 import de.intevation.flys.utils.DoubleUtil;
37 import de.intevation.flys.utils.FLYSUtils;
38
39 import gnu.trove.TDoubleArrayList;
40
41 import java.awt.geom.Point2D;
42
43 import java.util.Arrays;
44 import java.util.Calendar;
45 import java.util.Collections;
46 import java.util.GregorianCalendar;
47 import java.util.List;
48 import java.util.Map;
49
50 import org.apache.log4j.Logger;
51
52
53 /**
54 * The default WINFO artifact.
55 *
56 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
57 */
58 public class WINFOArtifact
59 extends FLYSArtifact
60 implements FacetTypes, WaterLineArtifact {
61
62 /** The logger for this class. */
63 private static Logger logger = Logger.getLogger(WINFOArtifact.class);
64
65 /** The name of the artifact. */
66 public static final String ARTIFACT_NAME = "winfo";
67
68 /** XPath */
69 public static final String XPATH_STATIC_UI ="/art:result/art:ui/art:static";
70
71 /** The default number of steps between the start end end of a selected Q
72 * range. */
73 public static final int DEFAULT_Q_STEPS = 30;
74
75 /** The default step width between the start end end kilometer. */
76 public static final double DEFAULT_KM_STEPS = 0.1;
77
78
79 /**
80 * The default constructor.
81 */
82 public WINFOArtifact() {
83 }
84
85
86
87 /**
88 * Returns the name of the concrete artifact.
89 *
90 * @return the name of the concrete artifact.
91 */
92 @Override
93 public String getName() {
94 return ARTIFACT_NAME;
95 }
96
97 protected static boolean reportGeneratedWs(
98 Calculation report,
99 double [] ws
100 ) {
101 if (ws == null || ws.length < 2) {
102 return false;
103 }
104
105 double lastW = ws[0];
106 boolean alreadyReported = false;
107
108 for (int i = 1; i < ws.length; ++i) {
109 if (Math.abs(lastW - ws[i]) < 1e-5) {
110 if (!alreadyReported) {
111 alreadyReported = true;
112 report.addProblem("more.than.one.q.for.w", ws[i]);
113 }
114 }
115 else {
116 alreadyReported = false;
117 }
118 lastW = ws[i];
119 }
120
121 return true;
122 }
123
124
125 //
126 // METHODS FOR RETRIEVING COMPUTED DATA FOR DIFFERENT CHART TYPES
127 //
128
129 /**
130 * Returns the data that is computed by a waterlevel computation.
131 *
132 * @return an array of data triples that consist of W, Q and Kms.
133 */
134 public CalculationResult getWaterlevelData()
135 {
136 logger.debug("WINFOArtifact.getWaterlevelData");
137
138 if (getDataAsString("calculation_mode").equals("calc.discharge.longitudinal.section")) {
139 return getDischargeLongitudinalSectionData();
140 }
141
142 River river = FLYSUtils.getRiver(this);
143 if (river == null) {
144 return error(new WQKms[0], "no.river.selected");
145 }
146
147 double[] kms = getKms();
148 if (kms == null) {
149 return error(new WQKms[0], "no.kms.selected");
150 }
151
152 double[] qs = getQs();
153 double[] ws = null;
154 boolean qSel = true;
155
156 Calculation report = new Calculation();
157
158 if (qs == null) {
159 logger.debug("Determine Q values based on a set of W values.");
160 qSel = false;
161 ws = getWs();
162 double [][] qws = getQsForWs(ws);
163 if (qws == null || qws.length == 0) {
164 return error(new WQKms[0], "converting.ws.to.qs.failed");
165 }
166 qs = qws[0];
167
168 if (reportGeneratedWs(report, qws[1])) {
169 ws = qws[1];
170 }
171 }
172
173 WstValueTable wst = WstValueTableFactory.getTable(river);
174 if (wst == null) {
175 return error(new WQKms[0], "no.wst.for.selected.river");
176 }
177
178
179 double [] range = FLYSUtils.getKmRange(this);
180 if (range == null) {
181 return error(new WQKms[0], "no.range.found");
182 }
183
184 double refKm;
185
186 if (isFreeQ() || isFreeW()) {
187 refKm = range[0];
188 logger.debug("'free' calculation (km " + refKm + ")");
189 }
190 else {
191 Gauge gauge = river.determineGaugeByPosition(range[0]);
192 if (gauge == null) {
193 return error(
194 new WQKms[0], "no.gauge.found.for.km");
195 }
196
197 refKm = gauge.getStation().doubleValue();
198
199 logger.debug(
200 "reference gauge: " + gauge.getName() + " (km " + refKm + ")");
201 }
202
203 return computeWaterlevelData(kms, qs, ws, wst, refKm, report);
204 }
205
206
207 /**
208 * Computes the data of a waterlevel computation based on the interpolation
209 * in WstValueTable.
210 *
211 * @param kms The kilometer values.
212 * @param qs The discharge values.
213 * @param wst The WstValueTable used for the interpolation.
214 *
215 * @return an array of data triples that consist of W, Q and Kms.
216 */
217 public static CalculationResult computeWaterlevelData(
218 double [] kms,
219 double [] qs,
220 double [] ws,
221 WstValueTable wst,
222 double refKm,
223 Calculation report
224 ) {
225 logger.info("WINFOArtifact.computeWaterlevelData");
226
227 Calculation1 calc1 = new Calculation1(kms, qs, ws, refKm);
228
229 if (report != null) {
230 calc1.addProblems(report);
231 }
232
233 return calc1.calculate(wst);
234 }
235
236
237 /**
238 * Returns the data that is computed by a duration curve computation.
239 *
240 * @return the data computed by a duration curve computation.
241 */
242 public CalculationResult getDurationCurveData() {
243 logger.debug("WINFOArtifact.getDurationCurveData");
244
245 River r = FLYSUtils.getRiver(this);
246
247 if (r == null) {
248 return error(null, "no.river.selected");
249 }
250
251 Gauge g = getGauge();
252
253 if (g == null) {
254 return error(null, "no.gauge.selected");
255 }
256
257 double[] locations = FLYSUtils.getLocations(this);
258
259 if (locations == null) {
260 return error(null, "no.locations.selected");
261 }
262
263 WstValueTable wst = WstValueTableFactory.getTable(r);
264 if (wst == null) {
265 return error(null, "no.wst.for.river");
266 }
267
268 return computeDurationCurveData(g, wst, locations[0]);
269 }
270
271
272 /**
273 * Computes the data used to create duration curves.
274 *
275 * @param gauge The selected gauge.
276 * @param location The selected location.
277 *
278 * @return the computed data.
279 */
280 public static CalculationResult computeDurationCurveData(
281 Gauge gauge,
282 WstValueTable wst,
283 double location)
284 {
285 logger.info("WINFOArtifact.computeDurationCurveData");
286
287 Object[] obj = MainValuesFactory.getDurationCurveData(gauge);
288
289 int[] days = (int[]) obj[0];
290 double[] qs = (double[]) obj[1];
291
292 Calculation3 calculation = new Calculation3(location, days, qs);
293
294 return calculation.calculate(wst);
295 }
296
297
298 /**
299 * Returns the data that is computed by a discharge curve computation.
300 *
301 * @return the data computed by a discharge curve computation.
302 */
303 public CalculationResult getComputedDischargeCurveData()
304 throws NullPointerException
305 {
306 logger.debug("WINFOArtifact.getComputedDischargeCurveData");
307
308 River r = FLYSUtils.getRiver(this);
309
310 if (r == null) {
311 return error(new WQKms[0], "no.river.selected");
312 }
313
314 double[] locations = FLYSUtils.getLocations(this);
315
316 if (locations == null) {
317 return error(new WQKms[0], "no.locations.selected");
318 }
319
320 WstValueTable wst = WstValueTableFactory.getTable(r);
321 if (wst == null) {
322 return error(new WQKms[0], "no.wst.for.river");
323 }
324
325 return computeDischargeCurveData(wst, locations[0]);
326 }
327
328
329 /**
330 * Computes the data used to create computed discharge curves.
331 *
332 * @param wst The WstValueTable that is used for the interpolation (river-
333 * bound).
334 * @param location The location where the computation should be based on.
335 *
336 * @return an object that contains tuples of W/Q values at the specified
337 * location.
338 */
339 public static CalculationResult computeDischargeCurveData(
340 WstValueTable wst,
341 double location)
342 {
343 logger.info("WINFOArtifact.computeDischargeCurveData");
344
345 Calculation2 calculation = new Calculation2(location);
346
347 return calculation.calculate(wst);
348 }
349
350
351 /** Create CalculationResult with data and message. */
352 protected static final CalculationResult error(Object data, String msg) {
353 return new CalculationResult(data, new Calculation(msg));
354 }
355
356
357 /**
358 * Returns the data computed by the discharge longitudinal section
359 * computation.
360 *
361 * @return an array of WQKms object - one object for each given Q value.
362 */
363 public CalculationResult getDischargeLongitudinalSectionData() {
364
365 logger.debug("WINFOArtifact.getDischargeLongitudinalSectionData");
366
367 River river = FLYSUtils.getRiver(this);
368 if (river == null) {
369 logger.debug("No river selected.");
370 return error(new WQKms[0], "no.river.selected");
371 }
372
373 WstValueTable table = WstValueTableFactory.getTable(river);
374 if (table == null) {
375 logger.debug("No wst found for selected river.");
376 return error(new WQKms[0], "no.wst.for.river");
377 }
378
379 List<Segment> segments = getSegments();
380
381 if (segments == null) {
382 logger.debug("Cannot create segments.");
383 return error(new WQKms[0], "cannot.create.segments");
384 }
385
386 double [] range = getFromToStep();
387
388 if (range == null) {
389 logger.debug("Cannot figure out range.");
390 return error(new WQKms[0], "no.range.found");
391 }
392
393 Calculation4 calc4 = new Calculation4(segments, river, isQ());
394
395 return calc4.calculate(table, range[0], range[1], range[2]);
396 }
397
398
399 /**
400 * Returns the data that is computed by a reference curve computation.
401 *
402 * @return the data computed by a reference curve computation.
403 */
404 public CalculationResult getReferenceCurveData(CallContext context) {
405
406 Double startKm = getReferenceStartKm();
407
408 if (startKm == null) {
409 return error(new WW[0], "no.reference.start.km");
410 }
411
412 double [] endKms = getReferenceEndKms();
413
414 if (endKms == null || endKms.length == 0) {
415 return error(new WW[0], "no.reference.end.kms");
416 }
417
418 Calculation5 calc5 = new Calculation5(startKm, endKms);
419
420 River r = FLYSUtils.getRiver(this);
421 if (r == null) {
422 return error(new WW[0], "no.river.found");
423 }
424
425 WstValueTable wst = WstValueTableFactory.getTable(r);
426 if (wst == null) {
427 return error(new WW[0], "no.wst.for.river");
428 }
429
430 Map<Double, Double> kms2gaugeDatums = r.queryGaugeDatumsKMs();
431
432 return calc5.calculate(wst, kms2gaugeDatums, context);
433 }
434
435
436 /** Get reference (start) km. */
437 public Double getReferenceStartKm() {
438 StateData sd = getData("reference_startpoint");
439
440 if (sd == null) {
441 logger.warn("no reference start given.");
442 return null;
443 }
444
445 logger.debug("Reference start km given: " + sd.getValue());
446
447 String input = (String) sd.getValue();
448
449 if (input == null || (input = input.trim()).length() == 0) {
450 logger.warn("reference start string is empty.");
451 return null;
452 }
453
454 try {
455 return Double.valueOf(input);
456 }
457 catch (NumberFormatException nfe) {
458 logger.warn("reference start string is not numeric.");
459 }
460
461 return null;
462 }
463
464
465 /**
466 * Get end kms for reference curve (null if none).
467 */
468 public double [] getReferenceEndKms() {
469 StateData sd = getData("reference_endpoint");
470
471 if (sd == null) {
472 logger.warn("no reference end given.");
473 return null;
474 }
475 else {
476 logger.debug("Reference end km : " + sd.getValue());
477 }
478
479 String input = (String) sd.getValue();
480
481 if (input == null || (input = input.trim()).length() == 0) {
482 logger.warn("reference end string is empty.");
483 return null;
484 }
485
486 TDoubleArrayList endKms = new TDoubleArrayList();
487
488 for (String part: input.split("\\s+")) {
489 try {
490 double km = Double.parseDouble(part);
491 if (!endKms.contains(km)) {
492 endKms.add(km);
493 }
494 }
495 catch (NumberFormatException nfe) {
496 logger.warn("reference end string is not numeric.");
497 }
498 }
499
500 return endKms.toNativeArray();
501 }
502
503
504 public CalculationResult getHistoricalDischargeData() {
505 Gauge gauge = FLYSUtils.getReferenceGauge(this);
506 String rawTimerange = getDataAsString("year_range");
507 String rawValues = getDataAsString("historical_values");
508 int mode = getDataAsInteger("historical_mode");
509
510 long[] timerange = FLYSUtils.longArrayFromString(rawTimerange);
511 double[] values = FLYSUtils.doubleArrayFromString(rawValues);
512
513 Calendar start = new GregorianCalendar();
514 start.setTimeInMillis(timerange[0]);
515 Calendar end = new GregorianCalendar();
516 end.setTimeInMillis(timerange[1]);
517
518 Calculation6 calc = new Calculation6(
519 mode,
520 new long[] { start.getTimeInMillis(), end.getTimeInMillis() },
521 values);
522
523 return calc.calculate(gauge);
524 }
525
526
527 public List<Segment> getSegments() {
528 StateData wqValues = getData("wq_values");
529 if (wqValues == null) {
530 logger.warn("no wq_values given");
531 return Collections.emptyList();
532 }
533 String input = (String)wqValues.getValue();
534 if (input == null || (input = input.trim()).length() == 0) {
535 logger.warn("wq_values are empty");
536 return Collections.emptyList();
537 }
538 return Segment.parseSegments(input);
539 }
540
541
542 /**
543 * Get corrected waterline against surface/profile.
544 */
545 public Lines.LineData waterLineC(int idx, FastCrossSectionLine csl) {
546 List<Point2D> points = csl.getPoints();
547
548 WQKms[] wqckms = (WQKms[])
549 getDischargeLongitudinalSectionData().getData();
550
551 // Find index of km.
552 double wishKM = csl.getKm();
553
554 // Find W/C at km, linear naive approach.
555 WQCKms triple = (WQCKms) wqckms[idx-1];
556
557 int old_idx = 0;
558
559 if (triple.size() == 0) {
560 logger.warn("Calculation of c/waterline is empty.");
561 return Lines.createWaterLines(points, 0.0f);
562 }
563
564 // Linear seach in WQKms for closest km.
565 double old_dist_wish = Math.abs(wishKM - triple.getKm(0));
566 double last_c = triple.getC(0);
567
568 for (int i = 0, T = triple.size(); i < T; i++) {
569 double diff = Math.abs(wishKM - triple.getKm(i));
570 if (diff > old_dist_wish) {
571 break;
572 }
573 last_c = triple.getC(i);
574 old_dist_wish = diff;
575 }
576
577 return Lines.createWaterLines(points, last_c);
578 }
579
580
581 /**
582 * Get points of line describing the surface of water at cross section.
583 *
584 * @param idx Index for getWaterlevelData.
585 * @param csl The profile/surface to fill with water.
586 * @param nextIgnored Ignored in this implementation of WaterLineArtifact.
587 * @param prevIgnored Ignored in this implementation of WaterLineArtifact.
588 *
589 * @return an array holding coordinates of points of surface of water (
590 * in the form {{x1, x2} {y1, y2}} ).
591 */
592 @Override
593 public Lines.LineData getWaterLines(int idx, FastCrossSectionLine csl,
594 double nextIgnored, double prevIgnored) {
595 logger.debug("getWaterLines(" + idx + ")");
596
597 List<Point2D> points = csl.getPoints();
598
599 // Need W at km
600 WQKms [] wqkms = (WQKms[]) getWaterlevelData().getData();
601 if (wqkms.length == 0) {
602 logger.error("No WQKms found.");
603 return Lines.createWaterLines(points, 0.0f);
604 }
605
606 if (wqkms.length <= idx) {
607 logger.error("getWaterLines() requested index ("
608 + idx + " not found.");
609 return waterLineC(idx, csl);
610 }
611
612 // Find index of km.
613 double wishKM = csl.getKm();
614
615 // Find W at km, linear naive approach.
616 WQKms triple = wqkms[idx];
617
618 int old_idx = 0;
619
620 if (triple.size() == 0) {
621 logger.warn("Calculation of waterline is empty.");
622 return Lines.createWaterLines(points, 0.0f);
623 }
624
625 // Linear seach in WQKms for closest km.
626 double old_dist_wish = Math.abs(wishKM - triple.getKm(0));
627 double last_w = triple.getW(0);
628
629 for (int i = 0, T = triple.size(); i < T; i++) {
630 double diff = Math.abs(wishKM - triple.getKm(i));
631 if (diff > old_dist_wish) {
632 break;
633 }
634 last_w = triple.getW(i);
635 old_dist_wish = diff;
636 }
637
638 return Lines.createWaterLines(points, last_w);
639 }
640
641
642 /**
643 * Returns the Qs for a number of Ws. This method makes use of
644 * DischargeTables.getQForW().
645 *
646 * @param ws An array of W values.
647 *
648 * @return an array of Q values.
649 */
650 public double [][] getQsForWs(double[] ws) {
651
652 if (ws == null) {
653 logger.error("getQsForWs: ws == null");
654 return null;
655 }
656
657 boolean debug = logger.isDebugEnabled();
658
659 if (debug) {
660 logger.debug("FLYSArtifact.getQsForWs");
661 }
662
663 River r = FLYSUtils.getRiver(this);
664 if (r == null) {
665 logger.warn("no river found");
666 return null;
667 }
668
669 double [] range = FLYSUtils.getKmRange(this);
670 if (range == null) {
671 logger.warn("no ranges found");
672 return null;
673 }
674
675 if (isFreeW()) {
676 logger.debug("Bezugslinienverfahren I: W auf freier Strecke");
677 // The simple case of the "Bezugslinienverfahren"
678 // "W auf freier Strecke".
679 WstValueTable wst = WstValueTableFactory.getTable(r);
680 if (wst == null) {
681 logger.warn("no wst value table found");
682 return null;
683 }
684 double km = range[0];
685
686 TDoubleArrayList outQs = new TDoubleArrayList(ws.length);
687 TDoubleArrayList outWs = new TDoubleArrayList(ws.length);
688
689 boolean generatedWs = false;
690
691 for (int i = 0; i < ws.length; ++i) {
692 double w = ws[i];
693 if (debug) {
694 logger.debug("getQsForWs: lookup Q for W: " + w);
695 }
696 // There could bemore than one Q per W.
697 double [] qs = wst.findQsForW(km, w);
698 for (int j = 0; j < qs.length; ++j) {
699 outWs.add(ws[i]);
700 outQs.add(qs[j]);
701 }
702 generatedWs |= qs.length != 1;
703 }
704
705 if (debug) {
706 logger.debug("getQsForWs: number of Qs: " + outQs.size());
707 }
708
709 return new double [][] {
710 outQs.toNativeArray(),
711 generatedWs ? outWs.toNativeArray() : null };
712 }
713
714 if (debug) {
715 logger.debug("range: " + Arrays.toString(range));
716 }
717
718 Gauge g = r.determineGaugeByPosition(range[0]);
719 if (g == null) {
720 logger.warn("no gauge found for km: " + range[0]);
721 return null;
722 }
723
724 if (debug) {
725 logger.debug("convert w->q with gauge '" + g.getName() + "'");
726 }
727
728 DischargeTable dt = g.fetchMasterDischargeTable();
729
730 if (dt == null) {
731 logger.warn("No master discharge table found for gauge '"
732 + g.getName() + "'");
733 return null;
734 }
735
736 double [][] values = DischargeTables.loadDischargeTableValues(dt, 1);
737
738 TDoubleArrayList wsOut = new TDoubleArrayList(ws.length);
739 TDoubleArrayList qsOut = new TDoubleArrayList(ws.length);
740
741 boolean generatedWs = false;
742
743 for (int i = 0; i < ws.length; i++) {
744 if (Double.isNaN(ws[i])) {
745 logger.warn("W is NaN: ignored");
746 continue;
747 }
748 double w = ws[i] / 100d;
749 double [] qs = DischargeTables.getQsForW(values, w);
750
751 if (qs.length == 0) {
752 logger.warn("No Qs found for W = " + ws[i]);
753 }
754 else {
755 for (double q: qs) {
756 wsOut.add(ws[i]);
757 qsOut.add(q * 100d);
758 }
759 }
760 generatedWs |= qs.length != 1;
761 }
762
763 return new double [][] {
764 qsOut.toNativeArray(),
765 generatedWs ? wsOut.toNativeArray() : null
766 };
767 }
768
769
770 /**
771 * Determines the selected mode of distance/range input.
772 *
773 * @return true, if the range mode is selected otherwise false.
774 */
775 public boolean isRange() {
776 StateData mode = getData("ld_mode");
777
778 if (mode == null) {
779 logger.warn("No mode location/range chosen. Defaults to range.");
780 return true;
781 }
782
783 String value = (String) mode.getValue();
784
785 return value.equals("distance");
786 }
787
788
789 /**
790 * Returns the selected distance based on a given range (from, to).
791 *
792 * @param dFrom The StateData that contains the lower value.
793 * @param dTo The StateData that contains the upper value.
794 *
795 * @return the selected distance.
796 */
797 protected double[] getDistanceByRange(StateData dFrom, StateData dTo) {
798 double from = Double.parseDouble((String) dFrom.getValue());
799 double to = Double.parseDouble((String) dTo.getValue());
800
801 return new double[] { from, to };
802 }
803
804
805 /**
806 * Returns the selected Kms.
807 *
808 * @param distance An 2dim array with [lower, upper] values.
809 *
810 * @return the selected Kms.
811 */
812 public double[] getKms(double[] distance) {
813 StateData dStep = getData("ld_step");
814
815 if (dStep == null) {
816 logger.warn("No step width given. Cannot compute Kms.");
817 return null;
818 }
819
820 double step = Double.parseDouble((String) dStep.getValue());
821
822 // transform step from 'm' into 'km'
823 step = step / 1000;
824
825 if (step == 0d) {
826 step = DEFAULT_KM_STEPS;
827 }
828
829 return DoubleUtil.explode(distance[0], distance[1], step);
830 }
831
832
833 /**
834 * Returns the selected Kms.
835 *
836 * @return the selected kms.
837 */
838 public double[] getKms() {
839 if (isRange()) {
840 double[] distance = FLYSUtils.getKmRange(this);
841 return getKms(distance);
842
843 }
844 else {
845 return LocationDistanceSelect.getLocations(this);
846 }
847 }
848
849
850 public double [] getFromToStep() {
851 if (!isRange()) {
852 return null;
853 }
854 double [] fromTo = FLYSUtils.getKmRange(this);
855
856 if (fromTo == null) {
857 return null;
858 }
859
860 StateData dStep = getData("ld_step");
861 if (dStep == null) {
862 return null;
863 }
864
865 double [] result = new double[3];
866 result[0] = fromTo[0];
867 result[1] = fromTo[1];
868
869 try {
870 String step = (String)dStep.getValue();
871 result[2] = DoubleUtil.round(Double.parseDouble(step) / 1000d);
872 }
873 catch (NumberFormatException nfe) {
874 return null;
875 }
876
877 return result;
878 }
879
880
881 /**
882 * Returns the gauge based on the current distance and river.
883 *
884 * @return the gauge.
885 */
886 public Gauge getGauge() {
887 return FLYSUtils.getGauge(this);
888 }
889
890
891 /**
892 * Returns the gauges that match the selected kilometer range.
893 *
894 * @return the gauges based on the selected kilometer range.
895 */
896 public List<Gauge> getGauges() {
897
898 River river = FLYSUtils.getRiver(this);
899 if (river == null) {
900 return null;
901 }
902
903 double [] dist = FLYSUtils.getKmRange(this);
904 if (dist == null) {
905 return null;
906 }
907
908 return river.determineGauges(dist[0], dist[1]);
909 }
910
911
912 /**
913 * This method returns the Q values.
914 *
915 * @return the selected Q values or null, if no Q values are selected.
916 */
917 public double[] getQs() {
918 StateData dMode = getData("wq_isq");
919 StateData dSelection = getData("wq_isrange");
920
921 boolean isRange = dSelection != null
922 ? Boolean.valueOf((String)dSelection.getValue())
923 : false;
924
925 if (isQ()) {
926 if (!isRange) {
927 return getSingleWQValues();
928 }
929 else {
930 return getWQTriple();
931 }
932 }
933 else {
934 logger.warn("You try to get Qs, but W has been inserted.");
935 return null;
936 }
937 }
938
939
940 public boolean isQ() {
941 StateData mode = getData("wq_isq");
942 String value = (mode != null) ? (String) mode.getValue() : null;
943 return value != null ? Boolean.valueOf(value) : false;
944 }
945
946 public boolean isW() {
947 StateData mode = getData("wq_isq");
948 String value = (mode != null) ? (String) mode.getValue() : null;
949 return value != null ? !Boolean.valueOf(value) : false;
950 }
951
952 public boolean isFreeW() {
953 if(!isW()) {
954 return false;
955 }
956 StateData mode = getData("wq_isfree");
957 String value = (mode != null) ? (String) mode.getValue() : null;
958
959 return value != null ? Boolean.valueOf(value) : false;
960 }
961
962
963 /**
964 * Returns true, if the parameter is set to compute data on a free range.
965 * Otherwise it returns false, which tells the calculation that it is bound
966 * to a gauge.
967 *
968 * @return true, if the calculation should compute on a free range otherwise
969 * false and the calculation is bound to a gauge.
970 */
971 public boolean isFreeQ() {
972 if(!isQ()) {
973 return false;
974 }
975 StateData mode = getData("wq_isfree");
976 String value = (mode != null) ? (String) mode.getValue() : null;
977
978 logger.debug("isFreeQ: " + value);
979
980 return value != null && Boolean.valueOf(value);
981 }
982
983
984 /**
985 * Returns the Q values based on a specified kilometer range.
986 *
987 * @param range A 2dim array with lower and upper kilometer range.
988 *
989 * @return an array of Q values.
990 */
991 public double[] getQs(double[] range) {
992 StateData dMode = getData("wq_isq");
993
994 if (isQ()) {
995 return getWQForDist(range);
996 }
997
998 logger.warn("You try to get Qs, but Ws has been inserted.");
999 return null;
1000 }
1001
1002
1003 /**
1004 * Returns the W values based on a specified kilometer range.
1005 *
1006 * @param range A 2dim array with lower and upper kilometer range.
1007 *
1008 * @return an array of W values.
1009 */
1010 public double[] getWs(double[] range) {
1011 if (isW()) {
1012 return getWQForDist(range);
1013 }
1014
1015 logger.warn("You try to get Ws, but Qs has been inserted.");
1016 return null;
1017 }
1018
1019
1020 /**
1021 * This method returns the W values.
1022 *
1023 * @return the selected W values or null, if no W values are selected.
1024 */
1025 public double[] getWs() {
1026 StateData dSingle = getData("wq_single");
1027
1028 if (isW()) {
1029 if (dSingle != null) {
1030 return getSingleWQValues();
1031 }
1032 else {
1033 return getWQTriple();
1034 }
1035 }
1036 else {
1037 logger.warn("You try to get Ws, but Q has been inserted.");
1038 return null;
1039 }
1040 }
1041
1042 /**
1043 * This method returns the given W or Q values for a specific range
1044 * (inserted in the WQ input panel for discharge longitudinal sections).
1045 *
1046 * @param dist A 2dim array with lower und upper kilometer values.
1047 *
1048 * @return an array of W or Q values.
1049 */
1050 protected double[] getWQForDist(double[] dist) {
1051 logger.debug("Search wq values for range: " + dist[0] + " - " + dist[1]);
1052 StateData data = getData("wq_values");
1053
1054 if (data == null) {
1055 logger.warn("Missing wq values!");
1056 return null;
1057 }
1058
1059 String dataString = (String) data.getValue();
1060 String[] ranges = dataString.split(":");
1061
1062 for (String range: ranges) {
1063 String[] parts = range.split(";");
1064
1065 double lower = Double.parseDouble(parts[0]);
1066 double upper = Double.parseDouble(parts[1]);
1067
1068 if (lower <= dist[0] && upper >= dist[1]) {
1069 String[] values = parts[2].split(",");
1070
1071 int num = values.length;
1072 double[] res = new double[num];
1073
1074 for (int i = 0; i < num; i++) {
1075 try {
1076 res[i] = Double.parseDouble(values[i]);
1077 }
1078 catch (NumberFormatException nfe) {
1079 logger.warn(nfe, nfe);
1080 }
1081 }
1082
1083 return res;
1084 }
1085 }
1086
1087 logger.warn("Specified range for WQ not found!");
1088
1089 return null;
1090 }
1091
1092
1093 /**
1094 * This method returns an array of inserted WQ triples that consist of from,
1095 * to and the step width.
1096 *
1097 * @return an array of from, to and step width.
1098 */
1099 protected double[] getWQTriple() {
1100 StateData dFrom = getData("wq_from");
1101 StateData dTo = getData("wq_to");
1102
1103 if (dFrom == null || dTo == null) {
1104 logger.warn("Missing start or end value for range.");
1105 return null;
1106 }
1107
1108 double from = Double.parseDouble((String) dFrom.getValue());
1109 double to = Double.parseDouble((String) dTo.getValue());
1110
1111 StateData dStep = getData("wq_step");
1112
1113 if (dStep == null) {
1114 logger.warn("No step width given. Cannot compute Qs.");
1115 return null;
1116 }
1117
1118 double step = Double.parseDouble((String) dStep.getValue());
1119
1120 // if no width is given, the DEFAULT_Q_STEPS is used to compute the step
1121 // width. Maybe, we should round the value to a number of digits.
1122 if (step == 0d) {
1123 double diff = to - from;
1124 step = diff / DEFAULT_Q_STEPS;
1125 }
1126
1127 return DoubleUtil.explode(from, to, step);
1128 }
1129
1130
1131 /**
1132 * Returns an array of inserted WQ double values stored as whitespace
1133 * separated list.
1134 *
1135 * @return an array of W or Q values.
1136 */
1137 protected double[] getSingleWQValues() {
1138 StateData dSingle = getData("wq_single");
1139
1140 if (dSingle == null) {
1141 logger.warn("Cannot determine single WQ values. No data given.");
1142 return null;
1143 }
1144
1145 String tmp = (String) dSingle.getValue();
1146 String[] strValues = tmp.split(" ");
1147
1148 TDoubleArrayList values = new TDoubleArrayList();
1149
1150 for (String strValue: strValues) {
1151 try {
1152 values.add(Double.parseDouble(strValue));
1153 }
1154 catch (NumberFormatException nfe) {
1155 logger.warn(nfe, nfe);
1156 }
1157 }
1158
1159 values.sort();
1160
1161 return values.toNativeArray();
1162 }
1163
1164
1165 /**
1166 * Determines Facets initial disposition regarding activity (think of
1167 * selection in Client ThemeList GUI). This will be checked one time
1168 * when the facet enters a collections describe document.
1169 *
1170 * @param facetName name of the facet.
1171 * @param index index of the facet.
1172 * @return 0 if not active
1173 */
1174 @Override
1175 public int getInitialFacetActivity(String outputName, String facetName, int index) {
1176
1177 logger.debug("WINFOArtifact.active?: "
1178 + outputName
1179 + "/"
1180 + facetName);
1181
1182 if (facetName.equals(COMPUTED_DISCHARGE_MAINVALUES_Q) ||
1183 facetName.equals(COMPUTED_DISCHARGE_MAINVALUES_W)
1184 && outputName.equals("computed_discharge_curve"))
1185 {
1186 return 0;
1187 }
1188
1189 return StringUtils.contains(facetName, INACTIVES) ? 0 : 1;
1190 }
1191
1192 private static final String [] INACTIVES = new String[] {
1193 LONGITUDINAL_Q,
1194 DURATION_Q
1195 };
1196
1197
1198 /**
1199 * Returns the WstValueTable of current river.
1200 */
1201 public WstValueTable getWstValueTable() {
1202 River r = FLYSUtils.getRiver(this);
1203 return WstValueTableFactory.getTable(r);
1204 }
1205 }
1206 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org