comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java @ 2793:6310b1582f2d

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

http://dive4elements.wald.intevation.org