001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2008, 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-2008, 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 * 17-Jun-2008 : Apply legend shape, font and paint attributes (DG);
068 * 26-Jun-2008 : Added crosshair support (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 setBaseLegendShape(new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0));
114 }
115
116 /**
117 * Returns a token that controls how the renderer draws the end points.
118 * The default value is {@link AreaRendererEndType#TAPER}.
119 *
120 * @return The end type (never <code>null</code>).
121 *
122 * @see #setEndType
123 */
124 public AreaRendererEndType getEndType() {
125 return this.endType;
126 }
127
128 /**
129 * Sets a token that controls how the renderer draws the end points, and
130 * sends a {@link RendererChangeEvent} to all registered listeners.
131 *
132 * @param type the end type (<code>null</code> not permitted).
133 *
134 * @see #getEndType()
135 */
136 public void setEndType(AreaRendererEndType type) {
137 if (type == null) {
138 throw new IllegalArgumentException("Null 'type' argument.");
139 }
140 this.endType = type;
141 fireChangeEvent();
142 }
143
144 /**
145 * Returns a legend item for a series.
146 *
147 * @param datasetIndex the dataset index (zero-based).
148 * @param series the series index (zero-based).
149 *
150 * @return The legend item.
151 */
152 public LegendItem getLegendItem(int datasetIndex, int series) {
153
154 // if there is no plot, there is no dataset to access...
155 CategoryPlot cp = getPlot();
156 if (cp == null) {
157 return null;
158 }
159
160 // check that a legend item needs to be displayed...
161 if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) {
162 return null;
163 }
164
165 CategoryDataset dataset = cp.getDataset(datasetIndex);
166 String label = getLegendItemLabelGenerator().generateLabel(dataset,
167 series);
168 String description = label;
169 String toolTipText = null;
170 if (getLegendItemToolTipGenerator() != null) {
171 toolTipText = getLegendItemToolTipGenerator().generateLabel(
172 dataset, series);
173 }
174 String urlText = null;
175 if (getLegendItemURLGenerator() != null) {
176 urlText = getLegendItemURLGenerator().generateLabel(dataset,
177 series);
178 }
179 Shape shape = lookupLegendShape(series);
180 Paint paint = lookupSeriesPaint(series);
181 Paint outlinePaint = lookupSeriesOutlinePaint(series);
182 Stroke outlineStroke = lookupSeriesOutlineStroke(series);
183
184 LegendItem result = new LegendItem(label, description, toolTipText,
185 urlText, shape, paint, outlineStroke, outlinePaint);
186 result.setLabelFont(lookupLegendTextFont(series));
187 Paint labelPaint = lookupLegendTextPaint(series);
188 if (labelPaint != null) {
189 result.setLabelPaint(labelPaint);
190 }
191 result.setDataset(dataset);
192 result.setDatasetIndex(datasetIndex);
193 result.setSeriesKey(dataset.getRowKey(series));
194 result.setSeriesIndex(series);
195 return result;
196
197 }
198
199 /**
200 * Draw a single data item.
201 *
202 * @param g2 the graphics device.
203 * @param state the renderer state.
204 * @param dataArea the data plot area.
205 * @param plot the plot.
206 * @param domainAxis the domain axis.
207 * @param rangeAxis the range axis.
208 * @param dataset the dataset.
209 * @param row the row index (zero-based).
210 * @param column the column index (zero-based).
211 * @param pass the pass index.
212 */
213 public void drawItem(Graphics2D g2,
214 CategoryItemRendererState state,
215 Rectangle2D dataArea,
216 CategoryPlot plot,
217 CategoryAxis domainAxis,
218 ValueAxis rangeAxis,
219 CategoryDataset dataset,
220 int row,
221 int column,
222 int pass) {
223
224 // do nothing if item is not visible
225 if (!getItemVisible(row, column)) {
226 return;
227 }
228
229 // plot non-null values only...
230 Number value = dataset.getValue(row, column);
231 if (value != null) {
232 PlotOrientation orientation = plot.getOrientation();
233 RectangleEdge axisEdge = plot.getDomainAxisEdge();
234 int count = dataset.getColumnCount();
235 float x0 = (float) domainAxis.getCategoryStart(column, count,
236 dataArea, axisEdge);
237 float x1 = (float) domainAxis.getCategoryMiddle(column, count,
238 dataArea, axisEdge);
239 float x2 = (float) domainAxis.getCategoryEnd(column, count,
240 dataArea, axisEdge);
241
242 x0 = Math.round(x0);
243 x1 = Math.round(x1);
244 x2 = Math.round(x2);
245
246 if (this.endType == AreaRendererEndType.TRUNCATE) {
247 if (column == 0) {
248 x0 = x1;
249 }
250 else if (column == getColumnCount() - 1) {
251 x2 = x1;
252 }
253 }
254
255 double yy1 = value.doubleValue();
256
257 double yy0 = 0.0;
258 if (column > 0) {
259 Number n0 = dataset.getValue(row, column - 1);
260 if (n0 != null) {
261 yy0 = (n0.doubleValue() + yy1) / 2.0;
262 }
263 }
264
265 double yy2 = 0.0;
266 if (column < dataset.getColumnCount() - 1) {
267 Number n2 = dataset.getValue(row, column + 1);
268 if (n2 != null) {
269 yy2 = (n2.doubleValue() + yy1) / 2.0;
270 }
271 }
272
273 RectangleEdge edge = plot.getRangeAxisEdge();
274 float y0 = (float) rangeAxis.valueToJava2D(yy0, dataArea, edge);
275 float y1 = (float) rangeAxis.valueToJava2D(yy1, dataArea, edge);
276 float y2 = (float) rangeAxis.valueToJava2D(yy2, dataArea, edge);
277 float yz = (float) rangeAxis.valueToJava2D(0.0, dataArea, edge);
278
279 g2.setPaint(getItemPaint(row, column));
280 g2.setStroke(getItemStroke(row, column));
281
282 GeneralPath area = new GeneralPath();
283
284 if (orientation == PlotOrientation.VERTICAL) {
285 area.moveTo(x0, yz);
286 area.lineTo(x0, y0);
287 area.lineTo(x1, y1);
288 area.lineTo(x2, y2);
289 area.lineTo(x2, yz);
290 }
291 else if (orientation == PlotOrientation.HORIZONTAL) {
292 area.moveTo(yz, x0);
293 area.lineTo(y0, x0);
294 area.lineTo(y1, x1);
295 area.lineTo(y2, x2);
296 area.lineTo(yz, x2);
297 }
298 area.closePath();
299
300 g2.setPaint(getItemPaint(row, column));
301 g2.fill(area);
302
303 // draw the item labels if there are any...
304 if (isItemLabelVisible(row, column)) {
305 drawItemLabel(g2, orientation, dataset, row, column, x1, y1,
306 (value.doubleValue() < 0.0));
307 }
308
309 // submit the current data point as a crosshair candidate
310 int datasetIndex = plot.indexOf(dataset);
311 updateCrosshairValues(state.getCrosshairState(),
312 dataset.getRowKey(row), dataset.getColumnKey(column),
313 yy1, datasetIndex, x1, y1, orientation);
314
315 // add an item entity, if this information is being collected
316 EntityCollection entities = state.getEntityCollection();
317 if (entities != null) {
318 addItemEntity(entities, dataset, row, column, area);
319 }
320 }
321
322 }
323
324 /**
325 * Tests this instance for equality with an arbitrary object.
326 *
327 * @param obj the object to test (<code>null</code> permitted).
328 *
329 * @return A boolean.
330 */
331 public boolean equals(Object obj) {
332 if (obj == this) {
333 return true;
334 }
335 if (!(obj instanceof AreaRenderer)) {
336 return false;
337 }
338 AreaRenderer that = (AreaRenderer) obj;
339 if (!this.endType.equals(that.endType)) {
340 return false;
341 }
342 return super.equals(obj);
343 }
344
345 /**
346 * Returns an independent copy of the renderer.
347 *
348 * @return A clone.
349 *
350 * @throws CloneNotSupportedException should not happen.
351 */
352 public Object clone() throws CloneNotSupportedException {
353 return super.clone();
354 }
355
356 }