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