view gnv-artifacts/src/main/java/de/intevation/gnv/chart/VerticalCrossSectionChart.java @ 774:d0a39efbfd96

Removed race-condition when trying to access the index of the next line color used in charts (applied patch in issue211). gnv-artifacts/trunk@834 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Fri, 26 Mar 2010 10:06:36 +0000
parents 79401c871da4
children 9a828e5a2390
line wrap: on
line source
package de.intevation.gnv.chart;

import de.intevation.gnv.jfreechart.PolygonDataset;
import de.intevation.gnv.jfreechart.PolygonPlot;
import de.intevation.gnv.jfreechart.PolygonRenderer;
import de.intevation.gnv.jfreechart.PolygonSeries;

import de.intevation.gnv.math.AttributedXYColumns;

import de.intevation.gnv.raster.Palette;

import java.awt.Color;
import java.awt.Paint;

import java.text.NumberFormat;

import java.util.HashSet;
import java.util.Locale;
import java.util.Map;

import org.jfree.chart.JFreeChart;

import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.SymbolAxis;
import org.jfree.chart.axis.ValueAxis;

import org.jfree.chart.plot.PlotOrientation;

import org.jfree.chart.renderer.LookupPaintScale;

import org.jfree.chart.title.PaintScaleLegend;
import org.jfree.chart.title.TextTitle;

import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;

/**
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
 */
public class VerticalCrossSectionChart
implements   Chart
{
    /**
     *  Lookup class for retrieving a color which corresponds to a specific
     *  integer value.
     */
    public static final class PalettePaintLookup
    implements PolygonRenderer.PaintLookup
    {
        /**
         * Object storing information about value ranges and its coresponding
         * colors and descriptions.
         */
        private Palette             palette;

        /**
         * Map containing some special <code>Paint</code> like ground fillcolor.
         */
        private Map<Integer, Paint> special;

        /**
         * Constructor
         *
         * @param palette See {@link #palette}
         */
        public PalettePaintLookup(Palette palette) {
            this(palette, null);
        }

        /**
         * Constructor
         *
         * @param palette See {@link #palette}
         * @param special See {@link #special}
         */
        public PalettePaintLookup(
            Palette             palette, 
            Map<Integer, Paint> special
        ) {
            this.palette = palette;
            this.special = special;
        }

        /**
         * @param index Index of a <code>Paint</code>
         *
         * @return <code>Paint</code> object for a given index.
         */
        public Paint getPaint(int index) {
            if (special != null) {
                Paint paint = special.get(index);
                if (paint != null) {
                    return paint;
                }
            }
            return index < 0
                ? Color.black
                : palette.getColor(index);
        }
    } // class PalettePaintLookup

    /**
     * This class is used to turn labels which represent a number into a 
     * specific format.
     */
    public static class LocalizedLabelGenerator
    extends             PolygonRenderer.DefaultLabelGenerator
    {
        /**
         * <code>NumberFormat</code> which is used to turn a number into a
         * specific format.
         */
        protected NumberFormat format;

        /**
         * Constructor
         */
        public LocalizedLabelGenerator() {
        }

        /**
         * Constructor 
         *
         * @param format See {@link #format}
         */
        public LocalizedLabelGenerator(NumberFormat format) {
            this.format = format;
        }

        /**
         * If label is a <code>Number</code>, it is turned into a format
         * specified by {@link #format}.
         *
         * @param label Label to format.
         *
         * @return String representation of label.
         */
        @Override
        protected String toString(Object label) {
            return label instanceof Number
                ? format.format(((Number)label).doubleValue())
                : super.toString(label);
        }
    } // class LocalizedLabelGenerator

    /**
     * JFreeChart object stored at this place after chart creation.
     */
    protected JFreeChart chart;

    /**
     * Stores {@link de.intevation.gnv.jfreechart.PolygonDataset} which is used
     * to create a vertical cross chart.
     */
    protected AttributedXYColumns columns;

    /**
     * Map which contains colors to fill polygons draw by this chart.
     */
    protected Map<Integer, Paint> special;

    /**
     * Object used to map value intervals to specific colors and descriptions.
     */
    protected Palette             palette;

    /**
     * Locale object used for i18n support.
     */
    protected Locale              locale;

    /**
     * Labels for decorating the chart.
     */
    protected ChartLabels         labels;

    /**
     * Default Constructor
     */
    public VerticalCrossSectionChart() {
    }

    /**
     * Constructor for VerticalCrossSectionChart creation.
     *
     * @param columns See {@link #columns}
     * @param palette See {@link #palette}
     * @param locale See {@link #locale}
     * @param labels See {@link #labels}
     */
    public VerticalCrossSectionChart(
        AttributedXYColumns columns,
        Palette             palette,
        Locale              locale,
        ChartLabels         labels
    ) {
        this(columns, palette, null, locale, labels);
    }

    /**
     * Constructor for VerticalCrossSectionChart creation.
     *
     * @param columns See {@link #columns}
     * @param palette See {@link #palette}
     * @param special See {@link #special}
     * @param locale See {@link #locale}
     * @param labels See {@link #labels}
     */
    public VerticalCrossSectionChart(
        AttributedXYColumns columns,
        Palette             palette,
        Map<Integer, Paint> special,
        Locale              locale,
        ChartLabels         labels
    ) {
        this.columns = columns;
        this.palette = palette;
        this.special = special;
        this.locale  = locale;
        this.labels  = labels;
    }

    /**
     * This method is used to create a JFreeChart from this object. A 2D plot is
     * drawn and a legend panel is created containing each value range.
     *
     * @return JFreeChart object
     */
    protected JFreeChart createChart() {

        boolean legendB  = false;
        boolean tooltips = false;
        boolean urls     = false;

        PlotOrientation po  = PlotOrientation.HORIZONTAL;
        PolygonDataset data = columns.getPolygonDataset();

        HashSet<Integer> usedColors = new HashSet<Integer>();

        for (int i = data.getSeriesCount()-1; i >= 0; --i) {
            PolygonSeries ps = data.getSeries(i);
            Integer fill = (Integer)ps.getAttribute("fill");
            if (fill != null
            && (special != null && !special.containsKey(fill))) {
                usedColors.add(fill);
            }
        }

        NumberFormat format = NumberFormat.getInstance(locale);
        format.setMinimumFractionDigits(0);
        format.setMaximumFractionDigits(2);

        PolygonRenderer renderer = new PolygonRenderer(
            new PalettePaintLookup(palette, special),
            new LocalizedLabelGenerator(format));

        ValueAxis domainAxis = new NumberAxis(this.labels.getDomainAxisLabel());
        ValueAxis rangeAxis  = new NumberAxis(this.labels.getRangeAxisLabel());

        PolygonPlot plot = new PolygonPlot(
            data,
            renderer,
            domainAxis,
            rangeAxis,
            null);

        plot.setOutlinePaint(Color.WHITE);

        String [] labels = new String[usedColors.size()];

        int colors = palette.getSize();
        LookupPaintScale lookupPaint =
            new LookupPaintScale(-0.5d, labels.length-0.5d, Color.white);

        Color color = null;

        for (int i = 0, j = labels.length-1; i < colors && j >= 0; i++) {
            if (usedColors.contains(i)) {
                Palette.Entry entry = palette.getEntryByIndex(i);
                color     = entry.getColor();
                labels[j] = entry.getDescription();
                lookupPaint.add(j-0.5d, color);
                --j;
            }
        }

        JFreeChart chart = new JFreeChart(
            this.labels.getTitle(),
            JFreeChart.DEFAULT_TITLE_FONT,
            plot,
            legendB);

        chart.removeLegend();
        chart.addSubtitle(new TextTitle(this.labels.getSubtitle()));

        SymbolAxis scale = new SymbolAxis(this.labels.getParameterName(), labels);
        scale.setRange(-1.5d, labels.length+0.5d);
        scale.setGridBandsVisible(false);
        scale.setPlot(plot);

        PaintScaleLegend legend = new PaintScaleLegend(
            lookupPaint, scale);
        legend.setMargin(new RectangleInsets(3d, 10d, 3d, 10d));
        legend.setPosition(RectangleEdge.LEFT);
        legend.setAxisOffset(5d);

        chart.addSubtitle(legend);

        return chart;
    }

    /**
     * @see de.intevation.gnv.chart.Chart#generateChart()
     */
    public JFreeChart generateChart() {
        if (chart == null) {
            chart = createChart();
        }

        return chart;
    }

    /**
     * Set the background paint of {@link #chart}.
     */
    public void setBackgroundPaint(Paint paint) {
        chart.setBackgroundPaint(paint);
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :

http://dive4elements.wald.intevation.org