edu.ucla.stat.SOCR.chart.gui.ExtendedStackedBarRenderer.java Source code

Java tutorial

Introduction

Here is the source code for edu.ucla.stat.SOCR.chart.gui.ExtendedStackedBarRenderer.java

Source

/****************************************************
Statistics Online Computational Resource (SOCR)
http://www.StatisticsResource.org
     
All SOCR programs, materials, tools and resources are developed by and freely disseminated to the entire community.
Users may revise, extend, redistribute, modify under the terms of the Lesser GNU General Public License
as published by the Open Source Initiative http://opensource.org/licenses/. All efforts should be made to develop and distribute
factually correct, useful, portable and extensible resource all available in all digital formats for free over the Internet.
     
SOCR resources are distributed in the hope that they will be useful, but without
any warranty; without any explicit, implicit or implied warranty for merchantability or
fitness for a particular purpose. See the GNU Lesser General Public License for
more details see http://opensource.org/licenses/lgpl-license.php.
     
http://www.SOCR.ucla.edu
http://wiki.stat.ucla.edu/socr
 It s Online, Therefore, It Exists! 
****************************************************/
package edu.ucla.stat.SOCR.chart.gui;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.geom.Rectangle2D;
import java.text.NumberFormat;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.entity.CategoryItemEntity;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.labels.CategoryItemLabelGenerator;
import org.jfree.chart.labels.CategoryToolTipGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.CategoryItemRendererState;
import org.jfree.chart.renderer.category.StackedBarRenderer;
import org.jfree.data.category.CategoryDataset;
import org.jfree.text.TextUtilities;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.TextAnchor;

/**
 * An extension of the {@link StackedBarRenderer} that can draw positive and 
 * negative totals at the top and bottom of the stacked bars.
 */
public class ExtendedStackedBarRenderer extends StackedBarRenderer {

    /**
    * Show positive label?
    * @uml.property  name="showPositiveTotal"
    */
    private boolean showPositiveTotal = true;

    /**
    * Show negative label?
    * @uml.property  name="showNegativeTotal"
    */
    private boolean showNegativeTotal = true;

    /**
    * Font for labels.
    * @uml.property  name="totalLabelFont"
    */
    private Font totalLabelFont = new Font("SansSerif", Font.BOLD, 12);

    /**
    * Formatter for total.
    * @uml.property  name="totalFormatter"
    */
    private NumberFormat totalFormatter;

    /**
     * Creates a new renderer.
     */
    public ExtendedStackedBarRenderer() {
        super();
        this.totalFormatter = NumberFormat.getInstance();
    }

    /**
     * Returns the total formatter.
     * 
     * @return the total formatter (never <code>null</code>).
     */
    public NumberFormat getTotalFormatter() {
        return this.totalFormatter;
    }

    /**
     * Sets the total formatter.
     * 
     * @param format  the formatter (<code>null</code> not permitted).
     */
    public void setTotalFormatter(NumberFormat format) {
        if (format == null) {
            throw new IllegalArgumentException("Null format not permitted.");
        }
        this.totalFormatter = format;
    }

    /**
     * Draws a stacked bar for a specific item.
     *
     * @param g2  the graphics device.
     * @param state  the renderer state.
     * @param dataArea  the plot area.
     * @param plot  the plot.
     * @param domainAxis  the domain (category) axis.
     * @param rangeAxis  the range (value) axis.
     * @param dataset  the data.
     * @param row  the row index (zero-based).
     * @param column  the column index (zero-based).
     * @param pass  the pass index.
     */
    public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot,
            CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass) {

        // nothing is drawn for null values...
        Number dataValue = dataset.getValue(row, column);
        if (dataValue == null) {
            return;
        }

        double value = dataValue.doubleValue();

        PlotOrientation orientation = plot.getOrientation();
        double barW0 = domainAxis.getCategoryMiddle(column, getColumnCount(), dataArea, plot.getDomainAxisEdge())
                - state.getBarWidth() / 2.0;

        double positiveBase = 0.0;
        double negativeBase = 0.0;

        for (int i = 0; i < row; i++) {
            Number v = dataset.getValue(i, column);
            if (v != null) {
                double d = v.doubleValue();
                if (d > 0) {
                    positiveBase = positiveBase + d;
                } else {
                    negativeBase = negativeBase + d;
                }
            }
        }

        double translatedBase;
        double translatedValue;
        RectangleEdge location = plot.getRangeAxisEdge();
        if (value > 0.0) {
            translatedBase = rangeAxis.valueToJava2D(positiveBase, dataArea, location);
            translatedValue = rangeAxis.valueToJava2D(positiveBase + value, dataArea, location);
        } else {
            translatedBase = rangeAxis.valueToJava2D(negativeBase, dataArea, location);
            translatedValue = rangeAxis.valueToJava2D(negativeBase + value, dataArea, location);
        }
        double barL0 = Math.min(translatedBase, translatedValue);
        double barLength = Math.max(Math.abs(translatedValue - translatedBase), getMinimumBarLength());

        Rectangle2D bar = null;
        if (orientation == PlotOrientation.HORIZONTAL) {
            bar = new Rectangle2D.Double(barL0, barW0, barLength, state.getBarWidth());
        } else {
            bar = new Rectangle2D.Double(barW0, barL0, state.getBarWidth(), barLength);
        }
        Paint seriesPaint = getItemPaint(row, column);
        g2.setPaint(seriesPaint);
        g2.fill(bar);
        if (isDrawBarOutline() && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
            g2.setStroke(getItemStroke(row, column));
            g2.setPaint(getItemOutlinePaint(row, column));
            g2.draw(bar);
        }

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

        if (value > 0.0) {
            if (this.showPositiveTotal) {
                if (isLastPositiveItem(dataset, row, column)) {
                    g2.setPaint(Color.black);
                    g2.setFont(this.totalLabelFont);
                    double total = calculateSumOfPositiveValuesForCategory(dataset, column);
                    if (orientation == PlotOrientation.HORIZONTAL)
                        TextUtilities.drawRotatedString(this.totalFormatter.format(total), g2,
                                (float) (bar.getMaxX() + 5.0), (float) bar.getCenterY(), TextAnchor.CENTER_LEFT,
                                0.0, TextAnchor.CENTER_LEFT);

                    else
                        TextUtilities.drawRotatedString(this.totalFormatter.format(total), g2,
                                (float) bar.getCenterX(), (float) (bar.getMinY() - 4.0), TextAnchor.BOTTOM_CENTER,
                                0.0, TextAnchor.BOTTOM_CENTER);
                }
            }
        } else {
            if (this.showNegativeTotal) {
                if (isLastNegativeItem(dataset, row, column)) {
                    g2.setPaint(Color.black);
                    g2.setFont(this.totalLabelFont);
                    double total = calculateSumOfNegativeValuesForCategory(dataset, column);
                    if (orientation == PlotOrientation.HORIZONTAL)
                        TextUtilities.drawRotatedString(String.valueOf(total), g2, (float) (bar.getMinX() - 5.0),
                                (float) bar.getCenterY(), TextAnchor.CENTER_RIGHT, 0.0, TextAnchor.CENTER_RIGHT);
                    else
                        TextUtilities.drawRotatedString(String.valueOf(total), g2, (float) bar.getCenterX(),
                                (float) (bar.getMaxY() + 4.0), TextAnchor.TOP_CENTER, 0.0, TextAnchor.TOP_CENTER);
                }
            }
        }

        // collect entity and tool tip information...
        if (state.getInfo() != null) {
            EntityCollection entities = state.getEntityCollection();
            if (entities != null) {
                String tip = null;
                CategoryToolTipGenerator tipster = getToolTipGenerator(row, column);
                if (tipster != null) {
                    tip = tipster.generateToolTip(dataset, row, column);
                }
                String url = null;
                if (getItemURLGenerator(row, column) != null) {
                    url = getItemURLGenerator(row, column).generateURL(dataset, row, column);
                }
                CategoryItemEntity entity = new CategoryItemEntity(bar, tip, url, dataset, dataset.getRowKey(row),
                        dataset.getColumnKey(column));
                entities.add(entity);
            }
        }

    }

    /**
     * Returns true if the specified item is the last positive value for that 
     * category.
     * 
     * @param dataset  the dataset.
     * @param row  the row (series).
     * @param column  the column (category).
     * 
     * @return a boolean.
     */
    private boolean isLastPositiveItem(CategoryDataset dataset, int row, int column) {
        boolean result = true;
        Number dataValue = dataset.getValue(row, column);
        if (dataValue == null) {
            return false; // value is null
        }
        for (int r = row + 1; r < dataset.getRowCount(); r++) {
            dataValue = dataset.getValue(r, column);
            if (dataValue != null) {
                result = result && (dataValue.doubleValue() <= 0.0);
            }
        }
        return result;
    }

    /**
     * Returns true if the specified item is the last negative value for that 
     * category.
     * 
     * @param dataset  the dataset.
     * @param row  the row (series).
     * @param column  the column (category).
     * 
     * @return a boolean.
     */
    private boolean isLastNegativeItem(CategoryDataset dataset, int row, int column) {
        boolean result = true;
        Number dataValue = dataset.getValue(row, column);
        if (dataValue == null) {
            return false; // value is null
        }
        for (int r = row + 1; r < dataset.getRowCount(); r++) {
            dataValue = dataset.getValue(r, column);
            if (dataValue != null) {
                result = result && (dataValue.doubleValue() >= 0.0);
            }
        }
        return result;
    }

    /**
     * Calculates the sum of the positive values within a category.
     * 
     * @param dataset  the dataset.
     * @param column  the column (category).
     * 
     * @return the sum of the positive values.
     */
    private double calculateSumOfPositiveValuesForCategory(CategoryDataset dataset, int column) {
        double result = 0.0;
        for (int r = 0; r < dataset.getRowCount(); r++) {
            Number dataValue = dataset.getValue(r, column);
            if (dataValue != null) {
                double v = dataValue.doubleValue();
                if (v > 0.0) {
                    result = result + v;
                }
            }
        }
        return result;
    }

    /**
     * Calculates the sum of the negative values within a category.
     * 
     * @param dataset  the dataset.
     * @param column  the column (category).
     * 
     * @return the sum of the negative values.
     */
    private double calculateSumOfNegativeValuesForCategory(CategoryDataset dataset, int column) {
        double result = 0.0;
        for (int r = 0; r < dataset.getRowCount(); r++) {
            Number dataValue = dataset.getValue(r, column);
            if (dataValue != null) {
                double v = dataValue.doubleValue();
                if (v < 0.0) {
                    result = result + v;
                }
            }
        }
        return result;
    }

}