comparison flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixWQCurveGenerator.java @ 5831:bd047b71ab37

Repaired internal references
author Sascha L. Teichmann <teichmann@intevation.de>
date Thu, 25 Apr 2013 12:06:39 +0200
parents flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixWQCurveGenerator.java@0516c1f8f674
children
comparison
equal deleted inserted replaced
5830:160f53ee0870 5831:bd047b71ab37
1 package org.dive4elements.river.exports.fixings;
2
3 import java.awt.BasicStroke;
4 import java.awt.Color;
5 import java.text.DateFormat;
6 import java.util.ArrayList;
7 import java.util.List;
8
9 import org.apache.log4j.Logger;
10 import org.jfree.chart.JFreeChart;
11 import org.jfree.chart.annotations.XYTextAnnotation;
12 import org.jfree.chart.plot.Marker;
13 import org.jfree.chart.plot.ValueMarker;
14 import org.jfree.chart.title.TextTitle;
15 import org.jfree.data.xy.XYSeries;
16 import org.jfree.ui.RectangleAnchor;
17 import org.jfree.ui.RectangleInsets;
18 import org.jfree.ui.TextAnchor;
19 import org.w3c.dom.Document;
20
21 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
22 import org.dive4elements.artifactdatabase.state.Facet;
23 import org.dive4elements.river.artifacts.FLYSArtifact;
24 import org.dive4elements.river.artifacts.StaticWKmsArtifact;
25 import org.dive4elements.river.artifacts.WINFOArtifact;
26 import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
27 import org.dive4elements.river.artifacts.model.DateRange;
28 import org.dive4elements.river.artifacts.model.FacetTypes;
29 import org.dive4elements.river.artifacts.model.NamedDouble;
30 import org.dive4elements.river.artifacts.model.QWDDateRange;
31 import org.dive4elements.river.artifacts.model.WKms;
32 import org.dive4elements.river.artifacts.model.WQKms;
33 import org.dive4elements.river.artifacts.model.fixings.FixFunction;
34 import org.dive4elements.river.artifacts.model.fixings.FixWQCurveFacet;
35 import org.dive4elements.river.artifacts.model.fixings.QWD;
36 import org.dive4elements.river.artifacts.model.fixings.QWI;
37 import org.dive4elements.river.artifacts.resources.Resources;
38 import org.dive4elements.river.exports.ChartGenerator;
39 import org.dive4elements.river.exports.StyledSeriesBuilder;
40 import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation;
41 import org.dive4elements.river.jfree.FLYSAnnotation;
42 import org.dive4elements.river.jfree.JFreeUtil;
43 import org.dive4elements.river.jfree.StickyAxisAnnotation;
44 import org.dive4elements.river.jfree.StyledXYSeries;
45 import org.dive4elements.river.model.Gauge;
46 import org.dive4elements.river.model.River;
47 import org.dive4elements.river.utils.FLYSUtils;
48 import org.dive4elements.river.utils.ThemeUtil;
49
50 /**
51 * Generator for WQ fixing charts.
52 * @author <a href="mailto:christian.lins@intevation.de">Christian Lins</a>
53 */
54 public class FixWQCurveGenerator
55 extends FixChartGenerator
56 implements FacetTypes
57 {
58 /** Private logger. */
59 private static Logger logger =
60 Logger.getLogger(FixWQCurveGenerator.class);
61
62 public static final String I18N_CHART_TITLE =
63 "chart.fixings.wq.title";
64
65 public static final String I18N_CHART_SUBTITLE =
66 "chart.fixings.wq.subtitle";
67
68 public static final String I18N_CHART_SUBTITLE1 =
69 "chart.fixings.wq.subtitle1";
70
71 public static final String I18N_XAXIS_LABEL =
72 "chart.fixings.wq.xaxis.label";
73
74 public static final String I18N_YAXIS_LABEL =
75 "chart.fixings.wq.yaxis.label";
76
77 public static final String I18N_CHART_TITLE_DEFAULT =
78 "Fixierungsanalyse";
79
80 public static final String I18N_XAXIS_LABEL_DEFAULT =
81 "Q [m\u00B3/s]";
82
83 public static final String I18N_YAXIS_LABEL_DEFAULT =
84 "W [NN + m]";
85
86 public static final double EPSILON = 0.001d;
87
88 public static enum YAXIS {
89 W(0),
90 Q(1);
91 public int idx;
92 private YAXIS(int c) {
93 idx = c;
94 }
95 }
96
97
98 /** Needed to access data to create subtitle. */
99 protected FLYSArtifact artifact;
100
101
102 @Override
103 public void doOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
104 logger.debug("doOut: " + aaf.getFacetName());
105 if (!prepareChartData(aaf, doc, visible)) {
106 logger.warn("Unknown facet, name " + aaf.getFacetName());
107 }
108 }
109
110
111 /** Return true if data could be handled. */
112 public boolean prepareChartData(ArtifactAndFacet aaf, Document doc, boolean visible) {
113 String name = aaf.getFacetName();
114
115 this.artifact = (FLYSArtifact)aaf.getArtifact();
116
117 if(name.startsWith(FIX_SECTOR_AVERAGE_WQ)) {
118 doSectorAverageOut(aaf, doc, visible);
119 }
120 else if(FIX_ANALYSIS_EVENTS_WQ.equals(name)) {
121 doAnalysisEventsOut(aaf, doc, visible);
122 }
123 else if(FIX_REFERENCE_EVENTS_WQ.equals(name)) {
124 doReferenceEventsOut(aaf, doc, visible);
125 }
126 else if(FIX_WQ_CURVE.equals(name)) {
127 doWQCurveOut(aaf, doc, visible);
128 }
129 else if(FIX_OUTLIER.equals(name)) {
130 doOutlierOut(aaf, doc, visible);
131 }
132 else if(QSECTOR.equals(name)) {
133 doQSectorOut(aaf, doc, visible);
134 }
135 else if(FIX_EVENTS.equals(name)) {
136 doEventsOut(aaf, doc, visible);
137 }
138 else if(/*STATIC_WKMS_INTERPOL.equals(name) ||*/
139 STATIC_WKMS_MARKS.equals(name) ||
140 STATIC_WKMS.equals(name) ||
141 HEIGHTMARKS_POINTS.equals(name) ) {
142 doWAnnotations(
143 aaf.getData(context),
144 aaf,
145 doc,
146 visible);
147 }
148 else if (LONGITUDINAL_W.equals(name) || STATIC_WQ.equals(name)
149 || STATIC_WKMS_INTERPOL.equals(name)) {
150 doWQOut(aaf.getData(context), aaf, doc, visible);
151 }
152 else if (name.equals(DISCHARGE_CURVE)) {
153 doDischargeOut(
154 (WINFOArtifact) aaf.getArtifact(),
155 aaf.getData(context),
156 aaf.getFacetDescription(),
157 doc,
158 visible);
159 }
160 else if (FacetTypes.IS.MANUALPOINTS(aaf.getFacetName())) {
161 doPoints(aaf.getData(context),
162 aaf,
163 doc, visible, YAXIS.W.idx);
164 }
165 else {
166 return false;
167 }
168 return true;
169 }
170
171
172 /** Add sector average points to chart */
173 protected void doSectorAverageOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
174 logger.debug("doSectorAverageOut");
175
176 QWDDateRange qwdd = (QWDDateRange) aaf.getData(context);
177 QWD qwd = qwdd != null ? qwdd.getQWD() : null;
178
179 if(qwd != null) {
180 addQWSeries(new QWD[] { qwd }, aaf, doc, visible);
181 }
182 else {
183 logger.debug("doSectorAverageOut: qwd == null");
184 }
185 }
186
187 /** Add analysis event points to chart */
188 protected void doAnalysisEventsOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
189 logger.debug("doAnalysisEventsOut");
190
191 QWD qwd = (QWD)aaf.getData(context);
192 if(qwd != null) {
193 XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), doc);
194 List<XYTextAnnotation> textAnnos = new ArrayList<XYTextAnnotation>();
195
196 DateFormat dateFormat = DateFormat.getDateInstance(
197 DateFormat.SHORT);
198
199 series.add(qwd.getQ(), qwd.getW());
200
201 XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
202 dateFormat.format(qwd.getDate()),
203 qwd.getQ(),
204 qwd.getW());
205 textAnnos.add(anno);
206
207 addAxisSeries(series, 0, visible);
208 if(visible && ThemeUtil.parseShowPointLabel(doc)) {
209 FLYSAnnotation flysAnno = new FLYSAnnotation(null, null, null, doc);
210 flysAnno.setTextAnnotations(textAnnos);
211 addAnnotations(flysAnno);
212 }
213 }
214 else {
215 logger.debug("doAnalysisEventsOut: qwds == null");
216 }
217 }
218
219
220 /** Add reference event points to chart */
221 protected void doReferenceEventsOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
222 logger.debug("doReferenceEventsOut");
223
224 QWI qwd = (QWI)aaf.getData(context);
225 if(qwd != null) {
226 XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), doc);
227 List<XYTextAnnotation> textAnnos = new ArrayList<XYTextAnnotation>();
228
229 DateFormat dateFormat = DateFormat.getDateInstance(
230 DateFormat.SHORT);
231
232 series.add(qwd.getQ(), qwd.getW());
233
234 XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
235 dateFormat.format(qwd.getDate()),
236 qwd.getQ(),
237 qwd.getW());
238 textAnnos.add(anno);
239
240 addAxisSeries(series, 0, visible);
241 if(visible && ThemeUtil.parseShowPointLabel(doc)) {
242 FLYSAnnotation flysAnno = new FLYSAnnotation(null, null, null, doc);
243 flysAnno.setTextAnnotations(textAnnos);
244 addAnnotations(flysAnno);
245 }
246 }
247 else {
248 logger.debug("doReferenceEventsOut: qwds == null");
249 }
250 }
251
252
253 private void addPointFromWQKms(WQKms wqkms,
254 String title,
255 Document theme,
256 boolean visible
257 ) {
258 XYSeries series = new StyledXYSeries(title, theme);
259 Double ckm = (Double) context.getContextValue(CURRENT_KM);
260 if (wqkms == null || wqkms.getKms().length == 0 || ckm == null) {
261 logger.info("addPointFromWQKms: No event data to show.");
262 return;
263 }
264 double[] kms = wqkms.getKms();
265 for (int i = 0 ; i< kms.length; i++) {
266 if (Math.abs(kms[i] - ckm) <= EPSILON) {
267 series.add(wqkms.getQ(i), wqkms.getW(i));
268 addAxisSeries(series, YAXIS.W.idx, visible);
269 return;
270 }
271 }
272 }
273
274 protected void doEventsOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
275 logger.debug("doEventsOut");
276 // Find W/Q at km.
277 addPointFromWQKms((WQKms) aaf.getData(context),
278 aaf.getFacetDescription(), doc, visible);
279 }
280
281
282 protected void doWQCurveOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
283 logger.debug("doWQCurveOut");
284
285 FixWQCurveFacet facet = (FixWQCurveFacet)aaf.getFacet();
286 FixFunction func = (FixFunction)facet.getData(
287 aaf.getArtifact(), context);
288
289 if (func == null) {
290 logger.warn("doWQCurveOut: Facet does not contain FixFunction");
291 return;
292 }
293
294 double maxQ = func.getMaxQ();
295
296 if (maxQ > 0) {
297 StyledXYSeries series = JFreeUtil.sampleFunction2D(
298 func.getFunction(),
299 doc,
300 aaf.getFacetDescription(),
301 500, // number of samples
302 0.0 , // start
303 maxQ); // end
304
305 addAxisSeries(series, 0, visible);
306 }
307 else {
308 logger.warn("doWQCurveOut: maxQ <= 0");
309 }
310 }
311
312 protected void doOutlierOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
313 logger.debug("doOutlierOut");
314
315 QWI[] qws = (QWI[])aaf.getData(context);
316 addQWSeries(qws, aaf, doc, visible);
317 }
318
319
320 /** Add markers for q sectors. */
321 protected void doQSectorOut(ArtifactAndFacet aaf, Document theme, boolean visible) {
322 logger.debug("doQSectorOut");
323 if (!visible) {
324 return;
325 }
326
327 Object qsectorsObj = aaf.getData(context);
328 if (qsectorsObj == null || !(qsectorsObj instanceof List)) {
329 logger.warn("No QSectors coming from data.");
330 return;
331 }
332
333 List<?> qsectorsList = (List<?>) qsectorsObj;
334 if (qsectorsList.size() == 0 || !(qsectorsList.get(0) instanceof NamedDouble)) {
335 logger.warn("No QSectors coming from data.");
336 return;
337 }
338
339 @SuppressWarnings("unchecked")
340 List<NamedDouble> qsectors = (List<NamedDouble>) qsectorsList;
341
342 for (NamedDouble qsector : qsectors) {
343 if (Double.isNaN(qsector.getValue())) {
344 continue;
345 }
346 Marker m = new ValueMarker(qsector.getValue());
347 m.setPaint(Color.black);
348
349 float[] dashes = ThemeUtil.parseLineStyle(theme);
350 int size = ThemeUtil.parseLineWidth(theme);
351 BasicStroke stroke;
352 if (dashes.length <= 1) {
353 stroke = new BasicStroke(size);
354 }
355 else {
356 stroke = new BasicStroke(size,
357 BasicStroke.CAP_BUTT,
358 BasicStroke.JOIN_ROUND,
359 1.0f,
360 dashes,
361 0.0f);
362 }
363 m.setStroke(stroke);
364
365 if (ThemeUtil.parseShowLineLabel(theme)) {
366 m.setLabel(qsector.getName());
367 m.setPaint(ThemeUtil.parseTextColor(theme));
368 m.setLabelFont(ThemeUtil.parseTextFont(theme));
369 }
370 Color paint = ThemeUtil.parseLineColorField(theme);
371 if (paint != null) {
372 m.setPaint(paint);
373 }
374 m.setLabelAnchor(RectangleAnchor.TOP_LEFT);
375 m.setLabelTextAnchor(TextAnchor.TOP_LEFT);
376 m.setLabelOffset(new RectangleInsets(5, 5, 10, 10));
377 addDomainMarker(m);
378 }
379 }
380
381
382 /**
383 * Add W-Annotations to plot.
384 * @param wqkms actual data (double[][]).
385 * @param theme theme to use.
386 */
387 protected void doWAnnotations(
388 Object wqkms,
389 ArtifactAndFacet aandf,
390 Document theme,
391 boolean visible
392 ) {
393 Facet facet = aandf.getFacet();
394
395 List<StickyAxisAnnotation> xy = new ArrayList<StickyAxisAnnotation>();
396 if (wqkms instanceof double[][]) {
397 logger.debug("Got double[][]");
398 double [][] data = (double [][]) wqkms;
399 for (int i = 0; i< data[0].length; i++) {
400 xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(),
401 (float) data[1][i], StickyAxisAnnotation.SimpleAxis.Y_AXIS));
402 }
403
404 doAnnotations(new FLYSAnnotation(facet.getDescription(), xy),
405 aandf, theme, visible);
406 }
407 else {
408 // Assume its WKms.
409 logger.debug("Got WKms");
410 WKms data = (WKms) wqkms;
411
412 Double ckm = (Double) context.getContextValue(CURRENT_KM);
413 double location = (ckm != null)
414 ? ckm.doubleValue()
415 : getRange()[0];
416 double w = StaticWKmsArtifact.getWAtKmLin(data, location);
417 xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(),
418 (float) w, StickyAxisAnnotation.SimpleAxis.Y_AXIS));
419
420 doAnnotations(new FLYSAnnotation(facet.getDescription(), xy),
421 aandf, theme, visible);
422 }
423 }
424
425
426 /**
427 * Add series with discharge curve to diagram.
428 */
429 protected void doDischargeOut(
430 WINFOArtifact artifact,
431 Object o,
432 String description,
433 Document theme,
434 boolean visible)
435 {
436 WQKms wqkms = (WQKms) o;
437
438 String gaugeName = wqkms.getName();
439
440 River river = FLYSUtils.getRiver(artifact);
441
442 if (river == null) {
443 logger.debug("no river found");
444 return;
445 }
446
447 Gauge gauge = river.determineGaugeByName(gaugeName);
448
449 if (gauge == null) {
450 logger.debug("no gauge found");
451 return;
452 }
453
454 XYSeries series = new StyledXYSeries(description, theme);
455 StyledSeriesBuilder.addPointsQW(series, wqkms);
456 addAxisSeries(series, YAXIS.W.idx, visible);
457 }
458
459
460 /**
461 * Add WQ Data to plot.
462 * @param wqkms data as double[][]
463 */
464 protected void doWQOut(
465 Object wqkms,
466 ArtifactAndFacet aaf,
467 Document theme,
468 boolean visible
469 ) {
470 logger.debug("FixWQCurveGenerator: doWQOut");
471 if (wqkms instanceof WQKms) {
472 // TODO As in doEventsOut, the value-searching should
473 // be delivered by the facet already (instead of in the Generator).
474 logger.debug("FixWQCurveGenerator: doWQOut: WQKms");
475
476 addPointFromWQKms((WQKms) aaf.getData(context), aaf.getFacetDescription(), theme, visible);
477 }
478 else {
479 logger.debug("FixWQCurveGenerator: doWQOut: double[][]");
480 double [][] data = (double [][]) wqkms;
481
482 XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
483 StyledSeriesBuilder.addPoints(series, data, true);
484
485 addAxisSeries(series, YAXIS.W.idx, visible);
486 }
487 }
488
489
490 protected void addQWSeries(
491 QWI [] qws,
492 ArtifactAndFacet aaf,
493 Document theme,
494 boolean visible
495 ) {
496 if (qws == null) {
497 return;
498 }
499
500 XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
501 List<XYTextAnnotation> textAnnos =
502 new ArrayList<XYTextAnnotation>(qws.length);
503
504 DateFormat dateFormat = DateFormat.getDateInstance(
505 DateFormat.SHORT);
506
507 for (QWI qw: qws) {
508 series.add(qw.getQ(), qw.getW());
509
510 XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
511 dateFormat.format(qw.getDate()),
512 qw.getQ(),
513 qw.getW());
514 textAnnos.add(anno);
515 }
516
517 addAxisSeries(series, 0, visible);
518 if (visible && ThemeUtil.parseShowPointLabel(theme)) {
519 FLYSAnnotation flysAnno =
520 new FLYSAnnotation(null, null, null, theme);
521 flysAnno.setTextAnnotations(textAnnos);
522 addAnnotations(flysAnno);
523 }
524 }
525
526 @Override
527 protected String getChartTitle() {
528 return Resources.format(
529 context.getMeta(),
530 I18N_CHART_TITLE,
531 I18N_CHART_TITLE_DEFAULT,
532 context.getContextValue(CURRENT_KM));
533 }
534
535 @Override
536 protected String getDefaultChartTitle() {
537 return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
538 }
539
540 @Override
541 protected String getDefaultChartSubtitle() {
542 FixAnalysisAccess access = new FixAnalysisAccess(artifact, context);
543 DateRange dateRange = access.getDateRange();
544 DateRange refRange = access.getReferencePeriod();
545
546 if (dateRange != null && refRange != null) {
547 return Resources.format(
548 context.getMeta(),
549 I18N_CHART_SUBTITLE,
550 "",
551 access.getRiver(),
552 dateRange.getFrom(),
553 dateRange.getTo(),
554 refRange.getFrom(),
555 refRange.getTo());
556 }
557
558 return null;
559 }
560
561 @Override
562 protected void addSubtitles(JFreeChart chart) {
563 String defaultSubtitle = getDefaultChartSubtitle();
564
565 if (defaultSubtitle == null || defaultSubtitle.length() == 0) {
566 return;
567 }
568
569 chart.addSubtitle(new TextTitle(defaultSubtitle));
570
571 StringBuilder buf = new StringBuilder();
572
573 // Add analysis periods as additional subtitle
574 FixAnalysisAccess access = new FixAnalysisAccess(artifact, context);
575 DateRange[] aperiods = access.getAnalysisPeriods();
576 buf.append(msg("fix.analysis.periods"));
577 buf.append(": ");
578 for(int n = 0; n < aperiods.length; n++) {
579 buf.append(
580 Resources.format(
581 context.getMeta(),
582 I18N_CHART_SUBTITLE1,
583 "",
584 aperiods[n].getFrom(),
585 aperiods[n].getTo()));
586 if(n + 1 < aperiods.length) {
587 buf.append("; ");
588 }
589 }
590
591 chart.addSubtitle(new TextTitle(buf.toString()));
592 }
593
594 @Override
595 protected String getDefaultXAxisLabel() {
596 return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
597 }
598
599 @Override
600 protected String getDefaultYAxisLabel(int pos) {
601 return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
602 }
603
604 @Override
605 protected ChartGenerator.YAxisWalker getYAxisWalker() {
606 return new YAxisWalker() {
607 @Override
608 public int length() {
609 return YAXIS.values().length;
610 }
611
612 @Override
613 public String getId(int idx) {
614 YAXIS[] yaxes = YAXIS.values();
615 return yaxes[idx].toString();
616 }
617 };
618 }
619 }
620 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org