org.jfree.chart.title.PaintScaleLegend Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jfreechart Show documentation
Show all versions of jfreechart Show documentation
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.
/* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-2011, by Object Refinery Limited 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.]
*
* ---------------------
* PaintScaleLegend.java
* ---------------------
* (C) Copyright 2007-2009, by Object Refinery Limited.
*
* Original Author: David Gilbert (for Object Refinery Limited);
* Contributor(s): Peter Kolb - see patch 2686872;
*
* Changes
* -------
* 22-Jan-2007 : Version 1 (DG);
* 18-Jun-2008 : Fixed bug drawing scale with log axis (DG);
* 16-Apr-2009 : Patch 2686872 implementing AxisChangeListener, and fix for
* ignored stripOutlineVisible flag (DG);
*
*/
package org.jfree.chart.title;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Stroke;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.AxisSpace;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.block.LengthConstraintType;
import org.jfree.chart.block.RectangleConstraint;
import org.jfree.chart.event.AxisChangeEvent;
import org.jfree.chart.event.AxisChangeListener;
import org.jfree.chart.event.TitleChangeEvent;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.PaintScale;
import org.jfree.data.Range;
import org.jfree.io.SerialUtilities;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.Size2D;
import org.jfree.util.PaintUtilities;
import org.jfree.util.PublicCloneable;
/**
* A legend that shows a range of values and their associated colors, driven
* by an underlying {@link PaintScale} implementation.
*
* @since 1.0.4
*/
public class PaintScaleLegend extends Title implements AxisChangeListener,
PublicCloneable {
/** For serialization. */
static final long serialVersionUID = -1365146490993227503L;
/** The paint scale (never null
). */
private PaintScale scale;
/** The value axis (never null
). */
private ValueAxis axis;
/**
* The axis location (handles both orientations, never
* null
).
*/
private AxisLocation axisLocation;
/** The offset between the axis and the paint strip (in Java2D units). */
private double axisOffset;
/** The thickness of the paint strip (in Java2D units). */
private double stripWidth;
/**
* A flag that controls whether or not an outline is drawn around the
* paint strip.
*/
private boolean stripOutlineVisible;
/** The paint used to draw an outline around the paint strip. */
private transient Paint stripOutlinePaint;
/** The stroke used to draw an outline around the paint strip. */
private transient Stroke stripOutlineStroke;
/** The background paint (never null
). */
private transient Paint backgroundPaint;
/**
* The number of subdivisions for the scale when rendering.
*
* @since 1.0.11
*/
private int subdivisions;
/**
* Creates a new instance.
*
* @param scale the scale (null
not permitted).
* @param axis the axis (null
not permitted).
*/
public PaintScaleLegend(PaintScale scale, ValueAxis axis) {
if (axis == null) {
throw new IllegalArgumentException("Null 'axis' argument.");
}
this.scale = scale;
this.axis = axis;
this.axis.addChangeListener(this);
this.axisLocation = AxisLocation.BOTTOM_OR_LEFT;
this.axisOffset = 0.0;
this.axis.setRange(scale.getLowerBound(), scale.getUpperBound());
this.stripWidth = 15.0;
this.stripOutlineVisible = true;
this.stripOutlinePaint = Color.gray;
this.stripOutlineStroke = new BasicStroke(0.5f);
this.backgroundPaint = Color.white;
this.subdivisions = 100;
}
/**
* Returns the scale used to convert values to colors.
*
* @return The scale (never null
).
*
* @see #setScale(PaintScale)
*/
public PaintScale getScale() {
return this.scale;
}
/**
* Sets the scale and sends a {@link TitleChangeEvent} to all registered
* listeners.
*
* @param scale the scale (null
not permitted).
*
* @see #getScale()
*/
public void setScale(PaintScale scale) {
if (scale == null) {
throw new IllegalArgumentException("Null 'scale' argument.");
}
this.scale = scale;
notifyListeners(new TitleChangeEvent(this));
}
/**
* Returns the axis for the paint scale.
*
* @return The axis (never null
).
*
* @see #setAxis(ValueAxis)
*/
public ValueAxis getAxis() {
return this.axis;
}
/**
* Sets the axis for the paint scale and sends a {@link TitleChangeEvent}
* to all registered listeners.
*
* @param axis the axis (null
not permitted).
*
* @see #getAxis()
*/
public void setAxis(ValueAxis axis) {
if (axis == null) {
throw new IllegalArgumentException("Null 'axis' argument.");
}
this.axis.removeChangeListener(this);
this.axis = axis;
this.axis.addChangeListener(this);
notifyListeners(new TitleChangeEvent(this));
}
/**
* Returns the axis location.
*
* @return The axis location (never null
).
*
* @see #setAxisLocation(AxisLocation)
*/
public AxisLocation getAxisLocation() {
return this.axisLocation;
}
/**
* Sets the axis location and sends a {@link TitleChangeEvent} to all
* registered listeners.
*
* @param location the location (null
not permitted).
*
* @see #getAxisLocation()
*/
public void setAxisLocation(AxisLocation location) {
if (location == null) {
throw new IllegalArgumentException("Null 'location' argument.");
}
this.axisLocation = location;
notifyListeners(new TitleChangeEvent(this));
}
/**
* Returns the offset between the axis and the paint strip.
*
* @return The offset between the axis and the paint strip.
*
* @see #setAxisOffset(double)
*/
public double getAxisOffset() {
return this.axisOffset;
}
/**
* Sets the offset between the axis and the paint strip and sends a
* {@link TitleChangeEvent} to all registered listeners.
*
* @param offset the offset.
*/
public void setAxisOffset(double offset) {
this.axisOffset = offset;
notifyListeners(new TitleChangeEvent(this));
}
/**
* Returns the width of the paint strip, in Java2D units.
*
* @return The width of the paint strip.
*
* @see #setStripWidth(double)
*/
public double getStripWidth() {
return this.stripWidth;
}
/**
* Sets the width of the paint strip and sends a {@link TitleChangeEvent}
* to all registered listeners.
*
* @param width the width.
*
* @see #getStripWidth()
*/
public void setStripWidth(double width) {
this.stripWidth = width;
notifyListeners(new TitleChangeEvent(this));
}
/**
* Returns the flag that controls whether or not an outline is drawn
* around the paint strip.
*
* @return A boolean.
*
* @see #setStripOutlineVisible(boolean)
*/
public boolean isStripOutlineVisible() {
return this.stripOutlineVisible;
}
/**
* Sets the flag that controls whether or not an outline is drawn around
* the paint strip, and sends a {@link TitleChangeEvent} to all registered
* listeners.
*
* @param visible the flag.
*
* @see #isStripOutlineVisible()
*/
public void setStripOutlineVisible(boolean visible) {
this.stripOutlineVisible = visible;
notifyListeners(new TitleChangeEvent(this));
}
/**
* Returns the paint used to draw the outline of the paint strip.
*
* @return The paint (never null
).
*
* @see #setStripOutlinePaint(Paint)
*/
public Paint getStripOutlinePaint() {
return this.stripOutlinePaint;
}
/**
* Sets the paint used to draw the outline of the paint strip, and sends
* a {@link TitleChangeEvent} to all registered listeners.
*
* @param paint the paint (null
not permitted).
*
* @see #getStripOutlinePaint()
*/
public void setStripOutlinePaint(Paint paint) {
if (paint == null) {
throw new IllegalArgumentException("Null 'paint' argument.");
}
this.stripOutlinePaint = paint;
notifyListeners(new TitleChangeEvent(this));
}
/**
* Returns the stroke used to draw the outline around the paint strip.
*
* @return The stroke (never null
).
*
* @see #setStripOutlineStroke(Stroke)
*/
public Stroke getStripOutlineStroke() {
return this.stripOutlineStroke;
}
/**
* Sets the stroke used to draw the outline around the paint strip and
* sends a {@link TitleChangeEvent} to all registered listeners.
*
* @param stroke the stroke (null
not permitted).
*
* @see #getStripOutlineStroke()
*/
public void setStripOutlineStroke(Stroke stroke) {
if (stroke == null) {
throw new IllegalArgumentException("Null 'stroke' argument.");
}
this.stripOutlineStroke = stroke;
notifyListeners(new TitleChangeEvent(this));
}
/**
* Returns the background paint.
*
* @return The background paint.
*/
public Paint getBackgroundPaint() {
return this.backgroundPaint;
}
/**
* Sets the background paint and sends a {@link TitleChangeEvent} to all
* registered listeners.
*
* @param paint the paint (null
permitted).
*/
public void setBackgroundPaint(Paint paint) {
this.backgroundPaint = paint;
notifyListeners(new TitleChangeEvent(this));
}
/**
* Returns the number of subdivisions used to draw the scale.
*
* @return The subdivision count.
*
* @since 1.0.11
*/
public int getSubdivisionCount() {
return this.subdivisions;
}
/**
* Sets the subdivision count and sends a {@link TitleChangeEvent} to
* all registered listeners.
*
* @param count the count.
*
* @since 1.0.11
*/
public void setSubdivisionCount(int count) {
if (count <= 0) {
throw new IllegalArgumentException("Requires 'count' > 0.");
}
this.subdivisions = count;
notifyListeners(new TitleChangeEvent(this));
}
/**
* Receives notification of an axis change event and responds by firing
* a title change event.
*
* @param event the event.
*
* @since 1.0.13
*/
public void axisChanged(AxisChangeEvent event) {
if (this.axis == event.getAxis()) {
notifyListeners(new TitleChangeEvent(this));
}
}
/**
* Arranges the contents of the block, within the given constraints, and
* returns the block size.
*
* @param g2 the graphics device.
* @param constraint the constraint (null
not permitted).
*
* @return The block size (in Java2D units, never null
).
*/
public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) {
RectangleConstraint cc = toContentConstraint(constraint);
LengthConstraintType w = cc.getWidthConstraintType();
LengthConstraintType h = cc.getHeightConstraintType();
Size2D contentSize = null;
if (w == LengthConstraintType.NONE) {
if (h == LengthConstraintType.NONE) {
contentSize = new Size2D(getWidth(), getHeight());
}
else if (h == LengthConstraintType.RANGE) {
throw new RuntimeException("Not yet implemented.");
}
else if (h == LengthConstraintType.FIXED) {
throw new RuntimeException("Not yet implemented.");
}
}
else if (w == LengthConstraintType.RANGE) {
if (h == LengthConstraintType.NONE) {
throw new RuntimeException("Not yet implemented.");
}
else if (h == LengthConstraintType.RANGE) {
contentSize = arrangeRR(g2, cc.getWidthRange(),
cc.getHeightRange());
}
else if (h == LengthConstraintType.FIXED) {
throw new RuntimeException("Not yet implemented.");
}
}
else if (w == LengthConstraintType.FIXED) {
if (h == LengthConstraintType.NONE) {
throw new RuntimeException("Not yet implemented.");
}
else if (h == LengthConstraintType.RANGE) {
throw new RuntimeException("Not yet implemented.");
}
else if (h == LengthConstraintType.FIXED) {
throw new RuntimeException("Not yet implemented.");
}
}
return new Size2D(calculateTotalWidth(contentSize.getWidth()),
calculateTotalHeight(contentSize.getHeight()));
}
/**
* Returns the content size for the title. This will reflect the fact that
* a text title positioned on the left or right of a chart will be rotated
* 90 degrees.
*
* @param g2 the graphics device.
* @param widthRange the width range.
* @param heightRange the height range.
*
* @return The content size.
*/
protected Size2D arrangeRR(Graphics2D g2, Range widthRange,
Range heightRange) {
RectangleEdge position = getPosition();
if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) {
float maxWidth = (float) widthRange.getUpperBound();
// determine the space required for the axis
AxisSpace space = this.axis.reserveSpace(g2, null,
new Rectangle2D.Double(0, 0, maxWidth, 100),
RectangleEdge.BOTTOM, null);
return new Size2D(maxWidth, this.stripWidth + this.axisOffset
+ space.getTop() + space.getBottom());
}
else if (position == RectangleEdge.LEFT || position
== RectangleEdge.RIGHT) {
float maxHeight = (float) heightRange.getUpperBound();
AxisSpace space = this.axis.reserveSpace(g2, null,
new Rectangle2D.Double(0, 0, 100, maxHeight),
RectangleEdge.RIGHT, null);
return new Size2D(this.stripWidth + this.axisOffset
+ space.getLeft() + space.getRight(), maxHeight);
}
else {
throw new RuntimeException("Unrecognised position.");
}
}
/**
* Draws the legend within the specified area.
*
* @param g2 the graphics target (null
not permitted).
* @param area the drawing area (null
not permitted).
*/
public void draw(Graphics2D g2, Rectangle2D area) {
draw(g2, area, null);
}
/**
* Draws the legend within the specified area.
*
* @param g2 the graphics target (null
not permitted).
* @param area the drawing area (null
not permitted).
* @param params drawing parameters (ignored here).
*
* @return null
.
*/
public Object draw(Graphics2D g2, Rectangle2D area, Object params) {
Rectangle2D target = (Rectangle2D) area.clone();
target = trimMargin(target);
if (this.backgroundPaint != null) {
g2.setPaint(this.backgroundPaint);
g2.fill(target);
}
getFrame().draw(g2, target);
getFrame().getInsets().trim(target);
target = trimPadding(target);
double base = this.axis.getLowerBound();
double increment = this.axis.getRange().getLength() / this.subdivisions;
Rectangle2D r = new Rectangle2D.Double();
if (RectangleEdge.isTopOrBottom(getPosition())) {
RectangleEdge axisEdge = Plot.resolveRangeAxisLocation(
this.axisLocation, PlotOrientation.HORIZONTAL);
if (axisEdge == RectangleEdge.TOP) {
for (int i = 0; i < this.subdivisions; i++) {
double v = base + (i * increment);
Paint p = this.scale.getPaint(v);
double vv0 = this.axis.valueToJava2D(v, target,
RectangleEdge.TOP);
double vv1 = this.axis.valueToJava2D(v + increment, target,
RectangleEdge.TOP);
double ww = Math.abs(vv1 - vv0) + 1.0;
r.setRect(Math.min(vv0, vv1), target.getMaxY()
- this.stripWidth, ww, this.stripWidth);
g2.setPaint(p);
g2.fill(r);
}
if (isStripOutlineVisible()) {
g2.setPaint(this.stripOutlinePaint);
g2.setStroke(this.stripOutlineStroke);
g2.draw(new Rectangle2D.Double(target.getMinX(),
target.getMaxY() - this.stripWidth,
target.getWidth(), this.stripWidth));
}
this.axis.draw(g2, target.getMaxY() - this.stripWidth
- this.axisOffset, target, target, RectangleEdge.TOP,
null);
}
else if (axisEdge == RectangleEdge.BOTTOM) {
for (int i = 0; i < this.subdivisions; i++) {
double v = base + (i * increment);
Paint p = this.scale.getPaint(v);
double vv0 = this.axis.valueToJava2D(v, target,
RectangleEdge.BOTTOM);
double vv1 = this.axis.valueToJava2D(v + increment, target,
RectangleEdge.BOTTOM);
double ww = Math.abs(vv1 - vv0) + 1.0;
r.setRect(Math.min(vv0, vv1), target.getMinY(), ww,
this.stripWidth);
g2.setPaint(p);
g2.fill(r);
}
if (isStripOutlineVisible()) {
g2.setPaint(this.stripOutlinePaint);
g2.setStroke(this.stripOutlineStroke);
g2.draw(new Rectangle2D.Double(target.getMinX(),
target.getMinY(), target.getWidth(),
this.stripWidth));
}
this.axis.draw(g2, target.getMinY() + this.stripWidth
+ this.axisOffset, target, target,
RectangleEdge.BOTTOM, null);
}
}
else {
RectangleEdge axisEdge = Plot.resolveRangeAxisLocation(
this.axisLocation, PlotOrientation.VERTICAL);
if (axisEdge == RectangleEdge.LEFT) {
for (int i = 0; i < this.subdivisions; i++) {
double v = base + (i * increment);
Paint p = this.scale.getPaint(v);
double vv0 = this.axis.valueToJava2D(v, target,
RectangleEdge.LEFT);
double vv1 = this.axis.valueToJava2D(v + increment, target,
RectangleEdge.LEFT);
double hh = Math.abs(vv1 - vv0) + 1.0;
r.setRect(target.getMaxX() - this.stripWidth,
Math.min(vv0, vv1), this.stripWidth, hh);
g2.setPaint(p);
g2.fill(r);
}
if (isStripOutlineVisible()) {
g2.setPaint(this.stripOutlinePaint);
g2.setStroke(this.stripOutlineStroke);
g2.draw(new Rectangle2D.Double(target.getMaxX()
- this.stripWidth, target.getMinY(),
this.stripWidth, target.getHeight()));
}
this.axis.draw(g2, target.getMaxX() - this.stripWidth
- this.axisOffset, target, target, RectangleEdge.LEFT,
null);
}
else if (axisEdge == RectangleEdge.RIGHT) {
for (int i = 0; i < this.subdivisions; i++) {
double v = base + (i * increment);
Paint p = this.scale.getPaint(v);
double vv0 = this.axis.valueToJava2D(v, target,
RectangleEdge.LEFT);
double vv1 = this.axis.valueToJava2D(v + increment, target,
RectangleEdge.LEFT);
double hh = Math.abs(vv1 - vv0) + 1.0;
r.setRect(target.getMinX(), Math.min(vv0, vv1),
this.stripWidth, hh);
g2.setPaint(p);
g2.fill(r);
}
if (isStripOutlineVisible()) {
g2.setPaint(this.stripOutlinePaint);
g2.setStroke(this.stripOutlineStroke);
g2.draw(new Rectangle2D.Double(target.getMinX(),
target.getMinY(), this.stripWidth,
target.getHeight()));
}
this.axis.draw(g2, target.getMinX() + this.stripWidth
+ this.axisOffset, target, target, RectangleEdge.RIGHT,
null);
}
}
return null;
}
/**
* Tests this legend for equality with an arbitrary object.
*
* @param obj the object (null
permitted).
*
* @return A boolean.
*/
public boolean equals(Object obj) {
if (!(obj instanceof PaintScaleLegend)) {
return false;
}
PaintScaleLegend that = (PaintScaleLegend) obj;
if (!this.scale.equals(that.scale)) {
return false;
}
if (!this.axis.equals(that.axis)) {
return false;
}
if (!this.axisLocation.equals(that.axisLocation)) {
return false;
}
if (this.axisOffset != that.axisOffset) {
return false;
}
if (this.stripWidth != that.stripWidth) {
return false;
}
if (this.stripOutlineVisible != that.stripOutlineVisible) {
return false;
}
if (!PaintUtilities.equal(this.stripOutlinePaint,
that.stripOutlinePaint)) {
return false;
}
if (!this.stripOutlineStroke.equals(that.stripOutlineStroke)) {
return false;
}
if (!PaintUtilities.equal(this.backgroundPaint, that.backgroundPaint)) {
return false;
}
if (this.subdivisions != that.subdivisions) {
return false;
}
return super.equals(obj);
}
/**
* 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();
SerialUtilities.writePaint(this.backgroundPaint, stream);
SerialUtilities.writePaint(this.stripOutlinePaint, stream);
SerialUtilities.writeStroke(this.stripOutlineStroke, stream);
}
/**
* 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();
this.backgroundPaint = SerialUtilities.readPaint(stream);
this.stripOutlinePaint = SerialUtilities.readPaint(stream);
this.stripOutlineStroke = SerialUtilities.readStroke(stream);
}
}