Mercurial > dive4elements > river
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) { |