这个程序能够生成单柱柱状图,也能生成多柱柱状图。


package com.hhh.Paint;



import java.io.File;
import java.io.FileOutputStream;
import java.math.BigDecimal;



/**
* 该类是使用SVG画动态柱状图,只需调用本类的静态方法createSVG(String fileRealPath,String[]
* pData,String[] pDataName)即可,我没有使用APACHE提供的SVG类库来生成SVG文件,只是将所有SVG描述用字符串返回
*
* @author 顾国勇
* @version 1.0
*/
public class PaintHistogram {



/**
* 正值所用的12种颜色
*/
public static String[][] color = { { #ff2222, #ffaaaa, #aa1111 },
{ #d0b83a, #f2e692, #9f8703 },
{ #ffdf00, #fef195, #ce9a31 },
{ #22FF22, #aaffaa, green },
{ #ff9e00, #ffbe08, #bd7500 },
{ #799AE1, #9aabEe, #5778a1 },
{ #99c741, #aef292, #3e941b },
{ #d0b83a, #f2e692, #9f8703 },
{ #319a00, #66cc00, #297110 },
{ #c27f34, #d6a97b, #82522b },
{ #2222ff, #aaaaff, #1111aa },
{ #ff2222, #ffaaaa, #aa1111 } }; //,{#99c741,#aef292,#3e941b}



/**
* 负值所用的颜色
*/
public static String[][] colorNeg = { { black, #4F4F4F, #757575 },
{ #1A0B0F, #4F4F4F, #757575 } };



/**
* 存放转换成double型的数据,用于单例柱形
*/
public static double[] data;



/**
* 存放转换成double型的数据,用于多例柱形
*/
public static double[][] data1;



/**
* 数据总数,默认值为0
*/
public static int dataNum = 0;



/**
* 默认柱状图X轴的起始X值
*/
public static double HistogramXSx = 50.0;



/**
* 默认柱状图X轴的Y值
*/
public static double HistogramXy = 440.0;



/**
* 将元数据从String转换成double,用于单例柱形
*
* @param data
* 字符串数组形式的元数据
* @return 转换好的数据
*/
static double[] convertDataToDouble(String[] data) {
double[] dData = new double[data.length];
dataNum = 0;
for (int i = 0; i < data.length; i++) {
if (data[i] != null && !data[i].equalsIgnoreCase()
&& !data[i].equalsIgnoreCase( )) {
dData[i] = Double.valueOf(data[i]).doubleValue();
dataNum += 1;
} else {
dData[i] = new Double(0.0).doubleValue();
dataNum += 1;
}
}
return dData;
}




/**
* 将元数据从String转换成double,用于多例柱形
*
* @param data
* 字符串数组形式的元数据
* @return 转换好的数据
*/
static double[][] convertDataToDouble(String[][] data) {
double[][] dData = new double[data.length][data[0].length];
dataNum = 0;
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
if (data[i][j] != null && !data[i][j].equalsIgnoreCase()
&& !data[i][j].equalsIgnoreCase( )) {
dData[i][j] = Double.parseDouble(data[i][j]);//Double.valueOf(data[i][j]).doubleValue();
dataNum += 1;
} else {
dData[i][j] = new Double(0.0).doubleValue();
dataNum += 1;
}
}
}
return dData;
}




/**
* @param fileRealPath
* 指定的SVG文件的全路径,用于单例柱形
* @param pData
* 元数据数组
* @param pDataName
* 元数据数组名称
*/
public static void createSVG(String fileRealPath, String[] pData,
String[] pDataName) throws Exception {
String sFile = paint(pData, pDataName);
try {
byte[] byteFil = sFile.getBytes(UTF-8);
File svgFile = new File(fileRealPath);
if (svgFile.exists()) {
svgFile.delete();
}
FileOutputStream fos = new FileOutputStream(svgFile);
fos.write(byteFil);
fos.close();
} catch (Exception ex) {
System.out.print(ex.getMessage());
}



}




/**
* @param fileRealPath
* 指定的SVG文件的全路径,用于多例柱形
* @param pData
* 元数据数组
* @param pDataName
* 元数据数组名称
*/
public static void createSVG(String fileRealPath, String[][] pData,
String[] pDataName) {
try {
String sFile = paint(pData, pDataName);



byte[] byteFil = sFile.getBytes(UTF-8);
File svgFile = new File(fileRealPath);
if (svgFile.exists()) {
svgFile.delete();
}
FileOutputStream fos = new FileOutputStream(svgFile);
fos.write(byteFil);
fos.close();
} catch (Exception ex) {
System.out.print(createSVG: + ex.getMessage());
}



}




/**
* 根据原始数据过滤出最大最小值,已经考虑了数据正负的各种情况(全是正数,全是负数,有正有负),如果元数据中最大值或最小值的绝对值是小于1的,那最后返回的相应值的绝对值是1;
* 如果最大或最小的绝对值小于100,那最后返回的相应值的绝对值是10的倍数;如果最大或最小的绝对值大于100,那最后返回的相应值的绝对值是50的倍数
*
* @param data
* 原始数据
* @return 包含绝对值最大的值
*/
static double getValueMax(double[] pData) {
//存放所有数据值
double max = pData[0];
double min = pData[0];



for (int i = 0; i < pData.length; i++) {
if (pData[i] > max)
max = pData[i];
if (pData[i] < min)
min = pData[i];
}



//全是负的
if (max <= 0 && min < 0) {
min = Math.floor(min);
double tMin = Math.abs(min);
max = 0;
if (tMin <= 1)
min = -1;
else {
min = -(tMin < 100 ? Math.ceil(tMin / 10) * 10 : Math
.ceil(tMin / 50) * 50);
}
}
//有正有负
if (min < 0 && max > 0) {
max = Math.ceil(max);
if (max <= 1)
max = 1;
else {
max = max < 100 ? Math.ceil(max / 10) * 10 : Math
.ceil(max / 50) * 50;
}



min = Math.abs(Math.floor(min));
if (min <= 1)
min = -1;
else {
min = -(min < 100 ? Math.ceil(min / 10) * 10 : Math
.ceil(min / 50) * 50);
}
}
//全是正的
if (min >= 0 && max >= 0) {
if (max == 0.0) {
max = min = 0.0;
} else {
if (max <= 1)
max = 1;
else {
max = max < 100 ? Math.ceil(max / 10) * 10 : Math
.ceil(max / 50) * 50;
}
}
}



double absMax = Math.abs(max) > Math.abs(min) ? Math.abs(max) : Math
.abs(min);
return absMax;
}




/**
* 类初始化,主要是声明了文件开头和滤镜,对于画正负Y轴都通用
*
* @return SVG文件头
*/
static String initialize() {
//文件头声明
StringBuffer sFile = new StringBuffer();
sFile.append();
sFile.append(\n);
sFile
.append();
sFile.append(\n);
sFile
.append();
sFile.append(\n);
sFile.append(Histogram);
sFile.append(\n);
sFile
.append();
sFile.append(\n);
//定义滤镜和箭头
sFile.append();
sFile.append(\n);
sFile
.append( );
sFile.append(\n);
sFile.append( );
sFile.append(\n);
sFile.append(
);
sFile.append(\n);
sFile.append( );
sFile.append(\n);
sFile.append( );
sFile.append(\n);
sFile.append( );
sFile.append(\n);
sFile.append( );
sFile.append(\n);
sFile.append(
);
sFile.append(\n);
sFile
.append( );
sFile.append(\n);
sFile
.append( );
sFile.append(\n);
sFile
.append( );
sFile.append(\n);
sFile.append(\n);
sFile
.append( );
sFile.append(\n);
sFile.append( );
sFile.append(\n);
sFile.append(
);
sFile.append(\n);
sFile
.append( );
sFile.append(\n);
sFile
.append( );
sFile.append(\n);
sFile.append( );
sFile.append(\n);
sFile.append( );
sFile.append(\n);
sFile.append( );
sFile.append(\n);
sFile.append(
);
sFile.append(\n);
sFile.append(
);
sFile.append(\n);
sFile.append();
sFile.append(\n);
sFile.append(
);
sFile.append(\n);
//X轴
sFile
.append();
sFile.append(\n);
//Y轴
sFile
.append();
sFile.append(\n);
sFile
.append();
/* Y轴刻度线 */
for (int i = 0; i < 11; i++) {
double tempY1 = 40 + i * 40.0; //每根Y轴刻度线的起始位置
sFile.append();
sFile.append();
sFile.append(\n);
// sFile.append( // sFile
// .append(
// style='fill:rgb(37,170,219);stroke:rgb(37,170,219);stroke-width:1;fill-opacity:0.3;stroke-opacity:0;opacity:0.8'/>);
// sFile.append(\n);
}



return sFile.toString();



}




/**
* @param args
*/
public static void main(String[] args) {
try {
String[][] data = { { 54, 44, 122 }, { , 12, -468 },
{ 78, 520, 10 } };
String[] data1 = { 54, 4424.225, 12200 ,-4658 };



String[] dataName = { 营业所一, 二二二二, III };
PaintHistogram.createSVG(d:\\t1.svg, data, dataName);
PaintHistogram.createSVG(d:\\t2.svg, data1, dataName);



} catch (Exception ex) {
System.out.print(ex.getMessage());
}
}




/**
* 画出柱状及刻度等,适用于X轴每格都只显示一个柱型
*
* @param pData
* 元数据
* @param pDataName
* 元数据的名称
*/
static String paint(String[] pData, String[] pDataName) throws Exception {
StringBuffer sFile = new StringBuffer();
data = convertDataToDouble(pData);



double valueMM = getValueMax(data); //取得最大值
sFile.append(initialize());
//X Y步长
double yStep = 400.0 / valueMM;
double xStep = (new BigDecimal(600 / pDataName.length).setScale(3,
BigDecimal.ROUND_HALF_UP)).doubleValue();



double colWidth = xStep / 2; //柱型的宽度



double colWidthPre = xStep / 4; //每个单元格起始与柱型的距离



double colWidthNext = xStep / 4; //每个单元格结束与柱型的距离
/* x轴刻度线 */
for (int i = 0; i < pDataName.length; i++) {



double tempX1 = i == 0 ? HistogramXSx + i * xStep : HistogramXSx
+ i * xStep + 10; //每根X轴刻度线的起始位置



//double tempX2 = tempX1 + colWidthPre; //每根柱状的起始位置
sFile.append();
sFile.append(\n);
}



/* 画Y轴刻度值 */
int maxValue = (int) valueMM;
int valueX; //Y轴刻度值起始的X坐标
String fontsize =;
if (maxValue > 10000) {
valueX = 5;
fontsize = 11px;
} else {
valueX = 15;
fontsize = 13px;
}
for (int i = 0; i < 10; i++) {
sFile.append();
sFile.append(\n);
sFile.append((maxValue / 10) * (10 - i));
sFile.append(\n);
sFile.append(
);
sFile.append(\n);
}



/* 画矩形 */
sFile.append();
sFile.append(\n);
for (int i = 0; i < pDataName.length; i++) {



double tempX1 = i == 0 ? HistogramXSx + i * xStep : HistogramXSx
+ i * xStep + 10; //每根X轴刻度线的起始位置



double tempX2 = tempX1 + colWidthPre; //每根柱状的起始位置
double colHeight = Math.ceil(Math.abs(data[i]) * yStep);
double tempY = 440 - colHeight;



String[] colorHistogram; //三个柱面的颜色
if (data[i] < 0) {
colorHistogram = colorNeg[0];
sFile.append(paintHistogram(colWidth, colHeight, tempX2, tempY,
colorHistogram, data[i]));
} else if (data[i] > 0) {
int n = i % color.length;
colorHistogram = color[n];
sFile.append(paintHistogram(colWidth, colHeight, tempX2, tempY,
colorHistogram, data[i]));
}



/* 添加X轴字段说明 字无法在一行内显示的话,需要换行 */
int n = (int) xStep / 13;//每行显示几个字符
int m = pDataName[i].length() % n == 0 ? pDataName[i].length() / n
: (int) (pDataName[i].length() / n + 1);//总共显示几行
int l = 0;//记录总共处理了多少字符
if (m > 1) {
sFile
.append();
for (int j = 0; j < m; j++) {
for (int z = 0; z < n; z++) {
l = j * n + z;
if (l == pDataName[i].length()) {
break;
}
sFile.append();
sFile.append(pDataName[i].substring(l, l + 1));
sFile.append(
);
}
if (l == pDataName[i].length()) {
break;
}
}
sFile.append(
);
sFile.append(\n);
} else {
sFile
.append(
+ pDataName[i] +
);
sFile.append(\n);
}
}
sFile.append(
);
sFile.append(\n);
sFile.append();
return sFile.toString();

}




/**
* 画出柱状及刻度等,适用于X轴每格都显示多个柱型
*
* @param pData
* 具体的元数据
* @param pDataName
* 元数据的名称
*/
static String paint(String[][] pData, String[] pDataName) {
StringBuffer sFile = new StringBuffer();
data1 = convertDataToDouble(pData);
//取得最大值



double[] tempValue = new double[dataNum];
for (int i = 0; i < data1.length; i++) {
for (int j = 0; j < data1[i].length; j++) {
tempValue[i * data1[i].length + j] = data1[i][j];
}
}
double valueMM = getValueMax(tempValue);
sFile.append(initialize());
double yStep = 400.0 / valueMM;
double xStep = (new BigDecimal(600 / pDataName.length).setScale(3,
BigDecimal.ROUND_HALF_UP)).doubleValue();



double colWidthSum = xStep / 2; //多个柱型的宽度之和
double colWidthPre = xStep / 4; //每个单元格起始与柱形的距离
double colWidthNext = xStep / 4; //每个单元格结束与柱形的距离
double colWidth = colWidthSum / data1[0].length; //单个柱形的宽度



/* x轴刻度线 */
for (int i = 0; i < pDataName.length; i++) {
double tempX1 = i == 0 ? HistogramXSx + i * xStep : HistogramXSx
+ i * xStep + 10; //每根X轴刻度线的起始位置
//double tempX2 = tempX1 + colWidthPre; //每根柱形的起始位置
sFile.append();
sFile.append(\n);
}




/* 画Y轴刻度值 */
int maxValue = (int) valueMM;
int valueX;//Y轴刻度值起始的X坐标
String fontsize =;
if (maxValue > 10000) {
valueX = 5;
fontsize = 11px;
} else {
valueX = 15;
fontsize = 13px;
}
for (int i = 0; i < 10; i++) {
sFile.append();
sFile.append(\n);
sFile.append((maxValue / 10) * (10 - i));
sFile.append(\n);
sFile.append(
);
sFile.append(\n);
}



/* 画矩形 */
sFile.append();
sFile.append(\n);
for (int i = 0; i < pDataName.length; i++) {
//每根X轴刻度线的起始位置
double tempX1 = i == 0 ? HistogramXSx + i * xStep : HistogramXSx
+ i * xStep + 10;
for (int j = 0; j < data1[i].length; j++) {
double tempX2 = tempX1 + colWidthPre + j * colWidth; //每根柱形的起始位置
double colHeight = Math.ceil(Math.abs(data1[i][j]) * yStep);
double tempY = 440 - colHeight;
String[] colorHistogram; //三个柱面的颜色
if (data1[i][j] < 0) {
colorHistogram = colorNeg[0];
sFile.append(paintHistogram(colWidth, colHeight, tempX2,
tempY, colorHistogram, data1[i][j]));
} else if (data1[i][j] > 0) {
int n = j % color.length;
colorHistogram = color[n];
sFile.append(paintHistogram(colWidth, colHeight, tempX2,
tempY, colorHistogram, data1[i][j]));
}
}
/* 添加X轴字段说明 字无法在一行内显示的话,需要换行 */
int n = (int) xStep / 13;//每行显示几个字符
int m = pDataName[i].length() % n == 0 ? pDataName[i].length() / n
: (int) (pDataName[i].length() / n + 1);//总共显示几行
int l = 0;//记录总共处理了多少字符
if (m > 1) {
sFile
.append();
for (int j = 0; j < m; j++) {
for (int z = 0; z < n; z++) {
l = j * n + z;
if (l == pDataName[i].length()) {
break;
}
sFile.append();
sFile.append(pDataName[i].substring(l, l + 1));
sFile.append(
);
}
if (l == pDataName[i].length()) {
break;
}
}
sFile.append(
);
sFile.append(\n);
} else {
sFile
.append(
+ pDataName[i] +
);
sFile.append(\n);
}
}
sFile.append(
);
sFile.append(\n);
sFile.append();
return sFile.toString();
}




/**
* 画单一柱形
*
* @param colWidth
* 柱形宽度
* @param colHeight
* 柱形高度
* @param x
* 柱形X值
* @param y
* 柱形Y值
* @param color
* 柱形三个面的颜色
* @param dataValue
* 柱形代表的值
* @return SVG字符串
*/
static String paintHistogram(double colWidth, double colHeight, double x,
double y, String[] color, double dataValue) {
StringBuffer sFile = new StringBuffer();
double dOffset = (colWidth / 3) > 10.0 ? 10.0 : (colWidth / 3);//斜面的偏移量
sFile.append();
sFile.append(\n);
sFile.append();
sFile.append(\n);
sFile.append();
return sFile.toString();
}
}