view flys-artifacts/src/main/java/de/intevation/flys/exports/ChartGenerator.java @ 2233:958a10e2e246

Added a new ChartGenerator for timeseries charts and refactored some code in XYChartGenerator. flys-artifacts/trunk@3877 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Thu, 02 Feb 2012 12:26:36 +0000
parents 79a94c4171cb
children 46ec09c7f578
line wrap: on
line source
package de.intevation.flys.exports;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Locale;

import javax.xml.xpath.XPathConstants;

import org.apache.log4j.Logger;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.data.Range;

import de.intevation.artifacts.Artifact;
import de.intevation.artifacts.CallContext;
import de.intevation.artifacts.CallMeta;
import de.intevation.artifacts.PreferredLocale;

import de.intevation.artifacts.ArtifactNamespaceContext;
import de.intevation.artifacts.common.utils.XMLUtils;

import de.intevation.artifactdatabase.state.ArtifactAndFacet;
import de.intevation.artifactdatabase.state.Settings;

import de.intevation.flys.model.River;

import de.intevation.flys.artifacts.FLYSArtifact;
import de.intevation.flys.artifacts.resources.Resources;
import de.intevation.flys.utils.FLYSUtils;


/**
 * The base class for chart creation. It should provide some basic things that
 * equal in all chart types.
 *
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public abstract class ChartGenerator implements OutGenerator {

    private static Logger logger = Logger.getLogger(ChartGenerator.class);

    /** The default chart width, if no other width is set. */
    public static final int DEFAULT_CHART_WIDTH  = 600;

    /** The default chart height, if no other height is set.*/
    public static final int DEFAULT_CHART_HEIGHT = 400;

    /** The default chart format, if no other height is set.*/
    public static final String DEFAULT_CHART_FORMAT = "png";

    /** The XPath that points to the chart size of the incoming request
     * document.*/
    public static final String XPATH_CHART_SIZE =
        "/art:action/art:attributes/art:size";

    public static final String XPATH_CHART_FORMAT =
        "/art:action/art:attributes/art:format/@art:value";

    public static final String XPATH_CHART_X_RANGE =
        "/art:action/art:attributes/art:xrange";

    public static final String XPATH_CHART_Y_RANGE =
        "/art:action/art:attributes/art:yrange";


    /** The document of the incoming out() request.*/
    protected Document request;

    /** The output stream where the data should be written to.*/
    protected OutputStream out;

    /** The CallContext object.*/
    protected CallContext context;

    /** The artifact that is used to decorate the chart with meta information.*/
    protected Artifact master;

    /** The settings that should be used during output creation.*/
    protected Settings settings;


    /**
     * A mini interface that allows to walk over the YAXIS enums defined in
     * subclasses.
     */
    public interface YAxisWalker {
        int length();
        String getId(int idx);
    }


    protected abstract YAxisWalker getYAxisWalker();

    protected abstract String getDefaultChartTitle();

    protected abstract String getDefaultXAxisLabel();

    protected abstract String getDefaultYAxisLabel(int pos);

    protected abstract void addSubtitles(JFreeChart chart);


    public void init(Document request, OutputStream out, CallContext context) {
        logger.debug("ChartGenerator.init");

        this.request = request;
        this.out     = out;
        this.context = context;
    }


    public void setMasterArtifact(Artifact master) {
        this.master = master;
    }


    @Override
    public void setSettings(Settings settings) {
        this.settings = settings;
    }


    /**
     * Returns the chart title provided by <i>settings</i>.
     *
     * @param settings A ChartSettings object.
     *
     * @return the title provided by <i>settings</i> or null if no
     * <i>ChartSection</i> is provided by <i>settings</i>.
     *
     * @throws NullPointerException if <i>settings</i> is null.
     */
    public String getChartTitle(ChartSettings settings) {
        ChartSection cs = settings.getChartSection();
        return cs != null ? cs.getTitle() : null;
    }


    /**
     * Returns the chart subtitle provided by <i>settings</i>.
     *
     * @param settings A ChartSettings object.
     *
     * @return the subtitle provided by <i>settings</i> or null if no
     * <i>ChartSection</i> is provided by <i>settings</i>.
     *
     * @throws NullPointerException if <i>settings</i> is null.
     */
    public String getChartSubtitle(ChartSettings settings) {
        ChartSection cs = settings.getChartSection();
        return cs != null ? cs.getSubtitle() : null;
    }


    /**
     * Returns a boolean object that determines if the chart grid should be
     * visible or not. This information needs to be provided by <i>settings</i>,
     * otherweise the default is true.
     *
     * @param settings A ChartSettings object.
     *
     * @return true, if the chart grid should be visible otherwise false.
     *
     * @throws NullPointerException if <i>settings</i> is null.
     */
    public boolean isGridVisible(ChartSettings settings) {
        ChartSection     cs = settings.getChartSection();
        Boolean displayGrid = cs.getDisplayGrid();

        return displayGrid != null ? displayGrid : true;
    }


    /**
     * Returns a boolean object that determines if the chart legend should be
     * visible or not. This information needs to be provided by <i>settings</i>,
     * otherwise the default is true.
     *
     * @param settings A ChartSettings object.
     *
     * @return true, if the chart legend should be visible otherwise false.
     *
     * @throws NullPointerException if <i>settings</i> is null.
     */
    public boolean isLegendVisible(ChartSettings settings) {
        LegendSection      ls = settings.getLegendSection();
        Boolean displayLegend = ls.getVisibility();

        return displayLegend != null ? displayLegend : true;
    }


    /**
     * Returns the legend font size specified in <i>settings</i> or null if no
     * <i>LegendSection</i> is provided by <i>settings</i>.
     *
     * @param settings A ChartSettings object.
     *
     * @return the legend font size or null.
     *
     * @throws NullPointerException if <i>settings</i> is null.
     */
    public Integer getLegendFontSize(ChartSettings settings) {
        LegendSection ls = settings.getLegendSection();
        return ls != null ? ls.getFontSize() : null;
    }


    /**
     * Returns the Y-Axis label of a chart at position <i>pos</i>.
     *
     * @return the Y-Axis label of a chart at position <i>0</i>.
     */
    protected String getYAxisLabel(int pos) {
        ChartSettings chartSettings = getChartSettings();
        if (chartSettings == null) {
            return getDefaultYAxisLabel(pos);
        }

        YAxisWalker walker = getYAxisWalker();
        AxisSection     as = chartSettings.getAxisSection(walker.getId(pos));
        if (as != null) {
            String label = as.getLabel();

            if (label != null) {
                return label;
            }
        }

        return getDefaultYAxisLabel(pos);
    }


    protected Locale getLocale() {
        CallMeta           meta = context.getMeta();
        PreferredLocale[] prefs = meta.getLanguages();

        int len = prefs != null ? prefs.length : 0;

        Locale[] locales = new Locale[len];

        for (int i = 0; i < len; i++) {
            locales[i] = prefs[i].getLocale();
        }

        return meta.getPreferredLocale(locales);
    }


    protected String msg(String key, String def) {
        return Resources.getMsg(context.getMeta(), key, def);
    }


    protected String msg(String key, String def, Object[] args) {
        return Resources.getMsg(context.getMeta(), key, def, args);
    }


    protected String getRiverName() {
        FLYSArtifact flys = (FLYSArtifact) master;

        River river = FLYSUtils.getRiver(flys);
        return (river != null) ? river.getName() : "";
    }


    protected double[] getRange() {
        FLYSArtifact flys = (FLYSArtifact) master;

        return FLYSUtils.getKmRange(flys);
    }


    /**
     * Returns the size of a chart export as array which has been specified by
     * the incoming request document.
     *
     * @return the size of a chart as [width, height] or null if no width or
     * height are given in the request document.
     */
    protected int[] getSize() {
        int[] size = new int[2];

        Element sizeEl = (Element)XMLUtils.xpath(
            request,
            XPATH_CHART_SIZE,
            XPathConstants.NODE,
            ArtifactNamespaceContext.INSTANCE);

        if (sizeEl != null) {
            String uri = ArtifactNamespaceContext.NAMESPACE_URI;

            String w = sizeEl.getAttributeNS(uri, "width");
            String h = sizeEl.getAttributeNS(uri, "height");

            if (w.length() > 0 && h.length() > 0) {
                try {
                    size[0] = Integer.parseInt(w);
                    size[1] = Integer.parseInt(h);
                }
                catch (NumberFormatException nfe) {
                    logger.warn("Wrong values for chart width/height.");
                }
            }
        }

        return size[0] > 0 && size[1] > 0 ? size : null;
    }


    protected String getFormat() {
        String format = (String) XMLUtils.xpath(
            request,
            XPATH_CHART_FORMAT,
            XPathConstants.STRING,
            ArtifactNamespaceContext.INSTANCE);

        return format == null || format.length() == 0
            ? DEFAULT_CHART_FORMAT
            : format;
    }


    /**
     * Get Range of Domain ("X"-) Axis from request.
     */
    protected Range getDomainAxisRange() {
        Element xrange = (Element)XMLUtils.xpath(
            request,
            XPATH_CHART_X_RANGE,
            XPathConstants.NODE,
            ArtifactNamespaceContext.INSTANCE);

        if (xrange == null) {
            return null;
        }

        String uri = ArtifactNamespaceContext.NAMESPACE_URI;

        String lower = xrange.getAttributeNS(uri, "from");
        String upper = xrange.getAttributeNS(uri, "to");

        if (lower.length() > 0 && upper.length() > 0) {
            try {
                double from = Double.parseDouble(lower);
                double to   = Double.parseDouble(upper);

                if (from == 0 && to == 0) {
                    logger.debug("No range specified. Lower and upper X == 0");
                    return null;
                }

                if (from > to) {
                    double tmp = to;
                    to         = from;
                    from       = tmp;
                }

                return new Range(from, to);
            }
            catch (NumberFormatException nfe) {
                logger.warn("Wrong values for domain axis range.");
            }
        }

        return null;
    }


    protected Range getValueAxisRange() {
        Element yrange = (Element)XMLUtils.xpath(
            request,
            XPATH_CHART_Y_RANGE,
            XPathConstants.NODE,
            ArtifactNamespaceContext.INSTANCE);

        if (yrange == null) {
            return null;
        }


        String uri = ArtifactNamespaceContext.NAMESPACE_URI;


        String lower = yrange.getAttributeNS(uri, "from");
        String upper = yrange.getAttributeNS(uri, "to");

        if (lower.length() > 0 && upper.length() > 0) {
            try {
                double from = Double.parseDouble(lower);
                double to   = Double.parseDouble(upper);

                if (from == 0 && to == 0) {
                    logger.debug("No range specified. Lower and upper Y == 0");
                    return null;
                }

                return from > to
                       ? new Range(to, from)
                       : new Range(from, to);
            }
            catch (NumberFormatException nfe) {
                logger.warn("Wrong values for value axis range.");
            }
        }

        return null;
    }


    /**
     * Returns the default size of a chart export as array.
     *
     * @return the default size of a chart as [width, height].
     */
    protected int[] getDefaultSize() {
        return new int[] { DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT };
    }


    public abstract void doOut(
        ArtifactAndFacet bundle,
        Document attr,
        boolean  visible);

    public abstract void generate() throws IOException;


    /**
     * Returns an instance of <i>EmptySettings</i> currently!
     *
     * @return an instance of <i>EmptySettings</i>.
     */
    public Settings getSettings() {
        return settings != null ? settings : new EmptySettings();
    }


    /**
     * Returns the <i>settings</i> as <i>ChartSettings</i>.
     *
     * @return the <i>settings</i> as <i>ChartSettings</i> or null, if
     * <i>settings</i> is not an instance of <i>ChartSettings</i>.
     */
    public ChartSettings getChartSettings() {
        if (settings instanceof ChartSettings) {
            return (ChartSettings) settings;
        }

        return null;
    }


    /**
     * Creates a new instance of <i>IdentifiableNumberAxis</i>.
     *
     * @param idx The index of the new axis.
     * @param label The label of the new axis.
     *
     * @return an instance of IdentifiableNumberAxis.
     */
    protected NumberAxis createNumberAxis(int idx, String label) {
        YAxisWalker walker = getYAxisWalker();

        return new IdentifiableNumberAxis(walker.getId(idx), label);
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org