    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     * XYAreaRenderer2.java
    029     * --------------------
    030     * (C) Copyright 2004, 2005, by Hari and Contributors.
    031     *
    032     * Original Author:  Hari (ourhari@hotmail.com);
    033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
    034     *                   Richard Atkinson;
    035     *                   Christian W. Zuckschwerdt;
    036     *
    037     * $Id: XYAreaRenderer2.java,v 1.12.2.4 2005/12/02 11:59:43 mungady Exp $
    038     *
    039     * Changes:
    040     * --------
    041     * 03-Apr-2002 : Version 1, contributed by Hari.  This class is based on the
    042     *               StandardXYItemRenderer class (DG);
    043     * 09-Apr-2002 : Removed the translated zero from the drawItem method -
    044     *               overridden the initialise() method to calculate it (DG);
    045     * 30-May-2002 : Added tool tip generator to constructor to match super
    046     *               class (DG);
    047     * 25-Jun-2002 : Removed unnecessary local variable (DG);
    048     * 05-Aug-2002 : Small modification to drawItem method to support URLs for
    049     *               HTML image maps (RA);
    050     * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG);
    051     * 07-Nov-2002 : Renamed AreaXYItemRenderer --> XYAreaRenderer (DG);
    052     * 25-Mar-2003 : Implemented Serializable (DG);
    053     * 01-May-2003 : Modified drawItem() method signature (DG);
    054     * 27-Jul-2003 : Made line and polygon properties protected rather than
    055     *               private (RA);
    056     * 30-Jul-2003 : Modified entity constructor (CZ);
    057     * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
    058     * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
    059     * 07-Oct-2003 : Added renderer state (DG);
    060     * 08-Dec-2003 : Modified hotspot for chart entity (DG);
    061     * 10-Feb-2004 : Changed the drawItem() method to make cut-and-paste
    062     *               overriding easier.  Also moved state class into this
    063     *               class (DG);
    064     * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState.  Renamed
    065     *               XYToolTipGenerator --> XYItemLabelGenerator (DG);
    066     * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
    067     *               getYValue() (DG);
    068     * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG);
    069     * 19-Jan-2005 : Now accesses only primitives from the dataset (DG);
    070     * 21-Mar-2005 : Override getLegendItem() (DG);
    071     * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG);
    072     *
    073     */
    074
    075    package org.jfree.chart.renderer.xy;
    076
    077    import java.awt.Graphics2D;
    078    import java.awt.Paint;
    079    import java.awt.Polygon;
    080    import java.awt.Shape;
    081    import java.awt.Stroke;
    082    import java.awt.geom.GeneralPath;
    083    import java.awt.geom.Rectangle2D;
    084    import java.io.IOException;
    085    import java.io.ObjectInputStream;
    086    import java.io.ObjectOutputStream;
    087    import java.io.Serializable;
    088
    089    import org.jfree.chart.LegendItem;
    090    import org.jfree.chart.axis.ValueAxis;
    091    import org.jfree.chart.entity.EntityCollection;
    092    import org.jfree.chart.entity.XYItemEntity;
    093    import org.jfree.chart.event.RendererChangeEvent;
    094    import org.jfree.chart.labels.XYSeriesLabelGenerator;
    095    import org.jfree.chart.labels.XYToolTipGenerator;
    096    import org.jfree.chart.plot.CrosshairState;
    097    import org.jfree.chart.plot.PlotOrientation;
    098    import org.jfree.chart.plot.PlotRenderingInfo;
    099    import org.jfree.chart.plot.XYPlot;
    100    import org.jfree.chart.urls.XYURLGenerator;
    101    import org.jfree.data.xy.XYDataset;
    102    import org.jfree.io.SerialUtilities;
    103    import org.jfree.util.PublicCloneable;
    104
    105    /**
    106     * Area item renderer for an {@link XYPlot}.
    107     */
    108    public class XYAreaRenderer2 extends AbstractXYItemRenderer
    109                                 implements XYItemRenderer,
    110                                            Cloneable,
    111                                            PublicCloneable,
    112                                            Serializable {
    113
    114        /** For serialization. */
    115        private static final long serialVersionUID = -7378069681579984133L;
    116
    117        /** A flag indicating whether or not lines are drawn between XY points. */
    118        private boolean plotLines;
    119
    120        /** A flag that controls whether or not the outline is shown. */
    121        private boolean showOutline;
    122
    123        /**
    124         * The shape used to represent an area in each legend item (this should
    125         * never be <code>null</code>).
    126         */
    127        private transient Shape legendArea;
    128
    129        /**
    130         * Constructs a new renderer.
    131         */
    132        public XYAreaRenderer2() {
    133            this(null, null);
    134        }
    135
    136        /**
    137         * Constructs a new renderer.
    138         * <p>
    139         * To specify the type of renderer, use one of the constants: SHAPES, LINES,
    140         * SHAPES_AND_LINES, AREA or AREA_AND_SHAPES.
    141         *
    142         * @param labelGenerator  the tool tip generator to use.  <code>null</code>
    143         *                        is none.
    144         * @param urlGenerator  the URL generator (null permitted).
    145         */
    146        public XYAreaRenderer2(XYToolTipGenerator labelGenerator,
    147                               XYURLGenerator urlGenerator) {
    148            super();
    149            this.plotLines = false;
    150            this.showOutline = false;
    151            setBaseToolTipGenerator(labelGenerator);
    152            setURLGenerator(urlGenerator);
    153            GeneralPath area = new GeneralPath();
    154            area.moveTo(0.0f, -4.0f);
    155            area.lineTo(3.0f, -2.0f);
    156            area.lineTo(4.0f, 4.0f);
    157            area.lineTo(-4.0f, 4.0f);
    158            area.lineTo(-3.0f, -2.0f);
    159            area.closePath();
    160            this.legendArea = area;
    161        }
    162
    163        /**
    164         * Returns a flag that controls whether or not outlines of the areas are
    165         * drawn.
    166         *
    167         * @return The flag.
    168         */
    169        public boolean isOutline() {
    170            return this.showOutline;
    171        }
    172
    173        /**
    174         * Sets a flag that controls whether or not outlines of the areas are drawn.
    175         *
    176         * @param show  the flag.
    177         */
    178        public void setOutline(boolean show) {
    179            this.showOutline = show;
    180        }
    181
    182        /**
    183         * Returns true if lines are being plotted by the renderer.
    184         *
    185         * @return <code>true</code> if lines are being plotted by the renderer.
    186         */
    187        public boolean getPlotLines() {
    188            return this.plotLines;
    189        }
    190
    191        /**
    192         * Returns the shape used to represent an area in the legend.
    193         *
    194         * @return The legend area (never <code>null</code>).
    195         */
    196        public Shape getLegendArea() {
    197            return this.legendArea;
    198        }
    199
    200        /**
    201         * Sets the shape used as an area in each legend item and sends a
    202         * {@link RendererChangeEvent} to all registered listeners.
    203         *
    204         * @param area  the area (<code>null</code> not permitted).
    205         */
    206        public void setLegendArea(Shape area) {
    207            if (area == null) {
    208                throw new IllegalArgumentException("Null 'area' argument.");
    209            }
    210            this.legendArea = area;
    211            notifyListeners(new RendererChangeEvent(this));
    212        }
    213
    214        /**
    215         * Returns a default legend item for the specified series.  Subclasses
    216         * should override this method to generate customised items.
    217         *
    218         * @param datasetIndex  the dataset index (zero-based).
    219         * @param series  the series index (zero-based).
    220         *
    221         * @return A legend item for the series.
    222         */
    223        public LegendItem getLegendItem(int datasetIndex, int series) {
    224            LegendItem result = null;
    225            XYPlot xyplot = getPlot();
    226            if (xyplot != null) {
    227                XYDataset dataset = xyplot.getDataset(datasetIndex);
    228                if (dataset != null) {
    229                    XYSeriesLabelGenerator lg = getLegendItemLabelGenerator();
    230                    String label = lg.generateLabel(dataset, series);
    231                    String description = label;
    232                    String toolTipText = null;
    233                    if (getLegendItemToolTipGenerator() != null) {
    234                        toolTipText = getLegendItemToolTipGenerator().generateLabel(
    235                            dataset, series
    236                        );
    237                    }
    238                    String urlText = null;
    239                    if (getLegendItemURLGenerator() != null) {
    240                        urlText = getLegendItemURLGenerator().generateLabel(
    241                            dataset, series
    242                        );
    243                    }
    244                    Paint paint = getSeriesPaint(series);
    245                    result = new LegendItem(label, description, toolTipText,
    246                            urlText, this.legendArea, paint);
    247                }
    248            }
    249            return result;
    250        }
    251
    252        /**
    253         * Draws the visual representation of a single data item.
    254         *
    255         * @param g2  the graphics device.
    256         * @param state  the renderer state.
    257         * @param dataArea  the area within which the data is being drawn.
    258         * @param info  collects information about the drawing.
    259         * @param plot  the plot (can be used to obtain standard color
    260         *              information etc).
    261         * @param domainAxis  the domain axis.
    262         * @param rangeAxis  the range axis.
    263         * @param dataset  the dataset.
    264         * @param series  the series index (zero-based).
    265         * @param item  the item index (zero-based).
    266         * @param crosshairState  crosshair information for the plot
    267         *                        (<code>null</code> permitted).
    268         * @param pass  the pass index.
    269         */
    270        public void drawItem(Graphics2D g2,
    271                             XYItemRendererState state,
    272                             Rectangle2D dataArea,
    273                             PlotRenderingInfo info,
    274                             XYPlot plot,
    275                             ValueAxis domainAxis,
    276                             ValueAxis rangeAxis,
    277                             XYDataset dataset,
    278                             int series,
    279                             int item,
    280                             CrosshairState crosshairState,
    281                             int pass) {
    282
    283            if (!getItemVisible(series, item)) {
    284                return;
    285            }
    286            // get the data point...
    287            double x1 = dataset.getXValue(series, item);
    288            double y1 = dataset.getYValue(series, item);
    289            if (Double.isNaN(y1)) {
    290                y1 = 0.0;
    291            }
    292
    293            double transX1 = domainAxis.valueToJava2D(
    294                x1, dataArea, plot.getDomainAxisEdge()
    295            );
    296            double transY1 = rangeAxis.valueToJava2D(
    297                y1, dataArea, plot.getRangeAxisEdge()
    298            );
    299
    300            // get the previous point and the next point so we can calculate a
    301            // "hot spot" for the area (used by the chart entity)...
    302            double x0 = dataset.getXValue(series, Math.max(item - 1, 0));
    303            double y0 = dataset.getYValue(series, Math.max(item - 1, 0));
    304            if (Double.isNaN(y0)) {
    305                y0 = 0.0;
    306            }
    307            double transX0 = domainAxis.valueToJava2D(
    308                x0, dataArea, plot.getDomainAxisEdge()
    309            );
    310            double transY0 = rangeAxis.valueToJava2D(
    311                y0, dataArea, plot.getRangeAxisEdge()
    312            );
    313
    314            int itemCount = dataset.getItemCount(series);
    315            double x2 = dataset.getXValue(
    316                series, Math.min(item + 1, itemCount - 1)
    317            );
    318            double y2 = dataset.getYValue(
    319                series, Math.min(item + 1, itemCount - 1)
    320            );
    321            if (Double.isNaN(y2)) {
    322                y2 = 0.0;
    323            }
    324            double transX2 = domainAxis.valueToJava2D(
    325                x2, dataArea, plot.getDomainAxisEdge()
    326            );
    327            double transY2 = rangeAxis.valueToJava2D(
    328                y2, dataArea, plot.getRangeAxisEdge()
    329            );
    330
    331            double transZero = rangeAxis.valueToJava2D(
    332                0.0, dataArea, plot.getRangeAxisEdge()
    333            );
    334            Polygon hotspot = null;
    335            if (plot.getOrientation() == PlotOrientation.HORIZONTAL) {
    336                hotspot = new Polygon();
    337                hotspot.addPoint(
    338                    (int) transZero, (int) ((transX0 + transX1) / 2.0)
    339                );
    340                hotspot.addPoint(
    341                    (int) ((transY0 + transY1) / 2.0),
    342                    (int) ((transX0 + transX1) / 2.0)
    343                );
    344                hotspot.addPoint((int) transY1, (int) transX1);
    345                hotspot.addPoint(
    346                    (int) ((transY1 + transY2) / 2.0),
    347                    (int) ((transX1 + transX2) / 2.0)
    348                );
    349                hotspot.addPoint(
    350                    (int) transZero, (int) ((transX1 + transX2) / 2.0)
    351                );
    352            }
    353            else {  // vertical orientation
    354                hotspot = new Polygon();
    355                hotspot.addPoint(
    356                    (int) ((transX0 + transX1) / 2.0), (int) transZero
    357                );
    358                hotspot.addPoint(
    359                    (int) ((transX0 + transX1) / 2.0),
    360                    (int) ((transY0 + transY1) / 2.0)
    361                );
    362                hotspot.addPoint((int) transX1, (int) transY1);
    363                hotspot.addPoint(
    364                    (int) ((transX1 + transX2) / 2.0),
    365                    (int) ((transY1 + transY2) / 2.0)
    366                );
    367                hotspot.addPoint(
    368                    (int) ((transX1 + transX2) / 2.0), (int) transZero
    369                );
    370            }
    371
    372            PlotOrientation orientation = plot.getOrientation();
    373            Paint paint = getItemPaint(series, item);
    374            Stroke stroke = getItemStroke(series, item);
    375            g2.setPaint(paint);
    376            g2.setStroke(stroke);
    377
    378            if (getPlotLines()) {
    379                if (item > 0) {
    380                    if (plot.getOrientation() == PlotOrientation.VERTICAL) {
    381                        state.workingLine.setLine(
    382                            transX0, transY0, transX1, transY1
    383                        );
    384                    }
    385                    else if (plot.getOrientation() == PlotOrientation.HORIZONTAL) {
    386                        state.workingLine.setLine(
    387                            transY0, transX0, transY1, transX1
    388                        );
    389                    }
    390                    g2.draw(state.workingLine);
    391                }
    392            }
    393
    394            // Check if the item is the last item for the series.
    395            // and number of items > 0.  We can't draw an area for a single point.
    396            g2.fill(hotspot);
    397
    398            // draw an outline around the Area.
    399            if (isOutline()) {
    400                g2.setStroke(getSeriesOutlineStroke(series));
    401                g2.setPaint(getSeriesOutlinePaint(series));
    402                g2.draw(hotspot);
    403            }
    404            updateCrosshairValues(
    405                crosshairState, x1, y1, transX1, transY1, orientation
    406            );
    407
    408            // collect entity and tool tip information...
    409            if (state.getInfo() != null) {
    410                EntityCollection entities = state.getEntityCollection();
    411                if (entities != null && hotspot != null) {
    412                    String tip = null;
    413                    XYToolTipGenerator generator = getToolTipGenerator(
    414                        series, item
    415                    );
    416                    if (generator != null) {
    417                        tip = generator.generateToolTip(dataset, series, item);
    418                    }
    419                    String url = null;
    420                    if (getURLGenerator() != null) {
    421                        url = getURLGenerator().generateURL(dataset, series, item);
    422                    }
    423                    XYItemEntity entity = new XYItemEntity(
    424                        hotspot, dataset, series, item, tip, url
    425                    );
    426                    entities.add(entity);
    427                }
    428            }
    429
    430        }
    431
    432        /**
    433         * Returns a clone of the renderer.
    434         *
    435         * @return A clone.
    436         *
    437         * @throws CloneNotSupportedException  if the renderer cannot be cloned.
    438         */
    439        public Object clone() throws CloneNotSupportedException {
    440            return super.clone();
    441        }
    442
    443        /**
    444         * Provides serialization support.
    445         *
    446         * @param stream  the input stream.
    447         *
    448         * @throws IOException  if there is an I/O error.
    449         * @throws ClassNotFoundException  if there is a classpath problem.
    450         */
    451        private void readObject(ObjectInputStream stream)
    452                throws IOException, ClassNotFoundException {
    453            stream.defaultReadObject();
    454            this.legendArea = SerialUtilities.readShape(stream);
    455        }
    456
    457        /**
    458         * Provides serialization support.
    459         *
    460         * @param stream  the output stream.
    461         *
    462         * @throws IOException  if there is an I/O error.
    463         */
    464        private void writeObject(ObjectOutputStream stream) throws IOException {
    465            stream.defaultWriteObject();
    466            SerialUtilities.writeShape(this.legendArea, stream);
    467        }
    468
    469    }
    470