    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2005, 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     * XYBubbleRenderer.java
    029     * ---------------------
    030     * (C) Copyright 2003-2005, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   Christian W. Zuckschwerdt;
    034     *
    035     * $Id: XYBubbleRenderer.java,v 1.8.2.3 2005/11/28 12:06:35 mungady Exp $
    036     *
    037     * Changes
    038     * -------
    039     * 28-Jan-2003 : Version 1 (DG);
    040     * 25-Mar-2003 : Implemented Serializable (DG);
    041     * 01-May-2003 : Modified drawItem() method signature (DG);
    042     * 30-Jul-2003 : Modified entity constructor (CZ);
    043     * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
    044     * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
    045     * 10-Feb-2004 : Small change to drawItem() method to make cut-and-paste
    046     *               overriding easier (DG);
    047     * 15-Jul-2004 : Switched getZ() and getZValue() methods (DG);
    048     * 19-Jan-2005 : Now accesses only primitives from dataset (DG);
    049     * 28-Feb-2005 : Modify renderer to use circles in legend (DG);
    050     * 17-Mar-2005 : Fixed bug in bubble bounds calculation (DG);
    051     * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG);
    052     *
    053     */
    054
    055    package org.jfree.chart.renderer.xy;
    056
    057    import java.awt.BasicStroke;
    058    import java.awt.Color;
    059    import java.awt.Graphics2D;
    060    import java.awt.Paint;
    061    import java.awt.Shape;
    062    import java.awt.Stroke;
    063    import java.awt.geom.Ellipse2D;
    064    import java.awt.geom.Rectangle2D;
    065    import java.io.Serializable;
    066
    067    import org.jfree.chart.LegendItem;
    068    import org.jfree.chart.axis.ValueAxis;
    069    import org.jfree.chart.entity.EntityCollection;
    070    import org.jfree.chart.entity.XYItemEntity;
    071    import org.jfree.chart.labels.XYToolTipGenerator;
    072    import org.jfree.chart.plot.CrosshairState;
    073    import org.jfree.chart.plot.PlotOrientation;
    074    import org.jfree.chart.plot.PlotRenderingInfo;
    075    import org.jfree.chart.plot.XYPlot;
    076    import org.jfree.data.xy.XYDataset;
    077    import org.jfree.data.xy.XYZDataset;
    078    import org.jfree.ui.RectangleEdge;
    079    import org.jfree.util.PublicCloneable;
    080
    081    /**
    082     * A renderer that draws a circle at each data point with a diameter that is
    083     * determined by the z-value in the dataset (the renderer requires the dataset
    084     * to be an instance of {@link XYZDataset}.
    085     */
    086    public class XYBubbleRenderer extends AbstractXYItemRenderer
    087                                  implements XYItemRenderer,
    088                                             Cloneable,
    089                                             PublicCloneable,
    090                                             Serializable {
    091
    092        /** For serialization. */
    093        public static final long serialVersionUID = -5221991598674249125L;
    094
    095        /** A useful constant. */
    096        public static final int SCALE_ON_BOTH_AXES = 0;
    097
    098        /** A useful constant. */
    099        public static final int SCALE_ON_DOMAIN_AXIS = 1;
    100
    101        /** A useful constant. */
    102        public static final int SCALE_ON_RANGE_AXIS = 2;
    103
    104        /** Controls how the width and height of the bubble are scaled. */
    105        private int scaleType;
    106
    107        /**
    108         * Constructs a new renderer.
    109         */
    110        public XYBubbleRenderer() {
    111            this(SCALE_ON_BOTH_AXES);
    112        }
    113
    114        /**
    115         * Constructs a new renderer with the specified type of scaling.
    116         *
    117         * @param scaleType  the type of scaling.
    118         */
    119        public XYBubbleRenderer(int scaleType) {
    120            super();
    121            this.scaleType = scaleType;
    122        }
    123
    124        /**
    125         * Returns the scale type.
    126         *
    127         * @return The scale type.
    128         */
    129        public int getScaleType() {
    130            return this.scaleType;
    131        }
    132
    133        /**
    134         * Draws the visual representation of a single data item.
    135         *
    136         * @param g2  the graphics device.
    137         * @param state  the renderer state.
    138         * @param dataArea  the area within which the data is being drawn.
    139         * @param info  collects information about the drawing.
    140         * @param plot  the plot (can be used to obtain standard color
    141         *              information etc).
    142         * @param domainAxis  the domain (horizontal) axis.
    143         * @param rangeAxis  the range (vertical) axis.
    144         * @param dataset  the dataset.
    145         * @param series  the series index (zero-based).
    146         * @param item  the item index (zero-based).
    147         * @param crosshairState  crosshair information for the plot
    148         *                        (<code>null</code> permitted).
    149         * @param pass  the pass index.
    150         */
    151        public void drawItem(Graphics2D g2,
    152                             XYItemRendererState state,
    153                             Rectangle2D dataArea,
    154                             PlotRenderingInfo info,
    155                             XYPlot plot,
    156                             ValueAxis domainAxis,
    157                             ValueAxis rangeAxis,
    158                             XYDataset dataset,
    159                             int series,
    160                             int item,
    161                             CrosshairState crosshairState,
    162                             int pass) {
    163
    164            PlotOrientation orientation = plot.getOrientation();
    165
    166            // get the data point...
    167            double x = dataset.getXValue(series, item);
    168            double y = dataset.getYValue(series, item);
    169            double z = Double.NaN;
    170            if (dataset instanceof XYZDataset) {
    171                XYZDataset xyzData = (XYZDataset) dataset;
    172                z = xyzData.getZValue(series, item);
    173            }
    174            if (!Double.isNaN(z)) {
    175                RectangleEdge domainAxisLocation = plot.getDomainAxisEdge();
    176                RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge();
    177                double transX = domainAxis.valueToJava2D(
    178                    x, dataArea, domainAxisLocation
    179                );
    180                double transY = rangeAxis.valueToJava2D(
    181                    y, dataArea, rangeAxisLocation
    182                );
    183
    184                double transDomain = 0.0;
    185                double transRange = 0.0;
    186                double zero;
    187
    188                switch(getScaleType()) {
    189                    case SCALE_ON_DOMAIN_AXIS:
    190                        zero = domainAxis.valueToJava2D(
    191                            0.0, dataArea, domainAxisLocation
    192                        );
    193                        transDomain = domainAxis.valueToJava2D(
    194                            z, dataArea, domainAxisLocation
    195                        ) - zero;
    196                        transRange = transDomain;
    197                        break;
    198                    case SCALE_ON_RANGE_AXIS:
    199                        zero = rangeAxis.valueToJava2D(
    200                            0.0, dataArea, rangeAxisLocation
    201                        );
    202                        transRange = zero - rangeAxis.valueToJava2D(
    203                            z, dataArea, rangeAxisLocation
    204                        );
    205                        transDomain = transRange;
    206                        break;
    207                    default:
    208                        double zero1 = domainAxis.valueToJava2D(
    209                            0.0, dataArea, domainAxisLocation
    210                        );
    211                        double zero2 = rangeAxis.valueToJava2D(
    212                            0.0, dataArea, rangeAxisLocation
    213                        );
    214                        transDomain = domainAxis.valueToJava2D(
    215                            z, dataArea, domainAxisLocation
    216                        ) - zero1;
    217                        transRange = zero2 - rangeAxis.valueToJava2D(
    218                            z, dataArea, rangeAxisLocation
    219                        );
    220                }
    221                transDomain = Math.abs(transDomain);
    222                transRange = Math.abs(transRange);
    223                Ellipse2D circle = null;
    224                if (orientation == PlotOrientation.VERTICAL) {
    225                    circle = new Ellipse2D.Double(
    226                        transX - transDomain / 2.0, transY - transRange / 2.0,
    227                        transDomain, transRange
    228                    );
    229                }
    230                else if (orientation == PlotOrientation.HORIZONTAL) {
    231                    circle = new Ellipse2D.Double(
    232                        transY - transRange / 2.0, transX - transDomain / 2.0,
    233                        transRange, transDomain
    234                    );
    235                }
    236                g2.setPaint(getItemPaint(series, item));
    237                g2.fill(circle);
    238                g2.setStroke(new BasicStroke(1.0f));
    239                g2.setPaint(Color.lightGray);
    240                g2.draw(circle);
    241
    242                // setup for collecting optional entity info...
    243                EntityCollection entities = null;
    244                if (info != null) {
    245                    entities = info.getOwner().getEntityCollection();
    246                }
    247
    248                // add an entity for the item...
    249                if (entities != null) {
    250                    String tip = null;
    251                    XYToolTipGenerator generator
    252                        = getToolTipGenerator(series, item);
    253                    if (generator != null) {
    254                        tip = generator.generateToolTip(dataset, series, item);
    255                    }
    256                    String url = null;
    257                    if (getURLGenerator() != null) {
    258                        url = getURLGenerator().generateURL(dataset, series, item);
    259                    }
    260                    XYItemEntity entity = new XYItemEntity(
    261                        circle, dataset, series, item, tip, url
    262                    );
    263                    entities.add(entity);
    264                }
    265
    266                updateCrosshairValues(
    267                    crosshairState, x, y, transX, transY, orientation
    268                );
    269            }
    270
    271        }
    272
    273        /**
    274         * Returns a legend item for the specified series.  The default method
    275         * is overridden so that the legend displays circles for all series.
    276         *
    277         * @param datasetIndex  the dataset index (zero-based).
    278         * @param series  the series index (zero-based).
    279         *
    280         * @return A legend item for the series.
    281         */
    282        public LegendItem getLegendItem(int datasetIndex, int series) {
    283            LegendItem result = null;
    284            XYPlot xyplot = getPlot();
    285            if (xyplot != null) {
    286                XYDataset dataset = xyplot.getDataset(datasetIndex);
    287                if (dataset != null) {
    288                    if (getItemVisible(series, 0)) {
    289                        String label = getLegendItemLabelGenerator().generateLabel(
    290                            dataset, series
    291                        );
    292                        String description = label;
    293                        String toolTipText = null;
    294                        if (getLegendItemToolTipGenerator() != null) {
    295                            toolTipText
    296                                = getLegendItemToolTipGenerator().generateLabel(
    297                                    dataset, series
    298                                );
    299                        }
    300                        String urlText = null;
    301                        if (getLegendItemURLGenerator() != null) {
    302                            urlText = getLegendItemURLGenerator().generateLabel(
    303                                dataset, series
    304                            );
    305                        }
    306                        Shape shape = new Ellipse2D.Double(-4.0, -4.0, 8.0, 8.0);
    307                        Paint paint = getSeriesPaint(series);
    308                        Paint outlinePaint = getSeriesOutlinePaint(series);
    309                        Stroke outlineStroke = getSeriesOutlineStroke(series);
    310                        result = new LegendItem(label, description,
    311                                toolTipText, urlText, shape, paint,
    312                                outlineStroke, outlinePaint);
    313                    }
    314                }
    315
    316            }
    317            return result;
    318        }
    319
    320        /**
    321         * Returns a clone of the renderer.
    322         *
    323         * @return A clone.
    324         *
    325         * @throws CloneNotSupportedException  if the renderer cannot be cloned.
    326         */
    327        public Object clone() throws CloneNotSupportedException {
    328            return super.clone();
    329        }
    330
    331    }