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