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