Mercurial > dive4elements > river
comparison flys-artifacts/src/main/java/de/intevation/flys/exports/XYChartGenerator.java @ 2242:7e8e1d5384c0
Further refactoring of XYChartGenerator / ChartGenerator with the result, that timerange charts are now able to display lines.
flys-artifacts/trunk@3890 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Ingo Weinzierl <ingo.weinzierl@intevation.de> |
---|---|
date | Fri, 03 Feb 2012 09:39:22 +0000 |
parents | 23c7c51df772 |
children | 99ef93ce18bd |
comparison
equal
deleted
inserted
replaced
2241:2b232871ba28 | 2242:7e8e1d5384c0 |
---|---|
3 import java.awt.BasicStroke; | 3 import java.awt.BasicStroke; |
4 import java.awt.Color; | 4 import java.awt.Color; |
5 import java.awt.Font; | 5 import java.awt.Font; |
6 import java.awt.Paint; | 6 import java.awt.Paint; |
7 import java.awt.Stroke; | 7 import java.awt.Stroke; |
8 import java.awt.TexturePaint; | |
9 | |
10 import java.awt.geom.Rectangle2D; | |
11 | |
12 import java.awt.image.BufferedImage; | |
13 | |
14 | 8 |
15 import java.text.NumberFormat; | 9 import java.text.NumberFormat; |
16 | 10 |
17 import java.util.ArrayList; | 11 import java.util.ArrayList; |
18 import java.util.HashMap; | 12 import java.util.HashMap; |
19 import java.util.TreeMap; | |
20 import java.util.List; | 13 import java.util.List; |
21 import java.util.Map; | 14 import java.util.Map; |
22 import java.util.SortedMap; | |
23 | 15 |
24 import org.w3c.dom.Document; | 16 import org.w3c.dom.Document; |
25 | 17 |
26 import org.apache.log4j.Logger; | 18 import org.apache.log4j.Logger; |
27 | 19 |
28 import org.jfree.chart.ChartFactory; | 20 import org.jfree.chart.ChartFactory; |
29 import org.jfree.chart.JFreeChart; | 21 import org.jfree.chart.JFreeChart; |
30 import org.jfree.chart.LegendItem; | |
31 import org.jfree.chart.LegendItemCollection; | 22 import org.jfree.chart.LegendItemCollection; |
32 import org.jfree.chart.annotations.XYBoxAnnotation; | 23 import org.jfree.chart.annotations.XYBoxAnnotation; |
33 import org.jfree.chart.annotations.XYLineAnnotation; | 24 import org.jfree.chart.annotations.XYLineAnnotation; |
34 import org.jfree.chart.annotations.XYTextAnnotation; | 25 import org.jfree.chart.annotations.XYTextAnnotation; |
35 import org.jfree.chart.axis.NumberAxis; | 26 import org.jfree.chart.axis.NumberAxis; |
36 import org.jfree.chart.axis.ValueAxis; | 27 import org.jfree.chart.axis.ValueAxis; |
37 import org.jfree.chart.plot.PlotOrientation; | 28 import org.jfree.chart.plot.PlotOrientation; |
38 import org.jfree.chart.plot.XYPlot; | 29 import org.jfree.chart.plot.XYPlot; |
39 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; | |
40 import org.jfree.data.Range; | 30 import org.jfree.data.Range; |
31 import org.jfree.data.general.Series; | |
41 import org.jfree.data.xy.XYSeries; | 32 import org.jfree.data.xy.XYSeries; |
42 import org.jfree.data.xy.XYSeriesCollection; | 33 import org.jfree.data.xy.XYSeriesCollection; |
43 import org.jfree.data.xy.XYDataset; | 34 import org.jfree.data.xy.XYDataset; |
44 | 35 |
45 import org.jfree.ui.RectangleInsets; | 36 import org.jfree.ui.RectangleInsets; |
46 import org.jfree.ui.TextAnchor; | 37 import org.jfree.ui.TextAnchor; |
47 | 38 |
48 | |
49 import de.intevation.artifactdatabase.state.Facet; | 39 import de.intevation.artifactdatabase.state.Facet; |
50 | 40 |
51 | |
52 import de.intevation.flys.jfree.EnhancedLineAndShapeRenderer; | |
53 import de.intevation.flys.jfree.FLYSAnnotation; | 41 import de.intevation.flys.jfree.FLYSAnnotation; |
54 import de.intevation.flys.jfree.StableXYDifferenceRenderer; | |
55 import de.intevation.flys.jfree.StickyAxisAnnotation; | 42 import de.intevation.flys.jfree.StickyAxisAnnotation; |
56 import de.intevation.flys.jfree.CollisionFreeXYTextAnnotation; | 43 import de.intevation.flys.jfree.CollisionFreeXYTextAnnotation; |
57 import de.intevation.flys.jfree.StyledAreaSeriesCollection; | 44 import de.intevation.flys.jfree.StyledAreaSeriesCollection; |
58 import de.intevation.flys.jfree.StyledXYSeries; | 45 import de.intevation.flys.jfree.StyledXYSeries; |
59 | 46 |
124 /** Add a dataset, include its range. */ | 111 /** Add a dataset, include its range. */ |
125 public void addDataset(XYSeries series) { | 112 public void addDataset(XYSeries series) { |
126 addDataset(new XYSeriesCollection(series)); | 113 addDataset(new XYSeriesCollection(series)); |
127 } | 114 } |
128 | 115 |
116 | |
117 @Override | |
118 public void setRange(Range range) { | |
119 this.range = range; | |
120 } | |
121 | |
122 | |
123 @Override | |
124 public Range getRange() { | |
125 return range; | |
126 } | |
127 | |
128 | |
129 @Override | |
130 public XYDataset[] getDatasets() { | |
131 return (XYDataset[]) | |
132 datasets.toArray(new XYDataset[datasets.size()]); | |
133 } | |
134 | |
129 public void addArea(StyledAreaSeriesCollection series) { | 135 public void addArea(StyledAreaSeriesCollection series) { |
130 this.datasets.add(series); | 136 this.datasets.add(series); |
131 } | 137 } |
132 | 138 |
133 /** True if to be renedered as area. */ | 139 /** True if to be renedered as area. */ |
134 public boolean isArea(XYSeriesCollection series) { | 140 @Override |
141 public boolean isArea(XYDataset series) { | |
135 return (series instanceof StyledAreaSeriesCollection); | 142 return (series instanceof StyledAreaSeriesCollection); |
136 } | 143 } |
137 | 144 |
138 /** Adjust range to include given dataset. */ | 145 /** Adjust range to include given dataset. */ |
139 public void includeYRange(XYSeries dataset) { | 146 public void includeYRange(XYSeries dataset) { |
140 mergeRanges(new Range(dataset.getMinY(), dataset.getMaxY())); | 147 mergeRanges(new Range(dataset.getMinY(), dataset.getMaxY())); |
141 } | 148 } |
142 | 149 |
143 /** True if no datasets given. */ | 150 /** True if no datasets given. */ |
151 @Override | |
144 public boolean isEmpty() { | 152 public boolean isEmpty() { |
145 return this.datasets.isEmpty(); | 153 return this.datasets.isEmpty(); |
146 } | 154 } |
147 | 155 |
148 /** Set the 'real' axis index that this axis is mapped to. */ | 156 /** Set the 'real' axis index that this axis is mapped to. */ |
157 @Override | |
149 public void setPlotAxisIndex(int axisIndex) { | 158 public void setPlotAxisIndex(int axisIndex) { |
150 this.plotAxisIndex = axisIndex; | 159 this.plotAxisIndex = axisIndex; |
151 } | 160 } |
152 | 161 |
153 /** Get the 'real' axis index that this axis is mapped to. */ | 162 /** Get the 'real' axis index that this axis is mapped to. */ |
163 @Override | |
154 public int getPlotAxisIndex() { | 164 public int getPlotAxisIndex() { |
155 return this.plotAxisIndex; | 165 return this.plotAxisIndex; |
156 } | 166 } |
157 } // class AxisDataset | 167 } // class AxisDataset |
158 | 168 |
235 | 245 |
236 // These have to go after the autozoom. | 246 // These have to go after the autozoom. |
237 addAnnotationsToRenderer(plot); | 247 addAnnotationsToRenderer(plot); |
238 | 248 |
239 return chart; | 249 return chart; |
250 } | |
251 | |
252 | |
253 @Override | |
254 protected Series getSeriesOf(XYDataset dataset, int idx) { | |
255 return ((XYSeriesCollection) dataset).getSeries(idx); | |
256 } | |
257 | |
258 | |
259 @Override | |
260 protected void setXRange(int axis, Range range) { | |
261 xRanges.put(Integer.valueOf(axis), range); | |
262 } | |
263 | |
264 | |
265 @Override | |
266 protected void setYRange(int axis, Range range) { | |
267 yRanges.put(Integer.valueOf(axis), range); | |
240 } | 268 } |
241 | 269 |
242 | 270 |
243 @Override | 271 @Override |
244 protected AxisDataset createAxisDataset(int idx) { | 272 protected AxisDataset createAxisDataset(int idx) { |
286 logger.debug("..............."); | 314 logger.debug("..............."); |
287 } | 315 } |
288 | 316 |
289 | 317 |
290 /** | 318 /** |
291 * Add datasets to plot. | |
292 * @param plot plot to add datasets to. | |
293 */ | |
294 protected void addDatasets(XYPlot plot) { | |
295 // AxisDatasets are sorted, but some might be empty. | |
296 // Thus, generate numbering on the fly. | |
297 int axisIndex = 0; | |
298 int datasetIndex = 0; | |
299 for (Map.Entry<Integer, AxisDataset> entry: datasets.entrySet()) { | |
300 if (!entry.getValue().isEmpty()) { | |
301 // Add axis and range information. | |
302 XYAxisDataset axisDataset = (XYAxisDataset) entry.getValue(); | |
303 NumberAxis axis = createYAxis(entry.getKey()); | |
304 | |
305 plot.setRangeAxis(axisIndex, axis); | |
306 if (axis.getAutoRangeIncludesZero()) { | |
307 axisDataset.range = Range.expandToInclude(axisDataset.range, 0d); | |
308 } | |
309 yRanges.put(axisIndex, expandPointRange(axisDataset.range)); | |
310 | |
311 // Add contained datasets, mapping to axis. | |
312 for (XYDataset dataset: axisDataset.datasets) { | |
313 plot.setDataset(datasetIndex, dataset); | |
314 plot.mapDatasetToRangeAxis(datasetIndex, axisIndex); | |
315 applyThemes(plot, (XYSeriesCollection) dataset, | |
316 datasetIndex, | |
317 axisDataset.isArea((XYSeriesCollection)dataset)); | |
318 datasetIndex++; | |
319 } | |
320 axisDataset.setPlotAxisIndex(axisIndex); | |
321 axisIndex++; | |
322 } | |
323 } | |
324 } | |
325 | |
326 | |
327 /** | |
328 * Registers an area to be drawn. | 319 * Registers an area to be drawn. |
329 * @param area Area to be drawn. | 320 * @param area Area to be drawn. |
330 * @param index 'axis index' | 321 * @param index 'axis index' |
331 * @param visible Whether or not to be visible (important for range calculations). | 322 * @param visible Whether or not to be visible (important for range calculations). |
332 */ | 323 */ |
354 * @param series the dataseries to include in plot. | 345 * @param series the dataseries to include in plot. |
355 * @param index ('symbolic') index of the series and of its axis. | 346 * @param index ('symbolic') index of the series and of its axis. |
356 * @param visible whether or not the data should be plotted. | 347 * @param visible whether or not the data should be plotted. |
357 */ | 348 */ |
358 public void addAxisSeries(XYSeries series, int index, boolean visible) { | 349 public void addAxisSeries(XYSeries series, int index, boolean visible) { |
359 addAxisDataset(new XYSeriesCollection(series), index, visible); | |
360 | |
361 if (series == null) { | 350 if (series == null) { |
362 return; | 351 return; |
363 } | 352 } |
353 | |
354 logger.debug("Y Range of XYSeries: " + | |
355 series.getMinY() + " | " + series.getMaxY()); | |
356 | |
357 addAxisDataset(new XYSeriesCollection(series), index, visible); | |
364 | 358 |
365 XYAxisDataset axisDataset = (XYAxisDataset) getAxisDataset(index); | 359 XYAxisDataset axisDataset = (XYAxisDataset) getAxisDataset(index); |
366 | 360 |
367 if (!visible) { | 361 if (!visible) { |
368 // Do this also when not visible to have axis scaled by default such | 362 // Do this also when not visible to have axis scaled by default such |
421 } | 415 } |
422 } | 416 } |
423 | 417 |
424 | 418 |
425 /** | 419 /** |
426 * Expands a given range if it collapses into one point. | |
427 * @param Range to be expanded if upper == lower bound. | |
428 */ | |
429 private Range expandPointRange(Range range) { | |
430 if (range != null && range.getLowerBound() == range.getUpperBound()) { | |
431 return expandRange(range, 5); | |
432 } | |
433 return range; | |
434 } | |
435 | |
436 | |
437 /** | |
438 * Expands X axes if only a point is shown. | 420 * Expands X axes if only a point is shown. |
439 */ | 421 */ |
440 private void preparePointRanges(XYPlot plot) { | 422 private void preparePointRanges(XYPlot plot) { |
441 for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) { | 423 for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) { |
442 logger.debug("Check whether to expand a x axis."); | 424 logger.debug("Check whether to expand a x axis."); |
443 Integer key = Integer.valueOf(i); | 425 Integer key = Integer.valueOf(i); |
444 | 426 |
445 Range r = xRanges.get(key); | 427 Range r = xRanges.get(key); |
446 if (r != null && r.getLowerBound() == r.getUpperBound()) { | 428 if (r != null && r.getLowerBound() == r.getUpperBound()) { |
447 xRanges.put(key, expandRange(r, 5)); | 429 setXRange(key, ChartHelper.expandRange(r, 5)); |
448 } | 430 } |
449 } | 431 } |
450 } | |
451 | |
452 | |
453 /** | |
454 * Expand range by percent. | |
455 */ | |
456 public static Range expandRange(Range range, double percent) { | |
457 if (range == null) { | |
458 return null; | |
459 } | |
460 | |
461 double value = range.getLowerBound(); | |
462 double expand = Math.abs(value / 100 * percent); | |
463 | |
464 return expand != 0 | |
465 ? new Range(value-expand, value+expand) | |
466 : new Range(-0.01 * percent, 0.01 * percent); | |
467 } | 432 } |
468 | 433 |
469 | 434 |
470 /** | 435 /** |
471 * This method zooms the plot to the specified ranges in the attribute | 436 * This method zooms the plot to the specified ranges in the attribute |
920 ((NumberAxis) rangeAxis).setNumberFormatOverride(nf); | 885 ((NumberAxis) rangeAxis).setNumberFormatOverride(nf); |
921 } | 886 } |
922 | 887 |
923 | 888 |
924 /** | 889 /** |
925 * @param idx "index" of dataset/series (first dataset to be drawn has | |
926 * index 0), correlates with renderer index. | |
927 * @param isArea true if the series describes an area and shall be rendered | |
928 * as such. | |
929 * @return idx increased by number of items addded. | |
930 */ | |
931 protected int applyThemes( | |
932 XYPlot plot, | |
933 XYSeriesCollection series, | |
934 int idx, | |
935 boolean isArea | |
936 ) { | |
937 LegendItemCollection lic = new LegendItemCollection(); | |
938 LegendItemCollection anno = plot.getFixedLegendItems(); | |
939 | |
940 Font legendFont = createLegendLabelFont(); | |
941 | |
942 int retidx = idx; | |
943 | |
944 if (isArea) { | |
945 logger.debug("Registering an 'area'renderer at idx: " + idx); | |
946 StyledAreaSeriesCollection area = (StyledAreaSeriesCollection) series; | |
947 | |
948 StableXYDifferenceRenderer dRenderer = new StableXYDifferenceRenderer(); | |
949 if (area.getMode() == StyledAreaSeriesCollection.FILL_MODE.UNDER) { | |
950 dRenderer.setPositivePaint(createTransparentPaint()); | |
951 } | |
952 plot.setRenderer(idx, dRenderer); | |
953 | |
954 area.applyTheme(dRenderer); | |
955 | |
956 LegendItem legendItem = dRenderer.getLegendItem(idx, 0); | |
957 if (legendItem != null) { | |
958 legendItem.setLabelFont(legendFont); | |
959 lic.add(legendItem); | |
960 } | |
961 else { | |
962 logger.warn("Could not get LegentItem for renderer: " | |
963 + idx + ", series-idx " + 0); | |
964 } | |
965 if (anno != null) { | |
966 lic.addAll(anno); | |
967 } | |
968 plot.setFixedLegendItems(lic); | |
969 return retidx + 1; | |
970 } | |
971 | |
972 XYLineAndShapeRenderer renderer = getRenderer(plot, idx); | |
973 | |
974 for (int s = 0, num = series.getSeriesCount(); s < num; s++) { | |
975 XYSeries serie = series.getSeries(s); | |
976 | |
977 if (serie instanceof StyledXYSeries) { | |
978 ((StyledXYSeries) serie).applyTheme(renderer, s); | |
979 } | |
980 | |
981 // special case: if there is just one single item, we need to enable | |
982 // points for this series, otherwise we would not see anything in | |
983 // the chart area. | |
984 if (serie.getItemCount() == 1) { | |
985 renderer.setSeriesShapesVisible(s, true); | |
986 } | |
987 | |
988 LegendItem legendItem = renderer.getLegendItem(idx, s); | |
989 if (legendItem != null) { | |
990 legendItem.setLabelFont(legendFont); | |
991 lic.add(legendItem); | |
992 } | |
993 else { | |
994 logger.warn("Could not get LegentItem for renderer: " | |
995 + idx + ", series-idx " + s); | |
996 } | |
997 // TODO: why that? isnt renderer set per dataset not per series? | |
998 retidx++; | |
999 } | |
1000 | |
1001 if (anno != null) { | |
1002 lic.addAll(anno); | |
1003 } | |
1004 | |
1005 plot.setFixedLegendItems(lic); | |
1006 | |
1007 plot.setRenderer(idx, renderer); | |
1008 | |
1009 return retidx; | |
1010 } | |
1011 | |
1012 | |
1013 /** Returns a transparently textured paint. */ | |
1014 // TODO why not use a transparent color? | |
1015 protected static Paint createTransparentPaint() { | |
1016 BufferedImage texture = new BufferedImage( | |
1017 1, 1, BufferedImage.TYPE_4BYTE_ABGR); | |
1018 | |
1019 return new TexturePaint( | |
1020 texture, new Rectangle2D.Double(0d, 0d, 0d, 0d)); | |
1021 } | |
1022 | |
1023 | |
1024 /** | |
1025 * Returns a new instance of EnhancedLineAndShapeRenderer always. | |
1026 */ | |
1027 protected XYLineAndShapeRenderer getRenderer(XYPlot plot, int idx) { | |
1028 logger.debug("getRenderer: " + idx); | |
1029 | |
1030 EnhancedLineAndShapeRenderer r = | |
1031 new EnhancedLineAndShapeRenderer(true, false); | |
1032 | |
1033 r.setPlot(plot); | |
1034 | |
1035 return r; | |
1036 } | |
1037 | |
1038 | |
1039 /** | |
1040 * Register annotations like MainValues for later plotting | 890 * Register annotations like MainValues for later plotting |
1041 * | 891 * |
1042 * @param o list of annotations (data of facet). | 892 * @param o list of annotations (data of facet). |
1043 * @param facet The facet. This facet does NOT support any data objects. Use | 893 * @param facet The facet. This facet does NOT support any data objects. Use |
1044 * FLYSArtifact.getNativeFacet() instead to retrieve a Facet which supports | 894 * FLYSArtifact.getNativeFacet() instead to retrieve a Facet which supports |