ingo@369: package de.intevation.flys.exports; ingo@369: ingo@419: import java.awt.BasicStroke; ingo@369: import java.awt.Color; ingo@419: import java.awt.Stroke; ingo@369: ingo@369: import java.io.IOException; ingo@369: ingo@1645: import java.text.NumberFormat; ingo@1645: ingo@1679: import java.util.ArrayList; ingo@1684: import java.util.HashMap; ingo@1679: import java.util.List; ingo@1684: import java.util.Map; ingo@1679: ingo@1679: import org.w3c.dom.Document; ingo@1679: ingo@369: import org.apache.log4j.Logger; ingo@369: ingo@369: import org.jfree.chart.ChartFactory; ingo@369: import org.jfree.chart.JFreeChart; ingo@1679: import org.jfree.chart.LegendItem; ingo@1679: import org.jfree.chart.LegendItemCollection; ingo@1679: import org.jfree.chart.annotations.XYTextAnnotation; ingo@369: import org.jfree.chart.axis.NumberAxis; ingo@652: import org.jfree.chart.axis.ValueAxis; ingo@369: import org.jfree.chart.plot.PlotOrientation; ingo@369: import org.jfree.chart.plot.XYPlot; ingo@1679: import org.jfree.chart.renderer.xy.XYItemRenderer; ingo@924: import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; ingo@652: import org.jfree.data.Range; ingo@923: import org.jfree.data.xy.XYSeries; ingo@923: import org.jfree.data.xy.XYSeriesCollection; ingo@654: ingo@654: import org.jfree.ui.RectangleInsets; ingo@369: felix@1849: import de.intevation.artifactdatabase.state.Facet; felix@1849: ingo@369: import de.intevation.flys.exports.ChartExportHelper; ingo@1679: import de.intevation.flys.jfree.FLYSAnnotation; raimund@1738: import de.intevation.flys.jfree.StickyAxisAnnotation; ingo@369: sascha@1754: import de.intevation.flys.utils.ThemeAccess; ingo@369: ingo@369: /** ingo@369: * An abstract base class for creating XY charts. ingo@369: * ingo@369: * @author Ingo Weinzierl ingo@369: */ ingo@369: public abstract class XYChartGenerator extends ChartGenerator { ingo@369: felix@1048: /** The logger that is used in this generator. */ ingo@654: private static Logger logger = Logger.getLogger(XYChartGenerator.class); ingo@369: felix@1048: /** SeriesCollection used for the first axis. */ ingo@923: protected XYSeriesCollection first; ingo@923: felix@1048: /** SeriesCollection used for the second axis. */ ingo@923: protected XYSeriesCollection second; ingo@923: ingo@1679: /** List of annotations to insert in plot. */ ingo@1679: protected List annotations; ingo@1679: felix@1685: /** The max X range to include all X values of all series for each axis. */ ingo@1684: protected Map xRanges; ingo@1684: felix@1685: /** The max Y range to include all Y values of all series for each axis. */ ingo@1684: protected Map yRanges; ingo@1684: ingo@419: public static final Color DEFAULT_GRID_COLOR = Color.GRAY; ingo@419: public static final float DEFAULT_GRID_LINE_WIDTH = 0.3f; ingo@419: ingo@419: ingo@369: /** ingo@369: * Returns the title of a chart. ingo@369: * ingo@369: * @return the title of a chart. ingo@369: */ ingo@369: protected abstract String getChartTitle(); ingo@369: ingo@369: /** ingo@369: * Returns the X-Axis label of a chart. ingo@369: * ingo@369: * @return the X-Axis label of a chart. ingo@369: */ ingo@369: protected abstract String getXAxisLabel(); ingo@369: ingo@369: /** ingo@369: * Returns the Y-Axis label of a chart. ingo@369: * ingo@369: * @return the Y-Axis label of a chart. ingo@369: */ ingo@369: protected abstract String getYAxisLabel(); ingo@369: ingo@369: ingo@369: public void generate() ingo@369: throws IOException ingo@369: { ingo@369: logger.debug("XYChartGenerator.generate"); ingo@369: ingo@653: JFreeChart chart = generateChart(); ingo@653: ingo@1735: String format = getFormat(); ingo@1735: int[] size = getSize(); ingo@653: ingo@1735: context.putContextValue("chart.width", size[0]); ingo@1735: context.putContextValue("chart.height", size[1]); ingo@1735: ingo@1735: if (format.equals(ChartExportHelper.FORMAT_PNG)) { ingo@1735: context.putContextValue("chart.image.format", "png"); ingo@1735: ingo@1735: ChartExportHelper.exportImage( ingo@1735: out, ingo@1735: chart, ingo@1735: context); ingo@1735: } ingo@1735: else if (format.equals(ChartExportHelper.FORMAT_PDF)) { ingo@1735: context.putContextValue("chart.marginLeft", 5f); ingo@1735: context.putContextValue("chart.marginRight", 5f); ingo@1735: context.putContextValue("chart.marginTop", 5f); ingo@1735: context.putContextValue("chart.marginBottom", 5f); ingo@1735: context.putContextValue( ingo@1735: "chart.page.format", ingo@1735: ChartExportHelper.DEFAULT_PAGE_SIZE); ingo@1735: ingo@1735: ChartExportHelper.exportPDF( ingo@1735: out, ingo@1735: chart, ingo@1735: context); ingo@1735: } ingo@1735: else if (format.equals(ChartExportHelper.FORMAT_SVG)) { ingo@1735: context.putContextValue( ingo@1735: "chart.encoding", ingo@1735: ChartExportHelper.DEFAULT_ENCODING); ingo@1735: ingo@1735: ChartExportHelper.exportSVG( ingo@1735: out, ingo@1735: chart, ingo@1735: context); ingo@1735: } ingo@653: } ingo@653: ingo@653: ingo@653: public JFreeChart generateChart() { ingo@653: logger.debug("XYChartGenerator.generateChart"); ingo@653: ingo@369: JFreeChart chart = ChartFactory.createXYLineChart( ingo@369: getChartTitle(), ingo@369: getXAxisLabel(), ingo@369: getYAxisLabel(), ingo@375: null, ingo@369: PlotOrientation.VERTICAL, ingo@369: true, ingo@369: false, ingo@369: false); ingo@369: ingo@369: chart.setBackgroundPaint(Color.WHITE); ingo@369: chart.getPlot().setBackgroundPaint(Color.WHITE); ingo@369: ingo@419: XYPlot plot = (XYPlot) chart.getPlot(); ingo@419: ingo@923: addDatasets(plot); ingo@1679: addAnnotations(plot); ingo@414: addSubtitles(chart); ingo@419: adjustPlot(plot); ingo@1645: localizeAxes(plot); ingo@923: ingo@923: removeEmptyRangeAxes(plot); ingo@1699: adjustAxes(plot); ingo@923: ingo@1686: preparePointRanges(plot); ingo@673: autoZoom(plot); ingo@652: ingo@924: applyThemes(plot); ingo@924: ingo@653: return chart; ingo@369: } ingo@369: ingo@369: felix@1685: /** felix@1685: * Add first and second dataset to plot. felix@1685: * @param plot plot to add datasets to. felix@1685: */ ingo@923: protected void addDatasets(XYPlot plot) { ingo@923: if (first != null) { ingo@923: logger.debug("Set the first axis dataset."); ingo@923: plot.setDataset(0, first); ingo@923: } ingo@923: if (second != null) { ingo@923: logger.debug("Set the second axis dataset."); ingo@923: plot.setDataset(1, second); ingo@923: } ingo@923: } ingo@923: ingo@923: ingo@1684: public void addFirstAxisSeries(XYSeries series, boolean visible) { ingo@923: if (first == null) { ingo@923: first = new XYSeriesCollection(); ingo@923: } ingo@923: ingo@923: if (series != null) { ingo@1684: if (visible) { ingo@1684: first.addSeries(series); ingo@1684: } ingo@1684: ingo@1684: combineYRanges(new Range(series.getMinY(), series.getMaxY()), 0); ingo@1684: combineXRanges(new Range(series.getMinX(), series.getMaxX()), 0); ingo@923: } ingo@923: } ingo@923: ingo@923: ingo@1684: public void addSecondAxisSeries(XYSeries series, boolean visible) { ingo@923: if (second == null) { ingo@923: second = new XYSeriesCollection(); ingo@923: } ingo@923: ingo@923: if (series != null) { ingo@1684: if (visible) { ingo@1684: second.addSeries(series); ingo@1684: } ingo@1684: ingo@1684: combineYRanges(new Range(series.getMinY(), series.getMaxY()), 1); ingo@1684: combineXRanges(new Range(series.getMinX(), series.getMaxX()), 0); ingo@923: } ingo@923: } ingo@923: ingo@923: ingo@1684: private void combineXRanges(Range range, int index) { sascha@1698: Integer key = Integer.valueOf(index); ingo@1684: ingo@1684: if (xRanges == null) { ingo@1684: xRanges = new HashMap(); ingo@1684: xRanges.put(key, range); ingo@1684: return; ingo@1684: } ingo@1684: ingo@1684: Range newX = null; ingo@1684: Range oldX = xRanges.get(key); ingo@1684: ingo@1684: if (oldX != null) { ingo@1684: newX = Range.combine(oldX, range); ingo@1684: } ingo@1684: else { ingo@1684: newX = range; ingo@1684: } ingo@1684: ingo@1684: xRanges.put(key, newX); ingo@1684: } ingo@1684: ingo@1684: ingo@1684: private void combineYRanges(Range range, int index) { sascha@1698: Integer key = Integer.valueOf(index); ingo@1684: ingo@1684: if (yRanges == null) { ingo@1684: yRanges = new HashMap(); ingo@1684: yRanges.put(key, range); ingo@1684: return; ingo@1684: } ingo@1684: ingo@1684: Range newY = null; ingo@1684: Range oldY = yRanges.get(key); ingo@1684: ingo@1684: if (oldY != null) { ingo@1684: newY = Range.combine(oldY, range); ingo@1684: } ingo@1684: else { ingo@1684: newY = range; ingo@1684: } ingo@1684: ingo@1684: yRanges.put(key, newY); ingo@1684: } ingo@1684: ingo@1684: felix@1711: /** felix@1711: * Adds annotations to list (if visible is true). felix@1711: */ ingo@1684: public void addAnnotations(FLYSAnnotation annotation, boolean visible) { ingo@1684: if (!visible) { ingo@1684: return; ingo@1684: } ingo@1684: ingo@1679: if (annotations == null) { ingo@1679: annotations = new ArrayList(); ingo@1679: } ingo@1679: ingo@1679: annotations.add(annotation); ingo@1679: } ingo@1679: ingo@1679: ingo@923: private void removeEmptyRangeAxes(XYPlot plot) { ingo@923: if (first == null) { ingo@923: plot.setRangeAxis(0, null); ingo@923: } ingo@923: ingo@923: if (second == null) { ingo@923: plot.setRangeAxis(1, null); ingo@923: } ingo@923: } ingo@923: ingo@923: ingo@1686: private void preparePointRanges(XYPlot plot) { ingo@1686: for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) { sascha@1698: Integer key = Integer.valueOf(i); ingo@1686: ingo@1686: Range r = xRanges.get(key); ingo@1687: if (r != null && r.getLowerBound() == r.getUpperBound()) { ingo@1686: xRanges.put(key, expandRange(r, 5)); ingo@1686: } ingo@1686: } ingo@1686: ingo@1686: for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) { sascha@1698: Integer key = Integer.valueOf(i); ingo@1686: ingo@1686: Range r = yRanges.get(key); ingo@1687: if (r != null && r.getLowerBound() == r.getUpperBound()) { ingo@1686: yRanges.put(key, expandRange(r, 5)); ingo@1686: } ingo@1686: } ingo@1686: } ingo@1686: ingo@1686: ingo@1686: public static Range expandRange(Range range, double percent) { ingo@1686: if (range == null) { ingo@1686: return null; ingo@1686: } ingo@1686: ingo@1686: double value = range.getLowerBound(); ingo@1686: double expand = value / 100 * percent; ingo@1686: ingo@1686: return expand != 0 ingo@1686: ? new Range(value-expand, value+expand) ingo@1686: : new Range(-0.01 * percent, 0.01 * percent); ingo@1686: } ingo@1686: ingo@1686: ingo@653: /** ingo@654: * This method zooms the plot to the specified ranges in the attribute ingo@654: * document or to the ranges specified by the min/max values in the ingo@654: * datasets. Note: We determine the range manually if no zoom ranges ingo@654: * are given, because JFreeCharts auto-zoom adds a margin to the left and ingo@654: * right of the data area. ingo@653: * ingo@653: * @param plot The XYPlot. ingo@653: */ ingo@673: protected void autoZoom(XYPlot plot) { ingo@652: logger.debug("Zoom to specified ranges."); ingo@654: ingo@673: Range xrange = getDomainAxisRange(); ingo@673: Range yrange = getValueAxisRange(); ingo@654: ingo@1699: zoomX(plot, plot.getDomainAxis(), xRanges.get(0), xrange); ingo@923: ingo@1699: for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) { ingo@673: ValueAxis yaxis = plot.getRangeAxis(i); ingo@654: ingo@673: if (yaxis == null) { ingo@1699: logger.debug("Zoom problem: no Y Axis for index: " + i); ingo@673: continue; ingo@673: } ingo@673: ingo@1699: logger.debug("Prepare zoom settings for y axis at index: " + i); ingo@1699: zoomY(plot, yaxis, yRanges.get(Integer.valueOf(i)), yrange); ingo@654: } ingo@653: } ingo@652: ingo@653: ingo@718: protected boolean zoomX(XYPlot plot, ValueAxis axis, Range range, Range x) { ingo@718: return zoom(plot, axis, range, x); ingo@718: } ingo@718: ingo@718: ingo@718: protected boolean zoomY(XYPlot plot, ValueAxis axis, Range range, Range x) { ingo@718: return zoom(plot, axis, range, x); ingo@718: } ingo@718: ingo@718: ingo@653: /** ingo@653: * Zooms the x axis to the range specified in the attribute document. ingo@653: * ingo@653: * @param plot The XYPlot. ingo@673: * @param axis The axis the shoud be modified. ingo@673: * @param range The whole range specified by a dataset. ingo@673: * @param x A user defined range (null permitted). ingo@654: * ingo@654: * @return true, if a zoom range was specified, otherwise false. ingo@653: */ ingo@673: protected boolean zoom(XYPlot plot, ValueAxis axis, Range range, Range x) { sascha@1736: sascha@1736: if (range == null) { sascha@1736: return false; sascha@1736: } sascha@1736: ingo@673: if (x != null) { ingo@673: double min = range.getLowerBound(); ingo@673: double max = range.getUpperBound(); ingo@673: double diff = max > min ? max - min : min - max; ingo@652: ingo@673: Range computed = new Range( ingo@673: min + x.getLowerBound() * diff, ingo@673: min + x.getUpperBound() * diff); ingo@673: ingo@717: axis.setRangeWithMargins(computed); ingo@673: ingo@673: logger.debug("Zoom axis to: " + computed); ingo@654: ingo@654: return true; ingo@652: } ingo@654: ingo@717: axis.setRangeWithMargins(range); ingo@654: return false; ingo@654: } ingo@654: ingo@654: ingo@654: /** ingo@1684: * This method extracts the minimum and maximum values for x and y axes ingo@1684: * which are stored in xRanges and yRanges. ingo@654: * ingo@1684: * @param index The index of the y-Axis. ingo@654: * ingo@654: * @return a Range[] as follows: [x-Range, y-Range]. ingo@654: */ ingo@1684: public Range[] getRangesForDataset(int index) { ingo@1684: return new Range[] { sascha@1698: xRanges.get(Integer.valueOf(0)), sascha@1698: yRanges.get(Integer.valueOf(index)) ingo@1684: }; ingo@652: } ingo@652: ingo@652: ingo@1679: protected void addAnnotations(XYPlot plot) { ingo@1679: plot.clearAnnotations(); ingo@1679: ingo@1679: if (annotations == null) { ingo@1679: logger.debug("No Annotations given."); ingo@1679: return; ingo@1679: } ingo@1679: ingo@1679: LegendItemCollection lic = new LegendItemCollection(); ingo@1679: ingo@1679: int idx = 0; ingo@1679: if (plot.getRangeAxis(idx) == null && plot.getRangeAxisCount() >= 2) { ingo@1679: idx = 1; ingo@1679: } ingo@1679: ingo@1679: XYItemRenderer renderer = plot.getRenderer(idx); ingo@1679: ingo@1679: for (FLYSAnnotation fa: annotations) { ingo@1679: Document theme = fa.getTheme(); ingo@1679: sascha@1754: ThemeAccess themeAccess = new ThemeAccess(theme); ingo@1679: sascha@1754: Color color = themeAccess.parseLineColorField(); sascha@1754: int lineWidth = themeAccess.parseLineWidth(); felix@1711: ingo@1679: lic.add(new LegendItem(fa.getLabel(), color)); ingo@1679: ingo@1679: for (XYTextAnnotation ta: fa.getAnnotations()) { raimund@1738: if(ta instanceof StickyAxisAnnotation) { raimund@1738: StickyAxisAnnotation sta = (StickyAxisAnnotation)ta; sascha@1754: sta.applyTheme(themeAccess); raimund@1738: renderer.addAnnotation(sta); raimund@1738: } raimund@1738: else { raimund@1738: ta.setPaint(color); raimund@1738: ta.setOutlineStroke(new BasicStroke((float) lineWidth)); raimund@1738: renderer.addAnnotation(ta); raimund@1738: } ingo@1679: } ingo@1679: ingo@1679: plot.setFixedLegendItems(lic); ingo@1679: } ingo@1679: } ingo@1679: ingo@1679: ingo@369: /** ingo@369: * Adjusts the axes of a plot. ingo@369: * ingo@369: * @param plot The XYPlot of the chart. ingo@369: */ ingo@369: protected void adjustAxes(XYPlot plot) { ingo@369: NumberAxis yAxis = (NumberAxis) plot.getRangeAxis(); ingo@369: ingo@369: yAxis.setAutoRangeIncludesZero(false); ingo@369: } ingo@414: ingo@414: ingo@419: protected void adjustPlot(XYPlot plot) { ingo@419: Stroke gridStroke = new BasicStroke( ingo@419: DEFAULT_GRID_LINE_WIDTH, ingo@419: BasicStroke.CAP_BUTT, ingo@419: BasicStroke.JOIN_MITER, ingo@419: 3.0f, ingo@419: new float[] { 3.0f }, ingo@419: 0.0f); ingo@419: ingo@419: plot.setDomainGridlineStroke(gridStroke); ingo@419: plot.setDomainGridlinePaint(DEFAULT_GRID_COLOR); ingo@419: plot.setDomainGridlinesVisible(true); ingo@419: ingo@419: plot.setRangeGridlineStroke(gridStroke); ingo@419: plot.setRangeGridlinePaint(DEFAULT_GRID_COLOR); ingo@419: plot.setRangeGridlinesVisible(true); ingo@654: ingo@654: plot.setAxisOffset(new RectangleInsets(0d, 0d, 0d, 0d)); ingo@923: ingo@923: if (plot.getDataset(0) != null) { ingo@923: plot.mapDatasetToRangeAxis(0, 0); ingo@923: } ingo@923: ingo@923: if (plot.getDataset(1) != null) { ingo@923: plot.mapDatasetToRangeAxis(1, 1); ingo@923: } ingo@419: } ingo@419: ingo@419: ingo@414: protected void addSubtitles(JFreeChart chart) { ingo@414: // override this method in subclasses that need subtitles ingo@414: } ingo@924: ingo@924: ingo@1645: /** ingo@1645: * This method walks over all axes (domain and range) of plot and ingo@1645: * calls localizeDomainAxis() for domain axes or localizeRangeAxis() for ingo@1645: * range axes. ingo@1645: * ingo@1645: * @param plot The XYPlot. ingo@1645: */ ingo@1645: private void localizeAxes(XYPlot plot) { ingo@1645: for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) { ingo@1645: ValueAxis axis = plot.getDomainAxis(i); ingo@1645: ingo@1645: if (axis != null) { ingo@1645: localizeDomainAxis(axis); ingo@1645: } ingo@1645: else { ingo@1645: logger.warn("Domain axis at " + i + " is null."); ingo@1645: } ingo@1645: } ingo@1645: ingo@1645: for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) { ingo@1645: ValueAxis axis = plot.getRangeAxis(i); ingo@1645: ingo@1645: if (axis != null) { ingo@1645: localizeRangeAxis(axis); ingo@1645: } ingo@1645: else { ingo@1645: logger.warn("Range axis at " + i + " is null."); ingo@1645: } ingo@1645: } ingo@1645: } ingo@1645: ingo@1645: ingo@1645: /** ingo@1645: * Overrides the NumberFormat with the NumberFormat for the current locale ingo@1645: * that is provided by getLocale(). ingo@1645: * ingo@1645: * @param domainAxis The domain axis that needs localization. ingo@1645: */ ingo@1645: protected void localizeDomainAxis(ValueAxis domainAxis) { ingo@1645: NumberFormat nf = NumberFormat.getInstance(getLocale()); ingo@1645: ((NumberAxis) domainAxis).setNumberFormatOverride(nf); ingo@1645: } ingo@1645: ingo@1645: ingo@1645: /** ingo@1645: * Overrides the NumberFormat with the NumberFormat for the current locale ingo@1645: * that is provided by getLocale(). ingo@1645: * ingo@1645: * @param domainAxis The domain axis that needs localization. ingo@1645: */ ingo@1645: protected void localizeRangeAxis(ValueAxis rangeAxis) { ingo@1645: NumberFormat nf = NumberFormat.getInstance(getLocale()); ingo@1645: ((NumberAxis) rangeAxis).setNumberFormatOverride(nf); ingo@1645: } ingo@1645: ingo@1645: ingo@924: protected void applyThemes(XYPlot plot) { ingo@924: if (first != null) { ingo@924: applyThemes(plot, first, 0); ingo@924: } ingo@924: ingo@924: if (second != null) { ingo@924: applyThemes(plot, second, 1); ingo@924: } ingo@924: } ingo@924: ingo@924: ingo@924: protected void applyThemes(XYPlot plot, XYSeriesCollection dataset, int i) { ingo@1679: LegendItemCollection lic = new LegendItemCollection(); ingo@1679: LegendItemCollection anno = plot.getFixedLegendItems(); ingo@1679: ingo@924: XYLineAndShapeRenderer r = getRenderer(plot, i); ingo@924: ingo@924: for (int s = 0, num = dataset.getSeriesCount(); s < num; s++) { ingo@924: XYSeries series = dataset.getSeries(s); ingo@924: ingo@924: if (series instanceof StyledXYSeries) { ingo@924: ((StyledXYSeries) series).applyTheme(r, s); ingo@924: } ingo@1679: ingo@1686: // special case: if there is just one single item, we need to enable ingo@1686: // points for this series, otherwise we would not see anything in ingo@1686: // the chart area. ingo@1686: if (series.getItemCount() == 1) { ingo@1686: r.setSeriesShapesVisible(s, true); ingo@1686: } ingo@1686: ingo@1679: lic.add(r.getLegendItem(i, s)); ingo@924: } ingo@924: ingo@1679: if (anno != null) { ingo@1679: lic.addAll(anno); ingo@1679: } ingo@1679: ingo@1679: plot.setFixedLegendItems(lic); ingo@1679: ingo@924: plot.setRenderer(i, r); ingo@924: } ingo@924: ingo@924: ingo@924: protected XYLineAndShapeRenderer getRenderer(XYPlot plot, int idx) { ingo@924: XYLineAndShapeRenderer r = ingo@924: (XYLineAndShapeRenderer) plot.getRenderer(idx); ingo@924: ingo@924: if (r != null) { ingo@924: return r; ingo@924: } ingo@924: ingo@924: if (idx == 0) { ingo@924: logger.warn("No default renderer set!"); ingo@924: return new XYLineAndShapeRenderer(); ingo@924: } ingo@924: ingo@924: r = (XYLineAndShapeRenderer) plot.getRenderer(0); ingo@924: ingo@924: try { ingo@924: return (XYLineAndShapeRenderer) r.clone(); ingo@924: } ingo@924: catch (CloneNotSupportedException cnse) { ingo@924: logger.warn(cnse, cnse); ingo@924: } ingo@924: ingo@924: logger.warn("No applicalable renderer found!"); ingo@924: ingo@924: return new XYLineAndShapeRenderer(); ingo@924: } felix@1849: felix@1849: felix@1849: /** felix@1849: * Store themed annotations like MainValues for later plotting. felix@1849: */ felix@1849: protected void doAnnotations( felix@1849: FLYSAnnotation annotations, felix@1849: Facet facet, felix@1849: Document theme, felix@1849: boolean visible felix@1849: ){ felix@1849: logger.debug("doAnnotations"); felix@1849: felix@1849: annotations.setTheme(theme); felix@1849: annotations.setLabel(facet.getDescription()); felix@1849: addAnnotations(annotations, visible); felix@1849: } ingo@369: } ingo@369: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :