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 }