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: * XYAreaRenderer2.java
029: * --------------------
030: * (C) Copyright 2004-2007, by Hari and Contributors.
031: *
032: * Original Author: Hari (ourhari@hotmail.com);
033: * Contributor(s): David Gilbert (for Object Refinery Limited);
034: * Richard Atkinson;
035: * Christian W. Zuckschwerdt;
036: *
037: * $Id: XYAreaRenderer2.java,v 1.12.2.10 2007/05/18 10:28:31 mungady Exp $
038: *
039: * Changes:
040: * --------
041: * 03-Apr-2002 : Version 1, contributed by Hari. This class is based on the
042: * StandardXYItemRenderer class (DG);
043: * 09-Apr-2002 : Removed the translated zero from the drawItem method -
044: * overridden the initialise() method to calculate it (DG);
045: * 30-May-2002 : Added tool tip generator to constructor to match super
046: * class (DG);
047: * 25-Jun-2002 : Removed unnecessary local variable (DG);
048: * 05-Aug-2002 : Small modification to drawItem method to support URLs for
049: * HTML image maps (RA);
050: * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG);
051: * 07-Nov-2002 : Renamed AreaXYItemRenderer --> XYAreaRenderer (DG);
052: * 25-Mar-2003 : Implemented Serializable (DG);
053: * 01-May-2003 : Modified drawItem() method signature (DG);
054: * 27-Jul-2003 : Made line and polygon properties protected rather than
055: * private (RA);
056: * 30-Jul-2003 : Modified entity constructor (CZ);
057: * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
058: * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
059: * 07-Oct-2003 : Added renderer state (DG);
060: * 08-Dec-2003 : Modified hotspot for chart entity (DG);
061: * 10-Feb-2004 : Changed the drawItem() method to make cut-and-paste
062: * overriding easier. Also moved state class into this
063: * class (DG);
064: * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState. Renamed
065: * XYToolTipGenerator --> XYItemLabelGenerator (DG);
066: * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
067: * getYValue() (DG);
068: * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG);
069: * 19-Jan-2005 : Now accesses only primitives from the dataset (DG);
070: * 21-Mar-2005 : Override getLegendItem() (DG);
071: * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG);
072: * ------------- JFREECHART 1.0.x ---------------------------------------------
073: * 30-Nov-2006 : Fixed equals() and clone() implementations (DG);
074: * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG);
075: * 20-Apr-2007 : Updated getLegendItem() and drawItem() for renderer
076: * change (DG);
077: * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG);
078: * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
079: *
080: */
081:
082: package org.jfree.chart.renderer.xy;
083:
084: import java.awt.Graphics2D;
085: import java.awt.Paint;
086: import java.awt.Polygon;
087: import java.awt.Shape;
088: import java.awt.Stroke;
089: import java.awt.geom.GeneralPath;
090: import java.awt.geom.Rectangle2D;
091: import java.io.IOException;
092: import java.io.ObjectInputStream;
093: import java.io.ObjectOutputStream;
094: import java.io.Serializable;
095:
096: import org.jfree.chart.LegendItem;
097: import org.jfree.chart.axis.ValueAxis;
098: import org.jfree.chart.entity.EntityCollection;
099: import org.jfree.chart.entity.XYItemEntity;
100: import org.jfree.chart.event.RendererChangeEvent;
101: import org.jfree.chart.labels.XYSeriesLabelGenerator;
102: import org.jfree.chart.labels.XYToolTipGenerator;
103: import org.jfree.chart.plot.CrosshairState;
104: import org.jfree.chart.plot.PlotOrientation;
105: import org.jfree.chart.plot.PlotRenderingInfo;
106: import org.jfree.chart.plot.XYPlot;
107: import org.jfree.chart.urls.XYURLGenerator;
108: import org.jfree.data.xy.XYDataset;
109: import org.jfree.io.SerialUtilities;
110: import org.jfree.util.PublicCloneable;
111: import org.jfree.util.ShapeUtilities;
112:
113: /**
114: * Area item renderer for an {@link XYPlot}.
115: */
116: public class XYAreaRenderer2 extends AbstractXYItemRenderer implements
117: XYItemRenderer, Cloneable, PublicCloneable, Serializable {
118:
119: /** For serialization. */
120: private static final long serialVersionUID = -7378069681579984133L;
121:
122: /** A flag that controls whether or not the outline is shown. */
123: private boolean showOutline;
124:
125: /**
126: * The shape used to represent an area in each legend item (this should
127: * never be <code>null</code>).
128: */
129: private transient Shape legendArea;
130:
131: /**
132: * Constructs a new renderer.
133: */
134: public XYAreaRenderer2() {
135: this (null, null);
136: }
137:
138: /**
139: * Constructs a new renderer.
140: *
141: * @param labelGenerator the tool tip generator to use. <code>null</code>
142: * is none.
143: * @param urlGenerator the URL generator (null permitted).
144: */
145: public XYAreaRenderer2(XYToolTipGenerator labelGenerator,
146: XYURLGenerator urlGenerator) {
147: super ();
148: this .showOutline = false;
149: setBaseToolTipGenerator(labelGenerator);
150: setURLGenerator(urlGenerator);
151: GeneralPath area = new GeneralPath();
152: area.moveTo(0.0f, -4.0f);
153: area.lineTo(3.0f, -2.0f);
154: area.lineTo(4.0f, 4.0f);
155: area.lineTo(-4.0f, 4.0f);
156: area.lineTo(-3.0f, -2.0f);
157: area.closePath();
158: this .legendArea = area;
159: }
160:
161: /**
162: * Returns a flag that controls whether or not outlines of the areas are
163: * drawn.
164: *
165: * @return The flag.
166: *
167: * @see #setOutline(boolean)
168: */
169: public boolean isOutline() {
170: return this .showOutline;
171: }
172:
173: /**
174: * Sets a flag that controls whether or not outlines of the areas are
175: * drawn, and sends a {@link RendererChangeEvent} to all registered
176: * listeners.
177: *
178: * @param show the flag.
179: *
180: * @see #isOutline()
181: */
182: public void setOutline(boolean show) {
183: this .showOutline = show;
184: notifyListeners(new RendererChangeEvent(this ));
185: }
186:
187: /**
188: * This method should not be used.
189: *
190: * @return <code>false</code> always.
191: *
192: * @deprecated This method was included in the API by mistake and serves
193: * no useful purpose. It has always returned <code>false</code>.
194: *
195: */
196: public boolean getPlotLines() {
197: return false;
198: }
199:
200: /**
201: * Returns the shape used to represent an area in the legend.
202: *
203: * @return The legend area (never <code>null</code>).
204: *
205: * @see #setLegendArea(Shape)
206: */
207: public Shape getLegendArea() {
208: return this .legendArea;
209: }
210:
211: /**
212: * Sets the shape used as an area in each legend item and sends a
213: * {@link RendererChangeEvent} to all registered listeners.
214: *
215: * @param area the area (<code>null</code> not permitted).
216: *
217: * @see #getLegendArea()
218: */
219: public void setLegendArea(Shape area) {
220: if (area == null) {
221: throw new IllegalArgumentException("Null 'area' argument.");
222: }
223: this .legendArea = area;
224: notifyListeners(new RendererChangeEvent(this ));
225: }
226:
227: /**
228: * Returns a default legend item for the specified series. Subclasses
229: * should override this method to generate customised items.
230: *
231: * @param datasetIndex the dataset index (zero-based).
232: * @param series the series index (zero-based).
233: *
234: * @return A legend item for the series.
235: */
236: public LegendItem getLegendItem(int datasetIndex, int series) {
237: LegendItem result = null;
238: XYPlot xyplot = getPlot();
239: if (xyplot != null) {
240: XYDataset dataset = xyplot.getDataset(datasetIndex);
241: if (dataset != null) {
242: XYSeriesLabelGenerator lg = getLegendItemLabelGenerator();
243: String label = lg.generateLabel(dataset, series);
244: String description = label;
245: String toolTipText = null;
246: if (getLegendItemToolTipGenerator() != null) {
247: toolTipText = getLegendItemToolTipGenerator()
248: .generateLabel(dataset, series);
249: }
250: String urlText = null;
251: if (getLegendItemURLGenerator() != null) {
252: urlText = getLegendItemURLGenerator()
253: .generateLabel(dataset, series);
254: }
255: Paint paint = lookupSeriesPaint(series);
256: result = new LegendItem(label, description,
257: toolTipText, urlText, this .legendArea, paint);
258: result.setDataset(dataset);
259: result.setDatasetIndex(datasetIndex);
260: result.setSeriesKey(dataset.getSeriesKey(series));
261: result.setSeriesIndex(series);
262: }
263: }
264: return result;
265: }
266:
267: /**
268: * Draws the visual representation of a single data item.
269: *
270: * @param g2 the graphics device.
271: * @param state the renderer state.
272: * @param dataArea the area within which the data is being drawn.
273: * @param info collects information about the drawing.
274: * @param plot the plot (can be used to obtain standard color
275: * information etc).
276: * @param domainAxis the domain axis.
277: * @param rangeAxis the range axis.
278: * @param dataset the dataset.
279: * @param series the series index (zero-based).
280: * @param item the item index (zero-based).
281: * @param crosshairState crosshair information for the plot
282: * (<code>null</code> permitted).
283: * @param pass the pass index.
284: */
285: public void drawItem(Graphics2D g2, XYItemRendererState state,
286: Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot,
287: ValueAxis domainAxis, ValueAxis rangeAxis,
288: XYDataset dataset, int series, int item,
289: CrosshairState crosshairState, int pass) {
290:
291: if (!getItemVisible(series, item)) {
292: return;
293: }
294: // get the data point...
295: double x1 = dataset.getXValue(series, item);
296: double y1 = dataset.getYValue(series, item);
297: if (Double.isNaN(y1)) {
298: y1 = 0.0;
299: }
300:
301: double transX1 = domainAxis.valueToJava2D(x1, dataArea, plot
302: .getDomainAxisEdge());
303: double transY1 = rangeAxis.valueToJava2D(y1, dataArea, plot
304: .getRangeAxisEdge());
305:
306: // get the previous point and the next point so we can calculate a
307: // "hot spot" for the area (used by the chart entity)...
308: double x0 = dataset.getXValue(series, Math.max(item - 1, 0));
309: double y0 = dataset.getYValue(series, Math.max(item - 1, 0));
310: if (Double.isNaN(y0)) {
311: y0 = 0.0;
312: }
313: double transX0 = domainAxis.valueToJava2D(x0, dataArea, plot
314: .getDomainAxisEdge());
315: double transY0 = rangeAxis.valueToJava2D(y0, dataArea, plot
316: .getRangeAxisEdge());
317:
318: int itemCount = dataset.getItemCount(series);
319: double x2 = dataset.getXValue(series, Math.min(item + 1,
320: itemCount - 1));
321: double y2 = dataset.getYValue(series, Math.min(item + 1,
322: itemCount - 1));
323: if (Double.isNaN(y2)) {
324: y2 = 0.0;
325: }
326: double transX2 = domainAxis.valueToJava2D(x2, dataArea, plot
327: .getDomainAxisEdge());
328: double transY2 = rangeAxis.valueToJava2D(y2, dataArea, plot
329: .getRangeAxisEdge());
330:
331: double transZero = rangeAxis.valueToJava2D(0.0, dataArea, plot
332: .getRangeAxisEdge());
333: Polygon hotspot = null;
334: if (plot.getOrientation() == PlotOrientation.HORIZONTAL) {
335: hotspot = new Polygon();
336: hotspot.addPoint((int) transZero,
337: (int) ((transX0 + transX1) / 2.0));
338: hotspot.addPoint((int) ((transY0 + transY1) / 2.0),
339: (int) ((transX0 + transX1) / 2.0));
340: hotspot.addPoint((int) transY1, (int) transX1);
341: hotspot.addPoint((int) ((transY1 + transY2) / 2.0),
342: (int) ((transX1 + transX2) / 2.0));
343: hotspot.addPoint((int) transZero,
344: (int) ((transX1 + transX2) / 2.0));
345: } else { // vertical orientation
346: hotspot = new Polygon();
347: hotspot.addPoint((int) ((transX0 + transX1) / 2.0),
348: (int) transZero);
349: hotspot.addPoint((int) ((transX0 + transX1) / 2.0),
350: (int) ((transY0 + transY1) / 2.0));
351: hotspot.addPoint((int) transX1, (int) transY1);
352: hotspot.addPoint((int) ((transX1 + transX2) / 2.0),
353: (int) ((transY1 + transY2) / 2.0));
354: hotspot.addPoint((int) ((transX1 + transX2) / 2.0),
355: (int) transZero);
356: }
357:
358: PlotOrientation orientation = plot.getOrientation();
359: Paint paint = getItemPaint(series, item);
360: Stroke stroke = getItemStroke(series, item);
361: g2.setPaint(paint);
362: g2.setStroke(stroke);
363:
364: if (getPlotLines()) {
365: if (item > 0) {
366: if (plot.getOrientation() == PlotOrientation.VERTICAL) {
367: state.workingLine.setLine(transX0, transY0,
368: transX1, transY1);
369: } else if (plot.getOrientation() == PlotOrientation.HORIZONTAL) {
370: state.workingLine.setLine(transY0, transX0,
371: transY1, transX1);
372: }
373: g2.draw(state.workingLine);
374: }
375: }
376:
377: // Check if the item is the last item for the series.
378: // and number of items > 0. We can't draw an area for a single point.
379: g2.fill(hotspot);
380:
381: // draw an outline around the Area.
382: if (isOutline()) {
383: g2.setStroke(lookupSeriesOutlineStroke(series));
384: g2.setPaint(lookupSeriesOutlinePaint(series));
385: g2.draw(hotspot);
386: }
387: int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
388: int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
389: updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex,
390: rangeAxisIndex, transX1, transY1, orientation);
391:
392: // collect entity and tool tip information...
393: if (state.getInfo() != null) {
394: EntityCollection entities = state.getEntityCollection();
395: if (entities != null && hotspot != null) {
396: String tip = null;
397: XYToolTipGenerator generator = getToolTipGenerator(
398: series, item);
399: if (generator != null) {
400: tip = generator.generateToolTip(dataset, series,
401: item);
402: }
403: String url = null;
404: if (getURLGenerator() != null) {
405: url = getURLGenerator().generateURL(dataset,
406: series, item);
407: }
408: XYItemEntity entity = new XYItemEntity(hotspot,
409: dataset, series, item, tip, url);
410: entities.add(entity);
411: }
412: }
413:
414: }
415:
416: /**
417: * Tests this renderer for equality with an arbitrary object.
418: *
419: * @param obj the object (<code>null</code> not permitted).
420: *
421: * @return A boolean.
422: */
423: public boolean equals(Object obj) {
424: if (obj == this ) {
425: return true;
426: }
427: if (!(obj instanceof XYAreaRenderer2)) {
428: return false;
429: }
430: XYAreaRenderer2 that = (XYAreaRenderer2) obj;
431: if (this .showOutline != that.showOutline) {
432: return false;
433: }
434: if (!ShapeUtilities.equal(this .legendArea, that.legendArea)) {
435: return false;
436: }
437: return super .equals(obj);
438: }
439:
440: /**
441: * Returns a clone of the renderer.
442: *
443: * @return A clone.
444: *
445: * @throws CloneNotSupportedException if the renderer cannot be cloned.
446: */
447: public Object clone() throws CloneNotSupportedException {
448: XYAreaRenderer2 clone = (XYAreaRenderer2) super .clone();
449: clone.legendArea = ShapeUtilities.clone(this .legendArea);
450: return clone;
451: }
452:
453: /**
454: * Provides serialization support.
455: *
456: * @param stream the input stream.
457: *
458: * @throws IOException if there is an I/O error.
459: * @throws ClassNotFoundException if there is a classpath problem.
460: */
461: private void readObject(ObjectInputStream stream)
462: throws IOException, ClassNotFoundException {
463: stream.defaultReadObject();
464: this .legendArea = SerialUtilities.readShape(stream);
465: }
466:
467: /**
468: * Provides serialization support.
469: *
470: * @param stream the output stream.
471: *
472: * @throws IOException if there is an I/O error.
473: */
474: private void writeObject(ObjectOutputStream stream)
475: throws IOException {
476: stream.defaultWriteObject();
477: SerialUtilities.writeShape(this.legendArea, stream);
478: }
479:
480: }
|