view artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator.java @ 9123:1cc7653ca84f

Cleanup of ChartGenerator and ChartGenerator2 code. Put some of the copy/pasted code into a common abstraction.
author gernotbelger
date Tue, 05 Jun 2018 19:21:16 +0200
parents 07d51fd4864c
children
line wrap: on
line source
/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU AGPL (>=v3)
 * and comes with ABSOLUTELY NO WARRANTY! Check out the
 * documentation coming with Dive4Elements River for details.
 */

package org.dive4elements.river.exports;

import java.awt.Font;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
import org.dive4elements.artifacts.CallContext;
import org.dive4elements.river.jfree.RiverAnnotation;
import org.dive4elements.river.themes.ThemeDocument;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.data.Range;

/**
 * The base class for chart creation. It should provide some basic things that
 * equal in all chart types.
 *
 * Annotations are added as RiverAnnotations and come in mutliple basic forms:
 * TextAnnotations are labels somewhere in data space, StickyAnnotations are
 * labels of a slice or line in one data dimension (i.e. visualized as label
 * on a single axis).
 *
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public abstract class ChartGenerator extends AbstractChartGenerator {

    /**
     * A mini interface that allows to walk over the YAXIS enums defined in
     * subclasses.
     */
    public interface YAxisWalker {

        int length();

        String getId(int idx);
    } // end of YAxisWalker interface


    protected abstract YAxisWalker getYAxisWalker();

    /**
     * Register annotations like MainValues for later plotting
     *
     * @param annotations list of annotations (data of facet).
     * @param aandf   Artifact and the facet.
     * @param theme   Theme document for given annotations.
     * @param visible The visibility of the annotations.
     */
    public void doAnnotations(
        RiverAnnotation annotations,
        ArtifactAndFacet aandf,
        ThemeDocument theme,
        boolean visible
    ){
        log.debug("doAnnotations");

        // Add all annotations to our annotation pool.
        annotations.setTheme(theme);
        if (aandf != null) {
            annotations.setLabel(aandf.getFacetDescription());
        }
        else {
            log.error(
                "Art/Facet for Annotations is null. " +
                "This should never happen!");
        }

        if (visible) {
            addAnnotations(annotations);
        }
    }

    @Override
    protected void doGenerate(CallContext context, OutputStream out, String outName) throws IOException {
        generateImage(context);
    }

    /**
     * Creates a new <i>ChartSection</i>.
     *
     * @return a new <i>ChartSection</i>.
     */
    @Override
    protected ChartSection buildChartSection(final CallContext context) {
        ChartSection chartSection = new ChartSection();
        chartSection.setTitle(getChartTitle(context));
        chartSection.setSubtitle(getChartSubtitle(context));
        chartSection.setDisplayGrid(isGridVisible());
        chartSection.setDisplayLogo(showLogo());
        chartSection.setLogoVPlacement(logoVPlace());
        chartSection.setLogoHPlacement(logoHPlace());
        return chartSection;
    }

    /**
     * Creates a list of Section for the chart's Y axes. This method makes use
     * of <i>getYAxisWalker</i> to be able to access all Y axes defined in
     * subclasses.
     *
     * @return a list of Y axis sections.
     */
    @Override
    protected final List<AxisSection> buildYAxisSections() {
        List<AxisSection> axisSections = new ArrayList<>();

        YAxisWalker walker = getYAxisWalker();
        for (int i = 0, n = walker.length(); i < n; i++) {
            AxisSection ySection = new AxisSection();
            ySection.setIdentifier(walker.getId(i));
            ySection.setLabel(getYAxisLabel(i));
            ySection.setFontSize(14);
            ySection.setFixed(false);

            // XXX We are able to find better default ranges that [0,0], the
            // only problem is, that we do NOT have a better range than [0,0]
            // for each axis, because the initial chart will not have a dataset
            // for each axis set!
            ySection.setUpperRange(0d);
            ySection.setLowerRange(0d);

            axisSections.add(ySection);
        }

        return axisSections;
    }

    /**
     * Returns the subtitle of a chart. The return value depends on the
     * existence of ChartSettings: if there are ChartSettings set, this method
     * returns the chart title provided by those settings. Otherwise, this
     * method returns getDefaultChartSubtitle().
     *
     * @return the subtitle of a chart.
     */
    @Override
    protected String getChartSubtitle(final CallContext context) {
        ChartSettings chartSettings = getChartSettings();

        if (chartSettings != null) 
            return getChartSubtitle(chartSettings);

        return getDefaultChartSubtitle(context);
    }

    /**
     * This method returns the font size for an Y axis. If the font size is
     * specified in ChartSettings (if <i>chartSettings</i> is set), this size is
     * returned. Otherwise the default font size 12 is returned.
     *
     * @return the font size for the x axis.
     */
    protected int getYAxisFontSize(int pos) {
        ChartSettings chartSettings = getChartSettings();
        if (chartSettings == null) {
            return DEFAULT_FONT_SIZE;
        }

        YAxisWalker walker = getYAxisWalker();

        AxisSection   as = chartSettings.getAxisSection(walker.getId(pos));
        if (as == null) {
            return DEFAULT_FONT_SIZE;
        }
        Integer fontSize = as.getFontSize();

        return fontSize != null ? fontSize : DEFAULT_FONT_SIZE;
    }

    /**
     * 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);
    }
    
    /**
     * This method is called to retrieve the default label for an Y axis at
     * position <i>pos</i>.
     *
     * @param pos
     *            The position of an Y axis.
     *
     * @return the default Y axis label at position <i>pos</i>.
     */
    protected abstract String getDefaultYAxisLabel(int pos);

    /**
     * This method searches for a specific axis in the <i>settings</i> if
     * <i>settings</i> is set. If the axis was found, this method returns the
     * specified axis range if the axis range is fixed. Otherwise, this method
     * returns null.
     *
     * @param axisId The identifier of an axis.
     *
     * @return the specified axis range from <i>settings</i> if the axis is
     * fixed, otherwise null.
     */
    public Range getRangeForAxisFromSettings(String axisId) {
        ChartSettings chartSettings = getChartSettings();
        if (chartSettings == null) {
            return null;
        }

        AxisSection as = chartSettings.getAxisSection(axisId);

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

        if (as.isFixed()) {

            /* Only time series charts have time ranges so prefer those. */
            if (axisId.equals("X")) {
                Long lowerTime = as.getLowerTimeRange();
                Long upperTime = as.getUpperTimeRange();
                if ( lowerTime != null && upperTime != null ) {
                    log.debug("Using time range: "
                        + lowerTime + " - " + upperTime);
                    return lowerTime < upperTime
                            ? new Range(lowerTime, upperTime)
                            : new Range(upperTime, lowerTime);
                }
            }

            Double upper = as.getUpperRange();
            Double lower = as.getLowerRange();

            if (upper != null && lower != null) {
                return lower < upper
                    ? new Range(lower, upper)
                    : new Range(upper, lower);
            }
        }

        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 final NumberAxis createNumberAxis(int idx, String label) {
        YAxisWalker walker = getYAxisWalker();

        return new IdentifiableNumberAxis(walker.getId(idx), label);
    }

    /**
     * Create Y (range) axis for given index.
     * Shall be overriden by subclasses.
     */
    @Override
    protected NumberAxis createYAxis(int index) {
        YAxisWalker walker = getYAxisWalker();

        Font labelFont = new Font(
            DEFAULT_FONT_NAME,
            Font.BOLD,
            getYAxisFontSize(index));

        IdentifiableNumberAxis axis = new IdentifiableNumberAxis(
            walker.getId(index),
            getYAxisLabel(index));

        axis.setAutoRangeIncludesZero(false);
        axis.setLabelFont(labelFont);
        axis.setTickLabelFont(labelFont);

        // REMARK: we overwrite the default values to 0.0, because in earlier version margins were never applied.
        axis.setLowerMargin(0);
        axis.setUpperMargin(0);
        
        return axis;
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org