ingo@348: package de.intevation.flys.exports; ingo@348: ingo@2553: import java.awt.BasicStroke; ingo@2234: import java.awt.Color; ingo@2236: import java.awt.Font; ingo@2242: import java.awt.Paint; ingo@2553: import java.awt.Stroke; ingo@2242: import java.awt.TexturePaint; ingo@2242: import java.awt.geom.Rectangle2D; ingo@2242: import java.awt.image.BufferedImage; ingo@2236: ingo@348: import java.io.IOException; ingo@348: import java.io.OutputStream; ingo@2236: ingo@2236: import java.util.ArrayList; ingo@2236: import java.util.List; ingo@1645: import java.util.Locale; ingo@2242: import java.util.Map; ingo@2238: import java.util.TreeMap; ingo@2238: import java.util.SortedMap; ingo@348: ingo@423: import javax.xml.xpath.XPathConstants; ingo@423: ingo@348: import org.apache.log4j.Logger; ingo@348: ingo@348: import org.w3c.dom.Document; sascha@719: import org.w3c.dom.Element; ingo@348: ingo@2233: import org.jfree.chart.JFreeChart; ingo@2236: import org.jfree.chart.LegendItem; ingo@2242: import org.jfree.chart.LegendItemCollection; ingo@2233: import org.jfree.chart.axis.NumberAxis; ingo@2242: import org.jfree.chart.plot.XYPlot; ingo@2242: import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; ingo@652: import org.jfree.data.Range; ingo@2242: import org.jfree.data.general.Series; ingo@2238: import org.jfree.data.xy.XYDataset; ingo@2553: import org.jfree.ui.RectangleInsets; ingo@652: ingo@348: import de.intevation.artifacts.Artifact; ingo@412: import de.intevation.artifacts.CallContext; ingo@1645: import de.intevation.artifacts.CallMeta; ingo@1645: import de.intevation.artifacts.PreferredLocale; ingo@412: ingo@423: import de.intevation.artifacts.ArtifactNamespaceContext; ingo@423: import de.intevation.artifacts.common.utils.XMLUtils; ingo@423: ingo@1979: import de.intevation.artifactdatabase.state.ArtifactAndFacet; ingo@1979: import de.intevation.artifactdatabase.state.Settings; ingo@1979: ingo@412: import de.intevation.flys.model.River; ingo@412: raimund@2132: import de.intevation.flys.artifacts.FLYSArtifact; ingo@408: import de.intevation.flys.artifacts.resources.Resources; ingo@2398: import de.intevation.flys.jfree.Bounds; ingo@2587: import de.intevation.flys.jfree.DoubleBounds; ingo@2242: import de.intevation.flys.jfree.EnhancedLineAndShapeRenderer; christian@3212: import de.intevation.flys.jfree.FLYSAnnotation; ingo@2242: import de.intevation.flys.jfree.StableXYDifferenceRenderer; ingo@2242: import de.intevation.flys.jfree.StyledAreaSeriesCollection; ingo@2321: import de.intevation.flys.jfree.Style; ingo@2321: import de.intevation.flys.jfree.StyledSeries; ingo@3227: import de.intevation.flys.themes.ThemeAccess; ingo@1095: import de.intevation.flys.utils.FLYSUtils; ingo@348: 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: * 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: 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: 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. */ christian@3212: protected List annotations; 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: public interface AxisDataset { ingo@2238: ingo@2238: void addDataset(XYDataset dataset); ingo@2238: ingo@2242: XYDataset[] getDatasets(); ingo@2242: ingo@2238: boolean isEmpty(); ingo@2238: ingo@2242: void setRange(Range range); ingo@2242: ingo@2242: Range getRange(); ingo@2242: ingo@2242: boolean isArea(XYDataset dataset); ingo@2242: ingo@2242: void setPlotAxisIndex(int idx); ingo@2242: ingo@2242: int getPlotAxisIndex(); ingo@2242: ingo@2238: } // end of AxisDataset interface 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: */ christian@3212: public void addAnnotations(FLYSAnnotation annotation) { christian@3212: if (annotations == null) { christian@3212: annotations = new ArrayList(); christian@3212: } christian@3212: christian@3212: annotations.add(annotation); christian@3212: } christian@3212: 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: ingo@2234: public abstract void doOut( ingo@2234: ArtifactAndFacet bundle, ingo@2234: Document 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: * ingo@2238: * @param range A new range. 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: * ingo@2587: * @param range A new range. ingo@2587: * @param idx 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: /** ingo@2234: * This method should be used by concrete subclasses to add subtitle to ingo@2234: * chart. The method in this implementation is empty. ingo@2234: * ingo@2234: * @param chart The JFreeChart chart object. ingo@2234: */ ingo@2234: protected void addSubtitles(JFreeChart chart) { ingo@2234: // do nothing ingo@2234: } ingo@2233: ingo@2233: ingo@2234: /** 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: ingo@2234: @Override ingo@412: public void setMasterArtifact(Artifact master) { ingo@412: this.master = master; ingo@412: } ingo@412: ingo@412: ingo@2047: @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()); ingo@2236: chartSection.setDisplayGird(isGridVisible()); 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: 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)); 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); 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@2553: * Adjust some Stroke/Grid parameters for plot. The chart ingo@2553: * Settings are applied in this method. ingo@2553: * ingo@2553: * @param plot The XYPlot which is adapted. ingo@2553: */ ingo@2553: protected void adjustPlot(XYPlot plot) { ingo@2553: Stroke gridStroke = new BasicStroke( ingo@2553: DEFAULT_GRID_LINE_WIDTH, ingo@2553: BasicStroke.CAP_BUTT, ingo@2553: BasicStroke.JOIN_MITER, ingo@2553: 3.0f, ingo@2553: new float[] { 3.0f }, ingo@2553: 0.0f); ingo@2553: ingo@2553: ChartSettings cs = getChartSettings(); ingo@2553: boolean isGridVisible = cs != null ? isGridVisible(cs) : true; ingo@2553: ingo@2553: plot.setDomainGridlineStroke(gridStroke); ingo@2553: plot.setDomainGridlinePaint(DEFAULT_GRID_COLOR); ingo@2553: plot.setDomainGridlinesVisible(isGridVisible); ingo@2553: ingo@2553: plot.setRangeGridlineStroke(gridStroke); ingo@2553: plot.setRangeGridlinePaint(DEFAULT_GRID_COLOR); ingo@2553: plot.setRangeGridlinesVisible(isGridVisible); ingo@2553: ingo@2553: plot.setAxisOffset(new RectangleInsets(0d, 0d, 0d, 0d)); ingo@2553: } ingo@2553: ingo@2553: ingo@2553: /** 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: ingo@408: protected String msg(String key, String def) { ingo@408: return Resources.getMsg(context.getMeta(), key, def); ingo@408: } ingo@408: 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() { raimund@2132: FLYSArtifact flys = (FLYSArtifact) master; ingo@412: felix@1104: River river = FLYSUtils.getRiver(flys); felix@1104: return (river != null) ? river.getName() : ""; ingo@412: } ingo@412: ingo@412: ingo@412: protected double[] getRange() { raimund@2132: FLYSArtifact flys = (FLYSArtifact) master; ingo@412: ingo@1095: return FLYSUtils.getKmRange(flys); 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. ingo@2395: * ingo@2395: * @return a String array with [lower, upper]. 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: 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@1944: 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: * @return idx increased by number of items addded. 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: * @return 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@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: * ingo@2242: * @param Range to be expanded if upper == lower bound. 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()) { ingo@2587: double hi = range.getUpperBound(); ingo@2587: double lo = range.getLowerBound(); ingo@2587: double add = (hi - lo) / 100 * 5; ingo@2587: ingo@2587: return new DoubleBounds(lo-add, hi+add); 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. ingo@2236: * @param The displayed name of the item. ingo@2236: * ingo@2236: * @return a new LegendItem instance. ingo@2236: */ ingo@2236: public LegendItem createLegendItem(Document theme, String name) { ingo@2236: // OPTIMIZE Pass font, parsed Theme items. ingo@2236: ThemeAccess themeAccess = new ThemeAccess(theme); ingo@2236: ingo@2236: Color color = themeAccess.parseLineColorField(); 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: 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: } ingo@348: } ingo@348: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :