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