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