Java生成svg曲线图

         参照“super的小歇”写的《JAVA生成SVG图表实例之柱状图 1.1》http://blog.csdn.net/turing_gu/archive/2004/09/03/94078.aspx,自己动手用Java写了一个类似的生成曲线图的代码。
        如何根据数组中的数据来确认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);
    }

}

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值