ingo@617: package de.intevation.gnv.chart;
ingo@617:
ingo@629: import java.util.Map;
ingo@629:
ingo@617: import org.apache.log4j.Logger;
ingo@617:
ingo@617: import org.jfree.chart.ChartTheme;
sascha@623:
ingo@617: import org.jfree.chart.plot.XYPlot;
ingo@617:
ingo@617: /**
ingo@767: * Default implementation of {@link de.intevation.gnv.chart.AbstractHistogram}.
ingo@767: *
ingo@767: * @author Ingo Weinzierl
ingo@617: */
ingo@617: public class DefaultHistogram
ingo@617: extends AbstractHistogram
ingo@617: {
ingo@767: /**
ingo@767: * Default bin count.
ingo@767: * TODO find a better default value
ingo@767: */
ingo@617: public static final int DEFAULT_BINS = 15;
ingo@767:
ingo@767: /**
ingo@767: * Constant field for limitating the number of bin in a single histogram.
ingo@767: */
ingo@629: public static final int MAXIMAL_BINS = 20;
ingo@767:
ingo@767: /**
ingo@767: * Default key to retrieve the number of bins from {@link
ingo@767: * #requestParameter}.
ingo@767: */
ingo@629: public static final String REQUEST_KEY_BIN_COUNT = "bincount";
ingo@767:
ingo@767: /**
ingo@767: * Default key to retrieve the width of a single bin from {@link
ingo@767: * #requestParameter}.
ingo@767: */
ingo@629: public static final String REQUEST_KEY_BIN_WIDTH = "binwidth";
ingo@767:
ingo@767: /**
ingo@767: * Default key to retrieve the chart width from {@link #requestParameter}.
ingo@767: */
ingo@629: public static final String REQUEST_KEY_CHART_WIDTH = "width";
ingo@767:
ingo@767: /**
sascha@778: * Default key to retrieve the object from {@link #requestParameter}. It
ingo@767: * defines which value this chart has to be used for bin calculation. You
ingo@767: * can either adjust the number of bins or the width of a single bin.
ingo@767: */
ingo@629: public static final String REQUEST_KEY_BIN_CHOICE = "bintype";
ingo@617:
ingo@767: /**
ingo@767: * Logger used for logging with log4j.
ingo@767: */
ingo@617: private static Logger logger = Logger.getLogger(DefaultHistogram.class);
ingo@617:
ingo@767: /**
ingo@767: * Object storing some further parameter used for chart settings.
ingo@767: */
ingo@629: protected Map requestParameter;
ingo@629:
ingo@1055: protected double[] minmax = null;
ingo@1055:
ingo@617:
ingo@767: /**
ingo@767: * Constructor to create DefaultHistogram objects.
ingo@767: *
ingo@767: * @param labels Labels to decorate this chart.
ingo@767: * @param data Raw data to be displayed in histogram.
ingo@767: * @param theme Theme used to adjust the chart look.
ingo@767: * @param requestParameter Object which serves some further settings.
ingo@767: */
ingo@617: public DefaultHistogram(
ingo@629: ChartLabels labels, Object[] data, ChartTheme theme, Map requestParameter
ingo@617: ) {
ingo@617: super(labels, data, theme);
ingo@629: this.requestParameter = requestParameter;
ingo@617: }
ingo@617:
ingo@617:
ingo@767: @Override
ingo@617: protected void applyDatasets() {
ingo@617: XYPlot plot = (XYPlot) chart.getPlot();
ingo@617:
ingo@617: // prepare data and create add them to histogram dataset
ingo@617: String name = (String) data[0];
ingo@617: double[] values = toDouble((Double[]) data[1]);
ingo@617:
ingo@1055: double binWidth = getBinWidth(values);
ingo@1055: int binCount = getBinCount();
ingo@1055:
ingo@1055: AdvancedHistogramDataset dataset =
ingo@1055: new AdvancedHistogramDataset(binCount, binWidth);
ingo@1055: dataset.addSeries(name, values);
ingo@617:
ingo@617: plot.setDataset(0, dataset);
ingo@617: }
ingo@617:
ingo@617:
ingo@767: /**
ingo@767: * Method which scans the hole bunch of values and returns an array with
ingo@767: * contains min and max value. Min value is stored at position 0, max value
ingo@767: * is stored at position 1 in that array.
ingo@767: *
ingo@767: * @param values Array which contains all values
ingo@767: *
ingo@767: * @return Array which contains min and max value
ingo@767: */
ingo@629: protected double[] getMinMax(double[] values) {
ingo@1055: if (minmax != null)
ingo@1055: return minmax;
ingo@1055:
ingo@629: double[] minmax = new double[2];
ingo@629: minmax[0] = Double.MAX_VALUE;
ingo@629: minmax[1] = Double.MIN_VALUE;
ingo@629:
ingo@629: int length = values.length;
ingo@629: for (int i = 0; i < length; i++) {
ingo@629: minmax[0] = values[i] < minmax[0] ? values[i] : minmax[0];
ingo@629: minmax[1] = values[i] > minmax[1] ? values[i] : minmax[1];
ingo@629: }
ingo@629:
ingo@1055: this.minmax = minmax;
ingo@1055:
ingo@629: return minmax;
ingo@629: }
ingo@629:
ingo@629:
ingo@767: /**
ingo@767: * Turn a Double[] into a double[].
ingo@767: *
ingo@767: * @param array Doube[]
ingo@767: *
ingo@767: * @return double[]
ingo@767: */
ingo@617: protected double[] toDouble(Double[] array) {
ingo@617: int length = array.length;
ingo@617: double[] values = new double[length];
ingo@617:
ingo@617: for(int i = 0; i < length; i++) {
ingo@617: values[i] = array[i].doubleValue();
ingo@617: }
ingo@617:
ingo@617: return values;
ingo@617: }
ingo@629:
ingo@629:
ingo@767: /**
ingo@1055: * Method to retrieve the number of bins.
ingo@767: *
ingo@1055: * @return the number of bins that is specified in requestParameter
ingo@1055: * or -1 if the number of bins is not the dominant value to calculate the
ingo@1055: * width of a single bin.
ingo@767: */
ingo@1055: protected int getBinCount() {
ingo@1055: // Return -1 to trigger a calculation of the number of bins in
ingo@1055: // AdvancedHistogramDataset if the user chose the bin width as dominant
ingo@1055: // value.
ingo@1055: String choice = (String) requestParameter.get(REQUEST_KEY_BIN_CHOICE);
ingo@1055: if (choice != null && choice.equalsIgnoreCase(REQUEST_KEY_BIN_WIDTH)) {
ingo@1055: return -1;
ingo@629: }
ingo@629:
ingo@629: int bins = -1;
ingo@629: String param = (String) requestParameter.get(REQUEST_KEY_BIN_COUNT);
ingo@629:
ingo@629: try {
ingo@629: bins = Integer.parseInt(param);
ingo@629: bins = bins <= 0 ? DEFAULT_BINS : bins;
ingo@629: bins = bins > MAXIMAL_BINS ? MAXIMAL_BINS : bins;
ingo@629:
ingo@629: return bins;
ingo@629: }
ingo@629: catch (NumberFormatException nfe) {
ingo@629: logger.warn("Invalid number of bins for histogram chart: " + param);
ingo@629: logger.warn("Return default bins: " + DEFAULT_BINS);
ingo@629:
ingo@629: return DEFAULT_BINS;
ingo@629: }
ingo@629: }
ingo@629:
ingo@629:
ingo@767: /**
ingo@1055: * Serves width of a single bin.
ingo@767: *
ingo@767: * @param values All values in this histogram
ingo@767: *
ingo@1055: * @return The bin width that is given in requestParameter or -1 if
ingo@1055: * the bin width is not the dominant value for calculating the number of
ingo@1055: * bins in the histogram.
ingo@767: */
ingo@1055: protected double getBinWidth(double[] values) {
ingo@1055: // Return -1 to trigger a calculation of the bin width in
ingo@1055: // AdvancedHistogramDataset if the user chose the number of bins as
ingo@1055: // dominant value.
ingo@1055: String choice = (String) requestParameter.get(REQUEST_KEY_BIN_CHOICE);
ingo@1055: if (choice == null || !choice.equalsIgnoreCase(REQUEST_KEY_BIN_WIDTH)) {
ingo@1055: return -1;
ingo@1055: }
ingo@1055:
ingo@1055: int bins = -1;
ingo@1055: String param = (String) requestParameter.get(REQUEST_KEY_BIN_WIDTH);
ingo@629:
ingo@1054: double[] minmax = getMinMax(values);
ingo@1054: double totalWidth = minmax[1] - minmax[0];
ingo@1054: double binWidth = Double.parseDouble(param);
ingo@629:
ingo@1054: double tmpBins = totalWidth / binWidth;
ingo@629:
ingo@1054: bins = (int) Math.round(tmpBins);
ingo@1054: bins = bins <= 0 ? DEFAULT_BINS : bins;
ingo@629:
ingo@1055: // the calculated number of bins with the given width exceed the maximum
ingo@1055: // number of bins.
ingo@1055: if (bins > MAXIMAL_BINS) {
ingo@1055: return totalWidth / (MAXIMAL_BINS);
ingo@1055: }
ingo@1055:
ingo@1055: return binWidth;
ingo@629: }
ingo@617: }
ingo@617: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :