comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java @ 3812:f788d2d901d6

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

http://dive4elements.wald.intevation.org