view artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator2.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 ef5754ba5573
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 static org.dive4elements.river.exports.injector.InjectorConstants.CURRENT_KM;

import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Transparency;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import org.dive4elements.artifacts.CallContext;
import org.dive4elements.artifacts.common.utils.XMLUtils;
import org.dive4elements.river.java2d.NOPGraphics2D;
import org.dive4elements.river.jfree.RiverAnnotation;
import org.dive4elements.river.utils.Formatter;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.Range;
import org.w3c.dom.Document;

/**
 * Implementation of the OutGenerator interface for charts.
 * It should provide some basic things that equal in all chart types.
 *
 */
public abstract class ChartGenerator2 extends AbstractChartGenerator {

    private static final boolean USE_NOP_GRAPHICS = Boolean.getBoolean("info.rendering.nop.graphics");

    /** Map of annotations to add at specific Y-axis. */
    protected SortedMap<Integer, RiverAnnotation> yAnnotations = new TreeMap<>();

    private Map<String, IdentifiableNumberAxis> axisNameToAxis = new HashMap<>();

    public void addYAnnotation(RiverAnnotation annotation, int axisIndex) {
        yAnnotations.put(axisIndex, annotation);
    }

    @Override
    protected void doGenerate(final CallContext context, final OutputStream out, final String outName) throws IOException {
        log.debug("ChartGenerator2.generate");

        if (outName.indexOf("chartinfo") > 0)
            generateInfo(context, out);
        else 
            generateImage(context);
    }

    /** 
     * Generate only meta infos 
     */
    private void generateInfo(final CallContext context, final OutputStream out) {

        log.debug("ChartInfoGenerator2.generateInfo");

        JFreeChart chart = generateChart(context);

        int[] size = getSize();
        if (size == null) {
            size = getDefaultSize();
        }

        ChartRenderingInfo info = new ChartRenderingInfo();

        long startTime = System.currentTimeMillis();

        if (USE_NOP_GRAPHICS) {
            BufferedImage image =
                new BufferedImage(size[0], size[1], Transparency.BITMASK);

            Graphics2D g2d  = image.createGraphics();
            Graphics2D nop = new NOPGraphics2D(g2d);

            chart.draw(
                nop,
                new Rectangle2D.Double(0, 0, size[0], size[1]),
                null,
                info);

            nop.dispose();
        }
        else {
            chart.createBufferedImage(
                size[0], size[1], Transparency.BITMASK, info);
        }

        long stopTime = System.currentTimeMillis();

        if (log.isDebugEnabled()) {
            log.debug("Rendering info took: " +
                (stopTime-startTime) + "ms");
        }


        InfoGeneratorHelper2 helper = new InfoGeneratorHelper2(this);
        Document doc = helper.createInfoDocument(chart, info);

        XMLUtils.toStream(doc, out);
    }

    /**
     * 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(getChartSubtitlePure(context));
        chartSection.setDisplayGrid(isGridVisible());
        chartSection.setDisplayLogo(showLogo());
        chartSection.setLogoVPlacement(logoVPlace());
        chartSection.setLogoHPlacement(logoHPlace());
        return chartSection;
    }

    protected String interpolateVariables(final CallContext context, String s) {
        log.debug("Interpolate variables in string '" + s + "'");
        Object radius = context.getContextValue("radius");
        if (radius instanceof Double) {
            NumberFormat f = Formatter.getCSVFormatter(context);
            s = s.replace("$RADIUS", f.format(radius) + " km");
        }
        else {
            s = s.replace("$RADIUS", "-");
        }

        Object km = context.getContextValue(CURRENT_KM);
        if (km instanceof Number && s.contains("$KM")) {
            NumberFormat f = Formatter.getCalculationKm(context.getMeta());
            s = s.replace("$KM", f.format(km));
        }

        return s;
    }

    /**
     * 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().
     * @param context 
     *
     * @return the subtitle of a chart.
     */
    protected String getChartSubtitlePure(CallContext context) {
        ChartSettings chartSettings = getChartSettings();

        String subTitle = chartSettings != null
            ? getChartSubtitle(chartSettings)
            : getDefaultChartSubtitle(context);

        String defSubTitle = getDefaultChartSubtitle(context);

        if (subTitle == null) {
            subTitle = defSubTitle != null ? defSubTitle : "";
        }

        return subTitle;
    }

    @Override
    protected String getChartSubtitle(CallContext context) {
        return interpolateVariables(context, getChartSubtitlePure(context));
    }

    /**
     * Glue between axis names and index.
     */
    protected abstract String axisIndexToName(int index);

    /**
     * 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 index) {
        ChartSettings chartSettings = getChartSettings();
        if (chartSettings == null) {
            return DEFAULT_FONT_SIZE;
        }

        AxisSection as = chartSettings.getAxisSection(axisIndexToName(index));
        if (as == null) {
            return DEFAULT_FONT_SIZE;
        }
        Integer fontSize = as.getFontSize();

        return fontSize != null ? fontSize : DEFAULT_FONT_SIZE;
    }

    protected abstract String getYAxisLabel(String axisName);

    /**
     * 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()) {
            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) {
        return new IdentifiableNumberAxis(axisIndexToName(idx), label);
    }

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

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

        String axisName = axisIndexToName(index);

        IdentifiableNumberAxis axis = new IdentifiableNumberAxis(axisName, getYAxisLabel(axisName));

        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);

        /* remember axis for lookup */
        axisNameToAxis.put( axisName, axis );
        
        return axis;
    }

    public final IdentifiableNumberAxis getAxis(final String axisName) {
        return axisNameToAxis.get(axisName);
    }
    
    /** Returns the number of registered y-axes */
    public final int getNumYAxes() {
        return axisNameToAxis.size();
    }
    
    protected final void addYAnnotationsToRenderer(final XYPlot plot) {
        final AnnotationRenderer annotationRenderer = new AnnotationRenderer(getChartSettings(), getDatasets(), DEFAULT_FONT_NAME);
        annotationRenderer.addYAnnotationsToRenderer(plot, this.yAnnotations);
    }
}

http://dive4elements.wald.intevation.org