1. /* ===========================================================
  2. * JFreeChart : a free chart library for the Java(tm) platform
  3. * ===========================================================
  4. *
  5. * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
  6. *
  7. * Project Info: http://www.jfree.org/jfreechart/index.html
  8. *
  9. * This library is free software; you can redistribute it and/or modify it
  10. * under the terms of the GNU Lesser General Public License as published by
  11. * the Free Software Foundation; either version 2.1 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This library is distributed in the hope that it will be useful, but
  15. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  16. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  17. * License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public License
  20. * along with this library; if not, write to the Free Software Foundation,
  21. * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  22. *
  23. * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  24. * in the United States and other countries.]
  25. *
  26. * ---------------------
  27. * XYBubbleRenderer.java
  28. * ---------------------
  29. * (C) Copyright 2003-2005, by Object Refinery Limited.
  30. *
  31. * Original Author: David Gilbert (for Object Refinery Limited);
  32. * Contributor(s): Christian W. Zuckschwerdt;
  33. *
  34. * $Id: XYBubbleRenderer.java,v 1.4 2005/02/28 12:03:11 mungady Exp $
  35. *
  36. * Changes
  37. * -------
  38. * 28-Jan-2003 : Version 1 (DG);
  39. * 25-Mar-2003 : Implemented Serializable (DG);
  40. * 01-May-2003 : Modified drawItem(...) method signature (DG);
  41. * 30-Jul-2003 : Modified entity constructor (CZ);
  42. * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
  43. * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
  44. * 10-Feb-2004 : Small change to drawItem() method to make cut-and-paste
  45. * overriding easier (DG);
  46. * 15-Jul-2004 : Switched getZ() and getZValue() methods (DG);
  47. * 19-Jan-2005 : Now accesses only primitives from dataset (DG);
  48. * 28-Feb-2005 : Modify renderer to use circles in legend (DG);
  49. *
  50. */
  51. package org.jfree.chart.renderer.xy;
  52. import java.awt.BasicStroke;
  53. import java.awt.Color;
  54. import java.awt.Graphics2D;
  55. import java.awt.Paint;
  56. import java.awt.Shape;
  57. import java.awt.Stroke;
  58. import java.awt.geom.Ellipse2D;
  59. import java.awt.geom.Rectangle2D;
  60. import java.io.Serializable;
  61. import org.jfree.chart.LegendItem;
  62. import org.jfree.chart.axis.ValueAxis;
  63. import org.jfree.chart.entity.EntityCollection;
  64. import org.jfree.chart.entity.XYItemEntity;
  65. import org.jfree.chart.labels.XYToolTipGenerator;
  66. import org.jfree.chart.plot.CrosshairState;
  67. import org.jfree.chart.plot.PlotOrientation;
  68. import org.jfree.chart.plot.PlotRenderingInfo;
  69. import org.jfree.chart.plot.XYPlot;
  70. import org.jfree.data.xy.XYDataset;
  71. import org.jfree.data.xy.XYZDataset;
  72. import org.jfree.ui.RectangleEdge;
  73. import org.jfree.util.PublicCloneable;
  74. /**
  75. * A renderer that draws a circle at each data point. The renderer expects
  76. * the dataset to be an {@link XYZDataset}.
  77. */
  78. public class XYBubbleRenderer extends AbstractXYItemRenderer
  79. implements XYItemRenderer,
  80. Cloneable,
  81. PublicCloneable,
  82. Serializable {
  83. /** A useful constant. */
  84. public static final int SCALE_ON_BOTH_AXES = 0;
  85. /** A useful constant. */
  86. public static final int SCALE_ON_DOMAIN_AXIS = 1;
  87. /** A useful constant. */
  88. public static final int SCALE_ON_RANGE_AXIS = 2;
  89. /** Controls how the width and height of the bubble are scaled. */
  90. private int scaleType;
  91. /**
  92. * Constructs a new renderer.
  93. */
  94. public XYBubbleRenderer() {
  95. this(SCALE_ON_BOTH_AXES);
  96. }
  97. /**
  98. * Constructs a new renderer.
  99. *
  100. * @param scaleType the type of scaling.
  101. */
  102. public XYBubbleRenderer(int scaleType) {
  103. super();
  104. this.scaleType = scaleType;
  105. }
  106. /**
  107. * Returns the scale type.
  108. *
  109. * @return The scale type.
  110. */
  111. public int getScaleType() {
  112. return this.scaleType;
  113. }
  114. /**
  115. * Draws the visual representation of a single data item.
  116. *
  117. * @param g2 the graphics device.
  118. * @param state the renderer state.
  119. * @param dataArea the area within which the data is being drawn.
  120. * @param info collects information about the drawing.
  121. * @param plot the plot (can be used to obtain standard color
  122. * information etc).
  123. * @param domainAxis the domain (horizontal) axis.
  124. * @param rangeAxis the range (vertical) axis.
  125. * @param dataset the dataset.
  126. * @param series the series index (zero-based).
  127. * @param item the item index (zero-based).
  128. * @param crosshairState crosshair information for the plot
  129. * (<code>null</code> permitted).
  130. * @param pass the pass index.
  131. */
  132. public void drawItem(Graphics2D g2,
  133. XYItemRendererState state,
  134. Rectangle2D dataArea,
  135. PlotRenderingInfo info,
  136. XYPlot plot,
  137. ValueAxis domainAxis,
  138. ValueAxis rangeAxis,
  139. XYDataset dataset,
  140. int series,
  141. int item,
  142. CrosshairState crosshairState,
  143. int pass) {
  144. PlotOrientation orientation = plot.getOrientation();
  145. // get the data point...
  146. double x = dataset.getXValue(series, item);
  147. double y = dataset.getYValue(series, item);
  148. double z = Double.NaN;
  149. if (dataset instanceof XYZDataset) {
  150. XYZDataset xyzData = (XYZDataset) dataset;
  151. z = xyzData.getZValue(series, item);
  152. }
  153. if (!Double.isNaN(z)) {
  154. RectangleEdge domainAxisLocation = plot.getDomainAxisEdge();
  155. RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge();
  156. double transX = domainAxis.valueToJava2D(
  157. x, dataArea, domainAxisLocation
  158. );
  159. double transY = rangeAxis.valueToJava2D(
  160. y, dataArea, rangeAxisLocation
  161. );
  162. double transDomain = 0.0;
  163. double transRange = 0.0;
  164. double zero;
  165. switch(getScaleType()) {
  166. case SCALE_ON_DOMAIN_AXIS:
  167. zero = domainAxis.valueToJava2D(
  168. 0.0, dataArea, domainAxisLocation
  169. );
  170. transDomain = domainAxis.valueToJava2D(
  171. z, dataArea, rangeAxisLocation
  172. ) - zero;
  173. transRange = transDomain;
  174. break;
  175. case SCALE_ON_RANGE_AXIS:
  176. zero = rangeAxis.valueToJava2D(
  177. 0.0, dataArea, rangeAxisLocation
  178. );
  179. transRange = zero - rangeAxis.valueToJava2D(
  180. z, dataArea, rangeAxisLocation
  181. );
  182. transDomain = transRange;
  183. break;
  184. default:
  185. double zero1 = domainAxis.valueToJava2D(
  186. 0.0, dataArea, domainAxisLocation
  187. );
  188. double zero2 = rangeAxis.valueToJava2D(
  189. 0.0, dataArea, rangeAxisLocation
  190. );
  191. transDomain = domainAxis.valueToJava2D(
  192. z, dataArea, domainAxisLocation
  193. ) - zero1;
  194. transRange = zero2 - rangeAxis.valueToJava2D(
  195. z, dataArea, rangeAxisLocation
  196. );
  197. }
  198. double transZ
  199. = -rangeAxis.valueToJava2D(z, dataArea, rangeAxisLocation)
  200. + rangeAxis.valueToJava2D(0.0, dataArea, rangeAxisLocation);
  201. transZ = Math.abs(transZ);
  202. transDomain = Math.abs(transDomain);
  203. transRange = Math.abs(transRange);
  204. Ellipse2D circle = null;
  205. if (orientation == PlotOrientation.VERTICAL) {
  206. circle = new Ellipse2D.Double(
  207. transX - transZ / 2.0, transY - transZ / 2.0,
  208. transDomain, transRange
  209. );
  210. }
  211. else if (orientation == PlotOrientation.HORIZONTAL) {
  212. circle = new Ellipse2D.Double(
  213. transY - transZ / 2.0, transX - transZ / 2.0,
  214. transRange, transDomain
  215. );
  216. }
  217. g2.setPaint(getItemPaint(series, item));
  218. g2.fill(circle);
  219. g2.setStroke(new BasicStroke(1.0f));
  220. g2.setPaint(Color.lightGray);
  221. g2.draw(circle);
  222. // setup for collecting optional entity info...
  223. EntityCollection entities = null;
  224. if (info != null) {
  225. entities = info.getOwner().getEntityCollection();
  226. }
  227. // add an entity for the item...
  228. if (entities != null) {
  229. String tip = null;
  230. XYToolTipGenerator generator
  231. = getToolTipGenerator(series, item);
  232. if (generator != null) {
  233. tip = generator.generateToolTip(dataset, series, item);
  234. }
  235. String url = null;
  236. if (getURLGenerator() != null) {
  237. url = getURLGenerator().generateURL(dataset, series, item);
  238. }
  239. XYItemEntity entity = new XYItemEntity(
  240. circle, dataset, series, item, tip, url
  241. );
  242. entities.add(entity);
  243. }
  244. updateCrosshairValues(
  245. crosshairState, x, y, transX, transY, orientation
  246. );
  247. }
  248. }
  249. /**
  250. * Returns a legend item for the specified series. The default method
  251. * is overridden so that the legend displays circles for all series.
  252. *
  253. * @param datasetIndex the dataset index (zero-based).
  254. * @param series the series index (zero-based).
  255. *
  256. * @return A legend item for the series.
  257. */
  258. public LegendItem getLegendItem(int datasetIndex, int series) {
  259. LegendItem result = null;
  260. XYPlot xyplot = getPlot();
  261. if (xyplot != null) {
  262. XYDataset dataset = xyplot.getDataset(datasetIndex);
  263. if (dataset != null) {
  264. if (getItemVisible(series, 0)) {
  265. String label = getLegendItemLabelGenerator().generateLabel(
  266. dataset, series
  267. );
  268. String description = label;
  269. Shape shape = new Ellipse2D.Double(-4.0, -4.0, 8.0, 8.0);
  270. Paint paint = getSeriesPaint(series);
  271. Paint outlinePaint = getSeriesOutlinePaint(series);
  272. Stroke outlineStroke = getSeriesOutlineStroke(series);
  273. result = new LegendItem(
  274. label, description, shape, paint,
  275. outlineStroke, outlinePaint
  276. );
  277. }
  278. }
  279. }
  280. return result;
  281. }
  282. /**
  283. * Returns a clone of the renderer.
  284. *
  285. * @return A clone.
  286. *
  287. * @throws CloneNotSupportedException if the renderer cannot be cloned.
  288. */
  289. public Object clone() throws CloneNotSupportedException {
  290. return super.clone();
  291. }
  292. }