Mercurial > dive4elements > river
comparison artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator.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/exports/LongitudinalSectionGenerator.java@bd047b71ab37 |
children | 4897a58c8746 |
comparison
equal
deleted
inserted
replaced
5837:d9901a08d0a6 | 5838:5aa05a7a34b7 |
---|---|
1 package org.dive4elements.river.exports; | |
2 | |
3 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; | |
4 import org.dive4elements.artifactdatabase.state.Facet; | |
5 import org.dive4elements.river.artifacts.FLYSArtifact; | |
6 import org.dive4elements.river.artifacts.geom.Lines; | |
7 import org.dive4elements.river.artifacts.model.AreaFacet; | |
8 import org.dive4elements.river.artifacts.model.FacetTypes; | |
9 import org.dive4elements.river.artifacts.model.WKms; | |
10 import org.dive4elements.river.artifacts.model.WQKms; | |
11 import org.dive4elements.river.exports.process.WOutProcessor; | |
12 import org.dive4elements.river.jfree.FLYSAnnotation; | |
13 import org.dive4elements.river.jfree.StyledAreaSeriesCollection; | |
14 import org.dive4elements.river.jfree.StyledXYSeries; | |
15 import org.dive4elements.river.utils.DataUtil; | |
16 import org.dive4elements.river.utils.FLYSUtils; | |
17 import org.apache.log4j.Logger; | |
18 import org.jfree.chart.axis.NumberAxis; | |
19 import org.jfree.chart.axis.ValueAxis; | |
20 import org.jfree.chart.plot.XYPlot; | |
21 import org.jfree.data.xy.XYSeries; | |
22 import org.w3c.dom.Document; | |
23 | |
24 | |
25 /** | |
26 * An OutGenerator that generates longitudinal section curves. | |
27 * | |
28 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> | |
29 */ | |
30 public class LongitudinalSectionGenerator | |
31 extends XYChartGenerator | |
32 implements FacetTypes | |
33 { | |
34 public enum YAXIS { | |
35 W(0), | |
36 D(1), | |
37 Q(2); | |
38 protected int idx; | |
39 private YAXIS(int c) { | |
40 idx = c; | |
41 } | |
42 } | |
43 | |
44 /** The logger that is used in this generator. */ | |
45 private static Logger logger = | |
46 Logger.getLogger(LongitudinalSectionGenerator.class); | |
47 | |
48 /** Key to look up internationalized String for annotations label. */ | |
49 public static final String I18N_ANNOTATIONS_LABEL = | |
50 "chart.longitudinal.annotations.label"; | |
51 | |
52 /** | |
53 * Key to look up internationalized String for LongitudinalSection diagrams | |
54 * titles. | |
55 */ | |
56 public static final String I18N_CHART_TITLE = | |
57 "chart.longitudinal.section.title"; | |
58 | |
59 /** | |
60 * Key to look up internationalized String for LongitudinalSection diagrams | |
61 * subtitles. | |
62 */ | |
63 public static final String I18N_CHART_SUBTITLE = | |
64 "chart.longitudinal.section.subtitle"; | |
65 | |
66 /** | |
67 * Key to look up internationalized String for LongitudinalSection diagrams | |
68 * short subtitles. | |
69 */ | |
70 public static final String I18N_CHART_SHORT_SUBTITLE = | |
71 "chart.longitudinal.section.shortsubtitle"; | |
72 | |
73 public static final String I18N_XAXIS_LABEL = | |
74 "chart.longitudinal.section.xaxis.label"; | |
75 | |
76 public static final String I18N_YAXIS_LABEL = | |
77 "chart.longitudinal.section.yaxis.label"; | |
78 | |
79 public static final String I18N_2YAXIS_LABEL = | |
80 "chart.longitudinal.section.yaxis.second.label"; | |
81 | |
82 public static final String I18N_CHART_TITLE_DEFAULT = "W-L\u00e4ngsschnitt"; | |
83 public static final String I18N_XAXIS_LABEL_DEFAULT = "km"; | |
84 public static final String I18N_YAXIS_LABEL_DEFAULT = "W [NN + m]"; | |
85 public static final String I18N_2YAXIS_LABEL_DEFAULT = "Q [m\u00b3/s]"; | |
86 | |
87 public final static String I18N_WDIFF_YAXIS_LABEL = | |
88 "chart.w_differences.yaxis.label"; | |
89 | |
90 public final static String I18N_WDIFF_YAXIS_LABEL_DEFAULT = "m"; | |
91 | |
92 public LongitudinalSectionGenerator() { | |
93 super(); | |
94 } | |
95 | |
96 | |
97 @Override | |
98 protected YAxisWalker getYAxisWalker() { | |
99 return new YAxisWalker() { | |
100 @Override | |
101 public int length() { | |
102 return YAXIS.values().length; | |
103 } | |
104 | |
105 @Override | |
106 public String getId(int idx) { | |
107 YAXIS[] yaxes = YAXIS.values(); | |
108 return yaxes[idx].toString(); | |
109 } | |
110 }; | |
111 } | |
112 | |
113 | |
114 /** | |
115 * Return left most data points x value (on first axis). | |
116 * Overridden because axis could be inverted. | |
117 */ | |
118 @Override | |
119 protected double getLeftX() { | |
120 if (isInverted()) { | |
121 return (Double)getXBounds(0).getUpper(); | |
122 } | |
123 return (Double)getXBounds(0).getLower(); | |
124 } | |
125 | |
126 | |
127 /** | |
128 * Return right most data points x value (on first axis). | |
129 * Overridden because axis could be inverted. | |
130 */ | |
131 @Override | |
132 protected double getRightX() { | |
133 if (isInverted()) { | |
134 return (Double)getXBounds(0).getLower(); | |
135 } | |
136 return (Double)getXBounds(0).getUpper(); | |
137 } | |
138 | |
139 | |
140 /** | |
141 * Returns the default title for this chart. | |
142 * | |
143 * @return the default title for this chart. | |
144 */ | |
145 @Override | |
146 public String getDefaultChartTitle() { | |
147 return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT); | |
148 } | |
149 | |
150 | |
151 /** | |
152 * Returns the default subtitle for this chart. | |
153 * | |
154 * @return the default subtitle for this chart. | |
155 */ | |
156 @Override | |
157 protected String getDefaultChartSubtitle() { | |
158 double[] dist = getRange(); | |
159 | |
160 Object[] args = null; | |
161 if (dist == null) { | |
162 args = new Object[] {getRiverName()}; | |
163 return msg(getChartShortSubtitleKey(), "", args); | |
164 } | |
165 args = new Object[] { | |
166 getRiverName(), | |
167 dist[0], | |
168 dist[1] | |
169 }; | |
170 return msg(getChartSubtitleKey(), "", args); | |
171 } | |
172 | |
173 | |
174 /** | |
175 * Gets key to look up internationalized String for the charts subtitle. | |
176 * @return key to look up translated subtitle. | |
177 */ | |
178 protected String getChartSubtitleKey() { | |
179 return I18N_CHART_SUBTITLE; | |
180 } | |
181 | |
182 | |
183 /** | |
184 * Gets key to look up internationalized String for the charts short | |
185 * subtitle. | |
186 * @return key to look up translated subtitle. | |
187 */ | |
188 protected String getChartShortSubtitleKey() { | |
189 return I18N_CHART_SHORT_SUBTITLE; | |
190 } | |
191 | |
192 | |
193 /** | |
194 * Get internationalized label for the x axis. | |
195 */ | |
196 @Override | |
197 protected String getDefaultXAxisLabel() { | |
198 FLYSArtifact flys = (FLYSArtifact) master; | |
199 | |
200 return msg( | |
201 I18N_XAXIS_LABEL, | |
202 I18N_XAXIS_LABEL_DEFAULT, | |
203 new Object[] { FLYSUtils.getRiver(flys).getName() }); | |
204 } | |
205 | |
206 | |
207 @Override | |
208 protected String getDefaultYAxisLabel(int index) { | |
209 String label = "default"; | |
210 | |
211 if (index == YAXIS.W.idx) { | |
212 label = getWAxisLabel(); | |
213 } | |
214 else if (index == YAXIS.Q.idx) { | |
215 label = msg(getQAxisLabelKey(), getQAxisDefaultLabel()); | |
216 } | |
217 else if (index == YAXIS.D.idx) { | |
218 label = msg(I18N_WDIFF_YAXIS_LABEL, I18N_WDIFF_YAXIS_LABEL_DEFAULT); | |
219 } | |
220 | |
221 return label; | |
222 } | |
223 | |
224 | |
225 /** | |
226 * Get internationalized label for the y axis. | |
227 */ | |
228 protected String getWAxisLabel() { | |
229 FLYSArtifact flys = (FLYSArtifact) master; | |
230 | |
231 String unit = FLYSUtils.getRiver(flys).getWstUnit().getName(); | |
232 | |
233 return msg( | |
234 I18N_YAXIS_LABEL, | |
235 I18N_YAXIS_LABEL_DEFAULT, | |
236 new Object[] { unit }); | |
237 } | |
238 | |
239 | |
240 /** | |
241 * Create Axis for given index. | |
242 * @return axis with according internationalized label. | |
243 */ | |
244 @Override | |
245 protected NumberAxis createYAxis(int index) { | |
246 NumberAxis axis = super.createYAxis(index); | |
247 | |
248 // "Q" Axis shall include 0. | |
249 if (index == YAXIS.Q.idx) { | |
250 axis.setAutoRangeIncludesZero(true); | |
251 } | |
252 else { | |
253 axis.setAutoRangeIncludesZero(false); | |
254 } | |
255 | |
256 return axis; | |
257 } | |
258 | |
259 | |
260 /** | |
261 * Get default value for the second Y-Axis' label (if no translation was | |
262 * found). | |
263 */ | |
264 protected String getQAxisDefaultLabel() { | |
265 return I18N_2YAXIS_LABEL_DEFAULT; | |
266 } | |
267 | |
268 | |
269 /** | |
270 * Get key for internationalization of the second Y-Axis' label. | |
271 */ | |
272 protected String getQAxisLabelKey() { | |
273 return I18N_2YAXIS_LABEL; | |
274 } | |
275 | |
276 | |
277 /** | |
278 * Trigger inversion. | |
279 */ | |
280 @Override | |
281 protected void adjustAxes(XYPlot plot) { | |
282 super.adjustAxes(plot); | |
283 invertXAxis(plot.getDomainAxis()); | |
284 } | |
285 | |
286 | |
287 /** | |
288 * This method inverts the x-axis based on the kilometer information of the | |
289 * selected river. If the head of the river is at kilometer 0, the axis is | |
290 * not inverted, otherwise it is. | |
291 * | |
292 * @param xaxis The domain axis. | |
293 */ | |
294 protected void invertXAxis(ValueAxis xaxis) { | |
295 if (isInverted()) { | |
296 logger.debug("X-Axis.setInverted(true)"); | |
297 xaxis.setInverted(true); | |
298 } | |
299 } | |
300 | |
301 | |
302 /** | |
303 * Produce output. | |
304 * @param artifactAndFacet current facet and artifact. | |
305 * @param attr theme for facet | |
306 */ | |
307 @Override | |
308 public void doOut( | |
309 ArtifactAndFacet artifactAndFacet, | |
310 Document attr, | |
311 boolean visible | |
312 ) { | |
313 String name = artifactAndFacet.getFacetName(); | |
314 | |
315 logger.debug("LongitudinalSectionGenerator.doOut: " + name); | |
316 | |
317 if (name == null) { | |
318 logger.error("No facet name for doOut(). No output generated!"); | |
319 return; | |
320 } | |
321 | |
322 Facet facet = artifactAndFacet.getFacet(); | |
323 | |
324 if (facet == null) { | |
325 return; | |
326 } | |
327 | |
328 WOutProcessor processor = new WOutProcessor(); | |
329 if (processor.canHandle(name)) { | |
330 processor.doOut(this, artifactAndFacet, attr, visible, YAXIS.W.idx); | |
331 } | |
332 else if (name.equals(LONGITUDINAL_Q)) { | |
333 doQOut( | |
334 (WQKms) artifactAndFacet.getData(context), | |
335 artifactAndFacet, | |
336 attr, | |
337 visible); | |
338 } | |
339 else if (name.equals(LONGITUDINAL_ANNOTATION)) { | |
340 doAnnotations( | |
341 (FLYSAnnotation) artifactAndFacet.getData(context), | |
342 artifactAndFacet, | |
343 attr, | |
344 visible); | |
345 } | |
346 else if (name.equals(STATIC_WQKMS_Q)) { | |
347 doQOut( | |
348 (WQKms) artifactAndFacet.getData(context), | |
349 artifactAndFacet, | |
350 attr, | |
351 visible); | |
352 } | |
353 else if (name.equals(W_DIFFERENCES)) { | |
354 doWDifferencesOut( | |
355 (WKms) artifactAndFacet.getData(context), | |
356 artifactAndFacet, | |
357 attr, | |
358 visible); | |
359 } | |
360 else if (FacetTypes.IS.AREA(name)) { | |
361 doArea( | |
362 artifactAndFacet.getData(context), | |
363 artifactAndFacet, | |
364 attr, | |
365 visible); | |
366 } | |
367 else if (FacetTypes.IS.MANUALPOINTS(name)) { | |
368 doPoints( | |
369 artifactAndFacet.getData(context), | |
370 artifactAndFacet, | |
371 attr, | |
372 visible, | |
373 YAXIS.W.idx); | |
374 } | |
375 else { | |
376 logger.warn("Unknown facet name: " + name); | |
377 return; | |
378 } | |
379 } | |
380 | |
381 /** | |
382 * Add items to dataseries which describes the differences. | |
383 */ | |
384 protected void doWDifferencesOut( | |
385 WKms wkms, | |
386 ArtifactAndFacet aandf, | |
387 Document theme, | |
388 boolean visible | |
389 ) { | |
390 logger.debug("WDifferencesCurveGenerator.doWDifferencesOut"); | |
391 if (wkms == null) { | |
392 logger.warn("No data to add to WDifferencesChart."); | |
393 return; | |
394 } | |
395 | |
396 XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme); | |
397 | |
398 if (logger.isDebugEnabled()) { | |
399 if (wkms.size() > 0) { | |
400 logger.debug("Generate series: " + series.getKey()); | |
401 logger.debug("Start km: " + wkms.getKm(0)); | |
402 logger.debug("End km: " + wkms.getKm(wkms.size() - 1)); | |
403 logger.debug("Values : " + wkms.size()); | |
404 } | |
405 } | |
406 | |
407 StyledSeriesBuilder.addPoints(series, wkms); | |
408 | |
409 addAxisSeries(series, YAXIS.D.idx, visible); | |
410 if (DataUtil.guessWaterIncreasing(wkms.allWs())) { | |
411 setInverted(true); | |
412 } | |
413 } | |
414 | |
415 | |
416 /** | |
417 * Process the output for Q facets in a longitudinal section curve. | |
418 * | |
419 * @param wqkms An array of WQKms values. | |
420 * @param aandf The facet and artifact. This facet does NOT support any data objects. Use | |
421 * FLYSArtifact.getNativeFacet() instead to retrieve a Facet which supports | |
422 * data. | |
423 * @param theme The theme that contains styling information. | |
424 * @param visible The visibility of the curve. | |
425 */ | |
426 protected void doQOut( | |
427 WQKms wqkms, | |
428 ArtifactAndFacet aandf, | |
429 Document theme, | |
430 boolean visible | |
431 ) { | |
432 logger.debug("LongitudinalSectionGenerator.doQOut"); | |
433 | |
434 XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme); | |
435 | |
436 StyledSeriesBuilder.addStepPointsKmQ(series, wqkms); | |
437 | |
438 addAxisSeries(series, YAXIS.Q.idx, visible); | |
439 | |
440 if (needInvertAxis(wqkms)) { | |
441 setInverted(true); | |
442 } | |
443 } | |
444 | |
445 /** | |
446 * This method determines - taking JFreeCharts auto x value ordering into | |
447 * account - if the x axis need to be inverted. Waterlines in these charts | |
448 * should decrease. | |
449 * | |
450 * @param wkms The data object that stores the x and y values used for this | |
451 * chart. | |
452 */ | |
453 public boolean needInvertAxis(WKms wkms) { | |
454 boolean wsUp = wkms.guessWaterIncreasing(); | |
455 boolean kmUp = DataUtil.guessWaterIncreasing(wkms.allKms()); | |
456 boolean inv = (wsUp && kmUp) || (!wsUp && !kmUp); | |
457 | |
458 int size = wkms.size(); | |
459 | |
460 if (logger.isDebugEnabled()) { | |
461 logger.debug("(Wkms)Values : " + size); | |
462 if (size > 0) { | |
463 logger.debug("Start km: " + wkms.getKm(0)); | |
464 logger.debug("End km: " + wkms.getKm(size-1)); | |
465 } | |
466 logger.debug("wsUp: " + wsUp); | |
467 logger.debug("kmUp: " + kmUp); | |
468 logger.debug("inv: " + inv); | |
469 } | |
470 | |
471 return inv; | |
472 } | |
473 | |
474 | |
475 /** | |
476 * Get name of series (displayed in legend). | |
477 * @return name of the series. | |
478 */ | |
479 protected String getSeriesName(WQKms wqkms, String mode) { | |
480 String name = wqkms.getName(); | |
481 String prefix = name != null && name.indexOf(mode) >= 0 ? null : mode; | |
482 | |
483 return prefix != null && prefix.length() > 0 | |
484 ? prefix + "(" + name +")" | |
485 : name; | |
486 } | |
487 | |
488 | |
489 /** Look up the axis identifier for a given facet type. */ | |
490 public int axisIdxForFacet(String facetName) { | |
491 if (FacetTypes.IS.W(facetName)) { | |
492 return YAXIS.W.idx; | |
493 } | |
494 else if (FacetTypes.IS.Q(facetName)) { | |
495 return YAXIS.Q.idx; | |
496 } | |
497 else { | |
498 logger.warn("Could not find axis for facet " + facetName); | |
499 return YAXIS.W.idx; | |
500 } | |
501 } | |
502 | |
503 | |
504 /** | |
505 * Do Area out. | |
506 * @param theme styling information. | |
507 * @param visible whether or not visible. | |
508 */ | |
509 protected void doArea( | |
510 Object o, | |
511 ArtifactAndFacet aandf, | |
512 Document theme, | |
513 boolean visible | |
514 ) { | |
515 logger.debug("LongitudinalSectionGenerator.doArea"); | |
516 StyledAreaSeriesCollection area = new StyledAreaSeriesCollection(theme); | |
517 | |
518 String seriesName = aandf.getFacetDescription(); | |
519 | |
520 AreaFacet.Data data = (AreaFacet.Data) o; | |
521 | |
522 XYSeries up = null; | |
523 XYSeries down = null; | |
524 | |
525 if (data.getUpperData() != null) { | |
526 up = new StyledXYSeries(seriesName, false, theme); | |
527 if (data.getUpperData() instanceof WQKms) { | |
528 if (FacetTypes.IS.Q(data.getRootFacetName())) { | |
529 StyledSeriesBuilder.addPointsKmQ(up, (WQKms) data.getUpperData()); | |
530 } | |
531 else { | |
532 StyledSeriesBuilder.addPoints(up, (WKms) data.getUpperData()); | |
533 } | |
534 } | |
535 else if (data.getUpperData() instanceof double[][]) { | |
536 StyledSeriesBuilder.addPoints(up, (double [][]) data.getUpperData(), false); | |
537 } | |
538 else if (data.getUpperData() instanceof WKms) { | |
539 StyledSeriesBuilder.addPoints(up, (WKms) data.getUpperData()); | |
540 } | |
541 else if (data.getUpperData() instanceof Lines.LineData) { | |
542 StyledSeriesBuilder.addPoints(up, ((Lines.LineData) data.getUpperData()).points, false); | |
543 } | |
544 else { | |
545 logger.error("Do not know how to deal with (up) area info from: " | |
546 + data.getUpperData()); | |
547 } | |
548 } | |
549 | |
550 // TODO Depending on style, the area (e.g. 20m^2) should be added as annotation. | |
551 | |
552 if (data.getLowerData() != null) { | |
553 // TODO: Sort this out: when the two series have the same name, | |
554 // the renderer (or anything in between) will not work correctly. | |
555 down = new StyledXYSeries(seriesName + " ", false, theme); | |
556 if (data.getLowerData() instanceof WQKms) { | |
557 if (FacetTypes.IS.Q(data.getRootFacetName())) { | |
558 StyledSeriesBuilder.addPointsKmQ(down, (WQKms) data.getLowerData()); | |
559 } | |
560 else { | |
561 StyledSeriesBuilder.addPoints(down, (WQKms) data.getLowerData()); | |
562 } | |
563 } | |
564 else if (data.getLowerData() instanceof double[][]) { | |
565 StyledSeriesBuilder.addPoints(down, (double[][]) data.getLowerData(), false); | |
566 } | |
567 else if (data.getLowerData() instanceof WKms) { | |
568 StyledSeriesBuilder.addPoints(down, (WKms) data.getLowerData()); | |
569 } | |
570 else if (data.getLowerData() instanceof Lines.LineData) { | |
571 StyledSeriesBuilder.addPoints(down, ((Lines.LineData) data.getLowerData()).points, false); | |
572 } | |
573 else { | |
574 logger.error("Do not know how to deal with (down) area info from: " | |
575 + data.getLowerData()); | |
576 } | |
577 } | |
578 | |
579 if (up == null && down != null) { | |
580 area.setMode(StyledAreaSeriesCollection.FILL_MODE.ABOVE); | |
581 down.setKey(seriesName); | |
582 area.addSeries(down); | |
583 area.addSeries(StyledSeriesBuilder.createGroundAtInfinity(down)); | |
584 } | |
585 else if (up != null && down == null) { | |
586 area.setMode(StyledAreaSeriesCollection.FILL_MODE.UNDER); | |
587 area.addSeries(up); | |
588 area.addSeries(StyledSeriesBuilder.createGroundAtInfinity(up)); | |
589 } | |
590 else if (up != null && down != null) { | |
591 if (data.doPaintBetween()) { | |
592 area.setMode(StyledAreaSeriesCollection.FILL_MODE.BETWEEN); | |
593 } | |
594 else { | |
595 area.setMode(StyledAreaSeriesCollection.FILL_MODE.ABOVE); | |
596 } | |
597 area.addSeries(up); | |
598 area.addSeries(down); | |
599 } | |
600 // Add area to the respective axis. | |
601 addAreaSeries(area, axisIdxForFacet(data.getRootFacetName()), visible); | |
602 } | |
603 } | |
604 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |