All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jfree.chart.plot.dial.DialPlot Maven / Gradle / Ivy

Go to download

JFreeChart is a class library, written in Java, for generating charts. Utilising the Java2D API, it supports a wide range of chart types including bar charts, pie charts, line charts, XY-plots, time series plots, Sankey charts and more.

There is a newer version: 1.5.5
Show newest version
/* ===========================================================
 * JFreeChart : a free chart library for the Java(tm) platform
 * ===========================================================
 *
 * (C) Copyright 2000-present, by David Gilbert and Contributors.
 *
 * Project Info:  http://www.jfree.org/jfreechart/index.html
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 *
 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
 * Other names may be trademarks of their respective owners.]
 *
 * -------------
 * DialPlot.java
 * -------------
 * (C) Copyright 2006-present, by David Gilbert.
 *
 * Original Author:  David Gilbert;
 * Contributor(s):   -;
 *
 */

package org.jfree.chart.plot.dial;

import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

import org.jfree.chart.JFreeChart;
import org.jfree.chart.event.PlotChangeEvent;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.PlotState;
import org.jfree.chart.util.ObjectList;
import org.jfree.chart.util.ObjectUtils;
import org.jfree.chart.util.Args;
import org.jfree.data.general.DatasetChangeEvent;
import org.jfree.data.general.ValueDataset;

/**
 * A dial plot composed of user-definable layers.
 * The example shown here is generated by the {@code DialDemo2.java}
 * program included in the JFreeChart Demo Collection:
 * 

* DialPlotSample.png */ public class DialPlot extends Plot implements DialLayerChangeListener { /** * The background layer (optional). */ private DialLayer background; /** * The needle cap (optional). */ private DialLayer cap; /** * The dial frame. */ private DialFrame dialFrame; /** * The dataset(s) for the dial plot. */ private ObjectList datasets; /** * The scale(s) for the dial plot. */ private ObjectList scales; /** Storage for keys that map datasets to scales. */ private ObjectList datasetToScaleMap; /** * The drawing layers for the dial plot. */ private List layers; /** * The pointer(s) for the dial. */ private List pointers; /** * The x-coordinate for the view window. */ private double viewX; /** * The y-coordinate for the view window. */ private double viewY; /** * The width of the view window, expressed as a percentage. */ private double viewW; /** * The height of the view window, expressed as a percentage. */ private double viewH; /** * Creates a new instance of {@code DialPlot}. */ public DialPlot() { this(null); } /** * Creates a new instance of {@code DialPlot}. * * @param dataset the dataset ({@code null} permitted). */ public DialPlot(ValueDataset dataset) { this.background = null; this.cap = null; this.dialFrame = new ArcDialFrame(); this.datasets = new ObjectList(); if (dataset != null) { setDataset(dataset); } this.scales = new ObjectList(); this.datasetToScaleMap = new ObjectList(); this.layers = new java.util.ArrayList(); this.pointers = new java.util.ArrayList(); this.viewX = 0.0; this.viewY = 0.0; this.viewW = 1.0; this.viewH = 1.0; } /** * Returns the background. * * @return The background (possibly {@code null}). * * @see #setBackground(DialLayer) */ public DialLayer getBackground() { return this.background; } /** * Sets the background layer and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param background the background layer ({@code null} permitted). * * @see #getBackground() */ public void setBackground(DialLayer background) { if (this.background != null) { this.background.removeChangeListener(this); } this.background = background; if (background != null) { background.addChangeListener(this); } fireChangeEvent(); } /** * Returns the cap. * * @return The cap (possibly {@code null}). * * @see #setCap(DialLayer) */ public DialLayer getCap() { return this.cap; } /** * Sets the cap and sends a {@link PlotChangeEvent} to all registered * listeners. * * @param cap the cap ({@code null} permitted). * * @see #getCap() */ public void setCap(DialLayer cap) { if (this.cap != null) { this.cap.removeChangeListener(this); } this.cap = cap; if (cap != null) { cap.addChangeListener(this); } fireChangeEvent(); } /** * Returns the dial's frame. * * @return The dial's frame (never {@code null}). * * @see #setDialFrame(DialFrame) */ public DialFrame getDialFrame() { return this.dialFrame; } /** * Sets the dial's frame and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param frame the frame ({@code null} not permitted). * * @see #getDialFrame() */ public void setDialFrame(DialFrame frame) { Args.nullNotPermitted(frame, "frame"); this.dialFrame.removeChangeListener(this); this.dialFrame = frame; frame.addChangeListener(this); fireChangeEvent(); } /** * Returns the x-coordinate of the viewing rectangle. This is specified * in the range 0.0 to 1.0, relative to the dial's framing rectangle. * * @return The x-coordinate of the viewing rectangle. * * @see #setView(double, double, double, double) */ public double getViewX() { return this.viewX; } /** * Returns the y-coordinate of the viewing rectangle. This is specified * in the range 0.0 to 1.0, relative to the dial's framing rectangle. * * @return The y-coordinate of the viewing rectangle. * * @see #setView(double, double, double, double) */ public double getViewY() { return this.viewY; } /** * Returns the width of the viewing rectangle. This is specified * in the range 0.0 to 1.0, relative to the dial's framing rectangle. * * @return The width of the viewing rectangle. * * @see #setView(double, double, double, double) */ public double getViewWidth() { return this.viewW; } /** * Returns the height of the viewing rectangle. This is specified * in the range 0.0 to 1.0, relative to the dial's framing rectangle. * * @return The height of the viewing rectangle. * * @see #setView(double, double, double, double) */ public double getViewHeight() { return this.viewH; } /** * Sets the viewing rectangle, relative to the dial's framing rectangle, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param x the x-coordinate (in the range 0.0 to 1.0). * @param y the y-coordinate (in the range 0.0 to 1.0). * @param w the width (in the range 0.0 to 1.0). * @param h the height (in the range 0.0 to 1.0). * * @see #getViewX() * @see #getViewY() * @see #getViewWidth() * @see #getViewHeight() */ public void setView(double x, double y, double w, double h) { this.viewX = x; this.viewY = y; this.viewW = w; this.viewH = h; fireChangeEvent(); } /** * Adds a layer to the plot and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param layer the layer ({@code null} not permitted). */ public void addLayer(DialLayer layer) { Args.nullNotPermitted(layer, "layer"); this.layers.add(layer); layer.addChangeListener(this); fireChangeEvent(); } /** * Returns the index for the specified layer. * * @param layer the layer ({@code null} not permitted). * * @return The layer index. */ public int getLayerIndex(DialLayer layer) { Args.nullNotPermitted(layer, "layer"); return this.layers.indexOf(layer); } /** * Removes the layer at the specified index and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the index. */ public void removeLayer(int index) { DialLayer layer = (DialLayer) this.layers.get(index); if (layer != null) { layer.removeChangeListener(this); } this.layers.remove(index); fireChangeEvent(); } /** * Removes the specified layer and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param layer the layer ({@code null} not permitted). */ public void removeLayer(DialLayer layer) { // defer argument checking removeLayer(getLayerIndex(layer)); } /** * Adds a pointer to the plot and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param pointer the pointer ({@code null} not permitted). */ public void addPointer(DialPointer pointer) { Args.nullNotPermitted(pointer, "pointer"); this.pointers.add(pointer); pointer.addChangeListener(this); fireChangeEvent(); } /** * Returns the index for the specified pointer. * * @param pointer the pointer ({@code null} not permitted). * * @return The pointer index. */ public int getPointerIndex(DialPointer pointer) { Args.nullNotPermitted(pointer, "pointer"); return this.pointers.indexOf(pointer); } /** * Removes the pointer at the specified index and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the index. */ public void removePointer(int index) { DialPointer pointer = (DialPointer) this.pointers.get(index); if (pointer != null) { pointer.removeChangeListener(this); } this.pointers.remove(index); fireChangeEvent(); } /** * Removes the specified pointer and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param pointer the pointer ({@code null} not permitted). */ public void removePointer(DialPointer pointer) { // defer argument checking removeLayer(getPointerIndex(pointer)); } /** * Returns the dial pointer that is associated with the specified * dataset, or {@code null}. * * @param datasetIndex the dataset index. * * @return The pointer. */ public DialPointer getPointerForDataset(int datasetIndex) { DialPointer result = null; Iterator iterator = this.pointers.iterator(); while (iterator.hasNext()) { DialPointer p = (DialPointer) iterator.next(); if (p.getDatasetIndex() == datasetIndex) { return p; } } return result; } /** * Returns the primary dataset for the plot. * * @return The primary dataset (possibly {@code null}). */ public ValueDataset getDataset() { return getDataset(0); } /** * Returns the dataset at the given index. * * @param index the dataset index. * * @return The dataset (possibly {@code null}). */ public ValueDataset getDataset(int index) { ValueDataset result = null; if (this.datasets.size() > index) { result = (ValueDataset) this.datasets.get(index); } return result; } /** * Sets the dataset for the plot, replacing the existing dataset, if there * is one, and sends a {@link PlotChangeEvent} to all registered * listeners. * * @param dataset the dataset ({@code null} permitted). */ public void setDataset(ValueDataset dataset) { setDataset(0, dataset); } /** * Sets a dataset for the plot. * * @param index the dataset index. * @param dataset the dataset ({@code null} permitted). */ public void setDataset(int index, ValueDataset dataset) { ValueDataset existing = (ValueDataset) this.datasets.get(index); if (existing != null) { existing.removeChangeListener(this); } this.datasets.set(index, dataset); if (dataset != null) { dataset.addChangeListener(this); } // send a dataset change event to self... DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); datasetChanged(event); } /** * Returns the number of datasets. * * @return The number of datasets. */ public int getDatasetCount() { return this.datasets.size(); } /** * Draws the plot. This method is usually called by the {@link JFreeChart} * instance that manages the plot. * * @param g2 the graphics target. * @param area the area in which the plot should be drawn. * @param anchor the anchor point (typically the last point that the * mouse clicked on, {@code null} is permitted). * @param parentState the state for the parent plot (if any). * @param info used to collect plot rendering info ({@code null} * permitted). */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { Shape origClip = g2.getClip(); g2.setClip(area); // first, expand the viewing area into a drawing frame Rectangle2D frame = viewToFrame(area); // draw the background if there is one... if (this.background != null && this.background.isVisible()) { if (this.background.isClippedToWindow()) { Shape savedClip = g2.getClip(); g2.clip(this.dialFrame.getWindow(frame)); this.background.draw(g2, this, frame, area); g2.setClip(savedClip); } else { this.background.draw(g2, this, frame, area); } } Iterator iterator = this.layers.iterator(); while (iterator.hasNext()) { DialLayer current = (DialLayer) iterator.next(); if (current.isVisible()) { if (current.isClippedToWindow()) { Shape savedClip = g2.getClip(); g2.clip(this.dialFrame.getWindow(frame)); current.draw(g2, this, frame, area); g2.setClip(savedClip); } else { current.draw(g2, this, frame, area); } } } // draw the pointers iterator = this.pointers.iterator(); while (iterator.hasNext()) { DialPointer current = (DialPointer) iterator.next(); if (current.isVisible()) { if (current.isClippedToWindow()) { Shape savedClip = g2.getClip(); g2.clip(this.dialFrame.getWindow(frame)); current.draw(g2, this, frame, area); g2.setClip(savedClip); } else { current.draw(g2, this, frame, area); } } } // draw the cap if there is one... if (this.cap != null && this.cap.isVisible()) { if (this.cap.isClippedToWindow()) { Shape savedClip = g2.getClip(); g2.clip(this.dialFrame.getWindow(frame)); this.cap.draw(g2, this, frame, area); g2.setClip(savedClip); } else { this.cap.draw(g2, this, frame, area); } } if (this.dialFrame.isVisible()) { this.dialFrame.draw(g2, this, frame, area); } g2.setClip(origClip); } /** * Returns the frame surrounding the specified view rectangle. * * @param view the view rectangle ({@code null} not permitted). * * @return The frame rectangle. */ private Rectangle2D viewToFrame(Rectangle2D view) { double width = view.getWidth() / this.viewW; double height = view.getHeight() / this.viewH; double x = view.getX() - (width * this.viewX); double y = view.getY() - (height * this.viewY); return new Rectangle2D.Double(x, y, width, height); } /** * Returns the value from the specified dataset. * * @param datasetIndex the dataset index. * * @return The data value. */ public double getValue(int datasetIndex) { double result = Double.NaN; ValueDataset dataset = getDataset(datasetIndex); if (dataset != null) { Number n = dataset.getValue(); if (n != null) { result = n.doubleValue(); } } return result; } /** * Adds a dial scale to the plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param index the scale index. * @param scale the scale ({@code null} not permitted). */ public void addScale(int index, DialScale scale) { Args.nullNotPermitted(scale, "scale"); DialScale existing = (DialScale) this.scales.get(index); if (existing != null) { removeLayer(existing); } this.layers.add(scale); this.scales.set(index, scale); scale.addChangeListener(this); fireChangeEvent(); } /** * Returns the scale at the given index. * * @param index the scale index. * * @return The scale (possibly {@code null}). */ public DialScale getScale(int index) { DialScale result = null; if (this.scales.size() > index) { result = (DialScale) this.scales.get(index); } return result; } /** * Maps a dataset to a particular scale. * * @param index the dataset index (zero-based). * @param scaleIndex the scale index (zero-based). */ public void mapDatasetToScale(int index, int scaleIndex) { this.datasetToScaleMap.set(index, scaleIndex); fireChangeEvent(); } /** * Returns the dial scale for a specific dataset. * * @param datasetIndex the dataset index. * * @return The dial scale. */ public DialScale getScaleForDataset(int datasetIndex) { DialScale result = (DialScale) this.scales.get(0); Integer scaleIndex = (Integer) this.datasetToScaleMap.get(datasetIndex); if (scaleIndex != null) { result = getScale(scaleIndex); } return result; } /** * A utility method that computes a rectangle using relative radius values. * * @param rect the reference rectangle ({@code null} not permitted). * @param radiusW the width radius (must be > 0.0) * @param radiusH the height radius. * * @return A new rectangle. */ public static Rectangle2D rectangleByRadius(Rectangle2D rect, double radiusW, double radiusH) { Args.nullNotPermitted(rect, "rect"); double x = rect.getCenterX(); double y = rect.getCenterY(); double w = rect.getWidth() * radiusW; double h = rect.getHeight() * radiusH; return new Rectangle2D.Double(x - w / 2.0, y - h / 2.0, w, h); } /** * Receives notification when a layer has changed, and responds by * forwarding a {@link PlotChangeEvent} to all registered listeners. * * @param event the event. */ @Override public void dialLayerChanged(DialLayerChangeEvent event) { fireChangeEvent(); } /** * Tests this {@code DialPlot} instance for equality with an * arbitrary object. The plot's dataset(s) is (are) not included in * the test. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DialPlot)) { return false; } DialPlot that = (DialPlot) obj; if (!Objects.equals(this.background, that.background)) { return false; } if (!Objects.equals(this.cap, that.cap)) { return false; } if (!this.dialFrame.equals(that.dialFrame)) { return false; } if (this.viewX != that.viewX) { return false; } if (this.viewY != that.viewY) { return false; } if (this.viewW != that.viewW) { return false; } if (this.viewH != that.viewH) { return false; } if (!this.layers.equals(that.layers)) { return false; } if (!this.pointers.equals(that.pointers)) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return The hash code. */ @Override public int hashCode() { int result = 193; result = 37 * result + ObjectUtils.hashCode(this.background); result = 37 * result + ObjectUtils.hashCode(this.cap); result = 37 * result + this.dialFrame.hashCode(); long temp = Double.doubleToLongBits(this.viewX); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.viewY); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.viewW); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.viewH); result = 37 * result + (int) (temp ^ (temp >>> 32)); return result; } /** * Returns the plot type. * * @return {@code "DialPlot"} */ @Override public String getPlotType() { return "DialPlot"; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy