JFreeChart(一)

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 成尚謙 lhx222@gmail.com
 * @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
     // polygon

     Point2D 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 polygon

  Paint 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;
 }
}

posted @ 2012-10-12 13:02  人生如若初见  阅读(596)  评论(0编辑  收藏  举报