Mercurial > dive4elements > river
comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java @ 430:7ab81ff32111 2.3
merged flys-artifacts/2.3
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:10 +0200 |
parents | 6ab62e5b05b5 |
children | 73bc64c4a7b0 |
comparison
equal
deleted
inserted
replaced
290:a6f56ed9238b | 430:7ab81ff32111 |
---|---|
1 package de.intevation.flys.artifacts; | |
2 | |
3 import java.util.HashMap; | |
4 import java.util.List; | |
5 import java.util.Map; | |
6 import java.util.Set; | |
7 import java.util.Vector; | |
8 | |
9 import javax.xml.xpath.XPathConstants; | |
10 | |
11 import gnu.trove.TDoubleArrayList; | |
12 | |
13 import org.apache.log4j.Logger; | |
14 | |
15 import org.w3c.dom.Document; | |
16 import org.w3c.dom.Element; | |
17 import org.w3c.dom.Node; | |
18 import org.w3c.dom.NodeList; | |
19 | |
20 import de.intevation.artifacts.ArtifactFactory; | |
21 import de.intevation.artifacts.CallContext; | |
22 | |
23 import de.intevation.artifacts.common.ArtifactNamespaceContext; | |
24 import de.intevation.artifacts.common.utils.XMLUtils; | |
25 | |
26 import de.intevation.artifactdatabase.DefaultArtifact; | |
27 import de.intevation.artifactdatabase.data.DefaultStateData; | |
28 import de.intevation.artifactdatabase.data.StateData; | |
29 import de.intevation.artifactdatabase.state.State; | |
30 import de.intevation.artifactdatabase.state.StateEngine; | |
31 import de.intevation.artifactdatabase.transition.TransitionEngine; | |
32 | |
33 import de.intevation.flys.model.Gauge; | |
34 import de.intevation.flys.model.Range; | |
35 import de.intevation.flys.model.River; | |
36 | |
37 import de.intevation.flys.artifacts.context.FLYSContext; | |
38 import de.intevation.flys.artifacts.model.DischargeTables; | |
39 import de.intevation.flys.artifacts.model.RiverFactory; | |
40 import de.intevation.flys.artifacts.states.DefaultState; | |
41 | |
42 | |
43 /** | |
44 * The defaul FLYS artifact. | |
45 * | |
46 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> | |
47 */ | |
48 public abstract class FLYSArtifact extends DefaultArtifact { | |
49 | |
50 /** The logger that is used in this artifact.*/ | |
51 private static Logger logger = Logger.getLogger(FLYSArtifact.class); | |
52 | |
53 | |
54 /** The XPath that points to the input data elements of the FEED document.*/ | |
55 public static final String XPATH_FEED_INPUT = | |
56 "/art:action/art:data/art:input"; | |
57 | |
58 /** The XPath that points to the name of the target state of ADVANCE.*/ | |
59 public static final String XPATH_ADVANCE_TARGET = | |
60 "/art:action/art:target/@art:name"; | |
61 | |
62 /** The constant string that shows that an operation was successful.*/ | |
63 public static final String OPERATION_SUCCESSFUL = "SUCCESS"; | |
64 | |
65 /** The constant string that shows that an operation failed.*/ | |
66 public static final String OPERATION_FAILED = "FAILURE"; | |
67 | |
68 /** The default number of steps between the start end end of a selected Q | |
69 * range.*/ | |
70 public static final int DEFAULT_Q_STEPS = 30; | |
71 | |
72 /** The default step width between the start end end kilometer.*/ | |
73 public static final double DEFAULT_KM_STEPS = 0.1; | |
74 | |
75 | |
76 /** The identifier of the current state. */ | |
77 protected String currentStateId; | |
78 | |
79 /** The identifiers of previous states on a stack.*/ | |
80 protected Vector<String> previousStateIds; | |
81 | |
82 /** The name of the artifact.*/ | |
83 protected String name; | |
84 | |
85 /** The data that have been inserted into this artifact.*/ | |
86 protected Map<String, StateData> data; | |
87 | |
88 | |
89 /** | |
90 * The default constructor that creates an empty FLYSArtifact. | |
91 */ | |
92 public FLYSArtifact() { | |
93 data = new HashMap<String, StateData>(); | |
94 previousStateIds = new Vector<String>(); | |
95 } | |
96 | |
97 | |
98 /** | |
99 * Returns the name of the concrete artifact. | |
100 * | |
101 * @return the name of the concrete artifact. | |
102 */ | |
103 public abstract String getName(); | |
104 | |
105 | |
106 /** | |
107 * Returns the FLYSContext from context object. | |
108 * | |
109 * @param context The CallContext or the FLYSContext. | |
110 * | |
111 * @return the FLYSContext. | |
112 */ | |
113 protected FLYSContext getFlysContext(Object context) { | |
114 return context instanceof FLYSContext | |
115 ? (FLYSContext) context | |
116 : (FLYSContext) ((CallContext) context).globalContext(); | |
117 } | |
118 | |
119 | |
120 /** | |
121 * Initialize the artifact and insert new data if <code>data</code> contains | |
122 * information necessary for this artifact. | |
123 * | |
124 * @param identifier The UUID. | |
125 * @param factory The factory that is used to create this artifact. | |
126 * @param context The CallContext. | |
127 * @param data Some optional data. | |
128 */ | |
129 @Override | |
130 public void setup( | |
131 String identifier, | |
132 ArtifactFactory factory, | |
133 Object context, | |
134 Document data) | |
135 { | |
136 logger.debug("Setup this artifact with the uuid: " + identifier); | |
137 | |
138 super.setup(identifier, factory, context, data); | |
139 | |
140 FLYSContext flysContext = (FLYSContext) context; | |
141 StateEngine engine = (StateEngine) flysContext.get( | |
142 FLYSContext.STATE_ENGINE_KEY); | |
143 | |
144 String name = getName(); | |
145 | |
146 logger.debug("Set initial state for artifact '" + name + "'"); | |
147 List<State> states = engine.getStates(name); | |
148 | |
149 setCurrentState(states.get(0)); | |
150 } | |
151 | |
152 | |
153 /** | |
154 * Insert new data included in <code>input</code> into the current state. | |
155 * | |
156 * @param target XML document that contains new data. | |
157 * @param context The CallContext. | |
158 * | |
159 * @return a document that contains a SUCCESS or FAILURE message. | |
160 */ | |
161 @Override | |
162 public Document feed(Document target, CallContext context) { | |
163 logger.info("FLYSArtifact.feed()"); | |
164 | |
165 Document doc = XMLUtils.newDocument(); | |
166 | |
167 XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( | |
168 doc, | |
169 ArtifactNamespaceContext.NAMESPACE_URI, | |
170 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
171 | |
172 Element result = creator.create("result"); | |
173 doc.appendChild(result); | |
174 | |
175 try { | |
176 saveData(target, XPATH_FEED_INPUT, context); | |
177 return describe(target, context); | |
178 } | |
179 catch (IllegalArgumentException iae) { | |
180 creator.addAttr(result, "type", OPERATION_FAILED, true); | |
181 | |
182 result.setTextContent(iae.getMessage()); | |
183 } | |
184 | |
185 return doc; | |
186 } | |
187 | |
188 | |
189 /** | |
190 * This method handles request for changing the current state of an | |
191 * artifact. It is possible to step forward or backward. | |
192 * | |
193 * @param target The incoming ADVANCE document. | |
194 * @param context The CallContext. | |
195 * | |
196 * @return a document that contains a SUCCESS or FAILURE message. | |
197 */ | |
198 public Document advance(Document target, CallContext context) { | |
199 Document doc = XMLUtils.newDocument(); | |
200 | |
201 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
202 doc, | |
203 ArtifactNamespaceContext.NAMESPACE_URI, | |
204 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
205 | |
206 Element result = ec.create("result"); | |
207 | |
208 String targetState = XMLUtils.xpathString( | |
209 target, XPATH_ADVANCE_TARGET, ArtifactNamespaceContext.INSTANCE); | |
210 | |
211 logger.info("FLYSArtifact.advance() to '" + targetState + "'"); | |
212 | |
213 if (isStateReachable(targetState, context)) { | |
214 logger.info("Advance: Step forward"); | |
215 | |
216 Vector<String> prev = getPreviousStateIds(); | |
217 prev.add(getCurrentStateId()); | |
218 | |
219 setCurrentStateId(targetState); | |
220 | |
221 return describe(target, context); | |
222 } | |
223 else if (isPreviousState(targetState, context)) { | |
224 logger.info("Advance: Step back to"); | |
225 | |
226 Vector<String> prevs = getPreviousStateIds(); | |
227 int targetIdx = prevs.indexOf(targetState); | |
228 int start = prevs.size() - 1; | |
229 | |
230 for (int i = start; i >= targetIdx; i--) { | |
231 String prev = prevs.get(i); | |
232 logger.debug("Remove state id '" + prev + "'"); | |
233 prevs.remove(prev); | |
234 } | |
235 | |
236 setCurrentStateId(targetState); | |
237 | |
238 return describe(target, context); | |
239 } | |
240 | |
241 logger.warn("Advance: Cannot advance to '" + targetState + "'"); | |
242 ec.addAttr(result, "type", OPERATION_FAILED, true); | |
243 | |
244 doc.appendChild(result); | |
245 | |
246 return doc; | |
247 } | |
248 | |
249 | |
250 /** | |
251 * Returns the identifier of the current state. | |
252 * | |
253 * @return the identifier of the current state. | |
254 */ | |
255 protected String getCurrentStateId() { | |
256 return currentStateId; | |
257 } | |
258 | |
259 | |
260 /** | |
261 * Sets the identifier of the current state. | |
262 * | |
263 * @param id the identifier of a state. | |
264 */ | |
265 protected void setCurrentStateId(String id) { | |
266 currentStateId = id; | |
267 } | |
268 | |
269 | |
270 /** | |
271 * Set the current state of this artifact. <b>NOTE</b>We don't store the | |
272 * State object itself - which is not necessary - but its identifier. So | |
273 * this method will just call the setCurrentStateId() method with the | |
274 * identifier of <i>state</i>. | |
275 * | |
276 * @param state The new current state. | |
277 */ | |
278 protected void setCurrentState(State state) { | |
279 setCurrentStateId(state.getID()); | |
280 } | |
281 | |
282 | |
283 /** | |
284 * Returns the current state of the artifact. | |
285 * | |
286 * @return the current State of the artifact. | |
287 */ | |
288 protected State getCurrentState(Object context) { | |
289 FLYSContext flysContext = getFlysContext(context); | |
290 StateEngine engine = (StateEngine) flysContext.get( | |
291 FLYSContext.STATE_ENGINE_KEY); | |
292 | |
293 return engine.getState(getCurrentStateId()); | |
294 } | |
295 | |
296 | |
297 /** | |
298 * Returns the vector of previous state identifiers. | |
299 * | |
300 * @return the vector of previous state identifiers. | |
301 */ | |
302 protected Vector<String> getPreviousStateIds() { | |
303 return previousStateIds; | |
304 } | |
305 | |
306 | |
307 /** | |
308 * Adds a new StateData item to the data pool of this artifact. | |
309 * | |
310 * @param name the name of the data object. | |
311 * @param data the data object itself. | |
312 */ | |
313 protected void addData(String name, StateData data) { | |
314 this.data.put(name, data); | |
315 } | |
316 | |
317 | |
318 /** | |
319 * This method returns a specific StateData object that is stored in the | |
320 * data pool of this artifact. | |
321 * | |
322 * @param name The name of the data object. | |
323 * | |
324 * @return the StateData object if existing, otherwise null. | |
325 */ | |
326 public StateData getData(String name) { | |
327 return data.get(name); | |
328 } | |
329 | |
330 | |
331 /** | |
332 * This method stores the data that is contained in the FEED document. | |
333 * | |
334 * @param feed The FEED document. | |
335 * @param xpath The XPath that points to the data nodes. | |
336 */ | |
337 public void saveData(Document feed, String xpath, CallContext context) | |
338 throws IllegalArgumentException | |
339 { | |
340 if (feed == null || xpath == null || xpath.length() == 0) { | |
341 throw new IllegalArgumentException("error_feed_no_data"); | |
342 } | |
343 | |
344 NodeList nodes = (NodeList) XMLUtils.xpath( | |
345 feed, | |
346 xpath, | |
347 XPathConstants.NODESET, | |
348 ArtifactNamespaceContext.INSTANCE); | |
349 | |
350 if (nodes == null || nodes.getLength() == 0) { | |
351 throw new IllegalArgumentException("error_feed_no_data"); | |
352 } | |
353 | |
354 int count = nodes.getLength(); | |
355 logger.debug("Try to save " + count + " data items."); | |
356 | |
357 for (int i = 0; i < count; i++) { | |
358 Node node = nodes.item(i); | |
359 | |
360 String name = XMLUtils.xpathString( | |
361 node, "@art:name", ArtifactNamespaceContext.INSTANCE); | |
362 String value = XMLUtils.xpathString( | |
363 node, "@art:value", ArtifactNamespaceContext.INSTANCE); | |
364 | |
365 if (name != null && value != null) { | |
366 logger.debug("Save data item for '" + name + "' : " + value); | |
367 | |
368 addData(name, new DefaultStateData(name, null, null, value)); | |
369 } | |
370 } | |
371 | |
372 State current = getCurrentState(context); | |
373 DefaultState toValidate = (DefaultState) fillState(current); | |
374 | |
375 toValidate.validate(this, context); | |
376 } | |
377 | |
378 | |
379 /** | |
380 * This method fills a state object with the data that have been inserted to | |
381 * this artifact. This is necessary to use the isStateReachable() method, | |
382 * because the Transitions need to know about the inserted data. | |
383 * | |
384 * @param state The state that needs to be filled with data. | |
385 * | |
386 * @return the filled state. | |
387 */ | |
388 protected State fillState(State state) { | |
389 Map<String, StateData> stateData = state.getData(); | |
390 | |
391 if (stateData == null) { | |
392 return state; | |
393 } | |
394 | |
395 Set<String> keys = stateData.keySet(); | |
396 | |
397 for (String key: keys) { | |
398 StateData tmp = getData(key); | |
399 | |
400 if (tmp != null) { | |
401 StateData data = stateData.get(key); | |
402 data.setValue(tmp.getValue()); | |
403 } | |
404 } | |
405 | |
406 return state; | |
407 } | |
408 | |
409 | |
410 /** | |
411 * Determines if the state with the identifier <i>stateId</i> is reachable | |
412 * from the current state. The determination itself takes place in the | |
413 * TransitionEngine. | |
414 * | |
415 * @param stateId The identifier of a state. | |
416 * @param context The context object. | |
417 * | |
418 * @return true, if the state specified by <i>stateId</i> is reacahble, | |
419 * otherwise false. | |
420 */ | |
421 protected boolean isStateReachable(String stateId, Object context) { | |
422 logger.debug("Determine if the state '" + stateId + "' is reachable."); | |
423 | |
424 FLYSContext flysContext = getFlysContext(context); | |
425 | |
426 State currentState = fillState(getCurrentState(context)); | |
427 StateEngine sEngine = (StateEngine) flysContext.get( | |
428 FLYSContext.STATE_ENGINE_KEY); | |
429 | |
430 TransitionEngine tEngine = (TransitionEngine) flysContext.get( | |
431 FLYSContext.TRANSITION_ENGINE_KEY); | |
432 | |
433 return tEngine.isStateReachable(this, stateId, currentState, sEngine); | |
434 } | |
435 | |
436 | |
437 /** | |
438 * Determines if the state with the identifier <i>stateId</i> is a previous | |
439 * state of the current state. | |
440 * | |
441 * @param stateId The target state identifier. | |
442 * @param context The context object. | |
443 */ | |
444 protected boolean isPreviousState(String stateId, Object context) { | |
445 logger.debug("Determine if the state '" + stateId + "' is old."); | |
446 | |
447 Vector<String> prevs = getPreviousStateIds(); | |
448 if (prevs.contains(stateId)) { | |
449 return true; | |
450 } | |
451 | |
452 return false; | |
453 } | |
454 | |
455 | |
456 /** | |
457 * Returns the selected River object based on the 'river' data that might | |
458 * have been inserted by the user. | |
459 * | |
460 * @return the selected River or null if no river has been chosen yet. | |
461 */ | |
462 public River getRiver() { | |
463 StateData dRiver = getData("river"); | |
464 | |
465 return dRiver != null | |
466 ? RiverFactory.getRiver((String) dRiver.getValue()) | |
467 : null; | |
468 } | |
469 | |
470 | |
471 /** | |
472 * Returns the selected distance of points. | |
473 * | |
474 * @return the selected distance or points. | |
475 */ | |
476 public double[] getDistance() { | |
477 StateData dFrom = getData("ld_from"); | |
478 StateData dTo = getData("ld_to"); | |
479 StateData dLocations = getData("ld_locations"); | |
480 | |
481 if (dFrom != null && dTo != null) { | |
482 return getDistanceByRange(dFrom, dTo); | |
483 } | |
484 else if (dLocations != null) { | |
485 double[] locations = getLocations(); | |
486 return new double[] { locations[0], locations[locations.length-1] }; | |
487 } | |
488 | |
489 logger.warn("No data found for distance determination!"); | |
490 | |
491 return null; | |
492 } | |
493 | |
494 | |
495 /** | |
496 * This method returns the given distance | |
497 * | |
498 * @return an array with lower and upper kilometer range for each | |
499 * intersected gauge. | |
500 */ | |
501 public double[][] getSplittedDistance() { | |
502 double[] dist = getDistance(); | |
503 List<Gauge> gauges = getGauges(); | |
504 | |
505 int num = gauges != null ? gauges.size() : 0; | |
506 | |
507 double[][] res = new double[num][2]; | |
508 | |
509 for (int i = 0; i < num; i++) { | |
510 Range range = gauges.get(i).getRange(); | |
511 | |
512 double lower = range.getA().doubleValue(); | |
513 double upper = range.getB().doubleValue(); | |
514 | |
515 res[i][0] = dist[0] < lower ? lower : dist[0]; | |
516 res[i][1] = dist[1] > upper ? upper : dist[1]; | |
517 } | |
518 | |
519 return res; | |
520 } | |
521 | |
522 | |
523 /** | |
524 * Returns the selected locations based on a given array of locations. | |
525 * | |
526 * @param locations The StateData that contains the locations. | |
527 * | |
528 * @return the selected locations. | |
529 */ | |
530 public double[] getLocations() { | |
531 StateData dLocations = getData("ld_locations"); | |
532 String locationStr = dLocations != null | |
533 ? (String) dLocations.getValue() | |
534 : ""; | |
535 | |
536 if (locationStr == null || locationStr.length() == 0) { | |
537 logger.warn("No valid location string found!"); | |
538 return null; | |
539 } | |
540 | |
541 String[] tmp = locationStr.split(" "); | |
542 TDoubleArrayList locations = new TDoubleArrayList(); | |
543 | |
544 for (String l: tmp) { | |
545 try { | |
546 locations.add(Double.parseDouble(l)); | |
547 } | |
548 catch (NumberFormatException nfe) { | |
549 logger.warn(nfe, nfe); | |
550 } | |
551 } | |
552 | |
553 locations.sort(); | |
554 | |
555 return locations.toNativeArray(); | |
556 } | |
557 | |
558 | |
559 /** | |
560 * Returns the selected distance based on a given range (from, to). | |
561 * | |
562 * @param dFrom The StateData that contains the lower value. | |
563 * @param dTo The StateData that contains the upper value. | |
564 * | |
565 * @return the selected distance. | |
566 */ | |
567 protected double[] getDistanceByRange(StateData dFrom, StateData dTo) { | |
568 double from = Double.parseDouble((String) dFrom.getValue()); | |
569 double to = Double.parseDouble((String) dTo.getValue()); | |
570 | |
571 return new double[] { from, to }; | |
572 } | |
573 | |
574 | |
575 /** | |
576 * Returns the selected Kms. | |
577 * | |
578 * @param distance An 2dim array with [lower, upper] values. | |
579 * | |
580 * @return the selected Kms. | |
581 */ | |
582 public double[] getKms(double[] distance) { | |
583 StateData dStep = getData("ld_step"); | |
584 | |
585 if (dStep == null) { | |
586 logger.warn("No step width given. Cannot compute Kms."); | |
587 return null; | |
588 } | |
589 | |
590 double step = Double.parseDouble((String) dStep.getValue()); | |
591 | |
592 // transform step from 'm' into 'km' | |
593 step = step / 1000; | |
594 | |
595 if (step == 0d) { | |
596 step = DEFAULT_KM_STEPS; | |
597 } | |
598 | |
599 return getExplodedValues(distance[0], distance[1], step); | |
600 } | |
601 | |
602 | |
603 /** | |
604 * Returns the selected Kms. | |
605 * | |
606 * @return the selected kms. | |
607 */ | |
608 public double[] getKms() { | |
609 double[] distance = getDistance(); | |
610 return getKms(distance); | |
611 } | |
612 | |
613 | |
614 /** | |
615 * Returns the gauge based on the current distance and river. | |
616 * | |
617 * @return the gauge. | |
618 */ | |
619 public Gauge getGauge() { | |
620 River river = getRiver(); | |
621 double[] dist = getDistance(); | |
622 | |
623 if (logger.isDebugEnabled()) { | |
624 logger.debug("Determine gauge for:"); | |
625 logger.debug("... river: " + river.getName()); | |
626 logger.debug("... distance: " + dist[0] + " - " + dist[1]); | |
627 } | |
628 | |
629 Gauge gauge = river.determineGauge(dist[0], dist[1]); | |
630 | |
631 String name = gauge != null ? gauge.getName() : "'n/a"; | |
632 logger.debug("Found gauge: " + name); | |
633 | |
634 return gauge; | |
635 } | |
636 | |
637 | |
638 /** | |
639 * Returns the gauges that match the selected kilometer range. | |
640 * | |
641 * @return the gauges based on the selected kilometer range. | |
642 */ | |
643 public List<Gauge> getGauges() { | |
644 River river = getRiver(); | |
645 double[] dist = getDistance(); | |
646 | |
647 return river.determineGauges(dist[0], dist[1]); | |
648 } | |
649 | |
650 | |
651 /** | |
652 * This method returns the Q values. | |
653 * | |
654 * @return the selected Q values or null, if no Q values are selected. | |
655 */ | |
656 public double[] getQs() { | |
657 StateData dMode = getData("wq_mode"); | |
658 StateData dSingle = getData("wq_single"); | |
659 | |
660 String mode = dMode != null ? (String) dMode.getValue() : ""; | |
661 | |
662 if (mode.equals("Q")) { | |
663 if (dSingle != null) { | |
664 return getSingleWQValues(); | |
665 } | |
666 else { | |
667 return getWQTriple(); | |
668 } | |
669 } | |
670 else { | |
671 logger.warn("You try to get Qs, but W has been inserted."); | |
672 return null; | |
673 } | |
674 } | |
675 | |
676 | |
677 /** | |
678 * Returns the Q values based on a specified kilometer range. | |
679 * | |
680 * @param range A 2dim array with lower and upper kilometer range. | |
681 * | |
682 * @return an array of Q values. | |
683 */ | |
684 public double[] getQs(double[] range) { | |
685 StateData dMode = getData("wq_mode"); | |
686 StateData dValues = getData("wq_values"); | |
687 | |
688 String mode = dMode != null ? (String) dMode.getValue() : ""; | |
689 | |
690 if (mode.equals("Q")) { | |
691 return getWQForDist(range); | |
692 } | |
693 | |
694 logger.warn("You try to get Qs, but Ws has been inserted."); | |
695 return null; | |
696 } | |
697 | |
698 | |
699 /** | |
700 * Returns the W values based on a specified kilometer range. | |
701 * | |
702 * @param range A 2dim array with lower and upper kilometer range. | |
703 * | |
704 * @return an array of W values. | |
705 */ | |
706 public double[] getWs(double[] range) { | |
707 StateData dMode = getData("wq_mode"); | |
708 StateData dValues = getData("wq_values"); | |
709 | |
710 String mode = dMode != null ? (String) dMode.getValue() : ""; | |
711 | |
712 if (mode.equals("W")) { | |
713 return getWQForDist(range); | |
714 } | |
715 | |
716 logger.warn("You try to get Ws, but Qs has been inserted."); | |
717 return null; | |
718 } | |
719 | |
720 | |
721 /** | |
722 * This method returns the W values. | |
723 * | |
724 * @return the selected W values or null, if no W values are selected. | |
725 */ | |
726 public double[] getWs() { | |
727 StateData dMode = getData("wq_mode"); | |
728 StateData dSingle = getData("wq_single"); | |
729 | |
730 String mode = dMode != null ? (String) dMode.getValue() : ""; | |
731 | |
732 if (mode.equals("W")) { | |
733 if (dSingle != null) { | |
734 return getSingleWQValues(); | |
735 } | |
736 else { | |
737 return getWQTriple(); | |
738 } | |
739 } | |
740 else { | |
741 logger.warn("You try to get Qs, but W has been inserted."); | |
742 return null; | |
743 } | |
744 } | |
745 | |
746 | |
747 /** | |
748 * Returns the Qs for a number of Ws. This method makes use of | |
749 * DischargeTables.getQForW(). | |
750 * | |
751 * @param ws An array of W values. | |
752 * | |
753 * @return an array of Q values. | |
754 */ | |
755 public double[] getQsForWs(double[] ws) { | |
756 logger.debug("FLYSArtifact.getQsForWs"); | |
757 | |
758 River r = getRiver(); | |
759 Gauge g = getGauge(); | |
760 | |
761 DischargeTables dt = new DischargeTables(r.getName(), g.getName()); | |
762 Map<String, double [][]> tmp = dt.getValues(); | |
763 | |
764 double[][] values = tmp.get(g.getName()); | |
765 double[] qs = new double[ws.length]; | |
766 | |
767 for (int i = 0; i < ws.length; i++) { | |
768 qs[i] = dt.getQForW(values, ws[i]); | |
769 } | |
770 | |
771 return qs; | |
772 } | |
773 | |
774 | |
775 /** | |
776 * This method returns the given W or Q values for a specific range | |
777 * (inserted in the WQ input panel for discharge longitudinal sections). | |
778 * | |
779 * @param dist A 2dim array with lower und upper kilometer values. | |
780 * | |
781 * @return an array of W or Q values. | |
782 */ | |
783 protected double[] getWQForDist(double[] dist) { | |
784 logger.debug("Search wq values for range: " + dist[0] + " - " + dist[1]); | |
785 StateData data = getData("wq_values"); | |
786 | |
787 if (data == null) { | |
788 logger.warn("Missing wq values!"); | |
789 return null; | |
790 } | |
791 | |
792 String dataString = (String) data.getValue(); | |
793 String[] ranges = dataString.split(":"); | |
794 | |
795 for (String range: ranges) { | |
796 String[] parts = range.split(";"); | |
797 | |
798 double lower = Double.parseDouble(parts[0]); | |
799 double upper = Double.parseDouble(parts[1]); | |
800 | |
801 if (lower <= dist[0] && upper >= dist[1]) { | |
802 String[] values = parts[2].split(","); | |
803 | |
804 int num = values.length; | |
805 double[] res = new double[num]; | |
806 | |
807 for (int i = 0; i < num; i++) { | |
808 try { | |
809 res[i] = Double.parseDouble(values[i]); | |
810 } | |
811 catch (NumberFormatException nfe) { | |
812 logger.warn(nfe, nfe); | |
813 } | |
814 } | |
815 | |
816 return res; | |
817 } | |
818 } | |
819 | |
820 logger.warn("Specified range for WQ not found!"); | |
821 | |
822 return null; | |
823 } | |
824 | |
825 | |
826 /** | |
827 * This method returns an array of inserted WQ triples that consist of from, | |
828 * to and the step width. | |
829 * | |
830 * @return an array of from, to and step width. | |
831 */ | |
832 protected double[] getWQTriple() { | |
833 StateData dFrom = getData("wq_from"); | |
834 StateData dTo = getData("wq_to"); | |
835 | |
836 if (dFrom == null || dTo == null) { | |
837 logger.warn("Missing start or end value for range."); | |
838 return null; | |
839 } | |
840 | |
841 double from = Double.parseDouble((String) dFrom.getValue()); | |
842 double to = Double.parseDouble((String) dTo.getValue()); | |
843 | |
844 StateData dStep = getData("wq_step"); | |
845 | |
846 if (dStep == null) { | |
847 logger.warn("No step width given. Cannot compute Qs."); | |
848 return null; | |
849 } | |
850 | |
851 double step = Double.parseDouble((String) dStep.getValue()); | |
852 | |
853 // if no width is given, the DEFAULT_Q_STEPS is used to compute the step | |
854 // width. Maybe, we should round the value to a number of digits. | |
855 if (step == 0d) { | |
856 double diff = to - from; | |
857 step = diff / DEFAULT_Q_STEPS; | |
858 } | |
859 | |
860 return getExplodedValues(from, to, step); | |
861 } | |
862 | |
863 | |
864 /** | |
865 * Returns an array of inserted WQ double values stored as whitespace | |
866 * separated list. | |
867 * | |
868 * @return an array of W or Q values. | |
869 */ | |
870 protected double[] getSingleWQValues() { | |
871 StateData dSingle = getData("wq_single"); | |
872 | |
873 if (dSingle == null) { | |
874 logger.warn("Cannot determine single WQ values. No data given."); | |
875 return null; | |
876 } | |
877 | |
878 String tmp = (String) dSingle.getValue(); | |
879 String[] strValues = tmp.split(" "); | |
880 | |
881 TDoubleArrayList values = new TDoubleArrayList(); | |
882 | |
883 for (String strValue: strValues) { | |
884 try { | |
885 values.add(Double.parseDouble(strValue)); | |
886 } | |
887 catch (NumberFormatException nfe) { | |
888 logger.warn(nfe, nfe); | |
889 } | |
890 } | |
891 | |
892 values.sort(); | |
893 | |
894 return values.toNativeArray(); | |
895 } | |
896 | |
897 | |
898 /** | |
899 * Returns an array of double values. The values contained in this array | |
900 * begin with the value <i>from</i> and end with the value <i>to</i>. The | |
901 * number of values in the result array depends on the <i>step</i> width. | |
902 * | |
903 * @param from The lower value. | |
904 * @param to The upper value. | |
905 * @param step The step width between two values in the result array. | |
906 * | |
907 * @return an array of double values. | |
908 */ | |
909 public double[] getExplodedValues(double from, double to, double step) { | |
910 double lower = from; | |
911 | |
912 double diff = to - from; | |
913 double tmp = diff / step; | |
914 int num = (int) Math.ceil(tmp) ; | |
915 | |
916 double[] values = new double[num]; | |
917 | |
918 for (int idx = 0; idx < num; idx++) { | |
919 values[idx] = lower; | |
920 lower += step; | |
921 } | |
922 | |
923 return values; | |
924 } | |
925 } | |
926 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |