view gnv-artifacts/src/main/java/de/intevation/gnv/chart/DefaultHistogram.java @ 1089:1dc08501a1be

Implemented necessary classes (artifact, output state) used for a new product 'Achsenparalleler Vertikalschnitt. gnv-artifacts/trunk@1203 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Sat, 12 Jun 2010 12:59:48 +0000
parents bb2679624c6a
children 86ca3c10523f
line wrap: on
line source
package de.intevation.gnv.chart;

import java.util.Map;

import org.apache.log4j.Logger;

import org.jfree.chart.ChartTheme;

import org.jfree.chart.plot.XYPlot;

/**
 * Default implementation of {@link de.intevation.gnv.chart.AbstractHistogram}.
 *
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public class DefaultHistogram
extends      AbstractHistogram
{
    /**
     * Default bin count.
     * TODO find a better default value
     */
    public static final int DEFAULT_BINS = 15;

    /**
     * Constant field for limitating the number of bin in a single histogram.
     */
    public static final int MAXIMAL_BINS = 20;

    /**
     * Default key to retrieve the number of bins from {@link
     * #requestParameter}.
     */
    public static final String REQUEST_KEY_BIN_COUNT   = "bincount";

    /**
     * Default key to retrieve the width of a single bin from {@link
     * #requestParameter}.
     */
    public static final String REQUEST_KEY_BIN_WIDTH   = "binwidth";

    /**
     * Default key to retrieve the chart width from {@link #requestParameter}.
     */
    public static final String REQUEST_KEY_CHART_WIDTH = "width";

    /**
     * Default key to retrieve the object from {@link #requestParameter}. It
     * defines which value this chart has to be used for bin calculation. You
     * can either adjust the number of bins or the width of a single bin.
     */
    public static final String REQUEST_KEY_BIN_CHOICE  = "bintype";

    /**
     * Logger used for logging with log4j.
     */
    private static Logger logger = Logger.getLogger(DefaultHistogram.class);

    /**
     * Object storing some further parameter used for chart settings.
     */
    protected Map requestParameter;

    protected double[] minmax = null;


    /**
     * Constructor to create DefaultHistogram objects.
     *
     * @param labels Labels to decorate this chart.
     * @param data Raw data to be displayed in histogram.
     * @param theme Theme used to adjust the chart look.
     * @param requestParameter Object which serves some further settings.
     */
    public DefaultHistogram(
        ChartLabels labels, Object[] data, ChartTheme theme, Map requestParameter
    ) {
        super(labels, data, theme);
        this.requestParameter = requestParameter;
    }


    @Override
    protected void applyDatasets() {
        XYPlot plot = (XYPlot) chart.getPlot();

        // prepare data and create add them to histogram dataset
        String   name   = (String)   data[0];
        double[] values = toDouble((Double[]) data[1]);

        double binWidth = getBinWidth(values);
        int    binCount = getBinCount();

        AdvancedHistogramDataset dataset =
            new AdvancedHistogramDataset(binCount, binWidth);
        dataset.addSeries(name, values);

        plot.setDataset(0, dataset);
    }


    /**
     * Method which scans the hole bunch of values and returns an array with
     * contains min and max value. Min value is stored at position 0, max value
     * is stored at position 1 in that array.
     *
     * @param values Array which contains all values
     *
     * @return Array which contains min and max value
     */
    protected double[] getMinMax(double[] values) {
        if (minmax != null)
            return minmax;

        double[] minmax = new double[2];
        minmax[0] = Double.MAX_VALUE;
        minmax[1] = Double.MIN_VALUE;

        int length = values.length;
        for (int i = 0; i < length; i++) {
            minmax[0] = values[i] < minmax[0] ? values[i] : minmax[0];
            minmax[1] = values[i] > minmax[1] ? values[i] : minmax[1];
        }

        this.minmax = minmax;

        return minmax;
    }


    /**
     * Turn a Double[] into a double[].
     *
     * @param array Doube[]
     *
     * @return double[]
     */
    protected double[] toDouble(Double[] array) {
        int length      = array.length;
        double[] values = new double[length];

        for(int i = 0; i < length; i++) {
            values[i] = array[i].doubleValue();
        }

        return values;
    }


    /**
     * Method to retrieve the number of bins.
     *
     * @return the number of bins that is specified in <i>requestParameter</i>
     * or -1 if the number of bins is not the dominant value to calculate the
     * width of a single bin.
     */
    protected int getBinCount() {
        // Return -1 to trigger a calculation of the number of bins in
        // AdvancedHistogramDataset if the user chose the bin width as dominant
        // value.
        String choice = (String) requestParameter.get(REQUEST_KEY_BIN_CHOICE);
        if (choice != null && choice.equalsIgnoreCase(REQUEST_KEY_BIN_WIDTH)) {
            return -1;
        }

        int    bins  = -1;
        String param = (String) requestParameter.get(REQUEST_KEY_BIN_COUNT);

        try {
            bins = Integer.parseInt(param);
            bins = bins <= 0 ? DEFAULT_BINS : bins;
            bins = bins >  MAXIMAL_BINS ? MAXIMAL_BINS : bins;

            return bins;
        }
        catch (NumberFormatException nfe) {
            logger.warn("Invalid number of bins for histogram chart: " + param);
            logger.warn("Return default bins: " + DEFAULT_BINS);

            return DEFAULT_BINS;
        }
    }


    /**
     * Serves width of a single bin.
     *
     * @param values All values in this histogram
     *
     * @return The bin width that is given in <i>requestParameter</i> or -1 if
     * the bin width is not the dominant value for calculating the number of
     * bins in the histogram.
     */
    protected double getBinWidth(double[] values) {
        // Return -1 to trigger a calculation of the bin width in
        // AdvancedHistogramDataset if the user chose the number of bins as
        // dominant value.
        String choice = (String) requestParameter.get(REQUEST_KEY_BIN_CHOICE);
        if (choice == null || !choice.equalsIgnoreCase(REQUEST_KEY_BIN_WIDTH)) {
            return -1;
        }

        int    bins  = -1;
        String param = (String) requestParameter.get(REQUEST_KEY_BIN_WIDTH);

        double[] minmax     = getMinMax(values);
        double   totalWidth = minmax[1] - minmax[0];
        double   binWidth   = Double.parseDouble(param);

        double tmpBins = totalWidth / binWidth;

        bins = (int) Math.round(tmpBins);
        bins = bins <= 0 ? DEFAULT_BINS : bins;

        // the calculated number of bins with the given width exceed the maximum
        // number of bins.
        if (bins > MAXIMAL_BINS) {
            return totalWidth / (MAXIMAL_BINS);
        }

        return binWidth;
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :

http://dive4elements.wald.intevation.org