如何根据数组中的数据来确认Y轴坐标刻度值是一个比较头痛的事,我在这段代码中尽管可以正常确认Y轴坐标,但是显示出来的刻度依旧不理想。这个算法也是照抄了super的算法,不知道有没有更好的算法。
package
SvgGraph;
import
java.io.File;
import
java.io.FileOutputStream;
import
java.io.FileWriter;
import
java.io.OutputStreamWriter;
import
java.math.BigDecimal;
import
org.dom4j.CDATA;
import
org.dom4j.Document;
import
org.dom4j.DocumentHelper;
import
org.dom4j.Element;
import
org.dom4j.io.OutputFormat;
import
org.dom4j.io.XMLWriter;
/**/
/*
* BackgroundMap.java
*
* Created on 2007年8月9日, 上午10:13
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
/** */
/**
*
* @author 周慧明
* 用来生成svg背景用的图形,包括标题、坐标网格和标签
*/
public
class
BackgroundMap
...
{
private int canvasWidth = 600; // 画布宽度
private int canvasHeight = 360; // 画布高度
private int mapWidth = 480; // 绘图区宽度
private int mapHeight = 300; // 绘图区高度
private int zeroX = 30; // 原点x坐标
private int zeroY = 40; // 原点y坐标
// private double yMinValue; // y轴最小值
// private double yMaxValue; // y轴最大值
// private String xBegin; // x轴起始值
// private String xEnd; // x轴结束值
// private int xNum; // x轴刻度总数
// private String xTitle; // x轴单位
// private String yTitle; // y轴单位
// private int xStep; // x轴步进长度(像素)
private int yStep = 30; // y轴步进长度(像素)
private static int dataNum = 0; // 数据总数,默认值为0
private String[] lineColors = ...{"#00008B", "#8A2BE2", "#FFEBCD", "#A52A2A", "#DEB887",
"#5F9EA0", "#7FFF00", "#D2691E", "#FF7F50", "#6495ED"};
/** *//** Creates a new instance of BackgroundMap */
public BackgroundMap() ...{
}
/** *//**
* 建立一个SVG文件,文件名由输入参数决定
* @param filename {String} 需要建立的文件名
* @param titleName {String} 标题名
* @param pData {String[]} 要绘制的数据源
* @param pDataName {String[]} x轴标识
* @return 返回操作结果,0表示失败,1表示成功
**/
public int createSvgFile(String filename,String titleName,
String[] pData, String[] pDataName)...{
/** *//** 返回操作结果,0表示失败,1表示成功*/
int returnValue = 0;
/** *//** 建立document对象*/
Document document = DocumentHelper.createDocument();
/** *//** 设置文件编码集*/
document.setXMLEncoding("UTF-8");
/** *//** !DOCTYPE声明*/
document.addDocType("svg","-//W3C//DTD SVG 1.0//EN","http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd");
/** *//** 建立SVG文档的根svg*/
Element svgRoot = CreateSvgRoot(document);
/** *//** 绘制标题*/
WriteTitle(svgRoot,titleName);
/** *//** 绘背景*/
WriteBackGround(svgRoot);
/** *//** 绘制数据*/
WriteDataLine(svgRoot,pData,pDataName);
/** *//** 将数据写入SVG文件 */
if(WriteDataToFile(document,filename))
return 1;
else
return 0;
}
/** *//**
* 建立一个SVG文件,文件名由输入参数决定
* @param filename {String} 需要建立的文件名
* @param titleName {String} 标题名
* @param pData {String[][]} 要绘制的数据源
* @param pDataName {String[]} x轴标识
* @return 返回操作结果,0表示失败,1表示成功
**/
public int createSvgFile(String filename,String titleName,
String[][] pData, String[] pDataName)...{
/** *//** 返回操作结果,0表示失败,1表示成功*/
int returnValue = 0;
/** *//** 建立document对象*/
Document document = DocumentHelper.createDocument();
/** *//** 设置文件编码集*/
document.setXMLEncoding("UTF-8");
/** *//** !DOCTYPE声明*/
document.addDocType("svg","-//W3C//DTD SVG 1.0//EN","http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd");
/** *//** 建立SVG文档的根svg*/
Element svgRoot = CreateSvgRoot(document);
/** *//** 绘制标题*/
WriteTitle(svgRoot,titleName);
/** *//** 绘背景*/
WriteBackGround(svgRoot);
/** *//** 绘制数据*/
WriteDataLine(svgRoot,pData,pDataName);
/** *//** 将数据写入SVG文件 */
if(WriteDataToFile(document,filename))
return 1;
else
return 0;
}
/** *//**
* 创建SVG文件根节点
* @param document {Document} 文档节点
* @return SVG根节点
**/
private Element CreateSvgRoot(Document document)...{
/** *//** 建立SVG文档的根svg*/
Element svgRoot = document.addElement("svg","http://www.w3.org/2000/svg");
svgRoot.addAttribute("width",Integer.toString(canvasWidth));
svgRoot.addAttribute("height",Integer.toString(canvasHeight));
svgRoot.addAttribute("font-family","SimSun");
return svgRoot;
}
private boolean WriteDataToFile(Document document,String filename)...{
try...{
/** *//** 将document中的内容写入文件中*/
OutputFormat format = OutputFormat.createPrettyPrint();
//format.setEncoding("UTF-8");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filename),"UTF-8");
XMLWriter writer= new XMLWriter(osw,format);
//XMLWriter writer = new XMLWriter(new FileWriter(new File(filename)),format);
writer.write(document);
writer.close();
/** *//** 执行成功,返回1*/
return true;
}catch(Exception ex)...{
System.out.println("写SVG文件时出错!");
ex.printStackTrace();
return false;
}
}
/** *//**
* 绘制SVG的背景及坐标网格
* @param svgRoot {Element} SVG根节点
**/
private void WriteBackGround(Element svgRoot)...{
/** *//**画布景*/
Element canvRec = svgRoot.addElement("rect");
canvRec.addAttribute("x","0");
canvRec.addAttribute("y","0");
canvRec.addAttribute("width",Integer.toString(canvasWidth));
canvRec.addAttribute("height",Integer.toString(canvasHeight));
canvRec.addAttribute("fill","none");
canvRec.addAttribute("stroke","blue");
canvRec.addAttribute("stroke-width","1");
/** *//**X轴*/
Element axesX = svgRoot.addElement("line");
axesX.addAttribute("x1",Integer.toString(zeroX));
axesX.addAttribute("y1",Integer.toString(zeroY + mapHeight));
axesX.addAttribute("x2",Integer.toString(zeroX + mapWidth));
axesX.addAttribute("y2",Integer.toString(zeroY + mapHeight));
axesX.addAttribute("stroke-width","2");
axesX.addAttribute("stroke-opacity",".4");
axesX.addAttribute("fill","blue");
axesX.addAttribute("stroke","#333300");
/** *//**Y轴*/
Element axesY = svgRoot.addElement("line");
axesY.addAttribute("x1",Integer.toString(zeroX));
axesY.addAttribute("y1",Integer.toString(zeroY + mapHeight));
axesY.addAttribute("x2",Integer.toString(zeroX));
axesY.addAttribute("y2",Integer.toString(zeroY));
axesY.addAttribute("stroke-width","2");
axesY.addAttribute("stroke-opacity",".4");
axesY.addAttribute("fill","blue");
axesY.addAttribute("stroke","#333300");
/** *//**Y轴刻度*/
for(int i=0; i<mapHeight/yStep; i++)...{
int tempY = zeroY + i * yStep;
Element scoreY = svgRoot.addElement("line");
scoreY.addAttribute("x1",Integer.toString(zeroX));
scoreY.addAttribute("y1",Integer.toString(tempY));
scoreY.addAttribute("x2",Integer.toString(zeroX + mapWidth));
scoreY.addAttribute("y2",Integer.toString(tempY));
scoreY.addAttribute("stroke-width","1");
scoreY.addAttribute("stroke-dasharray","3 2");
scoreY.addAttribute("stroke","#333300");
}
}
/** *//**
* 书写标题
* @param svgRoot {Element} SVG根节点
* @param titleName {String} 标题名
**/
private void WriteTitle(Element svgRoot,String titleName)...{
int wordNum = titleName.length(); // 标题字符个数
int font_size; // 字体大小
int titleLength; // 标题字符串栅格长度
int x,y; // 标题起始位置
if(wordNum <= 40)...{
font_size=15;
}else...{
font_size=canvasWidth/wordNum;
}
titleLength = font_size * wordNum;
x = (canvasWidth-titleLength)/2;
y = (zeroY-font_size);
/** *//** 绘标题元素*/
Element title = svgRoot.addElement("text");
title.addAttribute("x", Integer.toString(x));
title.addAttribute("y", Integer.toString(y));
title.addAttribute("fill", "black");
title.addAttribute("font-size", Integer.toString(font_size));
title.setText(titleName);
}
/** *//**
* 绘制折线,x轴刻度,x轴刻度名称,y轴刻度值
* @param svgRoot {Element} SVG根节点
* @param pData {String[]} 要绘制折线的数据
* @param pDataName {String[]} x轴刻度名称
**/
private void WriteDataLine(Element svgRoot,String[] pData, String[] pDataName)...{
/**//* 绘制x轴信息*/
double[] xAxis = WriteXData(svgRoot, pDataName);
/**//* 获取double型数据 */
double[] data = convertDataToDouble(pData);
/**//* y轴刻度值*/
WriteYData(svgRoot, data);
/** *//** 绘制数据折线*/
WriteLine(svgRoot, data, xAxis,lineColors[0]);
}
/** *//**
* 绘制折线,x轴刻度,x轴刻度名称,y轴刻度值
* @param svgRoot {Element} SVG根节点
* @param pData {String[][]} 要绘制折线的数据
* @param pDataName {String[]} x轴刻度名称
**/
private void WriteDataLine(Element svgRoot,String[][] pData, String[] pDataName)...{
/**//* 绘制x轴信息*/
double[] xAxis = WriteXData(svgRoot, pDataName);
/**//* 获取double型数据 */
double[][] data = convertDataToDouble(pData);
double[] tempValue = ChangePlanarQueue(data);
/**//* y轴刻度值*/
WriteYData(svgRoot, tempValue);
/** *//** 绘制数据折线*/
WriteLine(svgRoot, data, xAxis);
}
/** *//**
* 将二维数组转换为一维数组
**/
private double[] ChangePlanarQueue(double[][] data)...{
double[] tempValue = new double[dataNum];
for (int i = 0; i < data.length; i++) ...{
for (int j = 0; j < data[i].length; j++) ...{
tempValue[i * data[i].length + j] = data[i][j];
}
}
return tempValue;
}
/** *//**
* 绘制x轴刻度线和x轴标识
* @param svgRoot {Element} SVG根节点
* @param pDataName {String[]} x轴刻度名称
* @return double[] x刻度的x轴坐标
**/
private double[] WriteXData(Element svgRoot, String[] pDataName)...{
double xStep = (new BigDecimal(mapWidth / (pDataName.length-1)).setScale(3, BigDecimal.ROUND_HALF_UP)).doubleValue();
double[] xAxis = new double[pDataName.length]; // 保存每个x轴刻度值的数组
xAxis[0] = zeroX; // x轴第一个刻度为y轴
//System.out.println("length:"+pDataName.length);
/** *//** x轴刻度线 */
for (int i = 1; i < pDataName.length; i++) ...{
// i=0时的y线不用绘制
//double tempX1 = i == 0 ? zeroX + i * xStep : zeroX + i * xStep + 10; //每根X轴刻度线的起始位置
double tempX1 = zeroX + i*xStep;
//System.out.println(i+":"+tempX1);
xAxis[i] = tempX1; // 保存每个x轴刻度值
Element line = svgRoot.addElement("line");
line.addAttribute("x1",Double.toString(tempX1));
line.addAttribute("y1",Integer.toString(zeroY + mapHeight));
line.addAttribute("x2",Double.toString(tempX1));
line.addAttribute("y2",Integer.toString(zeroY));
line.addAttribute("stroke-width","1");
line.addAttribute("stroke-dasharray","3 2");
line.addAttribute("stroke","#333300");
}
/** *//** 添加X轴字段说明 字无法在一行内显示的话,需要换行 */
for(int i=0; i<pDataName.length; i++)...{
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; //记录总共处理了多少字符
int font_size = 13; // 字体大小
if(m>1)...{
Element xName = svgRoot.addElement("text");
xName.addAttribute("fill","blue");
xName.addAttribute("font-size",Integer.toString(font_size));
xName.addAttribute("font-family","SimSun");
for(int j=0; j<m; j++)...{
for(int z=0; z<n; z++)...{
l = j * n + z;
if(l == pDataName[i].length())...{
break;
}
Element tspan = xName.addElement("tspan");
tspan.addAttribute("x",Double.toString(xAxis[i]));
tspan.addAttribute("y",Integer.toString(zeroY + mapHeight + font_size + j*(font_size+3)));
tspan.setText(pDataName[i].substring(l,l+1));
}
if(l == pDataName[i].length())...{
break;
}
}
}else...{
double textOffset = pDataName[i].length() * font_size / 2;
Element xName = svgRoot.addElement("text");
xName.addAttribute("x",Double.toString(xAxis[i]-textOffset));
xName.addAttribute("y",Integer.toString(zeroY + mapHeight + font_size));
xName.addAttribute("fill","blue");
xName.addAttribute("font-size",Integer.toHexString(font_size));
xName.addAttribute("font-family","SimSun");
xName.setText(pDataName[i]);
}
}
return xAxis;
}
/** *//**
* 绘制y轴刻度值
* @param svgRoot {Element} SVG根节点
* @param data {double} 要绘制折线的数据
* @return double[] y轴最大最小值
**/
private void WriteYData(Element svgRoot, double[] data)...{
double[] extr = GetExtremum(data);
int maxValue = (int)(extr[0]-extr[1]);
int valueX; //Y轴刻度值起始的X坐标
int font_size;
if (maxValue > 10000) ...{
valueX = 5;
font_size = 9;
} else ...{
valueX = 10;
font_size = 11;
}
for(int i=0; i<11; i++)...{
Element yName = svgRoot.addElement("text");
yName.addAttribute("x",Integer.toString(valueX));
yName.addAttribute("y",Integer.toString(zeroY + i * yStep));
yName.addAttribute("fill","black");
yName.addAttribute("font-family","SimSun");
yName.addAttribute("font-size",Integer.toString(font_size));
yName.setText(Integer.toString((maxValue / 10) * (10 - i) + (int)extr[1]));
}
}
/** *//**
* 绘制数据折线
* @param svgRoot {Element} SVG根节点
* @param data {double[]} 需要绘制的数据
* @param xAxis {double[]} x轴刻度坐标值
**/
private void WriteLine(Element svgRoot, double[] data, double[] xAxis, String lineColor)...{
String strPoints = "";
double[] extr = GetExtremum(data); // 获取data的最大最小值
int yValueStep = (int)(extr[0]-extr[1]);
for(int i=0; i<xAxis.length; i ++)...{
double valueSpan = data[i] - extr[1];
double ySpan = valueSpan / yValueStep * mapHeight;
strPoints += xAxis[i] +","+ (zeroY + mapHeight - ySpan) + " ";
}
Element dataLine = svgRoot.addElement("polyline");
dataLine.addAttribute("stroke-width","2");
dataLine.addAttribute("stroke",lineColor);
dataLine.addAttribute("fill","none");
dataLine.addAttribute("points", strPoints.trim());
}
/** *//**
* 绘制数据折线
* @param svgRoot {Element} SVG根节点
* @param data {double[][]} 需要绘制的数据
* @param xAxis {double[]} x轴刻度坐标值
**/
private void WriteLine(Element svgRoot, double[][] data, double[] xAxis)...{
String strPoints = "";
double[] tempValue = ChangePlanarQueue(data);
double[] extr = GetExtremum(tempValue); // 获取data的最大最小值
int yValueStep = (int)(extr[0]-extr[1]);
Element dataLine;
for(int i=0; i<data.length; i++)...{
strPoints = "";
for(int j=0; j<data[i].length; j++)...{
double valueSpan = data[i][j] - extr[1];
double ySpan = valueSpan / yValueStep * mapHeight;
strPoints += xAxis[j] +","+ (zeroY + mapHeight - ySpan) + " ";
}
dataLine = svgRoot.addElement("polyline");
dataLine.addAttribute("stroke-width","2");
dataLine.addAttribute("stroke",lineColors[i%lineColors.length]);
dataLine.addAttribute("fill","none");
dataLine.addAttribute("points", strPoints.trim());
}
}
/** *//**
* 将元数据从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();
} 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 {double[]} pData 原始数据
* @return {double[]} 最大值和最小值
* 0位保存最大值
* 1位保存最小值
**/
static double[] GetExtremum(double[] pData) ...{
double max = pData[0];
double min = pData[0];
double[] mum = new double[2];
/** *//** 获取数组中最大最小值 */
for (int i = 0; i < pData.length; i++) ...{
if (pData[i] > max)
max = pData[i];
if (pData[i] < min)
min = pData[i];
}
/** *//** 将最大最小值整数化 */
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.floor(min);
if (Math.abs(min) <= 1 && max <= 1)
min = -1;
else ...{
min = min < 100 ? Math.ceil(min / 10) * 10 : Math.ceil(min / 50) * 50;
}
mum[0] = max;
mum[1] = min;
return mum;
}
public static void main(String[] args) ...{
System.out.println("createSvgFile");
String filename = "d:/test/1.svg";
String filename2 = "d:/test/2.svg";
BackgroundMap instance = new BackgroundMap();
int expResult = 1;
String[][] data = ...{ ...{ "54", "44", "122" }, ...{ "10", "12", "-468" },
...{ "78", "520", "10" } };
String[] data1 = ...{ "54", "4424.225", "12200", "-4658" };
String[] dataName = ...{ "营业所一", "二二二二", "III" ,"adf"};
int result = instance.createSvgFile(filename,"测试实例",data1, dataName);
String[] dataName1 = ...{ "营业所一", "二二二二", "III"};
int result1 = instance.createSvgFile(filename2,"测试实例",data, dataName1);
}
}