nl.strohalm.cyclos.utils.jfreeAsymmetric.AsymmetricStatisticalBarRenderer.java Source code

Java tutorial

Introduction

Here is the source code for nl.strohalm.cyclos.utils.jfreeAsymmetric.AsymmetricStatisticalBarRenderer.java

Source

/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
    
Cyclos is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
    
Cyclos is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    
 */
package nl.strohalm.cyclos.utils.jfreeAsymmetric;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.event.RendererChangeEvent;
import org.jfree.chart.labels.CategoryItemLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.chart.renderer.category.CategoryItemRendererState;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.statistics.StatisticalCategoryDataset;
import org.jfree.io.SerialUtilities;
import org.jfree.ui.RectangleEdge;
import org.jfree.util.PaintUtilities;
import org.jfree.util.PublicCloneable;

/**
 * A StatisticalBarRenderer which can display error bars inside a normal bar, which are asymmetric (that is: the lower and upper half of the bar need
 * not be equal). Can be used in stead of the JFreeChart statisticalBarRenderer. Is an alternative for StatisticalBarRenderer of JFreeChart; it
 * subclasses the parent of StatisticalBarRenderer, alters the needed code, and copies the rest of the code.
 * 
 * @author Rinke
 * 
 */
public class AsymmetricStatisticalBarRenderer extends BarRenderer
        implements CategoryItemRenderer, Cloneable, PublicCloneable, Serializable {

    private static final long serialVersionUID = 193574839451876016L;

    // *************** ALTERED METHODS (COMPARED TO STATISTICALBARRENDERER) ****************
    // ***********************************************************************************

    /** The paint used to show the error indicator. */
    private transient Paint errorIndicatorPaint;

    /**
     * Default constructor.
     */
    public AsymmetricStatisticalBarRenderer() {
        super();
        errorIndicatorPaint = Color.gray;
    }

    /**
     * Draws the bar with its standard deviation line range for a single (series, category) data item.
     * 
     * @param g2 the graphics device.
     * @param state the renderer state.
     * @param dataArea the data area.
     * @param plot the plot.
     * @param domainAxis the domain axis.
     * @param rangeAxis the range axis.
     * @param data the data.
     * @param row the row index (zero-based).
     * @param column the column index (zero-based).
     * @param pass the pass index.
     */
    @Override
    public void drawItem(final Graphics2D g2, final CategoryItemRendererState state, final Rectangle2D dataArea,
            final CategoryPlot plot, final CategoryAxis domainAxis, final ValueAxis rangeAxis,
            final CategoryDataset data, final int row, final int column, final int pass) {

        // defensive check
        if (!(data instanceof StatisticalCategoryDataset)) {
            throw new IllegalArgumentException("Requires StatisticalCategoryDataset.");
        }
        final StatisticalCategoryDataset statData = (StatisticalCategoryDataset) data;

        final PlotOrientation orientation = plot.getOrientation();
        if (orientation == PlotOrientation.HORIZONTAL) {
            drawHorizontalItem(g2, state, dataArea, plot, domainAxis, rangeAxis, statData, row, column);
        } else if (orientation == PlotOrientation.VERTICAL) {
            drawVerticalItem(g2, state, dataArea, plot, domainAxis, rangeAxis, statData, row, column);
        }
    }

    // ****************** COPIED CODE FROM StatisticalBarRenderer *******************************
    // *****************************************************************************************

    /**
     * Tests this renderer for equality with an arbitrary object.
     * 
     * @param obj the object (<code>null</code> permitted).
     * 
     * @return A boolean.
     */
    @Override
    public boolean equals(final Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof AsymmetricStatisticalBarRenderer)) {
            return false;
        }
        if (!super.equals(obj)) {
            return false;
        }
        final AsymmetricStatisticalBarRenderer that = (AsymmetricStatisticalBarRenderer) obj;
        if (!PaintUtilities.equal(getErrorIndicatorPaint(), that.getErrorIndicatorPaint())) {
            return false;
        }
        return true;
    }

    /**
     * Returns the paint used for the error indicators.
     * 
     * @return The paint used for the error indicators (possibly <code>null</code>).
     */
    public Paint getErrorIndicatorPaint() {
        return errorIndicatorPaint;
    }

    /**
     * Sets the paint used for the error indicators (if <code>null</code>, the item outline paint is used instead)
     * 
     * @param paint the paint (<code>null</code> permitted).
     */
    public void setErrorIndicatorPaint(final Paint paint) {
        errorIndicatorPaint = paint;
        notifyListeners(new RendererChangeEvent(this));
    }

    /**
     * Draws an item for a plot with a horizontal orientation.
     * 
     * @param g2 the graphics device.
     * @param state the renderer state.
     * @param dataArea the data area.
     * @param plot the plot.
     * @param domainAxis the domain axis.
     * @param rangeAxis the range axis.
     * @param dataset the data.
     * @param row the row index (zero-based).
     * @param column the column index (zero-based).
     */
    protected void drawHorizontalItem(final Graphics2D g2, final CategoryItemRendererState state,
            final Rectangle2D dataArea, final CategoryPlot plot, final CategoryAxis domainAxis,
            final ValueAxis rangeAxis, final StatisticalCategoryDataset dataset, final int row, final int column) {
        // nothing is drawn for null... //added this test block, Rinke
        final Number v = dataset.getValue(row, column);
        if (v == null) {
            return;
        }

        final RectangleEdge xAxisLocation = plot.getDomainAxisEdge();

        // BAR Y
        double rectY = domainAxis.getCategoryStart(column, getColumnCount(), dataArea, xAxisLocation);

        final int seriesCount = getRowCount();
        final int categoryCount = getColumnCount();
        if (seriesCount > 1) {
            final double seriesGap = dataArea.getHeight() * getItemMargin() / (categoryCount * (seriesCount - 1));
            rectY = rectY + row * (state.getBarWidth() + seriesGap);
        } else {
            rectY = rectY + row * state.getBarWidth();
        }

        // BAR X
        final Number meanValue = dataset.getMeanValue(row, column);

        double value = meanValue.doubleValue();
        double base = 0.0;
        final double lclip = getLowerClip();
        final double uclip = getUpperClip();

        if (uclip <= 0.0) { // cases 1, 2, 3 and 4
            if (value >= uclip) {
                return; // bar is not visible
            }
            base = uclip;
            if (value <= lclip) {
                value = lclip;
            }
        } else if (lclip <= 0.0) { // cases 5, 6, 7 and 8
            if (value >= uclip) {
                value = uclip;
            } else {
                if (value <= lclip) {
                    value = lclip;
                }
            }
        } else { // cases 9, 10, 11 and 12
            if (value <= lclip) {
                return; // bar is not visible
            }
            base = getLowerClip();
            if (value >= uclip) {
                value = uclip;
            }
        }

        final RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
        final double transY1 = rangeAxis.valueToJava2D(base, dataArea, yAxisLocation);
        final double transY2 = rangeAxis.valueToJava2D(value, dataArea, yAxisLocation);
        final double rectX = Math.min(transY2, transY1);

        final double rectHeight = state.getBarWidth();
        final double rectWidth = Math.abs(transY2 - transY1);

        final Rectangle2D bar = new Rectangle2D.Double(rectX, rectY, rectWidth, rectHeight);
        final Paint seriesPaint = getItemPaint(row, column);
        g2.setPaint(seriesPaint);
        g2.fill(bar);
        if (state.getBarWidth() > 3) {
            g2.setStroke(getItemStroke(row, column));
            g2.setPaint(getItemOutlinePaint(row, column));
            g2.draw(bar);
        }
        // ********** BLOCK WITH CHANGES RELATIVE TO StatisticalBarRenderere *********************
        // standard deviation lines
        final AsymmetricStatisticalCategoryDataset asymmDataset = (AsymmetricStatisticalCategoryDataset) dataset;
        final Number highValRaw = asymmDataset.getUpperValue(row, column);
        final Number lowValRaw = asymmDataset.getLowerValue(row, column);
        if (highValRaw != null && lowValRaw != null) { // ADDED THIS IF, RINKE
            final double highVal = rangeAxis.valueToJava2D(highValRaw.doubleValue(), dataArea, yAxisLocation);
            final double lowVal = rangeAxis.valueToJava2D(lowValRaw.doubleValue(), dataArea, yAxisLocation);
            // *************************** end of block with changes ******************************

            if (getErrorIndicatorPaint() != null) {
                g2.setPaint(getErrorIndicatorPaint());
            } else {
                g2.setPaint(getItemOutlinePaint(row, column));
            }
            Line2D line = null;
            line = new Line2D.Double(lowVal, rectY + rectHeight / 2.0d, highVal, rectY + rectHeight / 2.0d);
            g2.draw(line);
            line = new Line2D.Double(highVal, rectY + rectHeight * 0.25, highVal, rectY + rectHeight * 0.75);
            g2.draw(line);
            line = new Line2D.Double(lowVal, rectY + rectHeight * 0.25, lowVal, rectY + rectHeight * 0.75);
            g2.draw(line);
        }

        final CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column);
        if (generator != null && isItemLabelVisible(row, column)) {
            drawItemLabel(g2, dataset, row, column, plot, generator, bar, (value < 0.0));
        }

        // add an item entity, if this information is being collected
        final EntityCollection entities = state.getEntityCollection();
        if (entities != null) {
            addItemEntity(entities, dataset, row, column, bar);
        }

    }

    /**
     * Draws an item for a plot with a vertical orientation.
     * 
     * @param g2 the graphics device.
     * @param state the renderer state.
     * @param dataArea the data area.
     * @param plot the plot.
     * @param domainAxis the domain axis.
     * @param rangeAxis the range axis.
     * @param dataset the data.
     * @param row the row index (zero-based).
     * @param column the column index (zero-based).
     */
    protected void drawVerticalItem(final Graphics2D g2, final CategoryItemRendererState state,
            final Rectangle2D dataArea, final CategoryPlot plot, final CategoryAxis domainAxis,
            final ValueAxis rangeAxis, final StatisticalCategoryDataset dataset, final int row, final int column) {
        // nothing is drawn for null... //ADDED THIS BLOCK, RINKE
        final Number v = dataset.getValue(row, column);
        if (v == null) {
            return;
        }

        final RectangleEdge xAxisLocation = plot.getDomainAxisEdge();

        // BAR X
        double rectX = domainAxis.getCategoryStart(column, getColumnCount(), dataArea, xAxisLocation);

        final int seriesCount = getRowCount();
        final int categoryCount = getColumnCount();
        if (seriesCount > 1) {
            final double seriesGap = dataArea.getWidth() * getItemMargin() / (categoryCount * (seriesCount - 1));
            rectX = rectX + row * (state.getBarWidth() + seriesGap);
        } else {
            rectX = rectX + row * state.getBarWidth();
        }

        // BAR Y
        final Number meanValue = dataset.getMeanValue(row, column);

        double value = meanValue.doubleValue();
        double base = 0.0;
        final double lclip = getLowerClip();
        final double uclip = getUpperClip();

        if (uclip <= 0.0) { // cases 1, 2, 3 and 4
            if (value >= uclip) {
                return; // bar is not visible
            }
            base = uclip;
            if (value <= lclip) {
                value = lclip;
            }
        } else if (lclip <= 0.0) { // cases 5, 6, 7 and 8
            if (value >= uclip) {
                value = uclip;
            } else {
                if (value <= lclip) {
                    value = lclip;
                }
            }
        } else { // cases 9, 10, 11 and 12
            if (value <= lclip) {
                return; // bar is not visible
            }
            base = getLowerClip();
            if (value >= uclip) {
                value = uclip;
            }
        }

        final RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
        final double transY1 = rangeAxis.valueToJava2D(base, dataArea, yAxisLocation);
        final double transY2 = rangeAxis.valueToJava2D(value, dataArea, yAxisLocation);
        final double rectY = Math.min(transY2, transY1);

        final double rectWidth = state.getBarWidth();
        final double rectHeight = Math.abs(transY2 - transY1);

        final Rectangle2D bar = new Rectangle2D.Double(rectX, rectY, rectWidth, rectHeight);
        final Paint seriesPaint = getItemPaint(row, column);
        g2.setPaint(seriesPaint);
        g2.fill(bar);
        if (state.getBarWidth() > 3) {
            g2.setStroke(getItemStroke(row, column));
            g2.setPaint(getItemOutlinePaint(row, column));
            g2.draw(bar);
        }

        // standard deviation lines
        final AsymmetricStatisticalCategoryDataset asymmDataset = (AsymmetricStatisticalCategoryDataset) dataset;
        final Number highValRaw = asymmDataset.getUpperValue(row, column);
        final Number lowValRaw = asymmDataset.getLowerValue(row, column);
        // only draw if both error bars items are not null
        if (highValRaw != null && lowValRaw != null) { // ADDED THIS IF, RINKE
            final double highVal = rangeAxis.valueToJava2D(highValRaw.doubleValue(), dataArea, yAxisLocation);
            final double lowVal = rangeAxis.valueToJava2D(lowValRaw.doubleValue(), dataArea, yAxisLocation);

            if (getErrorIndicatorPaint() != null) {
                g2.setPaint(getErrorIndicatorPaint());
            } else {
                g2.setPaint(getItemOutlinePaint(row, column));
            }
            Line2D line = null;
            line = new Line2D.Double(rectX + rectWidth / 2.0d, lowVal, rectX + rectWidth / 2.0d, highVal);
            g2.draw(line);
            line = new Line2D.Double(rectX + rectWidth / 2.0d - 5.0d, highVal, rectX + rectWidth / 2.0d + 5.0d,
                    highVal);
            g2.draw(line);
            line = new Line2D.Double(rectX + rectWidth / 2.0d - 5.0d, lowVal, rectX + rectWidth / 2.0d + 5.0d,
                    lowVal);
            g2.draw(line);
        }

        final CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column);
        if (generator != null && isItemLabelVisible(row, column)) {
            drawItemLabel(g2, dataset, row, column, plot, generator, bar, (value < 0.0));
        }

        // add an item entity, if this information is being collected
        final EntityCollection entities = state.getEntityCollection();
        if (entities != null) {
            addItemEntity(entities, dataset, row, column, bar);
        }
    }

    /**
     * Provides serialization support.
     * 
     * @param stream the input stream.
     * 
     * @throws IOException if there is an I/O error.
     * @throws ClassNotFoundException if there is a classpath problem.
     */
    private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException {
        stream.defaultReadObject();
        errorIndicatorPaint = SerialUtilities.readPaint(stream);
    }

    /**
     * Provides serialization support.
     * 
     * @param stream the output stream.
     * 
     * @throws IOException if there is an I/O error.
     */
    private void writeObject(final ObjectOutputStream stream) throws IOException {
        stream.defaultWriteObject();
        SerialUtilities.writePaint(errorIndicatorPaint, stream);
    }

}