teichmann@5863: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@5863: * Software engineering by Intevation GmbH teichmann@5863: * teichmann@5994: * This file is Free Software under the GNU AGPL (>=v3) teichmann@5863: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@5994: * documentation coming with Dive4Elements River for details. teichmann@5863: */ teichmann@5863: teichmann@5831: package org.dive4elements.river.exports; ingo@348: teichmann@5831: import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; teichmann@5831: import org.dive4elements.artifactdatabase.state.Settings; teichmann@5831: import org.dive4elements.artifacts.Artifact; teichmann@5831: import org.dive4elements.artifacts.ArtifactNamespaceContext; teichmann@5831: import org.dive4elements.artifacts.CallContext; teichmann@5831: import org.dive4elements.artifacts.CallMeta; teichmann@5831: import org.dive4elements.artifacts.PreferredLocale; teichmann@5831: import org.dive4elements.artifacts.common.utils.XMLUtils; teichmann@5831: import org.dive4elements.river.artifacts.access.RangeAccess; teichmann@5867: import org.dive4elements.river.artifacts.D4EArtifact; teichmann@5831: import org.dive4elements.river.artifacts.resources.Resources; teichmann@5867: import org.dive4elements.river.collections.D4EArtifactCollection; teichmann@5831: import org.dive4elements.river.jfree.Bounds; teichmann@5831: import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation; teichmann@5831: import org.dive4elements.river.jfree.DoubleBounds; teichmann@5831: import org.dive4elements.river.jfree.EnhancedLineAndShapeRenderer; teichmann@5864: import org.dive4elements.river.jfree.RiverAnnotation; teichmann@5831: import org.dive4elements.river.jfree.StableXYDifferenceRenderer; teichmann@5831: import org.dive4elements.river.jfree.StickyAxisAnnotation; teichmann@5831: import org.dive4elements.river.jfree.Style; teichmann@5831: import org.dive4elements.river.jfree.StyledAreaSeriesCollection; teichmann@5831: import org.dive4elements.river.jfree.StyledSeries; aheinecke@7034: import org.dive4elements.river.jfree.AxisDataset; teichmann@5831: import org.dive4elements.river.model.River; teichmann@5831: import org.dive4elements.river.themes.LineStyle; teichmann@5831: import org.dive4elements.river.themes.TextStyle; teichmann@6905: import org.dive4elements.river.themes.ThemeDocument; teichmann@5865: import org.dive4elements.river.utils.RiverUtils; christian@3242: sascha@3257: import java.awt.BasicStroke; sascha@3257: import java.awt.Color; sascha@3257: import java.awt.Font; sascha@3257: import java.awt.Paint; sascha@3257: import java.awt.Stroke; sascha@3257: import java.awt.TexturePaint; sascha@3257: import java.awt.geom.Rectangle2D; sascha@3257: import java.awt.image.BufferedImage; sascha@3257: import java.io.IOException; sascha@3257: import java.io.OutputStream; sascha@3257: import java.util.ArrayList; sascha@3257: import java.util.List; sascha@3257: import java.util.Locale; sascha@3257: import java.util.Map; sascha@3257: import java.util.SortedMap; sascha@3257: import java.util.TreeMap; sascha@3257: sascha@3257: import javax.xml.xpath.XPathConstants; sascha@3257: sascha@3257: import org.apache.log4j.Logger; sascha@3257: import org.jfree.chart.JFreeChart; sascha@3257: import org.jfree.chart.LegendItem; sascha@3257: import org.jfree.chart.LegendItemCollection; sascha@3257: import org.jfree.chart.annotations.XYLineAnnotation; sascha@3257: import org.jfree.chart.annotations.XYTextAnnotation; sascha@3257: import org.jfree.chart.axis.NumberAxis; sascha@3257: import org.jfree.chart.plot.XYPlot; sascha@3257: import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; christian@3409: import org.jfree.chart.title.TextTitle; sascha@3257: import org.jfree.data.Range; sascha@3257: import org.jfree.data.general.Series; sascha@3257: import org.jfree.data.xy.XYDataset; sascha@3257: import org.jfree.ui.RectangleInsets; sascha@3257: import org.jfree.ui.TextAnchor; sascha@3257: import org.w3c.dom.Document; sascha@3257: import org.w3c.dom.Element; ingo@348: teichmann@5831: import org.dive4elements.river.utils.Formatter; ingo@348: ingo@348: /** ingo@348: * The base class for chart creation. It should provide some basic things that ingo@348: * equal in all chart types. ingo@348: * teichmann@5864: * Annotations are added as RiverAnnotations and come in mutliple basic forms: felix@4276: * TextAnnotations are labels somewhere in data space, StickyAnnotations are felix@4276: * labels of a slice or line in one data dimension (i.e. visualized as label felix@4276: * on a single axis). felix@4276: * ingo@348: * @author Ingo Weinzierl ingo@348: */ ingo@348: public abstract class ChartGenerator implements OutGenerator { ingo@348: ingo@348: private static Logger logger = Logger.getLogger(ChartGenerator.class); ingo@348: ingo@2234: public static final int DEFAULT_CHART_WIDTH = 600; ingo@2234: public static final int DEFAULT_CHART_HEIGHT = 400; ingo@2234: public static final String DEFAULT_CHART_FORMAT = "png"; ingo@2234: public static final Color DEFAULT_GRID_COLOR = Color.GRAY; ingo@2234: public static final float DEFAULT_GRID_LINE_WIDTH = 0.3f; ingo@2234: public static final int DEFAULT_FONT_SIZE = 12; ingo@2234: public static final String DEFAULT_FONT_NAME = "Tahoma"; ingo@423: christian@3242: protected static float ANNOTATIONS_AXIS_OFFSET = 0.02f; ingo@423: ingo@423: public static final String XPATH_CHART_SIZE = ingo@423: "/art:action/art:attributes/art:size"; ingo@423: ingo@1735: public static final String XPATH_CHART_FORMAT = ingo@1735: "/art:action/art:attributes/art:format/@art:value"; ingo@1735: ingo@652: public static final String XPATH_CHART_X_RANGE = ingo@652: "/art:action/art:attributes/art:xrange"; ingo@652: ingo@652: public static final String XPATH_CHART_Y_RANGE = ingo@652: "/art:action/art:attributes/art:yrange"; ingo@652: ingo@423: ingo@348: /** The document of the incoming out() request.*/ ingo@348: protected Document request; ingo@348: ingo@348: /** The output stream where the data should be written to.*/ ingo@348: protected OutputStream out; ingo@348: ingo@348: /** The CallContext object.*/ ingo@348: protected CallContext context; ingo@348: teichmann@5867: protected D4EArtifactCollection collection; ingo@3422: ingo@412: /** The artifact that is used to decorate the chart with meta information.*/ ingo@412: protected Artifact master; ingo@412: ingo@2047: /** The settings that should be used during output creation.*/ ingo@2047: protected Settings settings; ingo@2047: ingo@2238: /** Map of datasets ("index"). */ ingo@2238: protected SortedMap datasets; ingo@2238: christian@3212: /** List of annotations to insert in plot. */ teichmann@5864: protected List annotations = new ArrayList(); ingo@348: ingo@2233: /** ingo@2233: * A mini interface that allows to walk over the YAXIS enums defined in ingo@2233: * subclasses. ingo@2233: */ ingo@2233: public interface YAxisWalker { ingo@2238: ingo@2233: int length(); ingo@2238: ingo@2233: String getId(int idx); ingo@2238: } // end of YAxisWalker interface ingo@2238: ingo@2238: ingo@2238: ingo@2238: ingo@2238: ingo@2238: /** ingo@2238: * Default constructor that initializes internal data structures. ingo@2238: */ ingo@2238: public ChartGenerator() { ingo@2238: datasets = new TreeMap(); ingo@2233: } ingo@2233: ingo@2233: christian@3212: /** christian@3212: * Adds annotations to list. The given annotation will be visible. christian@3212: */ teichmann@5864: public void addAnnotations(RiverAnnotation annotation) { christian@3212: annotations.add(annotation); christian@3212: } christian@3245: christian@3242: /** christian@3242: * Add a text and a line annotation. christian@3242: * @param area convenience to determine positions in plot. christian@3242: * @param theme (optional) theme document christian@3242: */ christian@3242: protected void addStickyAnnotation( christian@3242: StickyAxisAnnotation annotation, christian@3242: XYPlot plot, christian@3242: ChartArea area, christian@3464: LineStyle lineStyle, christian@3464: TextStyle textStyle, teichmann@6905: ThemeDocument theme christian@3242: ) { christian@3242: // OPTIMIZE pre-calculate area-related values christian@3242: final float TEXT_OFF = 0.03f; christian@3212: christian@3242: XYLineAnnotation lineAnnotation = null; christian@3242: XYTextAnnotation textAnnotation = null; christian@3242: christian@3242: int rendererIndex = 0; christian@3242: christian@3242: if (annotation.atX()) { christian@3242: textAnnotation = new CollisionFreeXYTextAnnotation( christian@3242: annotation.getText(), annotation.getPos(), area.ofGround(TEXT_OFF)); christian@3242: // OPTIMIZE externalize the calculation involving PI. christian@3242: //textAnnotation.setRotationAngle(270f*Math.PI/180f); christian@3242: lineAnnotation = createGroundStickAnnotation( christian@3242: area, annotation.getPos(), lineStyle); christian@3242: textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT); christian@3242: textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT); christian@3242: } christian@3242: else { christian@3242: // Do the more complicated case where we stick to the Y-Axis. christian@3242: // There is one nasty case (duration curves, where annotations christian@3242: // might stick to the second y-axis). christian@3242: // FIXME: Remove dependency to XYChartGenerator here christian@3278: AxisDataset dataset = getAxisDataset( christian@3242: new Integer(annotation.getAxisSymbol())); christian@3242: if (dataset == null) { christian@3242: logger.warn("Annotation should stick to unfindable y-axis: " christian@3242: + annotation.getAxisSymbol()); christian@3242: rendererIndex = 0; christian@3242: } christian@3242: else { christian@3242: rendererIndex = dataset.getPlotAxisIndex(); christian@3242: } christian@3242: christian@3242: // Stick to the "right" (opposed to left) Y-Axis. christian@3242: if (rendererIndex != 0) { christian@3242: // OPTIMIZE: Pass a different area to this function, christian@3242: // do the adding to renderer outside (let this christian@3242: // function return the annotations). christian@3242: // Note that this path is travelled rarely. christian@3242: ChartArea area2 = new ChartArea(plot.getDomainAxis(), plot.getRangeAxis(rendererIndex)); christian@3242: textAnnotation = new CollisionFreeXYTextAnnotation( christian@3242: annotation.getText(), area2.ofRight(TEXT_OFF), annotation.getPos()); christian@3242: textAnnotation.setRotationAnchor(TextAnchor.CENTER_RIGHT); christian@3242: textAnnotation.setTextAnchor(TextAnchor.CENTER_RIGHT); christian@3242: lineAnnotation = createRightStickAnnotation( christian@3242: area2, annotation.getPos(), lineStyle); christian@3242: if (!Float.isNaN(annotation.getHitPoint()) && theme != null) { christian@3242: // New line annotation to hit curve. teichmann@6905: if (theme.parseShowVerticalLine()) { christian@3242: XYLineAnnotation hitLineAnnotation = christian@3242: createStickyLineAnnotation( christian@3242: StickyAxisAnnotation.SimpleAxis.X_AXIS, christian@3242: annotation.getHitPoint(), annotation.getPos(),// annotation.getHitPoint(), christian@3242: area2, lineStyle); christian@3242: plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation, christian@3242: org.jfree.ui.Layer.BACKGROUND); christian@3242: } teichmann@6905: if (theme.parseShowHorizontalLine()) { christian@3242: XYLineAnnotation lineBackAnnotation = christian@3242: createStickyLineAnnotation( christian@3242: StickyAxisAnnotation.SimpleAxis.Y_AXIS2, christian@3242: annotation.getPos(), annotation.getHitPoint(), christian@3242: area2, lineStyle); christian@3242: plot.getRenderer(rendererIndex).addAnnotation(lineBackAnnotation, christian@3242: org.jfree.ui.Layer.BACKGROUND); christian@3242: } christian@3242: } christian@3242: } christian@3242: else { // Stick to the left y-axis. christian@3242: textAnnotation = new CollisionFreeXYTextAnnotation( christian@3242: annotation.getText(), area.ofLeft(TEXT_OFF), annotation.getPos()); christian@3242: textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT); christian@3242: textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT); christian@3242: lineAnnotation = createLeftStickAnnotation(area, annotation.getPos(), lineStyle); christian@3242: if (!Float.isNaN(annotation.getHitPoint()) && theme != null) { christian@3242: // New line annotation to hit curve. teichmann@6905: if (theme.parseShowHorizontalLine()) { christian@3242: XYLineAnnotation hitLineAnnotation = christian@3242: createStickyLineAnnotation( christian@3242: StickyAxisAnnotation.SimpleAxis.Y_AXIS, christian@3242: annotation.getPos(), annotation.getHitPoint(), christian@3242: area, lineStyle); christian@3242: plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation, christian@3242: org.jfree.ui.Layer.BACKGROUND); christian@3242: } teichmann@6905: if (theme.parseShowVerticalLine()) { christian@3242: XYLineAnnotation lineBackAnnotation = christian@3242: createStickyLineAnnotation( christian@3242: StickyAxisAnnotation.SimpleAxis.X_AXIS, christian@3242: annotation.getHitPoint(), annotation.getPos(), christian@3242: area, lineStyle); christian@3242: plot.getRenderer(rendererIndex).addAnnotation(lineBackAnnotation, christian@3242: org.jfree.ui.Layer.BACKGROUND); christian@3242: } christian@3242: } christian@3242: } christian@3242: } christian@3242: christian@3242: // Style the text. christian@3242: if (textStyle != null) { christian@3242: textStyle.apply(textAnnotation); christian@3242: } christian@3242: christian@3242: // Add the Annotations to renderer. christian@3242: plot.getRenderer(rendererIndex).addAnnotation(textAnnotation, christian@3242: org.jfree.ui.Layer.FOREGROUND); christian@3242: plot.getRenderer(rendererIndex).addAnnotation(lineAnnotation, christian@3242: org.jfree.ui.Layer.FOREGROUND); christian@3242: } christian@3242: christian@3242: /** christian@3242: * Create annotation that sticks to "ground" (X) axis. christian@3242: * @param area helper to calculate coordinates christian@3242: * @param pos one-dimensional position (distance from axis) christian@3242: * @param lineStyle the line style to use for the line. christian@3242: */ christian@3242: protected static XYLineAnnotation createGroundStickAnnotation( christian@3464: ChartArea area, float pos, LineStyle lineStyle christian@3242: ) { christian@3242: // Style the line. christian@3242: if (lineStyle != null) { christian@3242: return new XYLineAnnotation( christian@3242: pos, area.atGround(), christian@3242: pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET), christian@3242: new BasicStroke(lineStyle.getWidth()),lineStyle.getColor()); christian@3242: } christian@3242: else { christian@3242: return new XYLineAnnotation( christian@3242: pos, area.atGround(), christian@3242: pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET)); christian@3242: } christian@3242: } christian@3242: christian@3242: christian@3242: /** christian@3242: * Create annotation that sticks to the second Y axis ("right"). christian@3242: * @param area helper to calculate coordinates christian@3242: * @param pos one-dimensional position (distance from axis) christian@3242: * @param lineStyle the line style to use for the line. christian@3242: */ christian@3242: protected static XYLineAnnotation createRightStickAnnotation( christian@3464: ChartArea area, float pos, LineStyle lineStyle christian@3242: ) { christian@3242: // Style the line. christian@3242: if (lineStyle != null) { christian@3242: return new XYLineAnnotation( christian@3242: area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos, christian@3242: area.atRight(), pos, christian@3242: new BasicStroke(lineStyle.getWidth()), lineStyle.getColor()); christian@3242: } christian@3242: else { christian@3242: return new XYLineAnnotation( christian@3242: area.atRight(), pos, christian@3242: area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos); christian@3242: } christian@3242: } christian@3242: christian@3242: christian@3242: /** christian@3242: * Create annotation that sticks to the first Y axis ("left"). christian@3242: * @param area helper to calculate coordinates christian@3242: * @param pos one-dimensional position (distance from axis) christian@3242: * @param lineStyle the line style to use for the line. christian@3242: */ christian@3242: protected static XYLineAnnotation createLeftStickAnnotation( christian@3464: ChartArea area, float pos, LineStyle lineStyle christian@3242: ) { christian@3242: // Style the line. christian@3242: if (lineStyle != null) { christian@3242: return new XYLineAnnotation( christian@3242: area.atLeft(), pos, christian@3242: area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos, christian@3242: new BasicStroke(lineStyle.getWidth()), lineStyle.getColor()); christian@3242: } christian@3242: else { christian@3242: return new XYLineAnnotation( christian@3242: area.atLeft(), pos, christian@3242: area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos); christian@3242: } christian@3242: } christian@3242: christian@3242: christian@3242: /** christian@3242: * Create a line from a axis to a given point. christian@3242: * @param axis The "simple" axis. christian@3242: * @param fromD1 from-location in first dimension. christian@3242: * @param toD2 to-location in second dimension. christian@3242: * @param area helper to calculate offsets. christian@3242: * @param lineStyle optional line style. christian@3242: */ christian@3242: protected static XYLineAnnotation createStickyLineAnnotation( christian@3242: StickyAxisAnnotation.SimpleAxis axis, float fromD1, float toD2, christian@3464: ChartArea area, LineStyle lineStyle christian@3242: ) { christian@3242: double anchorX1 = 0d, anchorX2 = 0d, anchorY1 = 0d, anchorY2 = 0d; christian@3242: switch(axis) { christian@3242: case X_AXIS: christian@3242: anchorX1 = fromD1; christian@3242: anchorX2 = fromD1; christian@3242: anchorY1 = area.atGround(); christian@3242: anchorY2 = toD2; christian@3242: break; christian@3242: case Y_AXIS: christian@3242: anchorX1 = area.atLeft(); christian@3242: anchorX2 = toD2; christian@3242: anchorY1 = fromD1; christian@3242: anchorY2 = fromD1; christian@3242: break; christian@3242: case Y_AXIS2: christian@3242: anchorX1 = area.atRight(); christian@3242: anchorX2 = toD2; christian@3242: anchorY1 = fromD1; christian@3242: anchorY2 = fromD1; christian@3242: break; christian@3242: } christian@3242: // Style the line. christian@3242: if (lineStyle != null) { christian@3242: return new XYLineAnnotation( christian@3242: anchorX1, anchorY1, christian@3242: anchorX2, anchorY2, christian@3242: new BasicStroke(lineStyle.getWidth()), lineStyle.getColor()); christian@3242: } christian@3242: else { christian@3242: return new XYLineAnnotation( christian@3242: anchorX1, anchorY1, christian@3242: anchorX2, anchorY2); christian@3242: } christian@3242: } christian@3242: christian@3242: /** christian@3242: * Add the annotations (Sticky, Text and hyk zones) stored christian@3242: * in the annotations field. felix@3270: * @param plot Plot to add annotations to. christian@3242: */ christian@3242: protected void addAnnotationsToRenderer(XYPlot plot) { christian@3242: logger.debug("addAnnotationsToRenderer"); christian@3242: sascha@3555: if (annotations == null || annotations.isEmpty()) { christian@3242: logger.debug("addAnnotationsToRenderer: no annotations."); christian@3242: return; christian@3242: } christian@3242: christian@3242: // OPTMIMIZE: Pre-calculate positions christian@3242: ChartArea area = new ChartArea( christian@3242: plot.getDomainAxis(0).getRange(), christian@3242: plot.getRangeAxis().getRange()); christian@3242: christian@3242: // Walk over all Annotation sets. teichmann@5864: for (RiverAnnotation fa: annotations) { felix@4433: christian@3242: // Access text styling, if any. teichmann@6905: ThemeDocument theme = fa.getTheme(); christian@3464: TextStyle textStyle = null; christian@3464: LineStyle lineStyle = null; christian@3242: christian@3242: // Get Themeing information and add legend item. christian@3242: if (theme != null) { teichmann@6906: textStyle = theme.parseComplexTextStyle(); teichmann@6905: lineStyle = theme.parseComplexLineStyle(); christian@3242: if (fa.getLabel() != null) { christian@3242: LegendItemCollection lic = new LegendItemCollection(); christian@3242: LegendItemCollection old = plot.getFixedLegendItems(); christian@3242: lic.add(createLegendItem(theme, fa.getLabel())); christian@3242: // (Re-)Add prior legend entries. christian@3242: if (old != null) { christian@3242: old.addAll(lic); christian@3242: } christian@3242: else { christian@3242: old = lic; christian@3242: } christian@3242: plot.setFixedLegendItems(old); christian@3242: } christian@3242: } christian@3242: christian@3242: // The 'Sticky' Annotations (at axis, with line and text). christian@3242: for (StickyAxisAnnotation sta: fa.getAxisTextAnnotations()) { christian@3242: addStickyAnnotation( christian@3242: sta, plot, area, lineStyle, textStyle, theme); christian@3242: } christian@3242: christian@3242: // Other Text Annotations (e.g. labels of (manual) points). christian@3242: for (XYTextAnnotation ta: fa.getTextAnnotations()) { christian@3242: // Style the text. christian@3242: if (textStyle != null) { christian@3242: textStyle.apply(ta); christian@3242: } christian@3242: ta.setY(area.above(0.05d, ta.getY())); christian@3242: plot.getRenderer().addAnnotation(ta, org.jfree.ui.Layer.FOREGROUND); christian@3242: } christian@3242: } christian@3242: } christian@3245: ingo@2238: ingo@2234: /** ingo@2234: * This method needs to be implemented by concrete subclasses to create new ingo@2234: * instances of JFreeChart. ingo@2234: * ingo@2234: * @return a new instance of a JFreeChart. ingo@2234: */ ingo@2234: public abstract JFreeChart generateChart(); ingo@2234: ingo@2234: felix@4589: /** For every outable (i.e. facets), this function is felix@4589: * called and handles the data accordingly. */ christian@3278: @Override ingo@2234: public abstract void doOut( ingo@2234: ArtifactAndFacet bundle, teichmann@6905: ThemeDocument attr, ingo@2234: boolean visible); ingo@2234: ingo@2234: ingo@2233: protected abstract YAxisWalker getYAxisWalker(); ingo@2233: ingo@2234: ingo@2242: protected abstract Series getSeriesOf(XYDataset dataset, int idx); ingo@2242: ingo@2234: /** ingo@2234: * Returns the default title of a chart. ingo@2234: * ingo@2234: * @return the default title of a chart. ingo@2234: */ ingo@2233: protected abstract String getDefaultChartTitle(); ingo@2233: ingo@2234: ingo@2234: /** ingo@2234: * Returns the default X-Axis label of a chart. ingo@2234: * ingo@2234: * @return the default X-Axis label of a chart. ingo@2234: */ ingo@2233: protected abstract String getDefaultXAxisLabel(); ingo@2233: ingo@2234: ingo@2234: /** ingo@2234: * This method is called to retrieve the default label for an Y axis at ingo@2234: * position pos. ingo@2234: * ingo@2234: * @param pos The position of an Y axis. ingo@2234: * ingo@2234: * @return the default Y axis label at position pos. ingo@2234: */ ingo@2233: protected abstract String getDefaultYAxisLabel(int pos); ingo@2233: ingo@2234: ingo@2234: /** ingo@2238: * This method is used to create new AxisDataset instances which may differ ingo@2238: * in concrete subclasses. ingo@2238: * ingo@2238: * @param idx The index of an axis. ingo@2238: */ ingo@2238: protected abstract AxisDataset createAxisDataset(int idx); ingo@2238: ingo@2238: ingo@2238: /** ingo@2238: * Combines the ranges of the X axis at index idx. ingo@2238: * felix@3270: * @param bounds A new Bounds. ingo@2238: * @param idx The index of the X axis that should be comined with ingo@2238: * range. ingo@2238: */ ingo@2587: protected abstract void combineXBounds(Bounds bounds, int idx); ingo@2587: ingo@2587: ingo@2587: /** ingo@2587: * Combines the ranges of the Y axis at index idx. ingo@2587: * felix@3270: * @param bounds A new Bounds. felix@3284: * @param index The index of the Y axis that should be comined with. ingo@2587: * range. ingo@2587: */ ingo@2587: protected abstract void combineYBounds(Bounds bounds, int index); ingo@2238: ingo@2238: ingo@2238: /** ingo@2261: * This method is used to determine the ranges for axes at a given index. ingo@2261: * ingo@2261: * @param index The index of the axes at the plot. ingo@2261: * ingo@2261: * @return a Range[] with [xrange, yrange]; ingo@2261: */ ingo@2261: public abstract Range[] getRangesForAxis(int index); ingo@2261: ingo@2398: public abstract Bounds getXBounds(int axis); ingo@2398: ingo@2398: protected abstract void setXBounds(int axis, Bounds bounds); ingo@2398: ingo@2398: public abstract Bounds getYBounds(int axis); ingo@2398: ingo@2398: protected abstract void setYBounds(int axis, Bounds bounds); ingo@2398: ingo@2261: ingo@2261: /** christian@3409: * This method retrieves the chart subtitle by calling getChartSubtitle() christian@3409: * and adds it as TextTitle to the chart. christian@3409: * The default implementation of getChartSubtitle() returns the same christian@3409: * as getDefaultChartSubtitle() which must be implemented by derived christian@3409: * classes. If you want to add multiple subtitles to the chart override christian@3409: * this method and add your subtitles manually. ingo@2234: * ingo@2234: * @param chart The JFreeChart chart object. ingo@2234: */ ingo@2234: protected void addSubtitles(JFreeChart chart) { christian@3409: String subtitle = getChartSubtitle(); christian@3409: christian@3409: if (subtitle != null && subtitle.length() > 0) { christian@3409: chart.addSubtitle(new TextTitle(subtitle)); christian@3409: } ingo@2234: } ingo@2233: ingo@2233: ingo@2234: /** christian@3242: * Register annotations like MainValues for later plotting christian@3242: * christian@3242: * @param annotations list of annotations (data of facet). christian@3242: * @param aandf Artifact and the facet. christian@3242: * @param theme Theme document for given annotations. christian@3242: * @param visible The visibility of the annotations. christian@3242: */ felix@6900: public void doAnnotations( teichmann@5864: RiverAnnotation annotations, christian@3242: ArtifactAndFacet aandf, teichmann@6905: ThemeDocument theme, christian@3242: boolean visible christian@3242: ){ christian@3242: logger.debug("doAnnotations"); christian@3242: christian@3242: // Add all annotations to our annotation pool. christian@3242: annotations.setTheme(theme); christian@3242: if (aandf != null) { christian@3242: annotations.setLabel(aandf.getFacetDescription()); christian@3242: } christian@3242: else { felix@4236: logger.error( christian@3242: "Art/Facet for Annotations is null. " + christian@3242: "This should never happen!"); christian@3242: } christian@3242: christian@3242: if (visible) { christian@3242: addAnnotations(annotations); christian@3242: } christian@3242: } christian@3245: christian@3242: christian@3242: /** ingo@2234: * Generate chart. ingo@2234: */ ingo@2234: @Override ingo@2234: public void generate() ingo@2234: throws IOException ingo@2234: { ingo@2234: logger.debug("ChartGenerator.generate"); ingo@2234: ingo@2234: JFreeChart chart = generateChart(); ingo@2234: ingo@2234: String format = getFormat(); ingo@2234: int[] size = getSize(); ingo@2234: ingo@2234: if (size == null) { ingo@2234: size = getExportDimension(); ingo@2234: } ingo@2234: ingo@2234: context.putContextValue("chart.width", size[0]); ingo@2234: context.putContextValue("chart.height", size[1]); ingo@2234: ingo@2234: if (format.equals(ChartExportHelper.FORMAT_PNG)) { ingo@2234: context.putContextValue("chart.image.format", "png"); ingo@2234: ingo@2234: ChartExportHelper.exportImage( ingo@2234: out, ingo@2234: chart, ingo@2234: context); ingo@2234: } ingo@2234: else if (format.equals(ChartExportHelper.FORMAT_PDF)) { ingo@2234: preparePDFContext(context); ingo@2234: ingo@2234: ChartExportHelper.exportPDF( ingo@2234: out, ingo@2234: chart, ingo@2234: context); ingo@2234: } ingo@2234: else if (format.equals(ChartExportHelper.FORMAT_SVG)) { ingo@2234: prepareSVGContext(context); ingo@2234: ingo@2234: ChartExportHelper.exportSVG( ingo@2234: out, ingo@2234: chart, ingo@2234: context); ingo@2234: } ingo@2234: else if (format.equals(ChartExportHelper.FORMAT_CSV)) { ingo@2234: context.putContextValue("chart.image.format", "csv"); ingo@2234: ingo@2234: ChartExportHelper.exportCSV( ingo@2234: out, ingo@2234: chart, ingo@2234: context); ingo@2234: } ingo@2234: } ingo@2234: ingo@2234: ingo@2234: @Override ingo@348: public void init(Document request, OutputStream out, CallContext context) { ingo@348: logger.debug("ChartGenerator.init"); ingo@348: ingo@348: this.request = request; ingo@348: this.out = out; ingo@348: this.context = context; ingo@348: } ingo@348: ingo@348: felix@4589: /** Sets the master artifact. */ ingo@2234: @Override ingo@412: public void setMasterArtifact(Artifact master) { ingo@412: this.master = master; ingo@412: } ingo@412: ingo@412: felix@6422: /** felix@6422: * Gets the master artifact. felix@6422: * @return the master artifact. felix@6422: */ felix@6422: public Artifact getMaster() { felix@6422: return master; felix@6422: } felix@6422: felix@6422: felix@4589: /** Sets the collection. */ ingo@2047: @Override teichmann@5867: public void setCollection(D4EArtifactCollection collection) { ingo@3422: this.collection = collection; ingo@3422: } ingo@3422: ingo@3422: ingo@3422: @Override ingo@2047: public void setSettings(Settings settings) { ingo@2047: this.settings = settings; ingo@2047: } ingo@2047: ingo@2047: ingo@2047: /** ingo@2236: * Returns an instance of ChartSettings with a chart specific section ingo@2236: * but with no axes settings. ingo@2236: * ingo@2236: * @return an instance of ChartSettings. ingo@2236: */ ingo@2236: @Override ingo@2236: public Settings getSettings() { ingo@2236: if (this.settings != null) { ingo@2236: return this.settings; ingo@2236: } ingo@2236: ingo@2236: ChartSettings settings = new ChartSettings(); ingo@2236: ingo@2236: ChartSection chartSection = buildChartSection(); ingo@2236: LegendSection legendSection = buildLegendSection(); ingo@2236: ExportSection exportSection = buildExportSection(); ingo@2236: ingo@2236: settings.setChartSection(chartSection); ingo@2236: settings.setLegendSection(legendSection); ingo@2236: settings.setExportSection(exportSection); ingo@2236: ingo@2236: List axisSections = buildAxisSections(); ingo@2236: for (AxisSection axisSection: axisSections) { ingo@2236: settings.addAxisSection(axisSection); ingo@2236: } ingo@2236: ingo@2236: return settings; ingo@2236: } ingo@2236: ingo@2236: ingo@2236: /** ingo@2236: * Creates a new ChartSection. ingo@2236: * ingo@2236: * @return a new ChartSection. ingo@2236: */ ingo@2236: protected ChartSection buildChartSection() { ingo@2236: ChartSection chartSection = new ChartSection(); ingo@2236: chartSection.setTitle(getChartTitle()); ingo@2236: chartSection.setSubtitle(getChartSubtitle()); felix@3613: chartSection.setDisplayGrid(isGridVisible()); felix@3615: chartSection.setDisplayLogo(showLogo()); felix@3618: chartSection.setLogoVPlacement(logoVPlace()); felix@3618: chartSection.setLogoHPlacement(logoHPlace()); ingo@2236: return chartSection; ingo@2236: } ingo@2236: ingo@2236: ingo@2236: /** ingo@2236: * Creates a new LegendSection. ingo@2236: * ingo@2236: * @return a new LegendSection. ingo@2236: */ ingo@2236: protected LegendSection buildLegendSection() { ingo@2236: LegendSection legendSection = new LegendSection(); ingo@2236: legendSection.setVisibility(isLegendVisible()); ingo@2236: legendSection.setFontSize(getLegendFontSize()); felix@3150: legendSection.setAggregationThreshold(10); ingo@2236: return legendSection; ingo@2236: } ingo@2236: ingo@2236: ingo@2236: /** ingo@2236: * Creates a new ExportSection with default values WIDTH=600 ingo@2236: * and HEIGHT=400. ingo@2236: * ingo@2236: * @return a new ExportSection. ingo@2236: */ ingo@2236: protected ExportSection buildExportSection() { ingo@2236: ExportSection exportSection = new ExportSection(); ingo@2236: exportSection.setWidth(600); ingo@2236: exportSection.setHeight(400); ingo@2236: return exportSection; ingo@2236: } ingo@2236: ingo@2236: ingo@2236: /** ingo@2236: * Creates a list of Sections that contains all axes of the chart (including ingo@2236: * X and Y axes). ingo@2236: * ingo@2236: * @return a list of Sections for each axis in this chart. ingo@2236: */ ingo@2236: protected List buildAxisSections() { ingo@2236: List axisSections = new ArrayList(); ingo@2236: ingo@2236: axisSections.addAll(buildXAxisSections()); ingo@2236: axisSections.addAll(buildYAxisSections()); ingo@2236: ingo@2236: return axisSections; ingo@2236: } ingo@2236: ingo@2236: ingo@2236: /** ingo@2236: * Creates a new Section for chart's X axis. ingo@2236: * ingo@2236: * @return a List that contains a Section for the X axis. ingo@2236: */ ingo@2236: protected List buildXAxisSections() { ingo@2236: List axisSections = new ArrayList(); ingo@2236: ingo@2236: String identifier = "X"; ingo@2236: ingo@2236: AxisSection axisSection = new AxisSection(); ingo@2236: axisSection.setIdentifier(identifier); ingo@2236: axisSection.setLabel(getXAxisLabel()); ingo@2236: axisSection.setFontSize(14); ingo@2236: axisSection.setFixed(false); ingo@2236: ingo@2236: // XXX We are able to find better default ranges that [0,0], but the Y ingo@2236: // axes currently have no better ranges set. ingo@2236: axisSection.setUpperRange(0d); ingo@2236: axisSection.setLowerRange(0d); ingo@2236: ingo@2236: axisSections.add(axisSection); ingo@2236: ingo@2236: return axisSections; ingo@2236: } ingo@2236: ingo@2236: ingo@2236: /** ingo@2236: * Creates a list of Section for the chart's Y axes. This method makes use ingo@2236: * of getYAxisWalker to be able to access all Y axes defined in ingo@2236: * subclasses. ingo@2236: * ingo@2236: * @return a list of Y axis sections. ingo@2236: */ ingo@2236: protected List buildYAxisSections() { ingo@2236: List axisSections = new ArrayList(); ingo@2236: ingo@2236: YAxisWalker walker = getYAxisWalker(); ingo@2236: for (int i = 0, n = walker.length(); i < n; i++) { ingo@2236: AxisSection ySection = new AxisSection(); ingo@2236: ySection.setIdentifier(walker.getId(i)); ingo@2236: ySection.setLabel(getYAxisLabel(i)); ingo@2236: ySection.setFontSize(14); ingo@2236: ySection.setFixed(false); ingo@2236: ingo@2236: // XXX We are able to find better default ranges that [0,0], the ingo@2236: // only problem is, that we do NOT have a better range than [0,0] ingo@2236: // for each axis, because the initial chart will not have a dataset ingo@2236: // for each axis set! ingo@2236: ySection.setUpperRange(0d); ingo@2236: ySection.setLowerRange(0d); ingo@2236: ingo@2236: axisSections.add(ySection); ingo@2236: } ingo@2236: ingo@2236: return axisSections; ingo@2236: } ingo@2236: ingo@2236: ingo@2236: /** ingo@2236: * Returns the settings as ChartSettings. ingo@2236: * ingo@2236: * @return the settings as ChartSettings or null, if ingo@2236: * settings is not an instance of ChartSettings. ingo@2236: */ ingo@2236: public ChartSettings getChartSettings() { ingo@2236: if (settings instanceof ChartSettings) { ingo@2236: return (ChartSettings) settings; ingo@2236: } ingo@2236: ingo@2236: return null; ingo@2236: } ingo@2236: ingo@2236: ingo@2236: /** ingo@2047: * Returns the chart title provided by settings. ingo@2047: * ingo@2047: * @param settings A ChartSettings object. ingo@2047: * ingo@2047: * @return the title provided by settings or null if no ingo@2047: * ChartSection is provided by settings. ingo@2047: * ingo@2047: * @throws NullPointerException if settings is null. ingo@2047: */ ingo@2047: public String getChartTitle(ChartSettings settings) { ingo@2047: ChartSection cs = settings.getChartSection(); ingo@2047: return cs != null ? cs.getTitle() : null; ingo@2047: } ingo@2047: ingo@2047: ingo@2047: /** ingo@2047: * Returns the chart subtitle provided by settings. ingo@2047: * ingo@2047: * @param settings A ChartSettings object. ingo@2047: * ingo@2047: * @return the subtitle provided by settings or null if no ingo@2047: * ChartSection is provided by settings. ingo@2047: * ingo@2047: * @throws NullPointerException if settings is null. ingo@2047: */ ingo@2047: public String getChartSubtitle(ChartSettings settings) { ingo@2047: ChartSection cs = settings.getChartSection(); ingo@2047: return cs != null ? cs.getSubtitle() : null; ingo@2047: } ingo@2047: ingo@2047: ingo@2047: /** ingo@2047: * Returns a boolean object that determines if the chart grid should be ingo@2047: * visible or not. This information needs to be provided by settings, ingo@2047: * otherweise the default is true. ingo@2047: * ingo@2047: * @param settings A ChartSettings object. ingo@2047: * ingo@2047: * @return true, if the chart grid should be visible otherwise false. ingo@2047: * ingo@2047: * @throws NullPointerException if settings is null. ingo@2047: */ ingo@2047: public boolean isGridVisible(ChartSettings settings) { ingo@2047: ChartSection cs = settings.getChartSection(); ingo@2047: Boolean displayGrid = cs.getDisplayGrid(); ingo@2047: ingo@2047: return displayGrid != null ? displayGrid : true; ingo@2047: } ingo@2047: ingo@2047: ingo@2047: /** ingo@2047: * Returns a boolean object that determines if the chart legend should be ingo@2047: * visible or not. This information needs to be provided by settings, ingo@2047: * otherwise the default is true. ingo@2047: * ingo@2047: * @param settings A ChartSettings object. ingo@2047: * ingo@2047: * @return true, if the chart legend should be visible otherwise false. ingo@2047: * ingo@2047: * @throws NullPointerException if settings is null. ingo@2047: */ ingo@2047: public boolean isLegendVisible(ChartSettings settings) { ingo@2047: LegendSection ls = settings.getLegendSection(); ingo@2047: Boolean displayLegend = ls.getVisibility(); ingo@2047: ingo@2047: return displayLegend != null ? displayLegend : true; ingo@2047: } ingo@2047: ingo@2047: ingo@2047: /** ingo@2047: * Returns the legend font size specified in settings or null if no ingo@2047: * LegendSection is provided by settings. ingo@2047: * ingo@2047: * @param settings A ChartSettings object. ingo@2047: * ingo@2047: * @return the legend font size or null. ingo@2047: * ingo@2047: * @throws NullPointerException if settings is null. ingo@2047: */ ingo@2047: public Integer getLegendFontSize(ChartSettings settings) { ingo@2047: LegendSection ls = settings.getLegendSection(); ingo@2047: return ls != null ? ls.getFontSize() : null; ingo@2047: } ingo@2047: ingo@2047: ingo@2233: /** ingo@2234: * Returns the title of a chart. The return value depends on the existence ingo@2234: * of ChartSettings: if there are ChartSettings set, this method returns the ingo@2234: * chart title provided by those settings. Otherwise, this method returns ingo@2234: * getDefaultChartTitle(). ingo@2234: * ingo@2234: * @return the title of a chart. ingo@2234: */ ingo@2234: protected String getChartTitle() { ingo@2234: ChartSettings chartSettings = getChartSettings(); ingo@2234: ingo@2234: if (chartSettings != null) { ingo@2234: return getChartTitle(chartSettings); ingo@2234: } ingo@2234: ingo@2234: return getDefaultChartTitle(); ingo@2234: } ingo@2234: ingo@2234: ingo@2234: /** ingo@2234: * Returns the subtitle of a chart. The return value depends on the ingo@2234: * existence of ChartSettings: if there are ChartSettings set, this method ingo@2234: * returns the chart title provided by those settings. Otherwise, this ingo@2234: * method returns getDefaultChartSubtitle(). ingo@2234: * ingo@2234: * @return the subtitle of a chart. ingo@2234: */ ingo@2234: protected String getChartSubtitle() { ingo@2234: ChartSettings chartSettings = getChartSettings(); ingo@2234: ingo@2234: if (chartSettings != null) { ingo@2234: return getChartSubtitle(chartSettings); ingo@2234: } ingo@2234: ingo@2234: return getDefaultChartSubtitle(); ingo@2234: } ingo@2234: ingo@2234: ingo@2234: /** ingo@2234: * This method always returns null. Override it in subclasses that require ingo@2234: * subtitles. ingo@2234: * ingo@2234: * @return null. ingo@2234: */ ingo@2234: protected String getDefaultChartSubtitle() { ingo@2234: // Override this method in subclasses ingo@2234: return null; ingo@2234: } ingo@2234: ingo@2234: ingo@2234: /** ingo@2234: * This method is used to determine, if the chart's legend is visible or ingo@2234: * not. If a settings instance is set, this instance determines the ingo@2234: * visibility otherwise, this method returns true as default if no ingo@2234: * settings is set. ingo@2234: * ingo@2234: * @return true, if the legend should be visible, otherwise false. ingo@2234: */ ingo@2234: protected boolean isLegendVisible() { ingo@2234: ChartSettings chartSettings = getChartSettings(); ingo@2234: if (chartSettings != null) { ingo@2234: return isLegendVisible(chartSettings); ingo@2234: } ingo@2234: ingo@2234: return true; ingo@2234: } ingo@2234: ingo@2234: felix@3618: /** Where to place the logo. */ felix@3618: protected String logoHPlace() { felix@3617: ChartSettings chartSettings = getChartSettings(); felix@3617: if (chartSettings != null) { felix@3617: ChartSection cs = chartSettings.getChartSection(); felix@3618: String place = cs.getLogoHPlacement(); felix@3618: felix@3618: return place; felix@3618: } felix@3639: return "center"; felix@3618: } felix@3618: felix@3618: felix@3618: /** Where to place the logo. */ felix@3618: protected String logoVPlace() { felix@3618: ChartSettings chartSettings = getChartSettings(); felix@3618: if (chartSettings != null) { felix@3618: ChartSection cs = chartSettings.getChartSection(); felix@3618: String place = cs.getLogoVPlacement(); felix@3617: felix@3617: return place; felix@3617: } felix@3639: return "top"; felix@3617: } felix@3617: felix@3617: felix@3615: /** Return the logo id from settings. */ felix@3615: protected String showLogo(ChartSettings chartSettings) { felix@3615: if (chartSettings != null) { felix@3615: ChartSection cs = chartSettings.getChartSection(); felix@3615: String logo = cs.getDisplayLogo(); felix@3615: felix@3615: return logo; felix@3615: } felix@3617: return "none"; felix@3615: } felix@3615: felix@3615: felix@3615: /** felix@3615: * This method is used to determine if a logo should be added to the plot. felix@3615: * felix@3615: * @return logo name (null if none). felix@3615: */ felix@3615: protected String showLogo() { felix@3615: ChartSettings chartSettings = getChartSettings(); felix@3615: return showLogo(chartSettings); felix@3615: } felix@3615: felix@3615: ingo@2234: /** ingo@2234: * This method is used to determine the font size of the chart's legend. If ingo@2234: * a settings instance is set, this instance determines the font ingo@2234: * size, otherwise this method returns 12 as default if no settings ingo@2234: * is set or if it doesn't provide a legend font size. ingo@2234: * ingo@2234: * @return a legend font size. ingo@2234: */ ingo@2234: protected int getLegendFontSize() { ingo@2234: Integer fontSize = null; ingo@2234: ingo@2234: ChartSettings chartSettings = getChartSettings(); ingo@2234: if (chartSettings != null) { ingo@2234: fontSize = getLegendFontSize(chartSettings); ingo@2234: } ingo@2234: ingo@2234: return fontSize != null ? fontSize : DEFAULT_FONT_SIZE; ingo@2234: } ingo@2234: ingo@2234: ingo@2234: /** ingo@2234: * This method is used to determine if the resulting chart should display ingo@2234: * grid lines or not. Note: this method always returns true! ingo@2234: * ingo@2234: * @return true, if the chart should display grid lines, otherwise false. ingo@2234: */ ingo@2234: protected boolean isGridVisible() { ingo@2234: return true; ingo@2234: } ingo@2234: ingo@2234: ingo@2234: /** ingo@2234: * Returns the X-Axis label of a chart. ingo@2234: * ingo@2234: * @return the X-Axis label of a chart. ingo@2234: */ ingo@2234: protected String getXAxisLabel() { ingo@2234: ChartSettings chartSettings = getChartSettings(); ingo@2234: if (chartSettings == null) { ingo@2234: return getDefaultXAxisLabel(); ingo@2234: } ingo@2234: ingo@2234: AxisSection as = chartSettings.getAxisSection("X"); ingo@2234: if (as != null) { ingo@2234: String label = as.getLabel(); ingo@2234: ingo@2234: if (label != null) { ingo@2234: return label; ingo@2234: } ingo@2234: } ingo@2234: ingo@2234: return getDefaultXAxisLabel(); ingo@2234: } ingo@2234: ingo@2234: ingo@2234: /** ingo@2234: * This method returns the font size for the X axis. If the font size is ingo@2234: * specified in ChartSettings (if chartSettings is set), this size is ingo@2234: * returned. Otherwise the default font size 12 is returned. ingo@2234: * ingo@2234: * @return the font size for the x axis. ingo@2234: */ ingo@2234: protected int getXAxisLabelFontSize() { ingo@2234: ChartSettings chartSettings = getChartSettings(); ingo@2234: if (chartSettings == null) { ingo@2234: return DEFAULT_FONT_SIZE; ingo@2234: } ingo@2234: ingo@2234: AxisSection as = chartSettings.getAxisSection("X"); ingo@2234: Integer fontSize = as.getFontSize(); ingo@2234: ingo@2234: return fontSize != null ? fontSize : DEFAULT_FONT_SIZE; ingo@2234: } ingo@2234: ingo@2234: ingo@2234: /** ingo@2234: * This method returns the font size for an Y axis. If the font size is ingo@2234: * specified in ChartSettings (if chartSettings is set), this size is ingo@2234: * returned. Otherwise the default font size 12 is returned. ingo@2234: * ingo@2234: * @return the font size for the x axis. ingo@2234: */ ingo@2234: protected int getYAxisFontSize(int pos) { ingo@2234: ChartSettings chartSettings = getChartSettings(); ingo@2234: if (chartSettings == null) { ingo@2234: return DEFAULT_FONT_SIZE; ingo@2234: } ingo@2234: ingo@2234: YAxisWalker walker = getYAxisWalker(); ingo@2234: ingo@2234: AxisSection as = chartSettings.getAxisSection(walker.getId(pos)); bjoern@4376: if (as == null) { bjoern@4376: return DEFAULT_FONT_SIZE; bjoern@4376: } ingo@2234: Integer fontSize = as.getFontSize(); ingo@2234: ingo@2234: return fontSize != null ? fontSize : DEFAULT_FONT_SIZE; ingo@2234: } ingo@2234: ingo@2234: ingo@2234: /** ingo@2234: * This method returns the export dimension specified in ChartSettings as ingo@2234: * int array [width,height]. ingo@2234: * ingo@2234: * @return an int array with [width,height]. ingo@2234: */ ingo@2234: protected int[] getExportDimension() { ingo@2234: ChartSettings chartSettings = getChartSettings(); ingo@2234: if (chartSettings == null) { ingo@2234: return new int[] { 600, 400 }; ingo@2234: } ingo@2234: ingo@2234: ExportSection export = chartSettings.getExportSection(); ingo@2234: Integer width = export.getWidth(); ingo@2234: Integer height = export.getHeight(); ingo@2234: ingo@2234: if (width != null && height != null) { ingo@2234: return new int[] { width, height }; ingo@2234: } ingo@2234: ingo@2234: return new int[] { 600, 400 }; ingo@2234: } ingo@2234: ingo@2234: ingo@2234: /** ingo@2233: * Returns the Y-Axis label of a chart at position pos. ingo@2233: * ingo@2233: * @return the Y-Axis label of a chart at position 0. ingo@2233: */ ingo@2233: protected String getYAxisLabel(int pos) { ingo@2233: ChartSettings chartSettings = getChartSettings(); ingo@2233: if (chartSettings == null) { ingo@2233: return getDefaultYAxisLabel(pos); ingo@2233: } ingo@2233: ingo@2233: YAxisWalker walker = getYAxisWalker(); ingo@2233: AxisSection as = chartSettings.getAxisSection(walker.getId(pos)); ingo@2233: if (as != null) { ingo@2233: String label = as.getLabel(); ingo@2233: ingo@2233: if (label != null) { ingo@2233: return label; ingo@2233: } ingo@2233: } ingo@2233: ingo@2233: return getDefaultYAxisLabel(pos); ingo@2233: } ingo@2233: ingo@2233: ingo@2234: /** ingo@2236: * This method searches for a specific axis in the settings if ingo@2236: * settings is set. If the axis was found, this method returns the ingo@2236: * specified axis range if the axis range is fixed. Otherwise, this method ingo@2236: * returns null. ingo@2236: * ingo@2236: * @param axisId The identifier of an axis. ingo@2236: * ingo@2236: * @return the specified axis range from settings if the axis is ingo@2236: * fixed, otherwise null. ingo@2236: */ ingo@2236: public Range getRangeForAxisFromSettings(String axisId) { ingo@2236: ChartSettings chartSettings = getChartSettings(); ingo@2236: if (chartSettings == null) { ingo@2236: return null; ingo@2236: } ingo@2236: ingo@2236: AxisSection as = chartSettings.getAxisSection(axisId); bjoern@4376: bjoern@4376: if (as == null) { bjoern@4376: return null; bjoern@4376: } bjoern@4376: ingo@2236: Boolean fixed = as.isFixed(); ingo@2236: ingo@2236: if (fixed != null && fixed) { ingo@2236: Double upper = as.getUpperRange(); ingo@2236: Double lower = as.getLowerRange(); ingo@2236: ingo@2236: if (upper != null && lower != null) { ingo@2236: return lower < upper ingo@2236: ? new Range(lower, upper) ingo@2236: : new Range(upper, lower); ingo@2236: } ingo@2236: } ingo@2236: ingo@2236: return null; ingo@2236: } ingo@2236: ingo@2236: ingo@2236: /** ingo@2238: * Adds a new AxisDataset which contains dataset at index idx. ingo@2238: * ingo@2238: * @param dataset An XYDataset. ingo@2238: * @param idx The axis index. ingo@2238: * @param visible Determines, if the dataset should be visible or not. ingo@2238: */ ingo@2238: public void addAxisDataset(XYDataset dataset, int idx, boolean visible) { ingo@2238: if (dataset == null || idx < 0) { ingo@2238: return; ingo@2238: } ingo@2238: ingo@2238: AxisDataset axisDataset = getAxisDataset(idx); ingo@2238: ingo@2587: Bounds[] xyBounds = ChartHelper.getBounds(dataset); ingo@2238: ingo@2587: if (xyBounds == null) { ingo@2242: logger.warn("Skip XYDataset for Axis (invalid ranges): " + idx); ingo@2242: return; ingo@2242: } ingo@2242: ingo@2238: if (visible) { ingo@2242: if (logger.isDebugEnabled()) { ingo@2242: logger.debug("Add new AxisDataset at index: " + idx); ingo@2587: logger.debug("X extent: " + xyBounds[0]); ingo@2587: logger.debug("Y extent: " + xyBounds[1]); ingo@2242: } ingo@2242: ingo@2238: axisDataset.addDataset(dataset); ingo@2238: } ingo@2238: ingo@2587: combineXBounds(xyBounds[0], 0); ingo@2587: combineYBounds(xyBounds[1], idx); ingo@2238: } ingo@2238: ingo@2238: ingo@2238: /** ingo@2238: * This method grants access to the AxisDatasets stored in datasets. ingo@2238: * If no AxisDataset exists for index idx, a new AxisDataset is ingo@2238: * created using createAxisDataset(). ingo@2238: * ingo@2238: * @param idx The index of the desired AxisDataset. ingo@2238: * ingo@2238: * @return an existing or new AxisDataset. ingo@2238: */ ingo@2238: public AxisDataset getAxisDataset(int idx) { ingo@2238: AxisDataset axisDataset = datasets.get(idx); ingo@2238: ingo@2238: if (axisDataset == null) { ingo@2238: axisDataset = createAxisDataset(idx); ingo@2238: datasets.put(idx, axisDataset); ingo@2238: } ingo@2238: ingo@2238: return axisDataset; ingo@2238: } ingo@2238: ingo@2238: ingo@2238: /** ingo@2422: * Adjust some Stroke/Grid parameters for plot. The chart ingo@2422: * Settings are applied in this method. ingo@2422: * ingo@2422: * @param plot The XYPlot which is adapted. ingo@2422: */ ingo@2422: protected void adjustPlot(XYPlot plot) { ingo@2422: Stroke gridStroke = new BasicStroke( ingo@2422: DEFAULT_GRID_LINE_WIDTH, ingo@2422: BasicStroke.CAP_BUTT, ingo@2422: BasicStroke.JOIN_MITER, ingo@2422: 3.0f, ingo@2422: new float[] { 3.0f }, ingo@2422: 0.0f); ingo@2422: ingo@2422: ChartSettings cs = getChartSettings(); ingo@2422: boolean isGridVisible = cs != null ? isGridVisible(cs) : true; ingo@2422: ingo@2422: plot.setDomainGridlineStroke(gridStroke); ingo@2422: plot.setDomainGridlinePaint(DEFAULT_GRID_COLOR); ingo@2422: plot.setDomainGridlinesVisible(isGridVisible); ingo@2422: ingo@2422: plot.setRangeGridlineStroke(gridStroke); ingo@2422: plot.setRangeGridlinePaint(DEFAULT_GRID_COLOR); ingo@2422: plot.setRangeGridlinesVisible(isGridVisible); ingo@2422: ingo@2422: plot.setAxisOffset(new RectangleInsets(0d, 0d, 0d, 0d)); ingo@2422: } ingo@2422: ingo@2422: ingo@2422: /** ingo@2234: * This helper mehtod is used to extract the current locale from instance ingo@2234: * vairable context. ingo@2234: * ingo@2234: * @return the current locale. ingo@2234: */ ingo@1645: protected Locale getLocale() { ingo@1645: CallMeta meta = context.getMeta(); ingo@1645: PreferredLocale[] prefs = meta.getLanguages(); ingo@1645: ingo@1645: int len = prefs != null ? prefs.length : 0; ingo@1645: ingo@1645: Locale[] locales = new Locale[len]; ingo@1645: ingo@1645: for (int i = 0; i < len; i++) { ingo@1645: locales[i] = prefs[i].getLocale(); ingo@1645: } ingo@1645: ingo@1645: return meta.getPreferredLocale(locales); ingo@1645: } ingo@1645: ingo@1645: felix@4589: /** felix@4589: * Look up \param key in i18n dictionary. felix@4589: * @param key key for which to find i18nd version. felix@4589: * @param def default, returned if lookup failed. felix@4589: * @return value found in i18n dictionary, \param def if no value found. felix@4589: */ ingo@408: protected String msg(String key, String def) { ingo@408: return Resources.getMsg(context.getMeta(), key, def); ingo@408: } ingo@408: felix@4589: /** felix@4589: * Look up \param key in i18n dictionary. felix@4589: * @param key key for which to find i18nd version. felix@4589: * @return value found in i18n dictionary, key itself if failed. felix@4589: */ sascha@2407: protected String msg(String key) { sascha@2407: return Resources.getMsg(context.getMeta(), key, key); sascha@2407: } ingo@408: ingo@412: protected String msg(String key, String def, Object[] args) { ingo@412: return Resources.getMsg(context.getMeta(), key, def, args); ingo@412: } ingo@412: ingo@412: ingo@412: protected String getRiverName() { teichmann@5867: D4EArtifact flys = (D4EArtifact) master; ingo@412: teichmann@5865: River river = RiverUtils.getRiver(flys); felix@1104: return (river != null) ? river.getName() : ""; ingo@412: } ingo@412: ingo@412: ingo@412: protected double[] getRange() { teichmann@5867: D4EArtifact flys = (D4EArtifact) master; ingo@412: teichmann@6101: RangeAccess rangeAccess = new RangeAccess(flys); felix@4858: return rangeAccess.getKmRange(); ingo@412: } ingo@412: ingo@412: ingo@423: /** ingo@423: * Returns the size of a chart export as array which has been specified by ingo@423: * the incoming request document. ingo@423: * ingo@2057: * @return the size of a chart as [width, height] or null if no width or ingo@2057: * height are given in the request document. ingo@423: */ ingo@423: protected int[] getSize() { ingo@423: int[] size = new int[2]; ingo@423: sascha@719: Element sizeEl = (Element)XMLUtils.xpath( ingo@423: request, ingo@423: XPATH_CHART_SIZE, ingo@423: XPathConstants.NODE, ingo@423: ArtifactNamespaceContext.INSTANCE); ingo@423: ingo@423: if (sizeEl != null) { sascha@719: String uri = ArtifactNamespaceContext.NAMESPACE_URI; ingo@423: sascha@719: String w = sizeEl.getAttributeNS(uri, "width"); sascha@719: String h = sizeEl.getAttributeNS(uri, "height"); ingo@423: sascha@719: if (w.length() > 0 && h.length() > 0) { ingo@423: try { ingo@423: size[0] = Integer.parseInt(w); ingo@423: size[1] = Integer.parseInt(h); ingo@423: } ingo@423: catch (NumberFormatException nfe) { ingo@423: logger.warn("Wrong values for chart width/height."); ingo@423: } ingo@423: } ingo@423: } ingo@423: ingo@2057: return size[0] > 0 && size[1] > 0 ? size : null; ingo@423: } ingo@423: ingo@423: ingo@2234: /** ingo@2234: * This method returns the format specified in the request document ingo@2234: * or DEFAULT_CHART_FORMAT if no format is specified in ingo@2234: * request. ingo@2234: * ingo@2234: * @return the format used to export this chart. ingo@2234: */ ingo@1735: protected String getFormat() { ingo@1735: String format = (String) XMLUtils.xpath( ingo@1735: request, ingo@1735: XPATH_CHART_FORMAT, ingo@1735: XPathConstants.STRING, ingo@1735: ArtifactNamespaceContext.INSTANCE); ingo@1735: ingo@1735: return format == null || format.length() == 0 ingo@1735: ? DEFAULT_CHART_FORMAT ingo@1735: : format; ingo@1735: } ingo@1735: ingo@1735: felix@1944: /** ingo@2395: * Returns the X-Axis range as String array from request document. felix@4276: * If the (x|y)range elements are not found in request document, return felix@4276: * null (i.e. not zoomed). ingo@2395: * felix@4276: * @return a String array with [lower, upper], null if not in document. felix@1944: */ ingo@2395: protected String[] getDomainAxisRangeFromRequest() { sascha@719: Element xrange = (Element)XMLUtils.xpath( ingo@652: request, ingo@652: XPATH_CHART_X_RANGE, ingo@652: XPathConstants.NODE, ingo@652: ArtifactNamespaceContext.INSTANCE); ingo@652: ingo@652: if (xrange == null) { ingo@652: return null; ingo@652: } ingo@652: sascha@719: String uri = ArtifactNamespaceContext.NAMESPACE_URI; ingo@652: sascha@719: String lower = xrange.getAttributeNS(uri, "from"); sascha@719: String upper = xrange.getAttributeNS(uri, "to"); ingo@652: ingo@2395: return new String[] { lower, upper }; ingo@652: } ingo@652: ingo@652: felix@4276: /** Returns null if the (x|y)range-element was not found in request document. felix@4276: * This usally means that the axis are not manually zoomed, i.e. showing felix@4276: * full data extent. */ ingo@2398: protected String[] getValueAxisRangeFromRequest() { sascha@719: Element yrange = (Element)XMLUtils.xpath( ingo@652: request, ingo@652: XPATH_CHART_Y_RANGE, ingo@652: XPathConstants.NODE, ingo@652: ArtifactNamespaceContext.INSTANCE); ingo@652: ingo@652: if (yrange == null) { ingo@652: return null; ingo@652: } ingo@652: felix@4433: sascha@719: String uri = ArtifactNamespaceContext.NAMESPACE_URI; ingo@652: sascha@719: String lower = yrange.getAttributeNS(uri, "from"); sascha@719: String upper = yrange.getAttributeNS(uri, "to"); ingo@652: ingo@2398: return new String[] { lower, upper }; ingo@652: } ingo@652: ingo@652: ingo@423: /** ingo@423: * Returns the default size of a chart export as array. ingo@423: * ingo@423: * @return the default size of a chart as [width, height]. ingo@423: */ ingo@423: protected int[] getDefaultSize() { ingo@423: return new int[] { DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT }; ingo@423: } ingo@423: ingo@423: ingo@1979: /** ingo@2242: * Add datasets stored in instance variable datasets to plot. ingo@2242: * datasets actually stores instances of AxisDataset, so each of this ingo@2242: * datasets is mapped to a specific axis as well. ingo@2242: * ingo@2242: * @param plot plot to add datasets to. ingo@2242: */ ingo@2242: protected void addDatasets(XYPlot plot) { christian@3155: logger.debug("addDatasets()"); sascha@3160: ingo@2242: // AxisDatasets are sorted, but some might be empty. ingo@2242: // Thus, generate numbering on the fly. ingo@2242: int axisIndex = 0; ingo@2242: int datasetIndex = 0; ingo@2242: ingo@2242: for (Map.Entry entry: datasets.entrySet()) { ingo@2242: if (!entry.getValue().isEmpty()) { ingo@2242: // Add axis and range information. ingo@2242: AxisDataset axisDataset = entry.getValue(); christian@3155: NumberAxis axis = createYAxis(entry.getKey()); ingo@2242: ingo@2242: plot.setRangeAxis(axisIndex, axis); ingo@2242: ingo@2242: if (axis.getAutoRangeIncludesZero()) { ingo@2242: axisDataset.setRange( ingo@2242: Range.expandToInclude(axisDataset.getRange(), 0d)); ingo@2242: } ingo@2242: ingo@2587: setYBounds(axisIndex, expandPointRange(axisDataset.getRange())); ingo@2242: ingo@2242: // Add contained datasets, mapping to axis. ingo@2242: for (XYDataset dataset: axisDataset.getDatasets()) { ingo@2242: plot.setDataset(datasetIndex, dataset); ingo@2242: plot.mapDatasetToRangeAxis(datasetIndex, axisIndex); ingo@2242: felix@2677: applyThemes(plot, dataset, ingo@2242: datasetIndex, felix@2677: axisDataset.isArea(dataset)); ingo@2242: ingo@2242: datasetIndex++; ingo@2242: } ingo@2242: ingo@2242: axisDataset.setPlotAxisIndex(axisIndex); ingo@2242: axisIndex++; ingo@2242: } ingo@2242: } ingo@2242: } ingo@2242: ingo@2242: felix@2677: /** ingo@2242: * @param idx "index" of dataset/series (first dataset to be drawn has ingo@2242: * index 0), correlates with renderer index. ingo@2242: * @param isArea true if the series describes an area and shall be rendered ingo@2242: * as such. ingo@2242: */ ingo@2242: protected void applyThemes( ingo@2242: XYPlot plot, ingo@2242: XYDataset series, ingo@2242: int idx, ingo@2242: boolean isArea ingo@2242: ) { ingo@2242: if (isArea) { ingo@2242: applyAreaTheme(plot, (StyledAreaSeriesCollection) series, idx); ingo@2242: } ingo@2242: else { ingo@2242: applyLineTheme(plot, series, idx); ingo@2242: } ingo@2242: } ingo@2242: ingo@2242: ingo@2242: /** ingo@2242: * This method applies the themes defined in the series itself. Therefore, ingo@2242: * StyledXYSeries.applyTheme() is called, which modifies the renderer ingo@2242: * for the series. ingo@2242: * ingo@2242: * @param plot The plot. ingo@2242: * @param dataset The XYDataset which needs to support Series objects. ingo@2242: * @param idx The index of the renderer / dataset. ingo@2242: */ ingo@2242: protected void applyLineTheme(XYPlot plot, XYDataset dataset, int idx) { ingo@2321: logger.debug("Apply LineTheme for dataset at index: " + idx); ingo@2321: ingo@2242: LegendItemCollection lic = new LegendItemCollection(); ingo@2242: LegendItemCollection anno = plot.getFixedLegendItems(); ingo@2242: ingo@2242: Font legendFont = createLegendLabelFont(); ingo@2242: ingo@2242: XYLineAndShapeRenderer renderer = createRenderer(plot, idx); ingo@2242: ingo@2242: for (int s = 0, num = dataset.getSeriesCount(); s < num; s++) { ingo@2242: Series series = getSeriesOf(dataset, s); ingo@2242: ingo@2321: if (series instanceof StyledSeries) { ingo@2321: Style style = ((StyledSeries) series).getStyle(); ingo@2321: style.applyTheme(renderer, s); ingo@2242: } ingo@2242: ingo@2242: // special case: if there is just one single item, we need to enable ingo@2242: // points for this series, otherwise we would not see anything in ingo@2242: // the chart area. ingo@2242: if (series.getItemCount() == 1) { ingo@2242: renderer.setSeriesShapesVisible(s, true); ingo@2242: } ingo@2242: ingo@2242: LegendItem legendItem = renderer.getLegendItem(idx, s); raimund@3169: if (legendItem.getLabel().endsWith(" ") || raimund@3169: legendItem.getLabel().endsWith("interpol")) { raimund@3134: legendItem = null; raimund@3134: } raimund@3167: ingo@2242: if (legendItem != null) { ingo@2242: legendItem.setLabelFont(legendFont); ingo@2242: lic.add(legendItem); ingo@2242: } ingo@2242: else { ingo@2242: logger.warn("Could not get LegentItem for renderer: " ingo@2242: + idx + ", series-idx " + s); ingo@2242: } ingo@2242: } ingo@2242: ingo@2242: if (anno != null) { ingo@2242: lic.addAll(anno); ingo@2242: } ingo@2242: ingo@2242: plot.setFixedLegendItems(lic); ingo@2242: ingo@2242: plot.setRenderer(idx, renderer); ingo@2242: } ingo@2242: ingo@2242: ingo@2242: /** ingo@2242: * @param plot The plot. ingo@2242: * @param area A StyledAreaSeriesCollection object. ingo@2242: * @param idx The index of the dataset. ingo@2242: */ ingo@2242: protected void applyAreaTheme( ingo@2242: XYPlot plot, ingo@2242: StyledAreaSeriesCollection area, ingo@2242: int idx ingo@2242: ) { ingo@2242: LegendItemCollection lic = new LegendItemCollection(); ingo@2242: LegendItemCollection anno = plot.getFixedLegendItems(); ingo@2242: ingo@2242: Font legendFont = createLegendLabelFont(); ingo@2242: ingo@2242: logger.debug("Registering an 'area'renderer at idx: " + idx); ingo@2242: ingo@2242: StableXYDifferenceRenderer dRenderer = ingo@2242: new StableXYDifferenceRenderer(); ingo@2242: ingo@2242: if (area.getMode() == StyledAreaSeriesCollection.FILL_MODE.UNDER) { ingo@2242: dRenderer.setPositivePaint(createTransparentPaint()); ingo@2242: } ingo@2242: ingo@2242: plot.setRenderer(idx, dRenderer); ingo@2242: ingo@2242: area.applyTheme(dRenderer); ingo@2242: ingo@3785: // i18n ingo@3785: dRenderer.setAreaLabelNumberFormat(Formatter.getFormatter(context.getMeta(), 2, 4)); ingo@3785: ingo@3785: dRenderer.setAreaLabelTemplate(Resources.getMsg( ingo@3785: context.getMeta(), "area.label.template", "Area=%sm2")); ingo@3785: ingo@2242: LegendItem legendItem = dRenderer.getLegendItem(idx, 0); ingo@2242: if (legendItem != null) { ingo@2242: legendItem.setLabelFont(legendFont); ingo@2242: lic.add(legendItem); ingo@2242: } ingo@2242: else { ingo@2242: logger.warn("Could not get LegentItem for renderer: " ingo@2242: + idx + ", series-idx " + 0); ingo@2242: } ingo@2242: ingo@2242: if (anno != null) { ingo@2242: lic.addAll(anno); ingo@2242: } ingo@2242: ingo@2242: plot.setFixedLegendItems(lic); ingo@2242: } ingo@2242: ingo@2242: ingo@2242: /** ingo@2242: * Expands a given range if it collapses into one point. ingo@2242: * felix@3270: * @param range Range to be expanded if upper == lower bound. felix@3248: * felix@3248: * @return Bounds of point plus 5 percent in each direction. ingo@2242: */ ingo@2587: private Bounds expandPointRange(Range range) { ingo@2587: if (range == null) { ingo@2587: return null; ingo@2242: } ingo@2587: else if (range.getLowerBound() == range.getUpperBound()) { felix@3248: Range expandedRange = ChartHelper.expandRange(range, 5d); felix@3248: return new DoubleBounds(expandedRange.getLowerBound(), expandedRange.getUpperBound()); ingo@2587: } ingo@2587: ingo@2587: return new DoubleBounds(range.getLowerBound(), range.getUpperBound()); ingo@2242: } ingo@2242: ingo@2242: ingo@2242: /** ingo@2242: * Creates a new instance of EnhancedLineAndShapeRenderer. ingo@2242: * ingo@2242: * @param plot The plot which is set for the new renderer. ingo@2242: * @param idx This value is not used in the current implementation. ingo@2242: * ingo@2242: * @return a new instance of EnhancedLineAndShapeRenderer. ingo@2242: */ ingo@2242: protected XYLineAndShapeRenderer createRenderer(XYPlot plot, int idx) { ingo@2242: logger.debug("Create EnhancedLineAndShapeRenderer for idx: " + idx); ingo@2242: ingo@2242: EnhancedLineAndShapeRenderer r = ingo@2242: new EnhancedLineAndShapeRenderer(true, false); ingo@2242: ingo@2242: r.setPlot(plot); ingo@2242: ingo@2242: return r; ingo@2242: } ingo@2242: ingo@2242: ingo@2242: /** ingo@2233: * Creates a new instance of IdentifiableNumberAxis. ingo@2233: * ingo@2233: * @param idx The index of the new axis. ingo@2233: * @param label The label of the new axis. ingo@2233: * ingo@2233: * @return an instance of IdentifiableNumberAxis. ingo@2233: */ ingo@2233: protected NumberAxis createNumberAxis(int idx, String label) { ingo@2233: YAxisWalker walker = getYAxisWalker(); ingo@2233: ingo@2233: return new IdentifiableNumberAxis(walker.getId(idx), label); ingo@2233: } ingo@2234: ingo@2234: ingo@2236: /** ingo@2238: * Create Y (range) axis for given index. ingo@2238: * Shall be overriden by subclasses. ingo@2238: */ ingo@2238: protected NumberAxis createYAxis(int index) { ingo@2238: YAxisWalker walker = getYAxisWalker(); ingo@2238: ingo@2238: Font labelFont = new Font( ingo@2238: DEFAULT_FONT_NAME, ingo@2238: Font.BOLD, ingo@2238: getYAxisFontSize(index)); ingo@2238: ingo@2238: IdentifiableNumberAxis axis = new IdentifiableNumberAxis( ingo@2238: walker.getId(index), ingo@2238: getYAxisLabel(index)); ingo@2238: ingo@2238: axis.setAutoRangeIncludesZero(false); ingo@2238: axis.setLabelFont(labelFont); ingo@2590: axis.setTickLabelFont(labelFont); ingo@2238: ingo@2238: return axis; ingo@2238: } ingo@2238: ingo@2238: ingo@2238: /** ingo@2236: * Creates a new LegendItem with name and font provided by ingo@2236: * createLegendLabelFont(). ingo@2236: * ingo@2236: * @param theme The theme of the chart line. felix@3270: * @param name The displayed name of the item. ingo@2236: * ingo@2236: * @return a new LegendItem instance. ingo@2236: */ teichmann@6905: public LegendItem createLegendItem(ThemeDocument theme, String name) { ingo@2236: // OPTIMIZE Pass font, parsed Theme items. ingo@2236: teichmann@6908: Color color = theme.parseLineColorField(); teichmann@6908: if (color == null) { teichmann@6908: color = Color.BLACK; teichmann@6908: } teichmann@6908: ingo@2236: LegendItem legendItem = new LegendItem(name, color); ingo@2236: ingo@2236: legendItem.setLabelFont(createLegendLabelFont()); ingo@2236: return legendItem; ingo@2236: } ingo@2236: ingo@2236: ingo@2236: /** ingo@2236: * Creates Font (Family and size) to use when creating Legend Items. The ingo@2236: * font size depends in the return value of getLegendFontSize(). ingo@2236: * ingo@2236: * @return a new Font instance with DEFAULT_FONT_NAME. ingo@2236: */ ingo@2236: protected Font createLegendLabelFont() { ingo@2236: return new Font( ingo@2236: DEFAULT_FONT_NAME, ingo@2236: Font.PLAIN, ingo@2236: getLegendFontSize() ingo@2236: ); ingo@2236: } ingo@2236: ingo@2236: ingo@2242: /** felix@3184: * Create new legend entries, dependent on settings. felix@3184: * @param plot The plot for which to modify the legend. felix@3184: */ felix@3184: public void aggregateLegendEntries(XYPlot plot) { felix@3184: int AGGR_THRESHOLD = 0; felix@3184: raimund@3296: if (getChartSettings() == null) { raimund@3296: return; raimund@3296: } felix@3184: Integer threshold = getChartSettings().getLegendSection() felix@3184: .getAggregationThreshold(); felix@3184: felix@3184: AGGR_THRESHOLD = (threshold != null) ? threshold.intValue() : 0; felix@3184: felix@3184: LegendProcessor.aggregateLegendEntries(plot, AGGR_THRESHOLD); felix@3184: } felix@3184: felix@3184: felix@3184: /** ingo@2242: * Returns a transparently textured paint. ingo@2242: * ingo@2242: * @return a transparently textured paint. ingo@2242: */ ingo@2242: protected static Paint createTransparentPaint() { ingo@2242: // TODO why not use a transparent color? ingo@2242: BufferedImage texture = new BufferedImage( ingo@2242: 1, 1, BufferedImage.TYPE_4BYTE_ABGR); ingo@2242: ingo@2242: return new TexturePaint( ingo@2242: texture, new Rectangle2D.Double(0d, 0d, 0d, 0d)); ingo@2242: } ingo@2242: ingo@2242: ingo@2234: protected void preparePDFContext(CallContext context) { ingo@2234: int[] dimension = getExportDimension(); ingo@2234: ingo@2234: context.putContextValue("chart.width", dimension[0]); ingo@2234: context.putContextValue("chart.height", dimension[1]); ingo@2234: context.putContextValue("chart.marginLeft", 5f); ingo@2234: context.putContextValue("chart.marginRight", 5f); ingo@2234: context.putContextValue("chart.marginTop", 5f); ingo@2234: context.putContextValue("chart.marginBottom", 5f); ingo@2234: context.putContextValue( ingo@2234: "chart.page.format", ingo@2234: ChartExportHelper.DEFAULT_PAGE_SIZE); ingo@2234: } ingo@2234: ingo@2234: ingo@2234: protected void prepareSVGContext(CallContext context) { ingo@2234: int[] dimension = getExportDimension(); ingo@2234: ingo@2234: context.putContextValue("chart.width", dimension[0]); ingo@2234: context.putContextValue("chart.height", dimension[1]); ingo@2234: context.putContextValue( ingo@2234: "chart.encoding", ingo@2234: ChartExportHelper.DEFAULT_ENCODING); ingo@2234: } bjoern@4444: bjoern@4444: /** bjoern@4444: * Retuns the call context. May be null if init hasn't been called yet. bjoern@4444: * bjoern@4444: * @return the CallContext instance bjoern@4444: */ bjoern@4444: public CallContext getCallContext() { bjoern@4444: return context; bjoern@4444: } ingo@348: } ingo@348: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :