comparison flys-artifacts/src/main/java/org/dive4elements/river/artifacts/WINFOArtifact.java @ 5831:bd047b71ab37

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

http://dive4elements.wald.intevation.org