自定义统计图的实例—曲线图
集智平台的报表中自定义统计图功能给客户提供了丰富的统计图实现方法,客户可以根据自己的需求通过实现润乾的API接口,用java类画出符合自己需求的自定义统计图。下面就介绍一种自定义统计图实现的例子。
这里采用java的画图方法,实现一个自定义的曲线图。
第一步:制作一张报表。
连接demo数据源,采用内建数据集,制作一张简单的报表,报表的样式如下图所示:
其中c1,c2,c3字段都是字符串类型的。
第二步:用java画图。
润乾的自定义统计图一定要继承DrawBase类,重写其中的draw()方法。
在自定义统计图中重写draw()方法里面比较重要的就是给统计图设置分类和系列的值,然后就是根据客户的需求设置一些边框属性,背景色之类的属性。具体的可以参见下面的完整代码:
package example;
import com.runqian.report4.model.expression.graph.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.Vector;
public class CustomCurveGraph extends DrawBase
{
public CustomCurveGraph()
{
}
public void draw(StringBuffer htmlLink)
{
gp.coorWidth = 0;
initGraphInset();
createCoorValue();
drawLegend(htmlLink);
drawTitle();
drawLabel();
keepGraphSpace();
adjustCoorInset();
gp.graphRect = new Rectangle(gp.leftInset, gp.topInset, gp.graphWidth – gp.leftInset – gp.rightInset, gp.graphHeight – gp.topInset – gp.bottomInset);
if (gp.graphRect.width < 10 || gp.graphRect.height < 10)
return;
if (gp.coorWidth < 0 || gp.coorWidth > 10000)
gp.coorWidth = 0;
double seriesWidth = (double)gp.graphRect.width / (((double)(gp.catNum + 1) * gp.categorySpan) / 100D + (double)gp.coorWidth / 200D + (double)(gp.catNum * gp.serNum));
double coorWidth = seriesWidth * ((double)gp.coorWidth / 200D);
double categorySpan = seriesWidth * (gp.categorySpan / 100D);
int tmpInt = (int)((double)(gp.catNum + 1) * categorySpan + coorWidth + (double)(gp.catNum * gp.serNum) * seriesWidth);
gp.graphRect.x += (gp.graphRect.width – tmpInt) / 2;
gp.graphRect.width = tmpInt;
double dely = ((double)gp.graphRect.height – coorWidth) / (double)gp.tickNum;
tmpInt = (int)(dely * (double)gp.tickNum + coorWidth);
gp.graphRect.y += (gp.graphRect.height – tmpInt) / 2;
gp.graphRect.height = tmpInt;
gp.gRect1 = new Rectangle(gp.graphRect);
gp.gRect2 = new Rectangle(gp.graphRect);
gp.gRect1.y += coorWidth;
gp.gRect1.width -= coorWidth;
gp.gRect1.height -= coorWidth;
gp.gRect2.x += coorWidth;
gp.gRect2.width -= coorWidth;
gp.gRect2.height -= coorWidth;
drawGraphRect();
Rectangle TR = new Rectangle();
for (int i = 0; i <= gp.tickNum; i++)
{
drawGridLine(dely, i);
Number coory = (Number)gp.coorValue.get(i);
String scoory = getFormattedValue(coory.doubleValue());
TR.setBounds(gp.GFV_YLABEL.getTextSize(scoory));
int x = gp.gRect1.x – TR.width – gp.tickLen;
int y = (int)(((double)(gp.gRect1.y + gp.gRect1.height) – (double)i * dely) + (double)(TR.height / 2));
gp.GFV_YLABEL.outText(x, y, scoory, (byte)11);
if (coory.doubleValue() == gp.baseValue + gp.minValue)
gp.valueBaseLine = (int)((double)(gp.gRect1.y + gp.gRect1.height) – (double)i * dely);
}
drawWarnLine();
Point beginPoint[] = new Point[gp.serNum];
double beginVal[] = new double[gp.serNum];
ArrayList catPoints[] = new ArrayList[gp.serNum];
for (int j = 0; j < gp.serNum; j++)
{
ArrayList catList = new ArrayList();
catPoints[j] = catList;
}
ArrayList cats = egp.categories;
int cc = cats.size();
for (int i = 0; i < cc; i++)
{
ExtGraphCategory egc = (ExtGraphCategory)cats.get(i);
int delx = (int)((double)(i + 1) * categorySpan + (double)i * seriesWidth * (double)gp.serNum + (seriesWidth * (double)gp.serNum) / 2D);
boolean vis = i % (gp.graphXInterval + 1) == 0;
if (vis)
drawLine(gp.gRect1.x + delx, gp.gRect1.y + gp.gRect1.height, gp.gRect1.x + delx, gp.gRect1.y + gp.gRect1.height + gp.tickLen, egp.getAxisColor(1));
String value = egc.getNameString();
TR.setBounds(gp.GFV_XLABEL.getTextSize(value));
int x = (gp.gRect1.x + delx) – TR.width / 2;
int y = gp.gRect1.y + gp.gRect1.height + gp.tickLen + TR.height;
gp.GFV_XLABEL.outText(x, y, value, vis);
for (int j = 0; j < gp.serNum; j++)
{
ExtGraphSery egs = egc.getExtGraphSery(gp.serNames.get(j));
double val = egs.getValue();
double tmp = val – gp.baseValue;
int len = (int)((dely * (double)gp.tickNum * (tmp – gp.minValue)) / (gp.maxValue * gp.coorScale));
double lb = (double)gp.gRect1.x + (double)(i + 1) * categorySpan + ((double)((2 * i + 1) * gp.serNum) * seriesWidth) / 2D;
Point endPoint;
if (egs.isNull())
endPoint = null;
else
endPoint = new Point((int)lb, gp.valueBaseLine – len);
int VALUE_RADIUS = 4;
if (gp.dispValueOntop && !egs.isNull() && vis)
{
String sval = getDispValue(egs);
TR.setBounds(gp.GFV_VALUE.getTextSize(sval));
x = endPoint.x;
y = endPoint.y;
gp.GFV_VALUE.outText(x, y – VALUE_RADIUS, sval);
}
if (!egs.isNull() && gp.drawLineDot && vis)
{
int xx = endPoint.x – VALUE_RADIUS;
int yy = endPoint.y – VALUE_RADIUS;
int ww = 2 * VALUE_RADIUS;
int hh = ww;
if (!gp.isMultiSeries)
setPaint(xx, yy, ww, hh, getColor(i), true);
else
setPaint(xx, yy, ww, hh, getColor(j), true);
fillRect(xx, yy, ww, hh);
drawRect(xx, yy, ww, hh, egp.getAxisColor(5));
htmlLink(xx, yy, ww, hh, htmlLink, egc.getNameString(), egs);
}
if (i > 0)
{
g.setColor(getColor(j));
DrawLine.drawVTrendLine(this, beginPoint[j], endPoint, val – beginVal[j]);
}
DrawLine.drawHTrendLine(this, beginPoint[j]);
beginPoint[j] = endPoint;
if (endPoint != null)
{
ArrayList catList = catPoints[j];
catList.add(endPoint);
}
beginVal[j] = val;
}
}
java.awt.Stroke stroke = getLineStroke();
if (stroke != null)
g.setStroke(stroke);
for (int j = 0; j < gp.serNum; j++)
{
ArrayList serPoints = catPoints[j];
if (serPoints.size() != 0)
{
Point p1 = (Point)serPoints.get(0);
Point p2 = (Point)serPoints.get(serPoints.size() – 1);
g.setColor(getColor(j));
int x1 = p1.x;
int y1 = p1.y;
for (int x2 = p1.x + 1; x2 <= p2.x; x2++)
{
int y2 = (int)Lagrange(serPoints, x2, gp.valueBaseLine);
g.drawLine(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
} }
}
}
private double Lagrange(ArrayList points, double deltaX, int BottomY)
{
double sum = 0.0D;
for (int i = 0; i < points.size(); i++)
{
double L = 1.0D;
Point pi = (Point)points.get(i);
for (int j = 0; j < points.size(); j++)
{
Point pj = (Point)points.get(j);
if (j != i)
L = (L * (deltaX – (double)pj.x)) / (double)(pi.x – pj.x);
} sum += L * (double)pi.y;
}
if (sum > (double)BottomY)
return (double)BottomY;
else
return sum;
}
}
自定义统计图里面要设置的属性比较复杂,但是大致的流程都跟上面的写法几乎一致,只是在定义不同种类统计图的时候可能有一些区别。
第三步:在报表中使用自定义统计图。
将第一步中的报表加入自定义统计图,把C列隐藏,统计图的写法可以参见下图:
然后把上面的java代码编译成java类,连同包一起放到reportHome\designer\web\WEB-INF\classes下
然后重启一下设计器,点击预览报表,可以看到下图的效果:
这样自定义统计图的需求就实现了。