Mercurial > dive4elements > river
comparison flys-artifacts/src/main/java/de/intevation/flys/exports/ChartGenerator.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 | 6aeb71517136 |
comparison
equal
deleted
inserted
replaced
2241:2b232871ba28 | 2242:7e8e1d5384c0 |
---|---|
1 package de.intevation.flys.exports; | 1 package de.intevation.flys.exports; |
2 | 2 |
3 import java.awt.Color; | 3 import java.awt.Color; |
4 import java.awt.Font; | 4 import java.awt.Font; |
5 import java.awt.Paint; | |
6 import java.awt.TexturePaint; | |
7 import java.awt.geom.Rectangle2D; | |
8 import java.awt.image.BufferedImage; | |
5 | 9 |
6 import java.io.IOException; | 10 import java.io.IOException; |
7 import java.io.OutputStream; | 11 import java.io.OutputStream; |
8 | 12 |
9 import java.util.ArrayList; | 13 import java.util.ArrayList; |
10 import java.util.List; | 14 import java.util.List; |
11 import java.util.Locale; | 15 import java.util.Locale; |
16 import java.util.Map; | |
12 import java.util.TreeMap; | 17 import java.util.TreeMap; |
13 import java.util.SortedMap; | 18 import java.util.SortedMap; |
14 | 19 |
15 import javax.xml.xpath.XPathConstants; | 20 import javax.xml.xpath.XPathConstants; |
16 | 21 |
19 import org.w3c.dom.Document; | 24 import org.w3c.dom.Document; |
20 import org.w3c.dom.Element; | 25 import org.w3c.dom.Element; |
21 | 26 |
22 import org.jfree.chart.JFreeChart; | 27 import org.jfree.chart.JFreeChart; |
23 import org.jfree.chart.LegendItem; | 28 import org.jfree.chart.LegendItem; |
29 import org.jfree.chart.LegendItemCollection; | |
24 import org.jfree.chart.axis.NumberAxis; | 30 import org.jfree.chart.axis.NumberAxis; |
31 import org.jfree.chart.plot.XYPlot; | |
32 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; | |
25 import org.jfree.data.Range; | 33 import org.jfree.data.Range; |
34 import org.jfree.data.general.Series; | |
26 import org.jfree.data.xy.XYDataset; | 35 import org.jfree.data.xy.XYDataset; |
27 | 36 |
28 import de.intevation.artifacts.Artifact; | 37 import de.intevation.artifacts.Artifact; |
29 import de.intevation.artifacts.CallContext; | 38 import de.intevation.artifacts.CallContext; |
30 import de.intevation.artifacts.CallMeta; | 39 import de.intevation.artifacts.CallMeta; |
38 | 47 |
39 import de.intevation.flys.model.River; | 48 import de.intevation.flys.model.River; |
40 | 49 |
41 import de.intevation.flys.artifacts.FLYSArtifact; | 50 import de.intevation.flys.artifacts.FLYSArtifact; |
42 import de.intevation.flys.artifacts.resources.Resources; | 51 import de.intevation.flys.artifacts.resources.Resources; |
52 import de.intevation.flys.jfree.EnhancedLineAndShapeRenderer; | |
53 import de.intevation.flys.jfree.StableXYDifferenceRenderer; | |
54 import de.intevation.flys.jfree.StyledAreaSeriesCollection; | |
55 import de.intevation.flys.jfree.StyledXYSeries; | |
43 import de.intevation.flys.utils.FLYSUtils; | 56 import de.intevation.flys.utils.FLYSUtils; |
44 import de.intevation.flys.utils.ThemeAccess; | 57 import de.intevation.flys.utils.ThemeAccess; |
45 | 58 |
46 | 59 |
47 /** | 60 /** |
111 | 124 |
112 public interface AxisDataset { | 125 public interface AxisDataset { |
113 | 126 |
114 void addDataset(XYDataset dataset); | 127 void addDataset(XYDataset dataset); |
115 | 128 |
129 XYDataset[] getDatasets(); | |
130 | |
116 boolean isEmpty(); | 131 boolean isEmpty(); |
132 | |
133 void setRange(Range range); | |
134 | |
135 Range getRange(); | |
136 | |
137 boolean isArea(XYDataset dataset); | |
138 | |
139 void setPlotAxisIndex(int idx); | |
140 | |
141 int getPlotAxisIndex(); | |
117 | 142 |
118 } // end of AxisDataset interface | 143 } // end of AxisDataset interface |
119 | 144 |
120 | 145 |
121 | 146 |
142 Document attr, | 167 Document attr, |
143 boolean visible); | 168 boolean visible); |
144 | 169 |
145 | 170 |
146 protected abstract YAxisWalker getYAxisWalker(); | 171 protected abstract YAxisWalker getYAxisWalker(); |
172 | |
173 | |
174 protected abstract Series getSeriesOf(XYDataset dataset, int idx); | |
175 | |
176 | |
177 /** | |
178 * This method is used to set the range of the X axis at index <i>axis</i>. | |
179 * | |
180 * @param axis The index of an X axis. | |
181 * @param range The new range for the X axis at index <i>axis</i>. | |
182 */ | |
183 protected abstract void setXRange(int axis, Range range); | |
184 | |
185 | |
186 /** | |
187 * This method is used to set the range of the Y axis at index <i>axis</i>. | |
188 * | |
189 * @param axis The index of an Y axis. | |
190 * @param range The new range for the Y axis at index <i>axis</i>. | |
191 */ | |
192 protected abstract void setYRange(int axis, Range range); | |
147 | 193 |
148 | 194 |
149 /** | 195 /** |
150 * Returns the default title of a chart. | 196 * Returns the default title of a chart. |
151 * | 197 * |
792 | 838 |
793 AxisDataset axisDataset = getAxisDataset(idx); | 839 AxisDataset axisDataset = getAxisDataset(idx); |
794 | 840 |
795 Range[] xyRanges = ChartHelper.getRanges(dataset); | 841 Range[] xyRanges = ChartHelper.getRanges(dataset); |
796 | 842 |
843 if (xyRanges == null) { | |
844 logger.warn("Skip XYDataset for Axis (invalid ranges): " + idx); | |
845 return; | |
846 } | |
847 | |
797 if (visible) { | 848 if (visible) { |
798 logger.debug("Add new AxisDataset at index: " + idx); | 849 if (logger.isDebugEnabled()) { |
850 logger.debug("Add new AxisDataset at index: " + idx); | |
851 logger.debug("X extent: " + xyRanges[0]); | |
852 logger.debug("Y extent: " + xyRanges[1]); | |
853 } | |
854 | |
799 axisDataset.addDataset(dataset); | 855 axisDataset.addDataset(dataset); |
800 combineXRanges(xyRanges[0], 0); | 856 combineXRanges(xyRanges[0], 0); |
801 } | 857 } |
802 else { | 858 else { |
803 combineXRanges(xyRanges[0], 0); | 859 combineXRanges(xyRanges[0], 0); |
1031 return new int[] { DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT }; | 1087 return new int[] { DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT }; |
1032 } | 1088 } |
1033 | 1089 |
1034 | 1090 |
1035 /** | 1091 /** |
1092 * Add datasets stored in instance variable <i>datasets</i> to plot. | |
1093 * <i>datasets</i> actually stores instances of AxisDataset, so each of this | |
1094 * datasets is mapped to a specific axis as well. | |
1095 * | |
1096 * @param plot plot to add datasets to. | |
1097 */ | |
1098 protected void addDatasets(XYPlot plot) { | |
1099 // AxisDatasets are sorted, but some might be empty. | |
1100 // Thus, generate numbering on the fly. | |
1101 int axisIndex = 0; | |
1102 int datasetIndex = 0; | |
1103 | |
1104 for (Map.Entry<Integer, AxisDataset> entry: datasets.entrySet()) { | |
1105 if (!entry.getValue().isEmpty()) { | |
1106 // Add axis and range information. | |
1107 AxisDataset axisDataset = entry.getValue(); | |
1108 NumberAxis axis = createYAxis(entry.getKey()); | |
1109 | |
1110 plot.setRangeAxis(axisIndex, axis); | |
1111 | |
1112 if (axis.getAutoRangeIncludesZero()) { | |
1113 axisDataset.setRange( | |
1114 Range.expandToInclude(axisDataset.getRange(), 0d)); | |
1115 } | |
1116 | |
1117 setYRange(axisIndex, expandPointRange(axisDataset.getRange())); | |
1118 | |
1119 // Add contained datasets, mapping to axis. | |
1120 for (XYDataset dataset: axisDataset.getDatasets()) { | |
1121 plot.setDataset(datasetIndex, dataset); | |
1122 plot.mapDatasetToRangeAxis(datasetIndex, axisIndex); | |
1123 | |
1124 applyThemes(plot, (XYDataset) dataset, | |
1125 datasetIndex, | |
1126 axisDataset.isArea((XYDataset) dataset)); | |
1127 | |
1128 datasetIndex++; | |
1129 } | |
1130 | |
1131 axisDataset.setPlotAxisIndex(axisIndex); | |
1132 axisIndex++; | |
1133 } | |
1134 } | |
1135 } | |
1136 | |
1137 | |
1138 /** | |
1139 * @param idx "index" of dataset/series (first dataset to be drawn has | |
1140 * index 0), correlates with renderer index. | |
1141 * @param isArea true if the series describes an area and shall be rendered | |
1142 * as such. | |
1143 * @return idx increased by number of items addded. | |
1144 */ | |
1145 protected void applyThemes( | |
1146 XYPlot plot, | |
1147 XYDataset series, | |
1148 int idx, | |
1149 boolean isArea | |
1150 ) { | |
1151 if (isArea) { | |
1152 applyAreaTheme(plot, (StyledAreaSeriesCollection) series, idx); | |
1153 } | |
1154 else { | |
1155 applyLineTheme(plot, series, idx); | |
1156 } | |
1157 } | |
1158 | |
1159 | |
1160 /** | |
1161 * This method applies the themes defined in the series itself. Therefore, | |
1162 * <i>StyledXYSeries.applyTheme()</i> is called, which modifies the renderer | |
1163 * for the series. | |
1164 * | |
1165 * @param plot The plot. | |
1166 * @param dataset The XYDataset which needs to support Series objects. | |
1167 * @param idx The index of the renderer / dataset. | |
1168 */ | |
1169 protected void applyLineTheme(XYPlot plot, XYDataset dataset, int idx) { | |
1170 LegendItemCollection lic = new LegendItemCollection(); | |
1171 LegendItemCollection anno = plot.getFixedLegendItems(); | |
1172 | |
1173 Font legendFont = createLegendLabelFont(); | |
1174 | |
1175 XYLineAndShapeRenderer renderer = createRenderer(plot, idx); | |
1176 | |
1177 for (int s = 0, num = dataset.getSeriesCount(); s < num; s++) { | |
1178 Series series = getSeriesOf(dataset, s); | |
1179 | |
1180 if (series instanceof StyledXYSeries) { | |
1181 ((StyledXYSeries) series).applyTheme(renderer, s); | |
1182 } | |
1183 | |
1184 // special case: if there is just one single item, we need to enable | |
1185 // points for this series, otherwise we would not see anything in | |
1186 // the chart area. | |
1187 if (series.getItemCount() == 1) { | |
1188 renderer.setSeriesShapesVisible(s, true); | |
1189 } | |
1190 | |
1191 LegendItem legendItem = renderer.getLegendItem(idx, s); | |
1192 if (legendItem != null) { | |
1193 legendItem.setLabelFont(legendFont); | |
1194 lic.add(legendItem); | |
1195 } | |
1196 else { | |
1197 logger.warn("Could not get LegentItem for renderer: " | |
1198 + idx + ", series-idx " + s); | |
1199 } | |
1200 } | |
1201 | |
1202 if (anno != null) { | |
1203 lic.addAll(anno); | |
1204 } | |
1205 | |
1206 plot.setFixedLegendItems(lic); | |
1207 | |
1208 plot.setRenderer(idx, renderer); | |
1209 } | |
1210 | |
1211 | |
1212 /** | |
1213 * @param plot The plot. | |
1214 * @param area A StyledAreaSeriesCollection object. | |
1215 * @param idx The index of the dataset. | |
1216 * | |
1217 * @return | |
1218 */ | |
1219 protected void applyAreaTheme( | |
1220 XYPlot plot, | |
1221 StyledAreaSeriesCollection area, | |
1222 int idx | |
1223 ) { | |
1224 LegendItemCollection lic = new LegendItemCollection(); | |
1225 LegendItemCollection anno = plot.getFixedLegendItems(); | |
1226 | |
1227 Font legendFont = createLegendLabelFont(); | |
1228 | |
1229 logger.debug("Registering an 'area'renderer at idx: " + idx); | |
1230 | |
1231 StableXYDifferenceRenderer dRenderer = | |
1232 new StableXYDifferenceRenderer(); | |
1233 | |
1234 if (area.getMode() == StyledAreaSeriesCollection.FILL_MODE.UNDER) { | |
1235 dRenderer.setPositivePaint(createTransparentPaint()); | |
1236 } | |
1237 | |
1238 plot.setRenderer(idx, dRenderer); | |
1239 | |
1240 area.applyTheme(dRenderer); | |
1241 | |
1242 LegendItem legendItem = dRenderer.getLegendItem(idx, 0); | |
1243 if (legendItem != null) { | |
1244 legendItem.setLabelFont(legendFont); | |
1245 lic.add(legendItem); | |
1246 } | |
1247 else { | |
1248 logger.warn("Could not get LegentItem for renderer: " | |
1249 + idx + ", series-idx " + 0); | |
1250 } | |
1251 | |
1252 if (anno != null) { | |
1253 lic.addAll(anno); | |
1254 } | |
1255 | |
1256 plot.setFixedLegendItems(lic); | |
1257 } | |
1258 | |
1259 | |
1260 /** | |
1261 * Expands a given range if it collapses into one point. | |
1262 * | |
1263 * @param Range to be expanded if upper == lower bound. | |
1264 */ | |
1265 private Range expandPointRange(Range range) { | |
1266 if (range != null && range.getLowerBound() == range.getUpperBound()) { | |
1267 return ChartHelper.expandRange(range, 5); | |
1268 } | |
1269 return range; | |
1270 } | |
1271 | |
1272 | |
1273 /** | |
1274 * Creates a new instance of EnhancedLineAndShapeRenderer. | |
1275 * | |
1276 * @param plot The plot which is set for the new renderer. | |
1277 * @param idx This value is not used in the current implementation. | |
1278 * | |
1279 * @return a new instance of EnhancedLineAndShapeRenderer. | |
1280 */ | |
1281 protected XYLineAndShapeRenderer createRenderer(XYPlot plot, int idx) { | |
1282 logger.debug("Create EnhancedLineAndShapeRenderer for idx: " + idx); | |
1283 | |
1284 EnhancedLineAndShapeRenderer r = | |
1285 new EnhancedLineAndShapeRenderer(true, false); | |
1286 | |
1287 r.setPlot(plot); | |
1288 | |
1289 return r; | |
1290 } | |
1291 | |
1292 | |
1293 /** | |
1036 * Creates a new instance of <i>IdentifiableNumberAxis</i>. | 1294 * Creates a new instance of <i>IdentifiableNumberAxis</i>. |
1037 * | 1295 * |
1038 * @param idx The index of the new axis. | 1296 * @param idx The index of the new axis. |
1039 * @param label The label of the new axis. | 1297 * @param label The label of the new axis. |
1040 * | 1298 * |
1101 return new Font( | 1359 return new Font( |
1102 DEFAULT_FONT_NAME, | 1360 DEFAULT_FONT_NAME, |
1103 Font.PLAIN, | 1361 Font.PLAIN, |
1104 getLegendFontSize() | 1362 getLegendFontSize() |
1105 ); | 1363 ); |
1364 } | |
1365 | |
1366 | |
1367 /** | |
1368 * Returns a transparently textured paint. | |
1369 * | |
1370 * @return a transparently textured paint. | |
1371 */ | |
1372 protected static Paint createTransparentPaint() { | |
1373 // TODO why not use a transparent color? | |
1374 BufferedImage texture = new BufferedImage( | |
1375 1, 1, BufferedImage.TYPE_4BYTE_ABGR); | |
1376 | |
1377 return new TexturePaint( | |
1378 texture, new Rectangle2D.Double(0d, 0d, 0d, 0d)); | |
1106 } | 1379 } |
1107 | 1380 |
1108 | 1381 |
1109 protected void preparePDFContext(CallContext context) { | 1382 protected void preparePDFContext(CallContext context) { |
1110 int[] dimension = getExportDimension(); | 1383 int[] dimension = getExportDimension(); |