comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java @ 1190:f514894ec2fd

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

http://dive4elements.wald.intevation.org