ingo@1115: /*
ingo@1115: * Copyright (c) 2010 by Intevation GmbH
ingo@1115: *
ingo@1115: * This program is free software under the LGPL (>=v2.1)
ingo@1115: * Read the file LGPL.txt coming with the software for details
ingo@1115: * or visit http://www.gnu.org/licenses/ if it does not exist.
ingo@1115: */
ingo@1115:
ingo@304: package de.intevation.gnv.chart;
ingo@304:
sascha@1117: import de.intevation.artifacts.common.utils.Config;
sascha@779:
ingo@304: import java.awt.Color;
sascha@779: import java.awt.Font;
ingo@617: import java.awt.Paint;
sascha@779:
ingo@327: import java.awt.geom.Ellipse2D;
sascha@779:
ingo@304: import org.apache.log4j.Logger;
ingo@304:
ingo@304: import org.jfree.chart.StandardChartTheme;
sascha@779:
ingo@304: import org.jfree.chart.plot.XYPlot;
sascha@779:
ingo@617: import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
ingo@617: import org.jfree.chart.renderer.xy.XYBarRenderer;
ingo@327: import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
sascha@779:
ingo@304: import org.jfree.ui.RectangleInsets;
sascha@779:
ingo@304: import org.jfree.util.UnitType;
ingo@304:
ingo@304: import org.w3c.dom.Document;
ingo@304:
ingo@304: /**
ingo@767: * Implementation of JFreeChart's default implementation
ingo@767: * StandardChartTheme
. This class takes an xml document with a
sascha@778: * bunch of parameters and turns it into a ChartTheme
to change
ingo@767: * the appearance of charts.
ingo@767: *
ingo@767: * @author Ingo Weinzierl
ingo@304: */
ingo@304: public class XMLChartTheme
ingo@304: extends StandardChartTheme
ingo@304: {
ingo@767: /**
ingo@767: * Default color.
ingo@767: */
ingo@304: private static final Color DEFAULT_COLOR = Color.BLACK;
ingo@304:
ingo@767: /**
ingo@767: * Logger used for logging with log4j.
ingo@767: */
ingo@304: private Logger log = Logger.getLogger(XMLChartTheme.class);
ingo@304:
ingo@767: /**
ingo@767: * Field storing the visibility of the domain crosshair
ingo@767: */
ingo@304: protected boolean domainCrosshairVisible;
ingo@767:
ingo@767: /**
ingo@767: * Field storing the visibility of the range crosshair
ingo@767: */
ingo@304: protected boolean rangeCrosshairVisible;
sascha@778:
ingo@767: /**
ingo@767: * Field storing the visiblity of lines.
ingo@767: */
ingo@327: protected boolean renderLines;
ingo@767:
ingo@767: /**
ingo@767: * Field storing the visibility of data points
ingo@767: */
ingo@327: protected boolean renderShapes;
ingo@327:
ingo@767: /**
ingo@767: * Field storing the width of a data point
ingo@767: */
ingo@327: protected int pointWidth;
ingo@767:
ingo@767: /**
ingo@767: * Field storing the height of a data point.
ingo@767: */
ingo@327: protected int pointHeight;
ingo@304:
ingo@767: /**
ingo@767: * Field storing the base color of a bin in a histogram chart
ingo@767: */
ingo@617: protected Paint histogramBasePaint;
ingo@617:
ingo@304:
ingo@767: /**
ingo@767: * Constructor
ingo@767: *
ingo@767: * @param name Name for this theme.
ingo@767: */
ingo@304: public XMLChartTheme(String name) {
ingo@304: super(name);
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Setter method for the visibility of the domain crosshair.
ingo@767: *
ingo@767: * @param visible True, if domain crosshair should be visible
ingo@767: */
ingo@304: public void setDomainCrosshairVisible(boolean visible) {
ingo@304: this.domainCrosshairVisible = visible;
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Getter method for retrieving the visibility of the domain crosshair.
ingo@767: *
ingo@767: * @return Visibility of the domain crosshair.
ingo@767: */
ingo@304: public boolean getDomainCrosshairVisible() {
ingo@304: return domainCrosshairVisible;
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Getter method for retrieving the visibility of the range crosshair.
ingo@767: *
ingo@767: * @return Visibility of the range crosshair.
ingo@767: */
ingo@304: public boolean getRangeCrosshairVisible() {
ingo@304: return rangeCrosshairVisible;
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Setter method for the visibility of the range crosshair.
ingo@767: *
ingo@767: * @param visible True, if range crosshair should be visible
ingo@767: */
ingo@304: public void setRangeCrosshairVisible(boolean visible) {
ingo@304: this.rangeCrosshairVisible = visible;
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Method to set the bin color of histograms.
ingo@767: *
ingo@767: * @param c Bin color
ingo@767: */
ingo@617: public void setHistogramBasePaint(Color c) {
ingo@617: this.histogramBasePaint = c;
ingo@617: }
ingo@617:
ingo@617:
ingo@767: /**
ingo@767: * Getter method for retrieving the bin color.
ingo@767: *
ingo@767: * @return Bin color
ingo@767: */
ingo@617: public Paint getHistogramBasePaint() {
ingo@617: return histogramBasePaint;
ingo@617: }
ingo@617:
ingo@617:
ingo@767: /**
ingo@767: * Take a given xml document and read the configuration of a chart
ingo@767: * appearance.
ingo@767: *
ingo@767: * @param document XML document
ingo@767: */
ingo@304: public void applyXMLConfiguration(Document document) {
ingo@304: log.debug("create XMLChartTheme");
ingo@304:
ingo@304: init(document);
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Start parsing the different settings from document
.
ingo@767: *
ingo@767: * @param document XML document
ingo@767: */
ingo@304: private void init(Document document) {
ingo@304: log.debug("init XMLChartTheme parameters");
ingo@304:
ingo@304: initChartParameters(document);
ingo@304: initTitleParameters(document);
ingo@304: initSubtitleParameters(document);
ingo@304: initPlotParameters(document);
ingo@304: initAxisParameters(document);
ingo@304: initLegendParameters(document);
ingo@327: initRenderer(document);
ingo@617: initHistogramColor(document);
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Read parameters configuring the title of a chart.
ingo@767: *
ingo@767: * @param document XML document
ingo@767: */
ingo@304: private void initTitleParameters(Document document) {
ingo@304: log.debug("init title parameters.");
ingo@304:
ingo@304: int size = getInt(document, "theme/title/font/size/@value");
ingo@304: String type = getString(document, "theme/title/font/type/@value");
ingo@304: boolean bold = getBool(document, "theme/title/font/bold/@value");
ingo@304:
ingo@304: setExtraLargeFont(createFont(type, size, bold));
ingo@304:
ingo@304: String color = getString(document, "theme/title/font/color/@value");
ingo@304: Color c = decodeColor(color);
ingo@304: if (c != null)
ingo@304: setTitlePaint(c);
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Read parameters configuring the subtitle of a chart.
ingo@767: *
ingo@767: * @param document XML document
ingo@767: */
ingo@304: private void initSubtitleParameters(Document document) {
ingo@304: log.debug("init title parameters.");
ingo@304:
ingo@304: int size = getInt(document, "theme/subtitle/font/size/@value");
ingo@304: String type = getString(document, "theme/subtitle/font/type/@value");
ingo@304: boolean bold = getBool(document, "theme/subtitle/font/bold/@value");
ingo@304:
ingo@304: setLargeFont(createFont(type, size, bold));
ingo@304:
ingo@304: String col = getString(document, "theme/subtitle/font/color/@value");
ingo@304: setSubtitlePaint(Color.decode(col));
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Read parameters configuring the background color of a chart.
ingo@767: *
ingo@767: * @param document XML document
ingo@767: */
ingo@304: private void initChartParameters(Document document) {
ingo@304: log.debug("init chart parameters.");
ingo@304:
ingo@304: String bg = getString(document, "theme/chart/background/color/@value");
ingo@304: Color c = decodeColor(bg);
ingo@304: if (c != null)
ingo@304: setChartBackgroundPaint(c);
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Read parameters configuring the plot of a chart.
ingo@767: *
ingo@767: * @param document XML document
ingo@767: */
ingo@304: private void initPlotParameters(Document document) {
ingo@304: log.debug("init plot parameters.");
ingo@304:
ingo@304: String tmp = null;
ingo@304: tmp = getString(document, "theme/plot/background/color/@value");
ingo@304: Color c = decodeColor(tmp);
ingo@304: if (c != null)
ingo@304: setPlotBackgroundPaint(c);
ingo@304:
ingo@304: tmp = getString(document, "theme/plot/outline/color/@value");
ingo@304: c = decodeColor(tmp);
ingo@304: if (c != null)
ingo@304: setPlotOutlinePaint(c);
ingo@304:
ingo@304: tmp = getString(document, "theme/plot/domaingridline/color/@value");
ingo@304: c = decodeColor(tmp);
ingo@304: if (c != null)
ingo@304: setDomainGridlinePaint(c);
ingo@304:
ingo@304: tmp = getString(document, "theme/plot/rangegridline/color/@value");
ingo@304: c = decodeColor(tmp);
ingo@304: if (c != null)
ingo@304: setRangeGridlinePaint(c);
ingo@304:
ingo@304: boolean rangeCrosshairVisible = getBool(
ingo@304: document, "theme/plot/rangecrosshair/visible/@value");
ingo@304: setRangeCrosshairVisible(rangeCrosshairVisible);
ingo@304:
ingo@304: boolean domainCrosshairVisible = getBool(
ingo@304: document, "theme/plot/domaincrosshair/visible/@value");
ingo@304: setDomainCrosshairVisible(domainCrosshairVisible);
ingo@304:
ingo@304: int top = getInt(document, "theme/plot/offset/top/@value");
ingo@304: int bottom = getInt(document, "theme/plot/offset/bottom/@value");
ingo@304: int left = getInt(document, "theme/plot/offset/left/@value");
ingo@304: int right = getInt(document, "theme/plot/offset/right/@value");
ingo@304: setAxisOffset(new RectangleInsets(
ingo@304: UnitType.RELATIVE,
ingo@304: top, left, bottom, right)
ingo@304: );
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Read parameters configuring the axes of a plot.
ingo@767: *
ingo@767: * @param document XML document
ingo@767: */
ingo@304: private void initAxisParameters(Document document) {
ingo@304: log.debug("init axis parameters.");
ingo@304:
ingo@304: String tmp = null;
ingo@304: tmp = getString(document, "theme/axis/label/color/@value");
ingo@304: Color c = decodeColor(tmp);
ingo@304: if (c != null)
ingo@304: setAxisLabelPaint(c);
ingo@304:
ingo@304: tmp = getString(document, "theme/axis/ticklabel/color/@value");
ingo@304: c = decodeColor(tmp);
ingo@304: if (c != null)
ingo@304: setTickLabelPaint(c);
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Read parameters configuring the legend of a chart.
ingo@767: *
ingo@767: * @param document XML document
ingo@767: */
ingo@304: private void initLegendParameters(Document document) {
ingo@304: log.debug("init legend parameters.");
ingo@304:
ingo@304: String tmp = null;
ingo@304: tmp = getString(document, "theme/legend/font/color/@value");
ingo@304: Color c = decodeColor(tmp);
ingo@304: if (c != null)
ingo@304: setLegendItemPaint(c);
ingo@304:
ingo@304: tmp = getString(document, "theme/legend/background/color/@value");
ingo@304: c = decodeColor(tmp);
ingo@304: if (c != null)
ingo@304: setLegendBackgroundPaint(c);
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Read parameters configuring the renderer of a plot.
ingo@767: *
ingo@767: * @param document XML document
ingo@767: */
ingo@327: private void initRenderer(Document document) {
ingo@327: log.debug("init renderer parameters.");
ingo@327:
ingo@327: pointWidth = getInt(document, "theme/plot/itemrenderer/width/@value");
ingo@327: log.debug("Read point width of " + pointWidth);
ingo@327: pointHeight = getInt(document, "theme/plot/itemrenderer/height/@value");
ingo@327: log.debug("Read point height of " + pointHeight);
ingo@327: renderLines = getBool(
ingo@327: document, "theme/plot/itemrenderer/renderLines/@value"
ingo@327: );
ingo@327: renderShapes = getBool(
ingo@327: document, "theme/plot/itemrenderer/renderPoints/@value"
ingo@327: );
ingo@327: }
ingo@327:
ingo@327:
ingo@767: /**
ingo@767: * Read base color of bins in histogram charts.
ingo@767: *
ingo@767: * @param document XML document
ingo@767: */
ingo@617: private void initHistogramColor(Document document) {
ingo@617: log.debug("init histogram color");
ingo@617: String tmp = getString(document, "theme/histogram/bar/color/@value");
ingo@617: Color c = decodeColor(tmp);
ingo@617:
ingo@617: if (c != null)
ingo@617: setHistogramBasePaint(c);
ingo@617: }
ingo@617:
ingo@617:
ingo@767: /**
ingo@767: * Read a xpath expression and return the matched string.
ingo@767: *
ingo@767: * @param document Document
ingo@767: * @param xpath XPath expression
ingo@767: *
ingo@767: * @return Matched string
ingo@767: */
ingo@304: private static String getString(Document document, String xpath) {
ingo@304: return Config.getStringXPath(document, xpath);
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Read a xpath and turn it into an integer.
ingo@767: *
ingo@767: * @param document Document
ingo@767: * @param xpath XPath expression
ingo@767: *
ingo@767: * @return Matched string as integer representation. Return 0 if no integer
ingo@767: * have been found at xpath
.
ingo@767: */
ingo@304: private static int getInt(Document document, String xpath) {
ingo@304: String tmp = getString(document, xpath);
ingo@304:
ingo@304: if (tmp != null)
ingo@304: return Integer.parseInt(tmp);
ingo@304: else
ingo@304: return 0;
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Read a xpath and turn it into a boolean.
ingo@767: *
ingo@767: * @param document Document
ingo@767: * @param xpath XPath expression
ingo@767: *
ingo@767: * @return Matched string as boolean representation. Return false if no
ingo@767: * boolean have been found at xpath
.
ingo@767: */
ingo@304: private static boolean getBool(Document document, String xpath) {
ingo@304: String tmp = getString(document, xpath);
ingo@304:
ingo@304: if (tmp != null)
ingo@304: return Boolean.parseBoolean(tmp);
ingo@304: else
ingo@304: return false;
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Turns a string into a color using {@link java.awt.Color}.
ingo@767: *
ingo@788: * @param color as string
ingo@767: *
ingo@767: * @return Color
ingo@767: */
ingo@304: protected Color decodeColor(String color) {
ingo@304: try {
ingo@304: if (color == null)
ingo@304: return null;
ingo@304:
ingo@304: return Color.decode(color);
ingo@304: }
ingo@304: catch (NumberFormatException nfe) {
ingo@304: log.warn("Error while parsing color: " + color, nfe);
ingo@304: }
ingo@304:
ingo@304: return null;
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Create a font with the given parameters.
ingo@767: *
ingo@767: * @param type Font type
ingo@767: * @param size Font size
ingo@767: * @param bold Font weight
ingo@767: *
ingo@767: * @return Font
ingo@767: */
ingo@304: protected Font createFont(String type, int size, boolean bold) {
ingo@304: Font font = null;
ingo@304: if (bold)
ingo@304: font = new Font(type, Font.BOLD, size);
ingo@304: else
ingo@304: font = new Font(type, Font.PLAIN, size);
ingo@304:
ingo@304: return font;
ingo@304: }
ingo@304:
ingo@304:
ingo@767: /**
ingo@767: * Apply settings of this ChartTheme
to the given
ingo@767: * XYPlot
.
ingo@767: *
ingo@767: * @param plot XYPlot
ingo@767: */
ingo@788: @Override
ingo@304: protected void applyToXYPlot(XYPlot plot) {
ingo@304: log.debug("apply theme parameter to XYPlot");
ingo@304:
ingo@304: super.applyToXYPlot(plot);
ingo@304: plot.setDomainCrosshairVisible(this.domainCrosshairVisible);
ingo@304: plot.setRangeCrosshairVisible(this.rangeCrosshairVisible);
ingo@327:
sascha@778: AbstractXYItemRenderer renderer = (AbstractXYItemRenderer)
ingo@617: plot.getRenderer();
ingo@617:
ingo@617: if (renderer instanceof XYLineAndShapeRenderer)
ingo@617: applyToXYLineAndShapeRenderer(plot);
ingo@617:
ingo@617: if (renderer instanceof XYBarRenderer)
ingo@617: applyToXYBarRenderer(plot);
ingo@327: }
ingo@327:
ingo@327:
ingo@767: /**
ingo@767: * Apply settings of this ChartTheme
to the
ingo@767: * XYLineAndShapeRenderer
of the given XYPlot
.
ingo@767: *
ingo@767: * @param plot XYPlot
ingo@767: */
ingo@327: protected void applyToXYLineAndShapeRenderer(XYPlot plot) {
ingo@327: if (plot == null)
ingo@327: return;
ingo@327:
ingo@327: XYLineAndShapeRenderer renderer =
ingo@327: (XYLineAndShapeRenderer) plot.getRenderer();
ingo@327:
ingo@327: Ellipse2D.Double point = new Ellipse2D.Double(
ingo@327: -(pointWidth/2), -(pointHeight/2), pointWidth, pointHeight
ingo@327: );
ingo@327:
ingo@327: renderer.setSeriesShape(0, point);
ingo@327: renderer.setSeriesShapesVisible(0, renderShapes);
ingo@327: renderer.setSeriesLinesVisible(0, renderLines);
ingo@327:
ingo@327: plot.setRenderer(renderer);
ingo@304: }
ingo@617:
ingo@617:
ingo@767: /**
ingo@767: * Apply settings of this ChartTheme
to the
ingo@767: * XYBarRenderer
of the given XYPlot
.
ingo@767: *
ingo@767: * @param plot XYPlot
ingo@767: */
ingo@617: protected void applyToXYBarRenderer(XYPlot plot) {
ingo@617: if (plot == null)
ingo@617: return;
ingo@617:
ingo@617: XYBarRenderer renderer = (XYBarRenderer) plot.getRenderer();
ingo@617:
ingo@617: renderer.setSeriesPaint(0, histogramBasePaint);
ingo@617: }
ingo@304: }
sascha@836: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :