comparison artifacts/src/main/java/org/dive4elements/river/exports/DiagramGenerator.java @ 7044:6ab1464021ae generator-refactoring

Add DiagramGenerator which should mainly replace xygenerator
author Andre Heinecke <aheinecke@intevation.de>
date Wed, 18 Sep 2013 17:13:17 +0200
parents
children c4bacc5ddd9b
comparison
equal deleted inserted replaced
7043:06a9a241faac 7044:6ab1464021ae
1 /* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde
2 * Software engineering by Intevation GmbH
3 *
4 * This file is Free Software under the GNU AGPL (>=v3)
5 * and comes with ABSOLUTELY NO WARRANTY! Check out the
6 * documentation coming with Dive4Elements River for details.
7 */
8
9 package org.dive4elements.river.exports;
10
11 import java.awt.Color;
12 import java.awt.Font;
13 import java.text.NumberFormat;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Map;
18
19 import javax.swing.ImageIcon;
20
21 import org.apache.log4j.Logger;
22 import org.jfree.chart.ChartFactory;
23 import org.jfree.chart.JFreeChart;
24 import org.jfree.chart.LegendItem;
25 import org.jfree.chart.annotations.XYAnnotation;
26 import org.jfree.chart.annotations.XYImageAnnotation;
27 import org.jfree.chart.annotations.XYTextAnnotation;
28 import org.jfree.chart.axis.NumberAxis;
29 import org.jfree.chart.axis.ValueAxis;
30 import org.jfree.chart.axis.LogarithmicAxis;
31 import org.jfree.chart.plot.Marker;
32 import org.jfree.chart.plot.PlotOrientation;
33 import org.jfree.chart.plot.XYPlot;
34 import org.jfree.data.Range;
35 import org.jfree.data.general.Series;
36 import org.jfree.data.xy.XYDataset;
37 import org.jfree.data.xy.XYSeries;
38 import org.jfree.data.xy.XYSeriesCollection;
39 import org.json.JSONArray;
40 import org.json.JSONException;
41
42 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
43 import org.dive4elements.artifactdatabase.state.Facet;
44 import org.dive4elements.river.jfree.AxisDataset;
45 import org.dive4elements.river.jfree.AnnotationHelper;
46 import org.dive4elements.river.jfree.Bounds;
47 import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation;
48 import org.dive4elements.river.jfree.DoubleBounds;
49 import org.dive4elements.river.jfree.RiverAnnotation;
50 import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
51 import org.dive4elements.river.jfree.StyledXYSeries;
52 import org.dive4elements.river.themes.ThemeDocument;
53
54 /* TODO remove after hackish testing */
55 import org.dive4elements.river.exports.process.Processor;
56 import org.dive4elements.river.exports.process.BedDiffHeightYearProcessor;
57 import org.dive4elements.river.exports.process.BedDiffYearProcessor;
58 import org.dive4elements.river.exports.process.BedheightProcessor;
59 import org.dive4elements.river.exports.process.QOutProcessor;
60 import org.dive4elements.river.exports.process.WOutProcessor;
61 /* end TODO*/
62
63 /**
64 * The main diagram creation class.
65 *
66 * This class is the glue between output processors and facets.
67 * The generator creates one diagram and calls the appropiate
68 * processors for the state and
69 *
70 * With respect to datasets, ranges and axis, there are following requirements:
71 * <ul>
72 * <li> First in, first drawn: "Early" datasets should be of lower Z-Oder
73 * than later ones (only works per-axis). </li>
74 * <li> Visible axis should initially show the range of all datasets that
75 * show data for this axis (even invisible ones). Motivation: Once
76 * a dataset (theme) has been activated, it should be on screen. </li>
77 * <li> There should always be a Y-Axis on the "left". </li>
78 * </ul>
79 */
80 public abstract class DiagramGenerator extends ChartGenerator2 {
81
82 /** Enumerator over existing axes. */
83 @Override
84 protected abstract YAxisWalker getYAxisWalker();
85
86 public static final int AXIS_SPACE = 5;
87
88 /** The logger that is used in this generator. */
89 private static Logger logger = Logger.getLogger(DiagramGenerator.class);
90
91 protected List<Marker> domainMarkers = new ArrayList<Marker>();
92
93 protected List<Marker> valueMarkers = new ArrayList<Marker>();
94
95 /** The max X range to include all X values of all series for each axis. */
96 protected Map<Integer, Bounds> xBounds;
97
98 /** The max Y range to include all Y values of all series for each axis. */
99 protected Map<Integer, Bounds> yBounds;
100
101 /** Whether or not the plot is inverted (left-right). */
102 private boolean inverted;
103
104 public DiagramGenerator() {
105 super();
106
107 xBounds = new HashMap<Integer, Bounds>();
108 yBounds = new HashMap<Integer, Bounds>();
109 }
110
111
112 /**
113 * Generate the chart anew (including localized axis and all).
114 */
115 @Override
116 public JFreeChart generateChart() {
117 logger.debug("DiagramGenerator.generateChart");
118
119 JFreeChart chart = ChartFactory.createXYLineChart(
120 getChartTitle(),
121 getXAxisLabel(),
122 getYAxisLabel(0),
123 null,
124 PlotOrientation.VERTICAL,
125 isLegendVisible(),
126 false,
127 false);
128
129 XYPlot plot = (XYPlot) chart.getPlot();
130 ValueAxis axis = createXAxis(getXAxisLabel());
131 plot.setDomainAxis(axis);
132
133 chart.setBackgroundPaint(Color.WHITE);
134 plot.setBackgroundPaint(Color.WHITE);
135 addSubtitles(chart);
136 adjustPlot(plot);
137
138 //debugAxis(plot);
139
140 addDatasets(plot);
141
142 //debugDatasets(plot);
143
144 addMarkers(plot);
145
146 recoverEmptyPlot(plot);
147 preparePointRanges(plot);
148
149 //debugAxis(plot);
150
151 localizeAxes(plot);
152 adjustAxes(plot);
153 if (!(axis instanceof LogarithmicAxis)) {
154 // XXX:
155 // The auto zoom without a range tries
156 // to include 0 in a logarithmic axis
157 // which triggers a bug in jfreechart that causes
158 // the values to be drawn carthesian
159 autoZoom(plot);
160 }
161
162 //debugAxis(plot);
163
164 // These have to go after the autozoom.
165 AnnotationHelper.addAnnotationsToRenderer(annotations, plot,
166 getChartSettings(), datasets);
167
168 // Add a logo (maybe).
169 addLogo(plot);
170
171 aggregateLegendEntries(plot);
172
173 return chart;
174 }
175
176
177 /**
178 * Return left most data points x value (on first axis).
179 * Shortcut, especially to be overridden in (LS) charts where
180 * axis could be inverted.
181 */
182 protected double getLeftX() {
183 return (Double)getXBounds(0).getLower();
184 }
185
186
187 /**
188 * Return right most data points x value (on first axis).
189 * Shortcut, especially to be overridden in (LS) charts where
190 * axis could be inverted.
191 */
192 protected double getRightX() {
193 return (Double)getXBounds(0).getUpper();
194 }
195
196
197 /** Add a logo as background annotation to plot. */
198 protected void addLogo(XYPlot plot) {
199 String logo = showLogo();
200 if (logo == null) {
201 logger.debug("No logo to show chosen");
202 return;
203 }
204
205 ImageIcon imageIcon = null;
206 if (logo.equals("none")) {
207 return;
208 }
209 /*
210 If you want to add images, remember to change code in these places:
211 flys-artifacts:
212 DiagramGenerator.java
213 Timeseries*Generator.java and
214 in the flys-client projects Chart*Propert*Editor.java.
215 Also, these images have to be put in
216 flys-artifacts/src/main/resources/images/
217 flys-client/src/main/webapp/images/
218 */
219 java.net.URL imageURL;
220 if (logo.equals("Intevation")) {
221 imageURL = DiagramGenerator.class.getResource("/images/intevation.png");
222 }
223 else { // TODO else if ...
224 imageURL = DiagramGenerator.class.getResource("/images/bfg_logo.gif");
225 }
226 imageIcon = new ImageIcon(imageURL);
227
228
229 double xPos = 0d, yPos = 0d;
230
231 String placeh = logoHPlace();
232 String placev = logoVPlace();
233
234 if (placev == null || placev.equals("none")) {
235 placev = "top";
236 }
237 if (placev.equals("top")) {
238 yPos = (Double)getYBounds(0).getUpper();
239 }
240 else if (placev.equals("bottom")) {
241 yPos = (Double)getYBounds(0).getLower();
242 }
243 else if (placev.equals("center")) {
244 yPos = ((Double)getYBounds(0).getUpper() + (Double)getYBounds(0).getLower())/2d;
245 }
246 else {
247 logger.debug("Unknown place-v value: " + placev);
248 }
249
250 if (placeh == null || placeh.equals("none")) {
251 placeh = "center";
252 }
253 if (placeh.equals("left")) {
254 xPos = getLeftX();
255 }
256 else if (placeh.equals("right")) {
257 xPos = getRightX();
258 }
259 else if (placeh.equals("center")) {
260 xPos = ((Double)getXBounds(0).getUpper() + (Double)getXBounds(0).getLower())/2d;
261 }
262 else {
263 logger.debug("Unknown place-h value: " + placeh);
264 }
265
266 logger.debug("logo position: " + xPos + "/" + yPos);
267
268 org.jfree.ui.RectangleAnchor anchor
269 = org.jfree.ui.RectangleAnchor.TOP;
270 if (placev.equals("top")) {
271 if (placeh.equals("left")) {
272 anchor = org.jfree.ui.RectangleAnchor.TOP_LEFT;
273 }
274 else if (placeh.equals("right")) {
275 anchor = org.jfree.ui.RectangleAnchor.TOP_RIGHT;
276 }
277 else if (placeh.equals("center")) {
278 anchor = org.jfree.ui.RectangleAnchor.TOP;
279 }
280 }
281 else if (placev.equals("bottom")) {
282 if (placeh.equals("left")) {
283 anchor = org.jfree.ui.RectangleAnchor.BOTTOM_LEFT;
284 }
285 else if (placeh.equals("right")) {
286 anchor = org.jfree.ui.RectangleAnchor.BOTTOM_RIGHT;
287 }
288 else if (placeh.equals("center")) {
289 anchor = org.jfree.ui.RectangleAnchor.BOTTOM;
290 }
291 }
292 else if (placev.equals("center")) {
293 if (placeh.equals("left")) {
294 anchor = org.jfree.ui.RectangleAnchor.LEFT;
295 }
296 else if (placeh.equals("right")) {
297 anchor = org.jfree.ui.RectangleAnchor.RIGHT;
298 }
299 else if (placeh.equals("center")) {
300 anchor = org.jfree.ui.RectangleAnchor.CENTER;
301 }
302 }
303
304 XYAnnotation xyannotation =
305 new XYImageAnnotation(xPos, yPos, imageIcon.getImage(), anchor);
306 plot.getRenderer().addAnnotation(xyannotation, org.jfree.ui.Layer.BACKGROUND);
307 }
308
309
310 protected NumberAxis createXAxis(String label) {
311 return new NumberAxis(label);
312 }
313
314
315 @Override
316 protected Series getSeriesOf(XYDataset dataset, int idx) {
317 return ((XYSeriesCollection) dataset).getSeries(idx);
318 }
319
320
321 @Override
322 protected AxisDataset createAxisDataset(int idx) {
323 logger.debug("Create new AxisDataset for index: " + idx);
324 return new AxisDataset(idx);
325 }
326
327
328 /**
329 * Put debug output about datasets.
330 */
331 public void debugDatasets(XYPlot plot) {
332 logger.debug("Number of datasets: " + plot.getDatasetCount());
333 for (int i = 0, P = plot.getDatasetCount(); i < P; i++) {
334 if (plot.getDataset(i) == null) {
335 logger.debug("Dataset #" + i + " is null");
336 continue;
337 }
338 logger.debug("Dataset #" + i + ":" + plot.getDataset(i));
339 XYSeriesCollection series = (XYSeriesCollection) plot.getDataset(i);
340 logger.debug("X-Extend of Dataset: " + series.getSeries(0).getMinX()
341 + " " + series.getSeries(0).getMaxX());
342 logger.debug("Y-Extend of Dataset: " + series.getSeries(0).getMinY()
343 + " " + series.getSeries(0).getMaxY());
344 }
345 }
346
347
348 /**
349 * Put debug output about axes.
350 */
351 public void debugAxis(XYPlot plot) {
352 logger.debug("...............");
353 for (int i = 0, P = plot.getRangeAxisCount(); i < P; i++) {
354 if (plot.getRangeAxis(i) == null)
355 logger.debug("Range-Axis #" + i + " == null");
356 else {
357 logger.debug("Range-Axis " + i + " != null [" +
358 plot.getRangeAxis(i).getRange().getLowerBound() +
359 " " + plot.getRangeAxis(i).getRange().getUpperBound() +
360 "]");
361 }
362 }
363 for (int i = 0, P = plot.getDomainAxisCount(); i < P; i++) {
364 if (plot.getDomainAxis(i) == null)
365 logger.debug("Domain-Axis #" + i + " == null");
366 else {
367 logger.debug("Domain-Axis " + i + " != null [" +
368 plot.getDomainAxis(i).getRange().getLowerBound() +
369 " " + plot.getDomainAxis(i).getRange().getUpperBound() +
370 "]");
371 }
372 }
373 logger.debug("...............");
374 }
375
376
377 /**
378 * Registers an area to be drawn.
379 * @param area Area to be drawn.
380 * @param index 'axis index'
381 * @param visible Whether or not to be visible (important for range calculations).
382 */
383 public void addAreaSeries(StyledAreaSeriesCollection area, int index, boolean visible) {
384 if (area == null) {
385 logger.warn("Cannot yet render above/under curve.");
386 return;
387 }
388
389 AxisDataset axisDataset = (AxisDataset) getAxisDataset(index);
390
391 if (visible) {
392 axisDataset.addArea(area);
393 }
394 else {
395 /* No range merging, for areas extending to infinity this
396 * causes problems. */
397 }
398 }
399
400
401 /**
402 * Add given series if visible, if not visible adjust ranges (such that
403 * all points in data would be plotted once visible).
404 * @param series the data series to include in plot.
405 * @param index ('symbolic') index of the series and of its axis.
406 * @param visible whether or not the data should be plotted.
407 */
408 public void addAxisSeries(XYSeries series, int index, boolean visible) {
409 if (series == null) {
410 return;
411 }
412
413 logger.debug("Y Range of XYSeries: " +
414 series.getMinY() + " | " + series.getMaxY());
415
416 addAxisDataset(new XYSeriesCollection(series), index, visible);
417
418 AxisDataset axisDataset = (AxisDataset) getAxisDataset(index);
419 }
420
421
422 /**
423 * Add the given vertical marker to the chart.
424 */
425 public void addDomainMarker(Marker marker) {
426 addDomainMarker(marker, true);
427 }
428
429
430 /**
431 * Add the given vertical marker to the chart.<b>Note:</b> the marker is
432 * added to the chart only if it is not null and if <i>visible</i> is true.
433 * @param marker The marker that should be added to the chart.
434 * @param visible The visibility of the marker.
435 */
436 public void addDomainMarker(Marker marker, boolean visible) {
437 if (visible && marker != null) {
438 domainMarkers.add(marker);
439 }
440 }
441
442
443 /**
444 * Add the given vertical marker to the chart.
445 */
446 public void addValueMarker(Marker marker) {
447 addValueMarker(marker, true);
448 }
449
450
451 /**
452 * Add the given horizontal marker to the chart.<b>Note:</b> the marker is
453 * added to the chart only if it is not null and if <i>visible</i> is true.
454 * @param marker The marker that should be added to the chart.
455 * @param visible The visibility of the marker.
456 */
457 public void addValueMarker(Marker marker, boolean visible) {
458 if (visible && marker != null) {
459 valueMarkers.add(marker);
460 }
461 }
462
463
464 protected void addMarkers(XYPlot plot) {
465 for(Marker marker : domainMarkers) {
466 plot.addDomainMarker(marker);
467 }
468 for(Marker marker : valueMarkers) {
469 plot.addRangeMarker(marker);
470 }
471 }
472
473
474 /**
475 * Effect: extend range of x axis to include given limits.
476 *
477 * @param bounds the given ("minimal") bounds.
478 * @param index index of axis to be merged.
479 */
480 @Override
481 protected void combineXBounds(Bounds bounds, int index) {
482 if (!(bounds instanceof DoubleBounds)) {
483 logger.warn("Unsupported Bounds type: " + bounds.getClass());
484 return;
485 }
486
487 DoubleBounds dBounds = (DoubleBounds) bounds;
488
489 if (dBounds == null
490 || Double.isNaN((Double) dBounds.getLower())
491 || Double.isNaN((Double) dBounds.getUpper())) {
492 return;
493 }
494
495 Bounds old = getXBounds(index);
496
497 if (old != null) {
498 dBounds = (DoubleBounds) dBounds.combine(old);
499 }
500
501 setXBounds(index, dBounds);
502 }
503
504
505 @Override
506 protected void combineYBounds(Bounds bounds, int index) {
507 if (!(bounds instanceof DoubleBounds)) {
508 logger.warn("Unsupported Bounds type: " + bounds.getClass());
509 return;
510 }
511
512 DoubleBounds dBounds = (DoubleBounds) bounds;
513
514 if (dBounds == null
515 || Double.isNaN((Double) dBounds.getLower())
516 || Double.isNaN((Double) dBounds.getUpper())) {
517 return;
518 }
519
520 Bounds old = getYBounds(index);
521
522 if (old != null) {
523 dBounds = (DoubleBounds) dBounds.combine(old);
524 }
525
526 setYBounds(index, dBounds);
527 }
528
529
530 /**
531 * If no data is visible, draw at least empty axis.
532 */
533 private void recoverEmptyPlot(XYPlot plot) {
534 if (plot.getRangeAxis() == null) {
535 logger.debug("debug: No range axis");
536 plot.setRangeAxis(createYAxis(0));
537 }
538 }
539
540
541 /**
542 * Expands X axes if only a point is shown.
543 */
544 private void preparePointRanges(XYPlot plot) {
545 for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) {
546
547 Integer key = Integer.valueOf(i);
548 Bounds b = getXBounds(key);
549
550
551 if (b != null && b.getLower().equals(b.getUpper())) {
552 logger.debug("Check whether to expand a x axis.i ("+b.getLower() + "-" + b.getUpper()+")");
553 setXBounds(key, ChartHelper.expandBounds(b, 5));
554 }
555 }
556 }
557
558
559 /**
560 * This method zooms the plot to the specified ranges in the attribute
561 * document or to the ranges specified by the min/max values in the
562 * datasets. <b>Note:</b> We determine the range manually if no zoom ranges
563 * are given, because JFreeCharts auto-zoom adds a margin to the left and
564 * right of the data area.
565 *
566 * @param plot The XYPlot.
567 */
568 protected void autoZoom(XYPlot plot) {
569 logger.debug("Zoom to specified ranges.");
570
571 Range xrange = getDomainAxisRange();
572 Range yrange = getValueAxisRange();
573
574 ValueAxis xAxis = plot.getDomainAxis();
575
576 Range fixedXRange = getRangeForAxisFromSettings("X");
577 if (fixedXRange != null) {
578 xAxis.setRange(fixedXRange);
579 }
580 else {
581 zoomX(plot, xAxis, getXBounds(0), xrange);
582 }
583
584 for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) {
585 ValueAxis yaxis = plot.getRangeAxis(i);
586
587 if (yaxis instanceof IdentifiableNumberAxis) {
588 IdentifiableNumberAxis idAxis = (IdentifiableNumberAxis) yaxis;
589
590 Range fixedRange = getRangeForAxisFromSettings(idAxis.getId());
591 if (fixedRange != null) {
592 yaxis.setRange(fixedRange);
593 continue;
594 }
595 }
596
597 if (yaxis == null) {
598 logger.debug("Zoom problem: no Y Axis for index: " + i);
599 continue;
600 }
601
602 logger.debug("Prepare zoom settings for y axis at index: " + i);
603 zoomY(plot, yaxis, getYBounds(Integer.valueOf(i)), yrange);
604 }
605 }
606
607
608 protected Range getDomainAxisRange() {
609 String[] ranges = getDomainAxisRangeFromRequest();
610
611 if (ranges == null || ranges.length < 2) {
612 logger.debug("No zoom range for domain axis specified.");
613 return null;
614 }
615
616 if (ranges[0].length() > 0 && ranges[1].length() > 0) {
617 try {
618 double from = Double.parseDouble(ranges[0]);
619 double to = Double.parseDouble(ranges[1]);
620
621 if (from == 0 && to == 0) {
622 logger.debug("No range specified. Lower and upper X == 0");
623 return null;
624 }
625
626 if (from > to) {
627 double tmp = to;
628 to = from;
629 from = tmp;
630 }
631
632 return new Range(from, to);
633 }
634 catch (NumberFormatException nfe) {
635 logger.warn("Wrong values for domain axis range.");
636 }
637 }
638
639 return null;
640 }
641
642
643 protected Range getValueAxisRange() {
644 String[] ranges = getValueAxisRangeFromRequest();
645
646 if (ranges == null || ranges.length < 2) {
647 logger.debug("No range specified. Lower and upper Y == 0");
648 return null;
649 }
650
651 if (ranges[0].length() > 0 && ranges[1].length() > 0) {
652 try {
653 double from = Double.parseDouble(ranges[0]);
654 double to = Double.parseDouble(ranges[1]);
655
656 if (from == 0 && to == 0) {
657 logger.debug("No range specified. Lower and upper Y == 0");
658 return null;
659 }
660
661 return from > to
662 ? new Range(to, from)
663 : new Range(from, to);
664 }
665 catch (NumberFormatException nfe) {
666 logger.warn("Wrong values for value axis range.");
667 }
668 }
669
670 return null;
671 }
672
673
674 protected boolean zoomX(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) {
675 return zoom(plot, axis, bounds, x);
676 }
677
678
679 protected boolean zoomY(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) {
680 return zoom(plot, axis, bounds, x);
681 }
682
683
684 /**
685 * Zooms the x axis to the range specified in the attribute document.
686 *
687 * @param plot The XYPlot.
688 * @param axis The axis the shoud be modified.
689 * @param bounds The whole range specified by a dataset.
690 * @param x A user defined range (null permitted).
691 *
692 * @return true, if a zoom range was specified, otherwise false.
693 */
694 protected boolean zoom(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) {
695
696 if (bounds == null) {
697 return false;
698 }
699
700 if (x != null) {
701 Bounds computed = calculateZoom(bounds, x);
702 computed.applyBounds(axis, AXIS_SPACE);
703
704 logger.debug("Zoom axis to: " + computed);
705
706 return true;
707 }
708
709 bounds.applyBounds(axis, AXIS_SPACE);
710 return false;
711 }
712
713 /**
714 * Calculates the start and end km for zoomed charts.
715 * @param bounds The given total bounds (unzoomed).
716 * @param range The range specifying the zoom.
717 *
718 * @return The start and end km for the zoomed chart.
719 */
720 protected Bounds calculateZoom(Bounds bounds, Range range) {
721 double min = bounds.getLower().doubleValue();
722 double max = bounds.getUpper().doubleValue();
723
724 if (logger.isDebugEnabled()) {
725 logger.debug("Minimum is: " + min);
726 logger.debug("Maximum is: " + max);
727 logger.debug("Lower zoom is: " + range.getLowerBound());
728 logger.debug("Upper zoom is: " + range.getUpperBound());
729 }
730
731 double diff = max > min ? max - min : min - max;
732
733 DoubleBounds computed = new DoubleBounds(
734 min + range.getLowerBound() * diff,
735 min + range.getUpperBound() * diff);
736 return computed;
737 }
738
739 /**
740 * Extract the minimum and maximum values for x and y axes
741 * which are stored in <i>xRanges</i> and <i>yRanges</i>.
742 *
743 * @param index The index of the y-Axis.
744 *
745 * @return a Range[] as follows: [x-Range, y-Range].
746 */
747 @Override
748 public Range[] getRangesForAxis(int index) {
749 logger.debug("getRangesForAxis " + index);
750
751 Bounds rx = getXBounds(Integer.valueOf(0));
752 Bounds ry = getYBounds(Integer.valueOf(index));
753
754 if (rx == null) {
755 logger.warn("Range for x axis not set." +
756 " Using default values: 0 - 1.");
757 rx = new DoubleBounds(0, 1);
758 }
759 if (ry == null) {
760 logger.warn("Range for y" + index +
761 " axis not set. Using default values: 0 - 1.");
762 ry = new DoubleBounds(0, 1);
763 }
764
765 return new Range[] {
766 new Range(rx.getLower().doubleValue(), rx.getUpper().doubleValue()),
767 new Range(ry.getLower().doubleValue(), ry.getUpper().doubleValue())
768 };
769 }
770
771
772 /** Get X (usually horizontal) extent for given axis. */
773 @Override
774 public Bounds getXBounds(int axis) {
775 return xBounds.get(axis);
776 }
777
778
779 /** Set X (usually horizontal) extent for given axis. */
780 @Override
781 protected void setXBounds(int axis, Bounds bounds) {
782 if (bounds.getLower() == bounds.getUpper()) {
783 xBounds.put(axis, ChartHelper.expandBounds(bounds, 5d));
784 }
785 else {
786 xBounds.put(axis, bounds);
787 }
788 }
789
790
791 /** Get Y (usually vertical) extent for given axis. */
792 @Override
793 public Bounds getYBounds(int axis) {
794 return yBounds.get(axis);
795 }
796
797
798 /** Set Y (usually vertical) extent for given axis. */
799 @Override
800 protected void setYBounds(int axis, Bounds bounds) {
801 yBounds.put(axis, bounds);
802 }
803
804
805 /**
806 * Adjusts the axes of a plot. This method sets the <i>labelFont</i> of the
807 * X axis.
808 *
809 * (Duplicate in TimeseriesChartGenerator)
810 *
811 * @param plot The XYPlot of the chart.
812 */
813 protected void adjustAxes(XYPlot plot) {
814 ValueAxis xaxis = plot.getDomainAxis();
815
816 ChartSettings chartSettings = getChartSettings();
817 if (chartSettings == null) {
818 return;
819 }
820
821 Font labelFont = new Font(
822 DEFAULT_FONT_NAME,
823 Font.BOLD,
824 getXAxisLabelFontSize());
825
826 xaxis.setLabelFont(labelFont);
827 xaxis.setTickLabelFont(labelFont);
828 }
829
830
831 /**
832 * This method walks over all axes (domain and range) of <i>plot</i> and
833 * calls localizeDomainAxis() for domain axes or localizeRangeAxis() for
834 * range axes.
835 *
836 * @param plot The XYPlot.
837 */
838 private void localizeAxes(XYPlot plot) {
839 for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) {
840 ValueAxis axis = plot.getDomainAxis(i);
841
842 if (axis != null) {
843 localizeDomainAxis(axis);
844 }
845 else {
846 logger.warn("Domain axis at " + i + " is null.");
847 }
848 }
849
850 for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) {
851 ValueAxis axis = plot.getRangeAxis(i);
852
853 if (axis != null) {
854 localizeRangeAxis(axis);
855 }
856 else {
857 logger.warn("Range axis at " + i + " is null.");
858 }
859 }
860 }
861
862
863 /**
864 * Overrides the NumberFormat with the NumberFormat for the current locale
865 * that is provided by getLocale().
866 *
867 * @param domainAxis The domain axis that needs localization.
868 */
869 protected void localizeDomainAxis(ValueAxis domainAxis) {
870 NumberFormat nf = NumberFormat.getInstance(getLocale());
871 ((NumberAxis) domainAxis).setNumberFormatOverride(nf);
872 }
873
874
875 /**
876 * Overrides the NumberFormat with the NumberFormat for the current locale
877 * that is provided by getLocale().
878 *
879 * @param rangeAxis The domain axis that needs localization.
880 */
881 protected void localizeRangeAxis(ValueAxis rangeAxis) {
882 NumberFormat nf = NumberFormat.getInstance(getLocale());
883 ((NumberAxis) rangeAxis).setNumberFormatOverride(nf);
884 }
885
886
887 /**
888 * Do Points out.
889 */
890 protected void doPoints(
891 Object o,
892 ArtifactAndFacet aandf,
893 ThemeDocument theme,
894 boolean visible,
895 int axisIndex
896 ) {
897 String seriesName = aandf.getFacetDescription();
898 XYSeries series = new StyledXYSeries(seriesName, theme);
899
900 // Add text annotations for single points.
901 List<XYTextAnnotation> xy = new ArrayList<XYTextAnnotation>();
902
903 try {
904 JSONArray points = new JSONArray((String) o);
905 for (int i = 0, P = points.length(); i < P; i++) {
906 JSONArray array = points.getJSONArray(i);
907 double x = array.getDouble(0);
908 double y = array.getDouble(1);
909 String name = array.getString(2);
910 boolean act = array.getBoolean(3);
911 if (!act) {
912 continue;
913 }
914 //logger.debug(" x " + x + " y " + y );
915 series.add(x, y, false);
916 xy.add(new CollisionFreeXYTextAnnotation(name, x, y));
917 }
918 }
919 catch(JSONException e){
920 logger.error("Could not decode json.");
921 }
922
923 RiverAnnotation annotation = new RiverAnnotation(null, null, null, theme);
924 annotation.setTextAnnotations(xy);
925
926 // Do not generate second legend entry. (null was passed for the aand before).
927 if (visible) {
928 annotations.add(annotation);
929 }
930 // doAnnotations(annotations, null, theme, visible);
931 addAxisSeries(series, axisIndex, visible);
932 }
933
934
935 /**
936 * Create a hash from a legenditem.
937 * This hash can then be used to merge legend items labels.
938 * @return hash for given legenditem to identify mergeables.
939 */
940 public static String legendItemHash(LegendItem li) {
941 // TODO Do proper implementation. Ensure that only mergable sets are created.
942 // getFillPaint()
943 // getFillPaintTransformer()
944 // getLabel()
945 // getLine()
946 // getLinePaint()
947 // getLineStroke()
948 // getOutlinePaint()
949 // getOutlineStroke()
950 // Shape getShape()
951 // String getToolTipText()
952 // String getURLText()
953 // boolean isLineVisible()
954 // boolean isShapeFilled()
955 // boolean isShapeOutlineVisible()
956 // boolean isShapeVisible()
957 String hash = li.getLinePaint().toString();
958 String label = li.getLabel();
959 if (label.startsWith("W (") || label.startsWith("W(")) {
960 hash += "-W-";
961 }
962 else if (label.startsWith("Q(") || label.startsWith("Q (")) {
963 hash += "-Q-";
964 }
965
966 // WQ.java holds example of using regex Matcher/Pattern.
967
968 return hash;
969 }
970
971 /** True if x axis has been inverted. */
972 public boolean isInverted() {
973 return inverted;
974 }
975
976
977 /** Set to true if x axis has been inverted. */
978 public void setInverted(boolean inverted) {
979 this.inverted = inverted;
980 }
981
982 /** Add the acutal data to the diagram according to the processors.
983 * For every outable facets, this function is
984 * called and handles the data accordingly. */
985 @Override
986 public void doOut(ArtifactAndFacet bundle, ThemeDocument theme,
987 boolean visible)
988 {
989 String facetName = bundle.getFacetName();
990 Facet facet = bundle.getFacet();
991
992 /* A conservative security check */
993 if (facetName == null || facet == null) {
994 /* Can't happen,.. */
995 logger.error("doOut called with null facet.");
996 }
997
998 logger.debug("DoOut for facet: " + facetName);
999
1000 /* TODO Here should the configured processors come into play */
1001 List<Processor> processors = new ArrayList<Processor>();
1002 processors.add(new WOutProcessor());
1003 processors.add(new QOutProcessor());
1004 processors.add(new BedheightProcessor());
1005 processors.add(new BedDiffYearProcessor());
1006 processors.add(new BedDiffHeightYearProcessor());
1007
1008 for (Processor pr: processors) {
1009 if (pr.canHandle(facetName)) {
1010 // pr.doOut(this, bundle, theme, visible, 0);
1011 }
1012 }
1013 }
1014
1015
1016 }

http://dive4elements.wald.intevation.org