comparison flys-artifacts/src/main/java/de/intevation/flys/exports/XYChartGenerator.java @ 1931:7c52e9cb2a72

Allow more than two datasets and more flexibility with axes in plots. Based on patch by S. Teichmann. flys-artifacts/trunk@3312 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Felix Wolfsteller <felix.wolfsteller@intevation.de>
date Thu, 24 Nov 2011 07:20:46 +0000
parents de0c2bbb27f9
children 17e18948fe5e
comparison
equal deleted inserted replaced
1930:de0c2bbb27f9 1931:7c52e9cb2a72
8 8
9 import java.text.NumberFormat; 9 import java.text.NumberFormat;
10 10
11 import java.util.ArrayList; 11 import java.util.ArrayList;
12 import java.util.HashMap; 12 import java.util.HashMap;
13 import java.util.TreeMap;
13 import java.util.List; 14 import java.util.List;
14 import java.util.Map; 15 import java.util.Map;
16 import java.util.SortedMap;
15 17
16 import org.w3c.dom.Document; 18 import org.w3c.dom.Document;
17 19
18 import org.apache.log4j.Logger; 20 import org.apache.log4j.Logger;
19 21
29 import org.jfree.chart.renderer.xy.XYItemRenderer; 31 import org.jfree.chart.renderer.xy.XYItemRenderer;
30 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; 32 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
31 import org.jfree.data.Range; 33 import org.jfree.data.Range;
32 import org.jfree.data.xy.XYSeries; 34 import org.jfree.data.xy.XYSeries;
33 import org.jfree.data.xy.XYSeriesCollection; 35 import org.jfree.data.xy.XYSeriesCollection;
36 import org.jfree.data.xy.XYDataset;
34 37
35 import org.jfree.ui.RectangleInsets; 38 import org.jfree.ui.RectangleInsets;
36 39
37 import de.intevation.artifactdatabase.state.Facet; 40 import de.intevation.artifactdatabase.state.Facet;
38 41
50 public abstract class XYChartGenerator extends ChartGenerator { 53 public abstract class XYChartGenerator extends ChartGenerator {
51 54
52 /** The logger that is used in this generator. */ 55 /** The logger that is used in this generator. */
53 private static Logger logger = Logger.getLogger(XYChartGenerator.class); 56 private static Logger logger = Logger.getLogger(XYChartGenerator.class);
54 57
55 /** SeriesCollection used for the first axis. */ 58 /** Map of datasets ("index"). */
56 protected XYSeriesCollection first; 59 protected SortedMap<Integer, List<XYDataset>> datasets;
57
58 /** SeriesCollection used for the second axis. */
59 protected XYSeriesCollection second;
60 60
61 /** List of annotations to insert in plot. */ 61 /** List of annotations to insert in plot. */
62 protected List<FLYSAnnotation> annotations; 62 protected List<FLYSAnnotation> annotations;
63 63
64 /** The max X range to include all X values of all series for each axis. */ 64 /** The max X range to include all X values of all series for each axis. */
67 /** The max Y range to include all Y values of all series for each axis. */ 67 /** The max Y range to include all Y values of all series for each axis. */
68 protected Map<Integer, Range> yRanges; 68 protected Map<Integer, Range> yRanges;
69 69
70 public static final Color DEFAULT_GRID_COLOR = Color.GRAY; 70 public static final Color DEFAULT_GRID_COLOR = Color.GRAY;
71 public static final float DEFAULT_GRID_LINE_WIDTH = 0.3f; 71 public static final float DEFAULT_GRID_LINE_WIDTH = 0.3f;
72
73
74 public XYChartGenerator() {
75 xRanges = new HashMap<Integer, Range>();
76 yRanges = new HashMap<Integer, Range>();
77 datasets = new TreeMap<Integer, List<XYDataset>>();
78 }
72 79
73 80
74 /** 81 /**
75 * Returns the title of a chart. 82 * Returns the title of a chart.
76 * 83 *
155 PlotOrientation.VERTICAL, 162 PlotOrientation.VERTICAL,
156 true, 163 true,
157 false, 164 false,
158 false); 165 false);
159 166
167 XYPlot plot = (XYPlot) chart.getPlot();
160 chart.setBackgroundPaint(Color.WHITE); 168 chart.setBackgroundPaint(Color.WHITE);
161 chart.getPlot().setBackgroundPaint(Color.WHITE); 169 plot.setBackgroundPaint(Color.WHITE);
162
163 XYPlot plot = (XYPlot) chart.getPlot();
164 170
165 addDatasets(plot); 171 addDatasets(plot);
166 addAnnotations(plot); 172 addAnnotations(plot);
167 addSubtitles(chart); 173 addSubtitles(chart);
168 adjustPlot(plot); 174 adjustPlot(plot);
169 localizeAxes(plot); 175 localizeAxes(plot);
170 176
171 removeEmptyRangeAxes(plot); 177 removeEmptyRangeAxes(plot);
178 createAxes(plot);
172 adjustAxes(plot); 179 adjustAxes(plot);
173 180
174 preparePointRanges(plot); 181 preparePointRanges(plot);
175 autoZoom(plot); 182 autoZoom(plot);
176 183
179 return chart; 186 return chart;
180 } 187 }
181 188
182 189
183 /** 190 /**
184 * Add first and second dataset to plot. 191 * Add datasets to plot.
185 * @param plot plot to add datasets to. 192 * @param plot plot to add datasets to.
186 */ 193 */
187 protected void addDatasets(XYPlot plot) { 194 protected void addDatasets(XYPlot plot) {
188 if (first != null) { 195 int count = 0;
189 logger.debug("Set the first axis dataset."); 196 for (Map.Entry<Integer, List<XYDataset>> entry: datasets.entrySet()) {
190 plot.setDataset(0, first); 197 List<Integer> axisList = new ArrayList<Integer>(1);
191 } 198 axisList.add(entry.getKey());
192 if (second != null) { 199 for (XYDataset dataset: entry.getValue()) {
193 logger.debug("Set the second axis dataset."); 200 int index = count++;
194 plot.setDataset(1, second); 201 plot.setDataset(index, dataset);
195 } 202 plot.mapDatasetToRangeAxes(index, axisList);
196 } 203 }
197 204 }
198 205 }
199 public void addFirstAxisSeries(XYSeries series, boolean visible) { 206
200 if (first == null) { 207
201 first = new XYSeriesCollection(); 208 /**
202 } 209 * Add given series.
203 210 * @param series the dataseries to include in plot.
204 if (series != null) { 211 * @param index index of the series and of its axis.
205 if (visible) { 212 * @param visible whether or not the data should be plotted.
206 first.addSeries(series); 213 */
207 } 214 public void addAxisSeries(XYSeries series, int index, boolean visible) {
208 215 if (series == null) {
209 combineYRanges(new Range(series.getMinY(), series.getMaxY()), 0); 216 return;
210 combineXRanges(new Range(series.getMinX(), series.getMaxX()), 0); 217 }
211 } 218
212 } 219 if (visible) {
213 220 XYSeriesCollection collection = new XYSeriesCollection(series);
214 221
215 public void addSecondAxisSeries(XYSeries series, boolean visible) { 222 List<XYDataset> dataset = datasets.get(index);
216 if (second == null) { 223
217 second = new XYSeriesCollection(); 224 if (dataset == null) {
218 } 225 dataset = new ArrayList<XYDataset>();
219 226 datasets.put(index, dataset);
220 if (series != null) { 227 }
221 if (visible) { 228
222 second.addSeries(series); 229 dataset.add(collection);
223 } 230 }
224 231
225 combineYRanges(new Range(series.getMinY(), series.getMaxY()), 1); 232 // Do this also when not visible to have axis scaled by default such
226 combineXRanges(new Range(series.getMinX(), series.getMaxX()), 0); 233 // that every data-point could be seen (except for annotations).
227 } 234 combineXRanges(new Range(series.getMinX(), series.getMaxX()), 0);
228 } 235 combineYRanges(new Range(series.getMinY(), series.getMaxY()), index);
229 236 }
230 237
238 /**
239 * Effect: extend range of x axis to include given limits.
240 * @param range the given ("minimal") range.
241 * @param index index of axis to be merged.
242 */
231 private void combineXRanges(Range range, int index) { 243 private void combineXRanges(Range range, int index) {
232 Integer key = Integer.valueOf(index); 244
233 245 Range old = xRanges.get(index);
234 if (xRanges == null) { 246
235 xRanges = new HashMap<Integer, Range>(); 247 if (old != null) {
236 xRanges.put(key, range); 248 range = Range.combine(old, range);
237 return; 249 }
238 } 250
239 251 xRanges.put(index, range);
240 Range newX = null;
241 Range oldX = xRanges.get(key);
242
243 if (oldX != null) {
244 newX = Range.combine(oldX, range);
245 }
246 else {
247 newX = range;
248 }
249
250 xRanges.put(key, newX);
251 } 252 }
252 253
253 254
254 /** 255 /**
255 * @param range the new range. 256 * @param range the new range.
256 */ 257 */
257 private void combineYRanges(Range range, int index) { 258 private void combineYRanges(Range range, int index) {
258 Integer key = Integer.valueOf(index); 259
259 260 Range old = yRanges.get(index);
260 if (yRanges == null) { 261
261 yRanges = new HashMap<Integer, Range>(); 262 if (old != null) {
262 yRanges.put(key, range); 263 range = Range.combine(old, range);
263 return; 264 }
264 } 265
265 266 yRanges.put(index, range);
266 Range newY = null;
267 Range oldY = yRanges.get(key);
268
269 if (oldY != null) {
270 newY = Range.combine(oldY, range);
271 }
272 else {
273 newY = range;
274 }
275
276 yRanges.put(key, newY);
277 } 267 }
278 268
279 269
280 /** 270 /**
281 * Adds annotations to list (if visible is true). 271 * Adds annotations to list (if visible is true).
291 281
292 annotations.add(annotation); 282 annotations.add(annotation);
293 } 283 }
294 284
295 285
286 /**
287 * Create y-axes.
288 */
289 public void createAxes(XYPlot plot) {
290 logger.debug("XYChartGenerator.createAxes");
291 Integer last = datasets.lastKey();
292
293 if (last != null) {
294 for (int i = last; i >= 0; --i) {
295 if (datasets.containsKey(i)) {
296 plot.setRangeAxis(i, createYAxis(i));
297 }
298 }
299 }
300 }
301
302 /**
303 * Create Y (range) axis for given index.
304 * Shall be overriden by subclasses.
305 */
306 protected NumberAxis createYAxis(int index) {
307 NumberAxis axis = new NumberAxis("default axis");
308 return axis;
309 }
310
296 private void removeEmptyRangeAxes(XYPlot plot) { 311 private void removeEmptyRangeAxes(XYPlot plot) {
297 if (first == null) { 312 Integer last = datasets.lastKey();
298 plot.setRangeAxis(0, null); 313
299 } 314 if (last != null) {
300 315 for (int i = last-1; i >= 0; --i) {
301 if (second == null) { 316 if (!datasets.containsKey(i)) {
302 plot.setRangeAxis(1, null); 317 plot.setRangeAxis(i, null);
303 } 318 }
304 } 319 }
305 320 }
306 321 }
322
323
324 /**
325 * Expands X and Y axes if only a point is shown.
326 */
307 private void preparePointRanges(XYPlot plot) { 327 private void preparePointRanges(XYPlot plot) {
308 for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) { 328 for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) {
309 Integer key = Integer.valueOf(i); 329 Integer key = Integer.valueOf(i);
310 330
311 Range r = xRanges.get(key); 331 Range r = xRanges.get(key);
323 } 343 }
324 } 344 }
325 } 345 }
326 346
327 347
348 /**
349 * Expand range by percent.
350 */
328 public static Range expandRange(Range range, double percent) { 351 public static Range expandRange(Range range, double percent) {
329 if (range == null) { 352 if (range == null) {
330 return null; 353 return null;
331 } 354 }
332 355
477 } 500 }
478 } 501 }
479 502
480 503
481 /** 504 /**
482 * Adjusts the axes of a plot. 505 * Adjusts the axes of a plot (the first axis does not include zero).
483 * 506 *
484 * @param plot The XYPlot of the chart. 507 * @param plot The XYPlot of the chart.
485 */ 508 */
486 protected void adjustAxes(XYPlot plot) { 509 protected void adjustAxes(XYPlot plot) {
487 NumberAxis yAxis = (NumberAxis) plot.getRangeAxis(); 510 NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
488 511 if (yAxis == null) {
489 yAxis.setAutoRangeIncludesZero(false); 512 logger.warn("No Axis to setAutoRangeIncludeZero.");
513 }
514 else {
515 yAxis.setAutoRangeIncludesZero(false);
516 }
490 } 517 }
491 518
492 519
493 protected void adjustPlot(XYPlot plot) { 520 protected void adjustPlot(XYPlot plot) {
494 Stroke gridStroke = new BasicStroke( 521 Stroke gridStroke = new BasicStroke(
506 plot.setRangeGridlineStroke(gridStroke); 533 plot.setRangeGridlineStroke(gridStroke);
507 plot.setRangeGridlinePaint(DEFAULT_GRID_COLOR); 534 plot.setRangeGridlinePaint(DEFAULT_GRID_COLOR);
508 plot.setRangeGridlinesVisible(true); 535 plot.setRangeGridlinesVisible(true);
509 536
510 plot.setAxisOffset(new RectangleInsets(0d, 0d, 0d, 0d)); 537 plot.setAxisOffset(new RectangleInsets(0d, 0d, 0d, 0d));
511
512 if (plot.getDataset(0) != null) {
513 plot.mapDatasetToRangeAxis(0, 0);
514 }
515
516 if (plot.getDataset(1) != null) {
517 plot.mapDatasetToRangeAxis(1, 1);
518 }
519 } 538 }
520 539
521 540
522 protected void addSubtitles(JFreeChart chart) { 541 protected void addSubtitles(JFreeChart chart) {
523 // override this method in subclasses that need subtitles 542 // override this method in subclasses that need subtitles
579 ((NumberAxis) rangeAxis).setNumberFormatOverride(nf); 598 ((NumberAxis) rangeAxis).setNumberFormatOverride(nf);
580 } 599 }
581 600
582 601
583 protected void applyThemes(XYPlot plot) { 602 protected void applyThemes(XYPlot plot) {
584 if (first != null) { 603
585 applyThemes(plot, first, 0); 604 for (Map.Entry<Integer, List<XYDataset>> entry: datasets.entrySet()) {
586 } 605 int axis = entry.getKey();
587 606 for (XYDataset dataset: entry.getValue()) {
588 if (second != null) { 607 if (dataset instanceof XYSeriesCollection) {
589 applyThemes(plot, second, 1); 608 applyThemes(plot, (XYSeriesCollection)dataset, axis);
609 }
610 }
590 } 611 }
591 } 612 }
592 613
593 614
594 protected void applyThemes(XYPlot plot, XYSeriesCollection dataset, int i) { 615 protected void applyThemes(XYPlot plot, XYSeriesCollection dataset, int i) {

http://dive4elements.wald.intevation.org