    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
    006     *
    007     * Project Info:  http://www.jfree.org/jfreechart/index.html
    008     *
    009     * This library is free software; you can redistribute it and/or modify it
    010     * under the terms of the GNU Lesser General Public License as published by
    011     * the Free Software Foundation; either version 2.1 of the License, or
    012     * (at your option) any later version.
    013     *
    014     * This library is distributed in the hope that it will be useful, but
    015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017     * License for more details.
    018     *
    019     * You should have received a copy of the GNU Lesser General Public
    020     * License along with this library; if not, write to the Free Software
    021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022     * USA.
    023     *
    024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025     * in the United States and other countries.]
    026     *
    027     * ------------------------------------
    028     * StatisticalLineAndShapeRenderer.java
    029     * ------------------------------------
    030     * (C) Copyright 2005, 2006, by Object Refinery Limited and Contributors.
    031     *
    032     * Original Author:  Mofeed Shahin;
    033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
    034     *
    035     * $Id: StatisticalLineAndShapeRenderer.java,v 1.4.2.7 2006/09/25 10:09:58 mungady Exp $
    036     *
    037     * Changes
    038     * -------
    039     * 01-Feb-2005 : Version 1, contributed by Mofeed Shahin (DG);
    040     * 16-Jun-2005 : Added errorIndicatorPaint to be consistent with
    041     *               StatisticalBarRenderer (DG);
    042     * ------------- JFREECHART 1.0.0 ---------------------------------------------
    043     * 11-Apr-2006 : Fixed bug 1468794, error bars drawn incorrectly when rendering
    044     *               plots with horizontal orientation (DG);
    045     * 25-Sep-2006 : Fixed bug 1562759, constructor ignoring arguments (DG);
    046     *
    047     */
    048
    049    package org.jfree.chart.renderer.category;
    050
    051    import java.awt.Graphics2D;
    052    import java.awt.Paint;
    053    import java.awt.Shape;
    054    import java.awt.geom.Line2D;
    055    import java.awt.geom.Rectangle2D;
    056    import java.io.IOException;
    057    import java.io.ObjectInputStream;
    058    import java.io.ObjectOutputStream;
    059    import java.io.Serializable;
    060
    061    import org.jfree.chart.axis.CategoryAxis;
    062    import org.jfree.chart.axis.ValueAxis;
    063    import org.jfree.chart.entity.CategoryItemEntity;
    064    import org.jfree.chart.entity.EntityCollection;
    065    import org.jfree.chart.event.RendererChangeEvent;
    066    import org.jfree.chart.labels.CategoryToolTipGenerator;
    067    import org.jfree.chart.plot.CategoryPlot;
    068    import org.jfree.chart.plot.PlotOrientation;
    069    import org.jfree.data.category.CategoryDataset;
    070    import org.jfree.data.statistics.StatisticalCategoryDataset;
    071    import org.jfree.io.SerialUtilities;
    072    import org.jfree.ui.RectangleEdge;
    073    import org.jfree.util.PaintUtilities;
    074    import org.jfree.util.PublicCloneable;
    075    import org.jfree.util.ShapeUtilities;
    076
    077    /**
    078     * A renderer that draws shapes for each data item, and lines between data
    079     * items.  Each point has a mean value and a standard deviation line. For use
    080     * with the {@link CategoryPlot} class.
    081     */
    082    public class StatisticalLineAndShapeRenderer extends LineAndShapeRenderer
    083        implements Cloneable, PublicCloneable, Serializable {
    084
    085        /** For serialization. */
    086        private static final long serialVersionUID = -3557517173697777579L;
    087
    088        /** The paint used to show the error indicator. */
    089        private transient Paint errorIndicatorPaint;
    090
    091        /**
    092         * Constructs a default renderer (draws shapes and lines).
    093         */
    094        public StatisticalLineAndShapeRenderer() {
    095            this(true, true);
    096        }
    097
    098        /**
    099         * Constructs a new renderer.
    100         *
    101         * @param linesVisible  draw lines?
    102         * @param shapesVisible  draw shapes?
    103         */
    104        public StatisticalLineAndShapeRenderer(boolean linesVisible,
    105                                               boolean shapesVisible) {
    106            super(linesVisible, shapesVisible);
    107            this.errorIndicatorPaint = null;
    108        }
    109
    110        /**
    111         * Returns the paint used for the error indicators.
    112         *
    113         * @return The paint used for the error indicators (possibly
    114         *         <code>null</code>).
    115         */
    116        public Paint getErrorIndicatorPaint() {
    117            return this.errorIndicatorPaint;
    118        }
    119
    120        /**
    121         * Sets the paint used for the error indicators (if <code>null</code>,
    122         * the item outline paint is used instead)
    123         *
    124         * @param paint  the paint (<code>null</code> permitted).
    125         */
    126        public void setErrorIndicatorPaint(Paint paint) {
    127            this.errorIndicatorPaint = paint;
    128            notifyListeners(new RendererChangeEvent(this));
    129        }
    130
    131        /**
    132         * Draw a single data item.
    133         *
    134         * @param g2  the graphics device.
    135         * @param state  the renderer state.
    136         * @param dataArea  the area in which the data is drawn.
    137         * @param plot  the plot.
    138         * @param domainAxis  the domain axis.
    139         * @param rangeAxis  the range axis.
    140         * @param dataset  the dataset (a {@link StatisticalCategoryDataset} is
    141         *   required).
    142         * @param row  the row index (zero-based).
    143         * @param column  the column index (zero-based).
    144         * @param pass  the pass.
    145         */
    146        public void drawItem(Graphics2D g2,
    147                             CategoryItemRendererState state,
    148                             Rectangle2D dataArea,
    149                             CategoryPlot plot,
    150                             CategoryAxis domainAxis,
    151                             ValueAxis rangeAxis,
    152                             CategoryDataset dataset,
    153                             int row,
    154                             int column,
    155                             int pass) {
    156
    157            // nothing is drawn for null...
    158            Number v = dataset.getValue(row, column);
    159            if (v == null) {
    160              return;
    161            }
    162
    163            StatisticalCategoryDataset statData
    164                = (StatisticalCategoryDataset) dataset;
    165
    166            Number meanValue = statData.getMeanValue(row, column);
    167
    168            PlotOrientation orientation = plot.getOrientation();
    169
    170            // current data point...
    171            double x1 = domainAxis.getCategoryMiddle(column, getColumnCount(),
    172                    dataArea, plot.getDomainAxisEdge());
    173
    174            double y1 = rangeAxis.valueToJava2D(meanValue.doubleValue(), dataArea,
    175                    plot.getRangeAxisEdge());
    176
    177            Shape shape = getItemShape(row, column);
    178            if (orientation == PlotOrientation.HORIZONTAL) {
    179                shape = ShapeUtilities.createTranslatedShape(shape, y1, x1);
    180            }
    181            else if (orientation == PlotOrientation.VERTICAL) {
    182                shape = ShapeUtilities.createTranslatedShape(shape, x1, y1);
    183            }
    184            if (getItemShapeVisible(row, column)) {
    185
    186                if (getItemShapeFilled(row, column)) {
    187                    g2.setPaint(getItemPaint(row, column));
    188                    g2.fill(shape);
    189                }
    190                else {
    191                    if (getUseOutlinePaint()) {
    192                        g2.setPaint(getItemOutlinePaint(row, column));
    193                    }
    194                    else {
    195                        g2.setPaint(getItemPaint(row, column));
    196                    }
    197                    g2.setStroke(getItemOutlineStroke(row, column));
    198                    g2.draw(shape);
    199                }
    200            }
    201
    202            if (getItemLineVisible(row, column)) {
    203                if (column != 0) {
    204
    205                    Number previousValue = statData.getValue(row, column - 1);
    206                    if (previousValue != null) {
    207
    208                        // previous data point...
    209                        double previous = previousValue.doubleValue();
    210                        double x0 = domainAxis.getCategoryMiddle(column - 1,
    211                                getColumnCount(), dataArea,
    212                                plot.getDomainAxisEdge());
    213                        double y0 = rangeAxis.valueToJava2D(previous, dataArea,
    214                                plot.getRangeAxisEdge());
    215
    216                        Line2D line = null;
    217                        if (orientation == PlotOrientation.HORIZONTAL) {
    218                            line = new Line2D.Double(y0, x0, y1, x1);
    219                        }
    220                        else if (orientation == PlotOrientation.VERTICAL) {
    221                            line = new Line2D.Double(x0, y0, x1, y1);
    222                        }
    223                        g2.setPaint(getItemPaint(row, column));
    224                        g2.setStroke(getItemStroke(row, column));
    225                        g2.draw(line);
    226                    }
    227                }
    228            }
    229
    230            RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
    231            RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
    232            double rectX = domainAxis.getCategoryStart(column, getColumnCount(),
    233                    dataArea, xAxisLocation);
    234
    235            rectX = rectX + row * state.getBarWidth();
    236
    237            g2.setPaint(getItemPaint(row, column));
    238
    239            //standard deviation lines
    240            double valueDelta = statData.getStdDevValue(row, column).doubleValue();
    241
    242            double highVal, lowVal;
    243            if ((meanValue.doubleValue() + valueDelta)
    244                    > rangeAxis.getRange().getUpperBound()) {
    245                highVal = rangeAxis.valueToJava2D(
    246                        rangeAxis.getRange().getUpperBound(), dataArea,
    247                        yAxisLocation);
    248            }
    249            else {
    250                highVal = rangeAxis.valueToJava2D(meanValue.doubleValue()
    251                        + valueDelta, dataArea, yAxisLocation);
    252            }
    253
    254            if ((meanValue.doubleValue() + valueDelta)
    255                    < rangeAxis.getRange().getLowerBound()) {
    256                lowVal = rangeAxis.valueToJava2D(
    257                        rangeAxis.getRange().getLowerBound(), dataArea,
    258                        yAxisLocation);
    259            }
    260            else {
    261                lowVal = rangeAxis.valueToJava2D(meanValue.doubleValue()
    262                        - valueDelta, dataArea, yAxisLocation);
    263            }
    264
    265            if (this.errorIndicatorPaint != null) {
    266                g2.setPaint(this.errorIndicatorPaint);
    267            }
    268            else {
    269                g2.setPaint(getItemPaint(row, column));
    270            }
    271            Line2D line = new Line2D.Double();
    272            if (orientation == PlotOrientation.HORIZONTAL) {
    273                line.setLine(lowVal, x1, highVal, x1);
    274                g2.draw(line);
    275                line.setLine(lowVal, x1 - 5.0d, lowVal, x1 + 5.0d);
    276                g2.draw(line);
    277                line.setLine(highVal, x1 - 5.0d, highVal, x1 + 5.0d);
    278                g2.draw(line);
    279            }
    280            else {  // PlotOrientation.VERTICAL
    281                line.setLine(x1, lowVal, x1, highVal);
    282                g2.draw(line);
    283                line.setLine(x1 - 5.0d, highVal, x1 + 5.0d, highVal);
    284                g2.draw(line);
    285                line.setLine(x1 - 5.0d, lowVal, x1 + 5.0d, lowVal);
    286                g2.draw(line);
    287            }
    288
    289            // draw the item label if there is one...
    290            if (isItemLabelVisible(row, column)) {
    291                if (orientation == PlotOrientation.HORIZONTAL) {
    292                  drawItemLabel(g2, orientation, dataset, row, column,
    293                      y1, x1, (meanValue.doubleValue() < 0.0));
    294                }
    295                else if (orientation == PlotOrientation.VERTICAL) {
    296                  drawItemLabel(g2, orientation, dataset, row, column,
    297                      x1, y1, (meanValue.doubleValue() < 0.0));
    298                }
    299            }
    300
    301            // collect entity and tool tip information...
    302            if (state.getInfo() != null) {
    303                EntityCollection entities = state.getEntityCollection();
    304                if (entities != null && shape != null) {
    305                    String tip = null;
    306                    CategoryToolTipGenerator tipster = getToolTipGenerator(row,
    307                            column);
    308                    if (tipster != null) {
    309                        tip = tipster.generateToolTip(dataset, row, column);
    310                    }
    311                    String url = null;
    312                    if (getItemURLGenerator(row, column) != null) {
    313                        url = getItemURLGenerator(row, column).generateURL(
    314                                dataset, row, column);
    315                    }
    316                    CategoryItemEntity entity = new CategoryItemEntity(shape, tip,
    317                            url, dataset, row, dataset.getColumnKey(column),
    318                            column);
    319                    entities.add(entity);
    320
    321                }
    322
    323            }
    324
    325        }
    326
    327        /**
    328         * Tests this renderer for equality with an arbitrary object.
    329         *
    330         * @param obj  the object (<code>null</code> permitted).
    331         *
    332         * @return A boolean.
    333         */
    334        public boolean equals(Object obj) {
    335            if (obj == this) {
    336                return true;
    337            }
    338            if (!(obj instanceof StatisticalLineAndShapeRenderer)) {
    339                return false;
    340            }
    341            if (!super.equals(obj)) {
    342                return false;
    343            }
    344            StatisticalLineAndShapeRenderer that
    345                = (StatisticalLineAndShapeRenderer) obj;
    346            if (!PaintUtilities.equal(this.errorIndicatorPaint,
    347                    that.errorIndicatorPaint)) {
    348                return false;
    349            }
    350            return true;
    351        }
    352
    353        /**
    354         * Provides serialization support.
    355         *
    356         * @param stream  the output stream.
    357         *
    358         * @throws IOException  if there is an I/O error.
    359         */
    360        private void writeObject(ObjectOutputStream stream) throws IOException {
    361            stream.defaultWriteObject();
    362            SerialUtilities.writePaint(this.errorIndicatorPaint, stream);
    363        }
    364
    365        /**
    366         * Provides serialization support.
    367         *
    368         * @param stream  the input stream.
    369         *
    370         * @throws IOException  if there is an I/O error.
    371         * @throws ClassNotFoundException  if there is a classpath problem.
    372         */
    373        private void readObject(ObjectInputStream stream)
    374            throws IOException, ClassNotFoundException {
    375            stream.defaultReadObject();
    376            this.errorIndicatorPaint = SerialUtilities.readPaint(stream);
    377        }
    378
    379    }