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