001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2007, 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 * AreaRenderer.java
029 * -----------------
030 * (C) Copyright 2002-2007, by Jon Iles and Contributors.
031 *
032 * Original Author: Jon Iles;
033 * Contributor(s): David Gilbert (for Object Refinery Limited);
034 * Christian W. Zuckschwerdt;
035 *
036 * $Id: AreaRenderer.java,v 1.6.2.9 2007/05/18 10:28:27 mungady Exp $
037 *
038 * Changes:
039 * --------
040 * 21-May-2002 : Version 1, contributed by John Iles (DG);
041 * 29-May-2002 : Now extends AbstractCategoryItemRenderer (DG);
042 * 11-Jun-2002 : Updated Javadoc comments (DG);
043 * 25-Jun-2002 : Removed unnecessary imports (DG);
044 * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG);
045 * 10-Oct-2002 : Added constructors and basic entity support (DG);
046 * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and
047 * CategoryToolTipGenerator interface (DG);
048 * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG);
049 * 06-Nov-2002 : Renamed drawCategoryItem() --> drawItem() and now using axis
050 * for category spacing. Renamed AreaCategoryItemRenderer
051 * --> AreaRenderer (DG);
052 * 17-Jan-2003 : Moved plot classes into a separate package (DG);
053 * 25-Mar-2003 : Implemented Serializable (DG);
054 * 10-Apr-2003 : Changed CategoryDataset to KeyedValues2DDataset in
055 * drawItem() method (DG);
056 * 12-May-2003 : Modified to take into account the plot orientation (DG);
057 * 30-Jul-2003 : Modified entity constructor (CZ);
058 * 13-Aug-2003 : Implemented Cloneable (DG);
059 * 07-Oct-2003 : Added renderer state (DG);
060 * 05-Nov-2004 : Modified drawItem() signature (DG);
061 * 20-Apr-2005 : Apply tooltips and URLs to legend items (DG);
062 * 09-Jun-2005 : Use addItemEntity() method from superclass (DG);
063 * ------------- JFREECHART 1.0.x ---------------------------------------------
064 * 11-Oct-2006 : Fixed bug in equals() method (DG);
065 * 30-Nov-2006 : Added checks for series visibility (DG);
066 * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG);
067 * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG);
068 * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
069 *
070 */
071
072 package org.jfree.chart.renderer.category;
073
074 import java.awt.Graphics2D;
075 import java.awt.Paint;
076 import java.awt.Shape;
077 import java.awt.Stroke;
078 import java.awt.geom.GeneralPath;
079 import java.awt.geom.Rectangle2D;
080 import java.io.Serializable;
081
082 import org.jfree.chart.LegendItem;
083 import org.jfree.chart.axis.CategoryAxis;
084 import org.jfree.chart.axis.ValueAxis;
085 import org.jfree.chart.entity.EntityCollection;
086 import org.jfree.chart.event.RendererChangeEvent;
087 import org.jfree.chart.plot.CategoryPlot;
088 import org.jfree.chart.plot.PlotOrientation;
089 import org.jfree.chart.renderer.AreaRendererEndType;
090 import org.jfree.data.category.CategoryDataset;
091 import org.jfree.ui.RectangleEdge;
092 import org.jfree.util.PublicCloneable;
093
094 /**
095 * A category item renderer that draws area charts. You can use this renderer
096 * with the {@link org.jfree.chart.plot.CategoryPlot} class.
097 */
098 public class AreaRenderer extends AbstractCategoryItemRenderer
099 implements Cloneable, PublicCloneable, Serializable {
100
101 /** For serialization. */
102 private static final long serialVersionUID = -4231878281385812757L;
103
104 /** A flag that controls how the ends of the areas are drawn. */
105 private AreaRendererEndType endType;
106
107 /**
108 * Creates a new renderer.
109 */
110 public AreaRenderer() {
111 super();
112 this.endType = AreaRendererEndType.TAPER;
113 }
114
115 /**
116 * Returns a token that controls how the renderer draws the end points.
117 * The default value is {@link AreaRendererEndType#TAPER}.
118 *
119 * @return The end type (never <code>null</code>).
120 *
121 * @see #setEndType
122 */
123 public AreaRendererEndType getEndType() {
124 return this.endType;
125 }
126
127 /**
128 * Sets a token that controls how the renderer draws the end points, and
129 * sends a {@link RendererChangeEvent} to all registered listeners.
130 *
131 * @param type the end type (<code>null</code> not permitted).
132 *
133 * @see #getEndType()
134 */
135 public void setEndType(AreaRendererEndType type) {
136 if (type == null) {
137 throw new IllegalArgumentException("Null 'type' argument.");
138 }
139 this.endType = type;
140 notifyListeners(new RendererChangeEvent(this));
141 }
142
143 /**
144 * Returns a legend item for a series.
145 *
146 * @param datasetIndex the dataset index (zero-based).
147 * @param series the series index (zero-based).
148 *
149 * @return The legend item.
150 */
151 public LegendItem getLegendItem(int datasetIndex, int series) {
152
153 // if there is no plot, there is no dataset to access...
154 CategoryPlot cp = getPlot();
155 if (cp == null) {
156 return null;
157 }
158
159 // check that a legend item needs to be displayed...
160 if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) {
161 return null;
162 }
163
164 CategoryDataset dataset = cp.getDataset(datasetIndex);
165 String label = getLegendItemLabelGenerator().generateLabel(dataset,
166 series);
167 String description = label;
168 String toolTipText = null;
169 if (getLegendItemToolTipGenerator() != null) {
170 toolTipText = getLegendItemToolTipGenerator().generateLabel(
171 dataset, series);
172 }
173 String urlText = null;
174 if (getLegendItemURLGenerator() != null) {
175 urlText = getLegendItemURLGenerator().generateLabel(dataset,
176 series);
177 }
178 Shape shape = new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0);
179 Paint paint = lookupSeriesPaint(series);
180 Paint outlinePaint = lookupSeriesOutlinePaint(series);
181 Stroke outlineStroke = lookupSeriesOutlineStroke(series);
182
183 LegendItem result = new LegendItem(label, description, toolTipText,
184 urlText, shape, paint, outlineStroke, outlinePaint);
185 result.setDataset(dataset);
186 result.setDatasetIndex(datasetIndex);
187 result.setSeriesKey(dataset.getRowKey(series));
188 result.setSeriesIndex(series);
189 return result;
190
191 }
192
193 /**
194 * Draw a single data item.
195 *
196 * @param g2 the graphics device.
197 * @param state the renderer state.
198 * @param dataArea the data plot area.
199 * @param plot the plot.
200 * @param domainAxis the domain axis.
201 * @param rangeAxis the range axis.
202 * @param dataset the dataset.
203 * @param row the row index (zero-based).
204 * @param column the column index (zero-based).
205 * @param pass the pass index.
206 */
207 public void drawItem(Graphics2D g2,
208 CategoryItemRendererState state,
209 Rectangle2D dataArea,
210 CategoryPlot plot,
211 CategoryAxis domainAxis,
212 ValueAxis rangeAxis,
213 CategoryDataset dataset,
214 int row,
215 int column,
216 int pass) {
217
218 // do nothing if item is not visible
219 if (!getItemVisible(row, column)) {
220 return;
221 }
222
223 // plot non-null values only...
224 Number value = dataset.getValue(row, column);
225 if (value != null) {
226 PlotOrientation orientation = plot.getOrientation();
227 RectangleEdge axisEdge = plot.getDomainAxisEdge();
228 int count = dataset.getColumnCount();
229 float x0 = (float) domainAxis.getCategoryStart(column, count,
230 dataArea, axisEdge);
231 float x1 = (float) domainAxis.getCategoryMiddle(column, count,
232 dataArea, axisEdge);
233 float x2 = (float) domainAxis.getCategoryEnd(column, count,
234 dataArea, axisEdge);
235
236 x0 = Math.round(x0);
237 x1 = Math.round(x1);
238 x2 = Math.round(x2);
239
240 if (this.endType == AreaRendererEndType.TRUNCATE) {
241 if (column == 0) {
242 x0 = x1;
243 }
244 else if (column == getColumnCount() - 1) {
245 x2 = x1;
246 }
247 }
248
249 double yy1 = value.doubleValue();
250
251 double yy0 = 0.0;
252 if (column > 0) {
253 Number n0 = dataset.getValue(row, column - 1);
254 if (n0 != null) {
255 yy0 = (n0.doubleValue() + yy1) / 2.0;
256 }
257 }
258
259 double yy2 = 0.0;
260 if (column < dataset.getColumnCount() - 1) {
261 Number n2 = dataset.getValue(row, column + 1);
262 if (n2 != null) {
263 yy2 = (n2.doubleValue() + yy1) / 2.0;
264 }
265 }
266
267 RectangleEdge edge = plot.getRangeAxisEdge();
268 float y0 = (float) rangeAxis.valueToJava2D(yy0, dataArea, edge);
269 float y1 = (float) rangeAxis.valueToJava2D(yy1, dataArea, edge);
270 float y2 = (float) rangeAxis.valueToJava2D(yy2, dataArea, edge);
271 float yz = (float) rangeAxis.valueToJava2D(0.0, dataArea, edge);
272
273 g2.setPaint(getItemPaint(row, column));
274 g2.setStroke(getItemStroke(row, column));
275
276 GeneralPath area = new GeneralPath();
277
278 if (orientation == PlotOrientation.VERTICAL) {
279 area.moveTo(x0, yz);
280 area.lineTo(x0, y0);
281 area.lineTo(x1, y1);
282 area.lineTo(x2, y2);
283 area.lineTo(x2, yz);
284 }
285 else if (orientation == PlotOrientation.HORIZONTAL) {
286 area.moveTo(yz, x0);
287 area.lineTo(y0, x0);
288 area.lineTo(y1, x1);
289 area.lineTo(y2, x2);
290 area.lineTo(yz, x2);
291 }
292 area.closePath();
293
294 g2.setPaint(getItemPaint(row, column));
295 g2.fill(area);
296
297 // draw the item labels if there are any...
298 if (isItemLabelVisible(row, column)) {
299 drawItemLabel(g2, orientation, dataset, row, column, x1, y1,
300 (value.doubleValue() < 0.0));
301 }
302
303 // add an item entity, if this information is being collected
304 EntityCollection entities = state.getEntityCollection();
305 if (entities != null) {
306 addItemEntity(entities, dataset, row, column, area);
307 }
308 }
309
310 }
311
312 /**
313 * Tests this instance for equality with an arbitrary object.
314 *
315 * @param obj the object to test (<code>null</code> permitted).
316 *
317 * @return A boolean.
318 */
319 public boolean equals(Object obj) {
320 if (obj == this) {
321 return true;
322 }
323 if (!(obj instanceof AreaRenderer)) {
324 return false;
325 }
326 AreaRenderer that = (AreaRenderer) obj;
327 if (!this.endType.equals(that.endType)) {
328 return false;
329 }
330 return super.equals(obj);
331 }
332
333 /**
334 * Returns an independent copy of the renderer.
335 *
336 * @return A clone.
337 *
338 * @throws CloneNotSupportedException should not happen.
339 */
340 public Object clone() throws CloneNotSupportedException {
341 return super.clone();
342 }
343
344 }