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