Mercurial > dive4elements > river
diff 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 |
line wrap: on
line diff
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ChartGenerator.java Fri Feb 03 09:35:46 2012 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/ChartGenerator.java Fri Feb 03 09:39:22 2012 +0000 @@ -2,6 +2,10 @@ import java.awt.Color; import java.awt.Font; +import java.awt.Paint; +import java.awt.TexturePaint; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; import java.io.IOException; import java.io.OutputStream; @@ -9,6 +13,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.TreeMap; import java.util.SortedMap; @@ -21,8 +26,12 @@ import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; +import org.jfree.chart.LegendItemCollection; import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.Range; +import org.jfree.data.general.Series; import org.jfree.data.xy.XYDataset; import de.intevation.artifacts.Artifact; @@ -40,6 +49,10 @@ import de.intevation.flys.artifacts.FLYSArtifact; import de.intevation.flys.artifacts.resources.Resources; +import de.intevation.flys.jfree.EnhancedLineAndShapeRenderer; +import de.intevation.flys.jfree.StableXYDifferenceRenderer; +import de.intevation.flys.jfree.StyledAreaSeriesCollection; +import de.intevation.flys.jfree.StyledXYSeries; import de.intevation.flys.utils.FLYSUtils; import de.intevation.flys.utils.ThemeAccess; @@ -113,8 +126,20 @@ void addDataset(XYDataset dataset); + XYDataset[] getDatasets(); + boolean isEmpty(); + void setRange(Range range); + + Range getRange(); + + boolean isArea(XYDataset dataset); + + void setPlotAxisIndex(int idx); + + int getPlotAxisIndex(); + } // end of AxisDataset interface @@ -146,6 +171,27 @@ protected abstract YAxisWalker getYAxisWalker(); + protected abstract Series getSeriesOf(XYDataset dataset, int idx); + + + /** + * This method is used to set the range of the X axis at index <i>axis</i>. + * + * @param axis The index of an X axis. + * @param range The new range for the X axis at index <i>axis</i>. + */ + protected abstract void setXRange(int axis, Range range); + + + /** + * This method is used to set the range of the Y axis at index <i>axis</i>. + * + * @param axis The index of an Y axis. + * @param range The new range for the Y axis at index <i>axis</i>. + */ + protected abstract void setYRange(int axis, Range range); + + /** * Returns the default title of a chart. * @@ -794,8 +840,18 @@ Range[] xyRanges = ChartHelper.getRanges(dataset); + if (xyRanges == null) { + logger.warn("Skip XYDataset for Axis (invalid ranges): " + idx); + return; + } + if (visible) { - logger.debug("Add new AxisDataset at index: " + idx); + if (logger.isDebugEnabled()) { + logger.debug("Add new AxisDataset at index: " + idx); + logger.debug("X extent: " + xyRanges[0]); + logger.debug("Y extent: " + xyRanges[1]); + } + axisDataset.addDataset(dataset); combineXRanges(xyRanges[0], 0); } @@ -1033,6 +1089,208 @@ /** + * Add datasets stored in instance variable <i>datasets</i> to plot. + * <i>datasets</i> actually stores instances of AxisDataset, so each of this + * datasets is mapped to a specific axis as well. + * + * @param plot plot to add datasets to. + */ + protected void addDatasets(XYPlot plot) { + // AxisDatasets are sorted, but some might be empty. + // Thus, generate numbering on the fly. + int axisIndex = 0; + int datasetIndex = 0; + + for (Map.Entry<Integer, AxisDataset> entry: datasets.entrySet()) { + if (!entry.getValue().isEmpty()) { + // Add axis and range information. + AxisDataset axisDataset = entry.getValue(); + NumberAxis axis = createYAxis(entry.getKey()); + + plot.setRangeAxis(axisIndex, axis); + + if (axis.getAutoRangeIncludesZero()) { + axisDataset.setRange( + Range.expandToInclude(axisDataset.getRange(), 0d)); + } + + setYRange(axisIndex, expandPointRange(axisDataset.getRange())); + + // Add contained datasets, mapping to axis. + for (XYDataset dataset: axisDataset.getDatasets()) { + plot.setDataset(datasetIndex, dataset); + plot.mapDatasetToRangeAxis(datasetIndex, axisIndex); + + applyThemes(plot, (XYDataset) dataset, + datasetIndex, + axisDataset.isArea((XYDataset) dataset)); + + datasetIndex++; + } + + axisDataset.setPlotAxisIndex(axisIndex); + axisIndex++; + } + } + } + + + /** + * @param idx "index" of dataset/series (first dataset to be drawn has + * index 0), correlates with renderer index. + * @param isArea true if the series describes an area and shall be rendered + * as such. + * @return idx increased by number of items addded. + */ + protected void applyThemes( + XYPlot plot, + XYDataset series, + int idx, + boolean isArea + ) { + if (isArea) { + applyAreaTheme(plot, (StyledAreaSeriesCollection) series, idx); + } + else { + applyLineTheme(plot, series, idx); + } + } + + + /** + * This method applies the themes defined in the series itself. Therefore, + * <i>StyledXYSeries.applyTheme()</i> is called, which modifies the renderer + * for the series. + * + * @param plot The plot. + * @param dataset The XYDataset which needs to support Series objects. + * @param idx The index of the renderer / dataset. + */ + protected void applyLineTheme(XYPlot plot, XYDataset dataset, int idx) { + LegendItemCollection lic = new LegendItemCollection(); + LegendItemCollection anno = plot.getFixedLegendItems(); + + Font legendFont = createLegendLabelFont(); + + XYLineAndShapeRenderer renderer = createRenderer(plot, idx); + + for (int s = 0, num = dataset.getSeriesCount(); s < num; s++) { + Series series = getSeriesOf(dataset, s); + + if (series instanceof StyledXYSeries) { + ((StyledXYSeries) series).applyTheme(renderer, s); + } + + // special case: if there is just one single item, we need to enable + // points for this series, otherwise we would not see anything in + // the chart area. + if (series.getItemCount() == 1) { + renderer.setSeriesShapesVisible(s, true); + } + + LegendItem legendItem = renderer.getLegendItem(idx, s); + if (legendItem != null) { + legendItem.setLabelFont(legendFont); + lic.add(legendItem); + } + else { + logger.warn("Could not get LegentItem for renderer: " + + idx + ", series-idx " + s); + } + } + + if (anno != null) { + lic.addAll(anno); + } + + plot.setFixedLegendItems(lic); + + plot.setRenderer(idx, renderer); + } + + + /** + * @param plot The plot. + * @param area A StyledAreaSeriesCollection object. + * @param idx The index of the dataset. + * + * @return + */ + protected void applyAreaTheme( + XYPlot plot, + StyledAreaSeriesCollection area, + int idx + ) { + LegendItemCollection lic = new LegendItemCollection(); + LegendItemCollection anno = plot.getFixedLegendItems(); + + Font legendFont = createLegendLabelFont(); + + logger.debug("Registering an 'area'renderer at idx: " + idx); + + StableXYDifferenceRenderer dRenderer = + new StableXYDifferenceRenderer(); + + if (area.getMode() == StyledAreaSeriesCollection.FILL_MODE.UNDER) { + dRenderer.setPositivePaint(createTransparentPaint()); + } + + plot.setRenderer(idx, dRenderer); + + area.applyTheme(dRenderer); + + LegendItem legendItem = dRenderer.getLegendItem(idx, 0); + if (legendItem != null) { + legendItem.setLabelFont(legendFont); + lic.add(legendItem); + } + else { + logger.warn("Could not get LegentItem for renderer: " + + idx + ", series-idx " + 0); + } + + if (anno != null) { + lic.addAll(anno); + } + + plot.setFixedLegendItems(lic); + } + + + /** + * Expands a given range if it collapses into one point. + * + * @param Range to be expanded if upper == lower bound. + */ + private Range expandPointRange(Range range) { + if (range != null && range.getLowerBound() == range.getUpperBound()) { + return ChartHelper.expandRange(range, 5); + } + return range; + } + + + /** + * Creates a new instance of EnhancedLineAndShapeRenderer. + * + * @param plot The plot which is set for the new renderer. + * @param idx This value is not used in the current implementation. + * + * @return a new instance of EnhancedLineAndShapeRenderer. + */ + protected XYLineAndShapeRenderer createRenderer(XYPlot plot, int idx) { + logger.debug("Create EnhancedLineAndShapeRenderer for idx: " + idx); + + EnhancedLineAndShapeRenderer r = + new EnhancedLineAndShapeRenderer(true, false); + + r.setPlot(plot); + + return r; + } + + + /** * Creates a new instance of <i>IdentifiableNumberAxis</i>. * * @param idx The index of the new axis. @@ -1106,6 +1364,21 @@ } + /** + * Returns a transparently textured paint. + * + * @return a transparently textured paint. + */ + protected static Paint createTransparentPaint() { + // TODO why not use a transparent color? + BufferedImage texture = new BufferedImage( + 1, 1, BufferedImage.TYPE_4BYTE_ABGR); + + return new TexturePaint( + texture, new Rectangle2D.Double(0d, 0d, 0d, 0d)); + } + + protected void preparePDFContext(CallContext context) { int[] dimension = getExportDimension();