package com.supcon.honcomb.basearchives.entity;
import java.awt.AlphaComposite;
import java.awt.Color;import java.awt.Composite;import java.awt.Graphics2D;import java.awt.Paint;import java.awt.Polygon;import java.awt.Rectangle;import java.awt.Shape;import java.awt.Stroke;import java.awt.font.FontRenderContext;import java.awt.font.LineMetrics;import java.awt.geom.AffineTransform;import java.awt.geom.Arc2D;import java.awt.geom.Ellipse2D;import java.awt.geom.Line2D;import java.awt.geom.Point2D;import java.awt.geom.Rectangle2D;import java.text.NumberFormat;import org.jfree.chart.entity.CategoryItemEntity;
import org.jfree.chart.entity.EntityCollection;import org.jfree.chart.plot.PlotRenderingInfo;import org.jfree.chart.plot.PlotState;import org.jfree.chart.plot.SpiderWebPlot;import org.jfree.data.category.CategoryDataset;import org.jfree.data.general.DatasetUtilities;import org.jfree.ui.RectangleInsets;import org.jfree.util.TableOrder;/**
* 刻度蜘蛛环图 * * @author 成尚謙 * @since 2011-09-14 * @version 1.0 */public class CalibrationSpiderWebPlot extends SpiderWebPlot { private static final long serialVersionUID = -11L; private NumberFormat format = NumberFormat.getInstance(); private static final double PERPENDICULAR = 90; private static final double TICK_SCALE = 0.015; private int valueLabelGap = DEFAULT_GAP; private static final int DEFAULT_GAP = 10; private static final double THRESHOLD = 15; public static final int DEFAULT_TICKS = 5; // 刻度数 public static final double DEFAULT_MAX_VALUE = 100; // 坐标最大值 public static final boolean DEFAULT_DRAW_RING = true; // 画换默认值/**
* 刻度数/环数 */ private int ticks = DEFAULT_TICKS;/**
* 画环 */ private boolean drawRing = false;/**
* 刻度前缀 */ private String lablePrefix = "";/**
* 刻度后缀 */ private String lableSuffix = "";/**
* 默认坐标最大值和画环 * * @param createCategoryDataset */ public CalibrationSpiderWebPlot(CategoryDataset createCategoryDataset) { this(createCategoryDataset, DEFAULT_MAX_VALUE); // 设置坐标最大值为默认的值 }/**
* 默认画环,与刻度数 * * @param createCategoryDataset * @param maxValue */ public CalibrationSpiderWebPlot(CategoryDataset createCategoryDataset, double maxValue) { this(createCategoryDataset, maxValue, DEFAULT_DRAW_RING); // 设置画换默认值 }/**
* 自定义坐标最大值和画环 * * @param createCategoryDataset * @param maxValue * @param drawRing */ public CalibrationSpiderWebPlot(CategoryDataset createCategoryDataset, double maxValue, boolean drawRing) { this(createCategoryDataset, maxValue, drawRing, DEFAULT_TICKS);// 设置刻度数默认值 }/**
* 自定义坐标最大值和画环、刻度数 * * @param createCategoryDataset * @param maxValue * @param drawRing */ public CalibrationSpiderWebPlot(CategoryDataset createCategoryDataset, double maxValue, boolean drawRing, int ticks) { this(createCategoryDataset, maxValue, drawRing, ticks, "");// 设置刻度前缀默认值 }/**
* 自定义坐标最大值和画环以及刻度前缀、刻度数 * * @param createCategoryDataset * @param maxValue * @param drawRing */ public CalibrationSpiderWebPlot(CategoryDataset createCategoryDataset, double maxValue, boolean drawRing, int ticks, String lablePrefix) { this(createCategoryDataset, maxValue, drawRing, ticks, lablePrefix, ""); ;// 设置刻度后缀默认值 }/**
* 自定义坐标最大值和画环以及刻度前/后缀、刻度数 * * @param createCategoryDataset * @param maxValue * @param drawRing */ public CalibrationSpiderWebPlot(CategoryDataset createCategoryDataset, double maxValue, boolean drawRing, int ticks, String lablePrefix, String lableSuffix) { super(createCategoryDataset); this.setMaxValue(maxValue);// 设置坐标最大值 this.setDrawRing(drawRing);// 设置画换 // this.setTicks(ticks);//设置刻度数 this.setLablePrefix(lablePrefix);// 刻度前缀 this.setLableSuffix(lableSuffix);// 刻度后缀 }/**
* 画图,支持添加圆环 * * @param g2 * the graphics device. * @param area * the area within which the plot should be drawn. * @param anchor * the anchor point (<code>null</code> permitted). * @param parentState * the state from the parent plot, if there is one. * @param info * collects info about the drawing. */ public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) {// adjust for insets...
RectangleInsets insets = getInsets(); insets.trim(area);if (info != null) {
info.setPlotArea(area); info.setDataArea(area); }drawBackground(g2, area);
drawOutline(g2, area);Shape savedClip = g2.getClip();
g2.clip(area);
Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getForegroundAlpha()));if (!DatasetUtilities.isEmptyOrNull(this.getDataset())) {
int seriesCount = 0, catCount = 0;if (this.getDataExtractOrder() == TableOrder.BY_ROW) {
seriesCount = this.getDataset().getRowCount(); catCount = this.getDataset().getColumnCount(); } else { seriesCount = this.getDataset().getColumnCount(); catCount = this.getDataset().getRowCount(); }// ensure we have a maximum value to use on the axes
if (this.getMaxValue() == DEFAULT_MAX_VALUE) calculateMaxValue(seriesCount, catCount);// Next, setup the plot area
// adjust the plot area by the interior spacing value
double gapHorizontal = area.getWidth() * getInteriorGap();
double gapVertical = area.getHeight() * getInteriorGap();double X = area.getX() + gapHorizontal / 2;
double Y = area.getY() + gapVertical / 2; double W = area.getWidth() - gapHorizontal; double H = area.getHeight() - gapVertical;double headW = area.getWidth() * this.headPercent;
double headH = area.getHeight() * this.headPercent;// make the chart area a square
double min = Math.min(W, H) / 2; X = (X + X + W) / 2 - min; Y = (Y + Y + H) / 2 - min; W = 2 * min; H = 2 * min;Point2D centre = new Point2D.Double(X + W / 2, Y + H / 2);
Rectangle2D radarArea = new Rectangle2D.Double(X, Y, W, H);// draw the axis and category label
for (int cat = 0; cat < catCount; cat++) { double angle = getStartAngle() + (getDirection().getFactor() * cat * 360 / catCount);// 如果只有两个分类,设置固定角度
if (catCount == 2 && cat == 1) { angle = 0; }Point2D endPoint = getWebPoint(radarArea, angle, 1);
// 1 = end of axis Line2D line = new Line2D.Double(centre, endPoint); g2.setPaint(this.getAxisLinePaint()); g2.setStroke(this.getAxisLineStroke()); g2.draw(line); drawLabel(g2, radarArea, 0.0, cat, angle, 360.0 / catCount); }// 画环
if (this.isDrawRing()) { Point2D topPoint = getWebPoint(radarArea, 90, 1);// 以90度为轴心,计算各个圆环的x、y坐标 double topPointR = centre.getY() - topPoint.getY();// 轴心顶点圆的半径 double step = topPointR / this.getTicks();// 每个刻度的半径长for (int p = this.getTicks(); p >= 1; p--) {
double r = p * step; double upperLeftX = centre.getX() - r; double upperLeftY = centre.getY() - r; double d = 2 * r; Ellipse2D ring = new Ellipse2D.Double(upperLeftX, upperLeftY, d, d); g2.setPaint(Color.WHITE); g2.draw(ring); } }// Now actually plot each of the series polygons..
for (int series = 0; series < seriesCount; series++) { drawRadarPoly(g2, radarArea, centre, info, series, catCount, headH, headW); } } else { drawNoDataMessage(g2, area); } g2.setClip(savedClip); g2.setComposite(originalComposite); drawOutline(g2, area); }/**
* 增加刻度 */ /* * @Override protected void drawLabel(final Graphics2D g2, final Rectangle2D * plotArea, final double value, final int cat, final double startAngle, * final double extent) { super.drawLabel(g2, plotArea, value, cat, * startAngle, extent); final FontRenderContext frc = * g2.getFontRenderContext(); final double[] transformed = new double[2]; * final double[] transformer = new double[2]; final Arc2D arc1 = new * Arc2D.Double(plotArea, startAngle, 0, Arc2D.OPEN); *//** * 设置默认值 */ /* * if(ticks <= 0){ ticks = DEFAULT_TICKS; } * * for (int i = 1; i <= ticks; i++) { * * final Point2D point1 = arc1.getEndPoint(); * * final double deltaX = plotArea.getCenterX(); final double deltaY = * plotArea.getCenterY(); double labelX = point1.getX() - deltaX; double * labelY = point1.getY() - deltaY; * * final double scale = ((double) i / (double) ticks); final AffineTransform * tx = AffineTransform.getScaleInstance(scale, scale); final * AffineTransform pointTrans = AffineTransform.getScaleInstance(scale + * TICK_SCALE, scale + TICK_SCALE); transformer[0] = labelX; transformer[1] * = labelY; pointTrans.transform(transformer, 0, transformed, 0, 1); final * double pointX = transformed[0] + deltaX; final double pointY = * transformed[1] + deltaY; tx.transform(transformer, 0, transformed, 0, 1); * labelX = transformed[0] + deltaX; labelY = transformed[1] + deltaY; * * double rotated = (PERPENDICULAR); * * AffineTransform rotateTrans = * AffineTransform.getRotateInstance(Math.toRadians(rotated), labelX, * labelY); transformer[0] = pointX; transformer[1] = pointY; * rotateTrans.transform(transformer, 0, transformed, 0, 1); final double x1 * = transformed[0]; final double y1 = transformed[1]; * * rotated = (-PERPENDICULAR); rotateTrans = * AffineTransform.getRotateInstance(Math.toRadians(rotated), labelX, * labelY); * * rotateTrans.transform(transformer, 0, transformed, 0, 1); * * final Composite saveComposite = g2.getComposite(); * g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, * 1.0f)); * * g2.draw(new Line2D.Double(transformed[0], transformed[1], x1, y1)); * * if (startAngle == this.getStartAngle()) { String label = * format.format(((double) i / (double) ticks) * this.getMaxValue()); final * LineMetrics lm = getLabelFont().getLineMetrics(label, frc); final double * ascent = lm.getAscent(); if (Math.abs(labelX - plotArea.getCenterX()) < * THRESHOLD) { labelX += valueLabelGap; labelY += ascent / (float) 2; } * else if (Math.abs(labelY - plotArea.getCenterY()) < THRESHOLD) { labelY * += valueLabelGap; } else if (labelX >= plotArea.getCenterX()) { if * (labelY < plotArea.getCenterY()) { labelX += valueLabelGap; labelY += * valueLabelGap; } else { labelX -= valueLabelGap; labelY += valueLabelGap; * } } else { if (labelY > plotArea.getCenterY()) { labelX -= valueLabelGap; * labelY -= valueLabelGap; } else { labelX += valueLabelGap; labelY -= * valueLabelGap; } } g2.setPaint(getLabelPaint()); * g2.setFont(getLabelFont()); * * //添加刻度的前缀和后缀 if(null != this.getLablePrefix() && * !"".equals(this.getLablePrefix())){ label = this.getLablePrefix() + * label; } if(null != this.getLableSuffix() && * !"".equals(this.getLableSuffix())){ label = label + * this.getLableSuffix(); } * * g2.drawString(label, (float) labelX, (float) labelY); } * g2.setComposite(saveComposite); } } *//**
* Draws a radar plot polygon. 如果只有两个分类,设置固定角度 * * @param g2 * the graphics device. * @param plotArea * the area we are plotting in (already adjusted). * @param centre * the centre point of the radar axes * @param info * chart rendering info. * @param series * the series within the dataset we are plotting * @param catCount * the number of categories per radar plot * @param headH * the data point height * @param headW * the data point width */ protected void drawRadarPoly(Graphics2D g2, Rectangle2D plotArea, Point2D centre, PlotRenderingInfo info, int series, int catCount, double headH, double headW) {Polygon polygon = new Polygon();
EntityCollection entities = null;
if (info != null) { entities = info.getOwner().getEntityCollection(); }// plot the data...
for (int cat = 0; cat < catCount; cat++) {Number dataValue = getPlotValue(series, cat);
if (dataValue != null) {
double value = dataValue.doubleValue();if (value >= 0) { // draw the polygon series...
// Finds our starting angle from the centre for this axis
double angle = getStartAngle()
+ (getDirection().getFactor() * cat * 360 / catCount);// 如果只有两个分类,设置固定角度
if (catCount == 2 && cat == 1) { angle = 0; }// The following angle calc will ensure there isn't a top
// vertical axis - this may be useful if you don't want any // given criteria to 'appear' move important than the // others.. // + (getDirection().getFactor() // * (cat + 0.5) * 360 / catCount);// find the point at the appropriate distance end point
// along the axis/angle identified above and add it to the // polygonPoint2D point = getWebPoint(plotArea, angle, value
/ this.getMaxValue()); polygon.addPoint((int) point.getX(), (int) point.getY());// put an elipse at the point being plotted..
Paint paint = getSeriesPaint(series);
Paint outlinePaint = getSeriesOutlinePaint(series); Stroke outlineStroke = getSeriesOutlineStroke(series);Ellipse2D head = new Ellipse2D.Double(point.getX() - headW
/ 2, point.getY() - headH / 2, headW, headH); g2.setPaint(paint); g2.fill(head); g2.setStroke(outlineStroke); g2.setPaint(outlinePaint); g2.draw(head);if (entities != null) {
int row = 0; int col = 0; if (this.getDataExtractOrder() == TableOrder.BY_ROW) { row = series; col = cat; } else { row = cat; col = series; } String tip = null; if (this.getToolTipGenerator() != null) { tip = this.getToolTipGenerator().generateToolTip( this.getDataset(), row, col); }String url = null;
if (this.getURLGenerator() != null) { url = this.getURLGenerator().generateURL( this.getDataset(), row, col); }Shape area = new Rectangle(
(int) (point.getX() - headW), (int) (point .getY() - headH), (int) (headW * 2), (int) (headH * 2)); CategoryItemEntity entity = new CategoryItemEntity( area, tip, url, this.getDataset(), this .getDataset().getRowKey(row), this .getDataset().getColumnKey(col)); entities.add(entity); }}
} } // Plot the polygonPaint paint = getSeriesPaint(series);
g2.setPaint(paint); g2.setStroke(getSeriesOutlineStroke(series)); g2.draw(polygon);// Lastly, fill the web polygon if this is required
if (this.isWebFilled()) {
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f)); g2.fill(polygon); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getForegroundAlpha())); } }/**
*获取分类的最大值 * * @param seriesCount * the number of series * @param catCount * the number of categories */ private void calculateMaxValue(int seriesCount, int catCount) { double v = 0; Number nV = null;for (int seriesIndex = 0; seriesIndex < seriesCount; seriesIndex++) {
for (int catIndex = 0; catIndex < catCount; catIndex++) { nV = getPlotValue(seriesIndex, catIndex); if (nV != null) { v = nV.doubleValue(); if (v > this.getMaxValue()) { this.setMaxValue(v); } } } } }public String getLablePrefix() {
return lablePrefix; }public void setLablePrefix(String lablePrefix) {
this.lablePrefix = lablePrefix; }public String getLableSuffix() {
return lableSuffix; }public void setLableSuffix(String lableSuffix) {
this.lableSuffix = lableSuffix; }public boolean isDrawRing() {
return drawRing; }public void setDrawRing(boolean drawRing) {
this.drawRing = drawRing; }public int getTicks() {
return ticks; }public void setTicks(int ticks) {
this.ticks = ticks; }}