JFreeChart
是一個開源的
JAVA
項目,它主要用來開發(fā)各種各樣的圖表,這些圖表包括:餅圖、柱狀圖
(
普通柱狀圖以及堆棧柱狀圖
)
、線圖、區(qū)域圖、分布圖、混合圖、甘特圖以及一些儀表盤等等。在這些不同式樣的圖表上可以滿足目前商業(yè)系統(tǒng)的要求。
JFreeChart
是一種基于
JAVA
語言的圖表開發(fā)技術(shù)。
JFreeChart
可用于
Servlet
、
JSP
、
Applet
、
Java Appication
環(huán)境中,通過
JDBC
可動態(tài)顯示任何數(shù)據(jù)庫數(shù)據(jù),結(jié)合
Itext
可以輸出至
PDF
文件。
JFreeChart
主要是由三個類構(gòu)成:
A
)
org.jfree.chart.servlet.ChartDeleter
繼承自
HttpSessionBindingListener
,用于實現(xiàn)當(dāng)
Session
關(guān)閉時,刪除臨時目中的圖象文件。
B
)
org.jfree.chart.servlet.DisplayChart
繼承自
Httpservlet
用于處理顯示圖象。
C
)
org.jfree.chart.servlet.ServletUtilities
有一系列方法,例如,
saveChartAs*;saveChartAs*
是把圖表按照不同的形式存儲為圖象;
sendTempFile
方法被重載了很多次,用于把文件流發(fā)送
response
。
下面以柱狀圖和餅圖為例,介紹圖形創(chuàng)建方法。
1 柱狀圖
org.jfree.chart.ChartFactory
這個工廠類有
createBarChart
、
createStackedBarChart
、
createBarChart3D
、
createStackedBarChart3D
,這幾個工廠方法創(chuàng)建不同類型的柱狀圖,比較重要的是
PlotOrientation.VERTICAL
讓平行柱垂直顯示,而
PlotOrientation.HORIZONTAL
則讓平行柱水平顯示。對柱狀圖影響較大的幾個類包括:
org.jfree.chart.axis.CategoryAxis
、
org.jfree.chart.axis.ValueAxis
、
org.jfree.chart.renderer.BarRenderer
、
org.jfree.chart.renderer. BarRenderer3D
。
具體實現(xiàn)步驟:
1
)創(chuàng)建用于圖形生成所要的數(shù)據(jù)集對象。
CategoryDataset dataset = DatasetUtilities.createCategoryDataset(rowKeys, columnKeys, data)
;
其中:
rowKeys
表示
X
軸數(shù)據(jù),
columnKeys
表示
Y
軸數(shù)據(jù),
data
表示填充柱狀圖所要的實際數(shù)據(jù)(來自于數(shù)據(jù)庫)。
2
)創(chuàng)建圖形對象。
JFreeChart chart = ChartFactory.createBarChart3D("
標(biāo)題
",?null
,
null
,
dataset
,
PlotOrientation.VERTICAL,
,
true
,
false
,
false)
;
createBarChart3D
方法是
ChartFactory
工廠類里的一個方法,用于
3D
柱狀圖的生成,該類繼承自
JFreeChart
。其中的八個參數(shù)分別代表:圖形的標(biāo)題、
X
軸標(biāo)題、
Y
軸標(biāo)題、
dataset
就是
CategoryDataset
類的實例對象、顯示標(biāo)題、啟用熱鍵、啟用超鍵接。
3
)設(shè)置圖形顯示的屬性。
a ) ValueAxis
類,設(shè)置柱到圖上下邊的距離。實現(xiàn)方法是:
ValueAxis rangeAxis = plot.getRangeAxis();
設(shè)置最高的一個柱與圖片頂端的距離:
rangeAxis.setUpperMargin(0.15)
設(shè)置最低的一個柱與圖片底端的距離:
rangeAxis.setLowerMargin(0.15)
b
)
org.jfree.chart.renderer.BarRenderer3D
類,設(shè)置圖形上顯示的數(shù)值。實現(xiàn)方法如下:
BarRenderer3D renderer = new BarRenderer3D();
renderer.setBaseOutlinePaint(Color.BLACK);
設(shè)置
Wall
的顏色:
renderer.setWallPaint(Color.gray);
設(shè)置每個柱的顏色:
renderer.setSeriesPaint(0, new Color(0, 0, 255));
renderer.setSeriesPaint(1, new Color(0, 100, 255));
renderer.setSeriesPaint(2, Color.GREEN);
設(shè)置每個柱的
Outline
顏色
renderer.setSeriesOutlinePaint(0, Color.BLACK);
renderer.setSeriesOutlinePaint(1, Color.BLACK);
renderer.setSeriesOutlinePaint(2, Color.BLACK);
設(shè)置每個地區(qū)所包含的平行柱之間的距離
renderer.setItemMargin(0.1);
顯示每個柱的數(shù)值,并修改該數(shù)值的字體屬性
renderer.setItemLabelGenerator(new StandardCategoryItemLabelGenerator());
renderer.setItemLabelFont(new Font("
黑體
",Font.PLAIN,12));
renderer.setItemLabelsVisible(true);
為圖形加入超連接
renderer.setItemURLGenerator(new StandardCategoryURLGenerator());
renderer.setToolTipGenerator(new StandardCategoryToolTipGenerator());
2 餅圖
org.jfree.chart.plot
包,包含創(chuàng)建餅形圖的所有方法和屬性。
筆者根據(jù)業(yè)務(wù)需求創(chuàng)建了
setURLGenerator(PieURLGenerator generator)
方法,在圖片上建立連接,就是圖片不同部分連接不同的資源。
setSectionLabelType(int type)
方法:
指定
section
標(biāo)簽的類型,共有
7
種類型。如果不指定,默認(rèn)是
NAME_LABELS
,其中類型分別是:
PiePlot.NO_LABELS
PiePlot.NAME_LABELS
PiePlot.VALUE_LABELS
PiePlot.PERCENT_LABELS
、
PiePlot.NAME_AND_VALUE_LABELS
、
PiePlot. NAME_AND_PERCENT_LABELS
、
PiePlot.VALUE_AND_PERCENT_LABELS
。
setDefaultOutlinePaint(java.awt.Paint paint)
方法,指定
section
輪廓線的顏色,如果不指定,默認(rèn)值為
NULL
。
setDefaultOutlineStroke(java.awt.Stroke stroke)
方法,指定
section
輪廓線的厚度。
setRadius(double percent)
和
setExplodePercent(int section, double percent)
方法,抽離
section
,就是把某一
section
從餅形圖剝離出來,需要兩個方法一起使用。
setStartAngle(double angle)
方法,設(shè)置第一個
section
開始位置,默認(rèn)從
12
點鐘方向開始。
setPaint(int section, java.awt.Paint paint)
方法指定
section
的顏色。
setDirection(int direction)
方法指定
section
順序,默認(rèn)是順時針方向。順時針:
PiePlot.CLOCKWISE
;逆時針:
PiePlot.ANTICLOCKWISE
。
具體實現(xiàn)步驟:
1
)創(chuàng)建用于圖形生成所要的數(shù)據(jù)集對象。
首先實例化類
DefaultPieDataset dataset = new DefaultPieDataset()
。然后利用
DefaultPieDataset
類提供的
setValue
(
value1,value2
)方法,把從數(shù)據(jù)庫里提取的數(shù)據(jù)存入
DefaultPieDataset
對象。其中
value1
是數(shù)據(jù)名稱、
value2
是數(shù)據(jù)值。
2
)創(chuàng)建圖形對象。
首先實例化JFreeChart chart = ChartFactory.createPieChart3D(title, dataset, true, true, false)createPieChart3D方法是用于餅圖生成的主要方法。其中title代表圖形的標(biāo)題、dataset就是DefaultPieDataset對象的實例。
3
)設(shè)置圖形顯示的屬性。
String filename = ServletUtilities.saveChartAsPNG(jFreeChart, 700, 450, info, session);
ChartUtilities.writeImageMap(pw, filename, info);
pw.flush()
saveChartAsPNG
方法在
ServletUtilities
工廠類定義完成。主要用于把圖形對象
JFreeChart
以圖片的形式保存。其中的
jFreeChart
就是
JFreeChart
對象的實例。該方法返回一個文件名。
writeImageMap
(
pw, filename, info
)方法用于把保存的圖片文件以字節(jié)流的形式寫入用戶界面。
其中pw是java.io包的PrintWriter類的實例對象,該對象創(chuàng)建一個圖形輸出流。Filename是輸出圖片的文件名。該文件名來自ServletUtilities.saveChartAsPNG方法創(chuàng)建。
參數(shù)info用于圖形信息的顯示。
用ChartRenderingInfo info=new ChartRenderingInfo(new StandardEntityCollection())創(chuàng)建。
最后輸出完成圖形,調(diào)用pw.flush()方法關(guān)閉IO流。
------------------------------------------------------------------------------------------
使用JFreeChart生成各種樣式的圖表
限于篇幅的問題我們在這里只實現(xiàn)兩種常用的圖表,其他類型圖表讀者可以觸類旁通。我們先給出柱狀圖的實現(xiàn),餅圖的實現(xiàn)再來跟柱狀圖進(jìn)行比較。
1 柱狀圖
package lius.chart.demo;
import java.io.*;
import org.jfree.data.*; import org.jfree.chart.*; import org.jfree.chart.plot.*; /** * 該類用于演示最簡單的柱狀圖生成 * @author Winter Lau */ public class BarChartDemo {
public static void main(String[] args) throws IOException{
CategoryDataset dataset = getDataSet2(); JFreeChart chart = ChartFactory.createBarChart3D( "水果產(chǎn)量圖", // 圖表標(biāo)題 "水果", // 目錄軸的顯示標(biāo)簽 "產(chǎn)量", // 數(shù)值軸的顯示標(biāo)簽 dataset, // 數(shù)據(jù)集 PlotOrientation.VERTICAL, // 圖表方向:水平、垂直 true, // 是否顯示圖例(對于簡單的柱狀圖必須是false) false, // 是否生成工具 false // 是否生成URL鏈接 );
FileOutputStream fos_jpg = null; try { fos_jpg = new FileOutputStream("D:\\fruit.jpg"); ChartUtilities.writeChartAsJPEG(fos_jpg,100,chart,400,300,null); } finally { try { fos_jpg.close(); } catch (Exception e) {} } } /** * 獲取一個演示用的簡單數(shù)據(jù)集對象 * @return */ private static CategoryDataset getDataSet() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(100, null, "蘋果"); dataset.addValue(200, null, "梨子"); dataset.addValue(300, null, "葡萄"); dataset.addValue(400, null, "香蕉"); dataset.addValue(500, null, "荔枝"); return dataset; } /** * 獲取一個演示用的組合數(shù)據(jù)集對象 * @return */ private static CategoryDataset getDataSet2() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(100, "北京", "蘋果"); dataset.addValue(100, "上海", "蘋果"); dataset.addValue(100, "廣州", "蘋果"); dataset.addValue(200, "北京", "梨子"); dataset.addValue(200, "上海", "梨子"); dataset.addValue(200, "廣州", "梨子"); dataset.addValue(300, "北京", "葡萄"); dataset.addValue(300, "上海", "葡萄"); dataset.addValue(300, "廣州", "葡萄"); dataset.addValue(400, "北京", "香蕉"); dataset.addValue(400, "上海", "香蕉"); dataset.addValue(400, "廣州", "香蕉"); dataset.addValue(500, "北京", "荔枝"); dataset.addValue(500, "上海", "荔枝"); dataset.addValue(500, "廣州", "荔枝"); return dataset; } }
|
程序運行結(jié)束后生成的圖片文件效果如下圖所示:
圖4
如果是使用簡單的數(shù)據(jù)即使用getDataSet方法獲取數(shù)據(jù)集時產(chǎn)生的圖片文件如下:
圖5
2 餅圖
對于餅圖而言,數(shù)據(jù)集的獲取用的不是同一個數(shù)據(jù)集類,另外餅圖不支持同一個類別的項目中還有子項目這樣的數(shù)據(jù)。我們只給出創(chuàng)建餅圖的代碼,至于寫圖表到一個文件則與柱狀圖一致,無需重復(fù)。
package lius.chart.demo;
import java.io.*;
import org.jfree.data.*; import org.jfree.chart.*; /** * 用于演示餅圖的生成 * @author Winter Lau */ public class PieChartDemo {
public static void main(String[] args) throws IOException{ DefaultPieDataset data = getDataSet(); JFreeChart chart = ChartFactory.createPie3DChart("水果產(chǎn)量圖", // 圖表標(biāo)題 data, true, // 是否顯示圖例 false, false ); //寫圖表對象到文件,參照柱狀圖生成源碼 } /** * 獲取一個演示用的簡單數(shù)據(jù)集對象 * @return */ private static DefaultPieDataset getDataSet() { DefaultPieDataset dataset = new DefaultPieDataset(); dataset.setValue("蘋果",100); dataset.setValue("梨子",200); dataset.setValue("葡萄",300); dataset.setValue("香蕉",400); dataset.setValue("荔枝",500); return dataset; } }
|
生成的餅圖文件效果如下:
圖6
將生成的圖表移到瀏覽器上
為了將生成的圖表直接傳給客戶端瀏覽器,只需要將前面兩個例子中的文件流換成是通過HttpServletResponse對象獲取到的輸出流,詳細(xì)代碼清單如下:
package lius.chart.demo;
import java.io.IOException; import javax.servlet.*; import javax.servlet.http.HttpServlet;
import org.jfree.data.*; import org.jfree.chart.*; /** * 演示通過servlet直接輸出圖表 * @author Winter Lau */ public class ChartDemoServlet extends HttpServlet {
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { res.setContentType("image/jpeg"); DefaultPieDataset data = getDataSet(); JFreeChart chart = ChartFactory.createPie3DChart("水果產(chǎn)量圖", data, true, false, false );
ChartUtilities.writeChartAsJPEG(res.getOutputStream(), 100,chart,400,300,null); } /** * 獲取一個演示用的簡單數(shù)據(jù)集對象 * @return */ private static DefaultPieDataset getDataSet() { DefaultPieDataset dataset = new DefaultPieDataset(); dataset.setValue("蘋果",100); dataset.setValue("梨子",200); dataset.setValue("葡萄",300); dataset.setValue("香蕉",400); dataset.setValue("荔枝",500); return dataset; } }
|
高級主題
很多情況我們不僅僅要求可以在瀏覽器上顯示一個圖表,我們更需要客戶可以直接在圖表上做一下交互的操作,例如獲取信息提示,點擊圖表某個部分進(jìn)行更詳細(xì)信 息的展示等等。例如前面生成的簡單柱狀圖,用戶需要在看到柱狀圖后點擊某種水果例如是蘋果即可看到各個地區(qū)蘋果產(chǎn)量的情況。為此就要求該圖形具有交互操作 的功能。在HTML中為了讓一個圖像具有可交互的功能就必須給該圖像定義一個Map對象。下表節(jié)選一段具有該功能的HTML代碼
<MAP NAME="chartMap"> <AREA SHAPE="RECT" COORDS="81,15,126,254" href="?series=0&category=100" title="100 = 7,048" onclick="javascript:clickChart('100');return false;"> <AREA SHAPE="RECT" COORDS="143,27,188,255" href="?series=0&category=200" title="200 = 6,721" onclick="javascript: clickChart ('200');return false;"> <AREA SHAPE="RECT" COORDS="205,54,250,255" href="?series=0&category=300" title="300 = 5,929" onclick="javascript: clickChart ('300');return false;"> <AREA SHAPE="RECT" COORDS="267,85,312,255" href="?series=0&category=400" title="400 = 5,005" onclick="javascript: clickChart ('400');return false;"> <AREA SHAPE="RECT" COORDS="329,17,374,255" href="?series=0&category=Diet" title="Diet = 7,017" onclick="javascript: clickChart ('Diet');return false;"> </MAP>
|
由此就產(chǎn)生了一個問題:如果 根據(jù)一個圖像來生成對應(yīng)的MAP對象。我們回頭看看剛才的代碼,在創(chuàng)建一個圖表對象時候有兩個參數(shù),我們舉柱狀圖的例子來講這兩個參數(shù)就是 ChartFactory. createBarChart3D方法中的最后兩個參數(shù),這兩個參數(shù)的類型都是布爾值。這兩個參數(shù)意思分別是:是否創(chuàng)建工具提示(tooltip)以及是 否生成URL。這兩個參數(shù)分別對應(yīng)著MAP中一個AREA的title屬性以及href屬性。
可是我想知道的是怎么來產(chǎn)生這個MAP啊!哈哈,不要著急,JFreeChart已經(jīng)幫我們做好生成MAP對象的功 能。為了生成MAP對象就要引入另外一個對象:ChartRenderingInfo。因為JFreeChart沒有直接的方法利用一個圖表對象直接生成 MAP數(shù)據(jù),它需要一個中間對象來過渡,這個對象就是ChartRenderingInfo。下圖是生成MAP數(shù)據(jù)的流程圖:
圖7
如 上圖所示,ChartUtilities類是整個流程的核心,它周圍的對象都是一些例如數(shù)據(jù)對象或者是文件等。這個流程簡單描述如下:首先創(chuàng)建一個 ChartRenderingInfo對象并在調(diào)用ChartUtilities的writeChartAsJPEG時作為最后一個參數(shù)傳遞進(jìn)去。調(diào)用該 方法結(jié)束后將產(chǎn)生一個圖像文件以及一個填充好MAP數(shù)據(jù)的ChartRenderingInfo對象,有了這個對象我們還是沒有辦法獲取具體的MAP數(shù) 據(jù),我們還必須借助于ChartUtilities的writeImageMap方法來將ChartRenderingInfo對象讀取出來,獲取MAP 數(shù)據(jù)的代碼片斷如下:
PrintWriter w = null; FileOutputStream fos_jpg = null; FileOutputStream fos_cri = null; try{ //根據(jù)不同類型的圖表使用不同類,以下是針對餅圖的操作 PiePlot plot = (PiePlot) chart.getPlot(); plot.setURLGenerator(new StandardPieURLGenerator(url)); //設(shè)置工具提示 plot.setToolTipGenerator(new StandardPieToolTipGenerator()); fos_jpg = new FileOutputStream(“d:\\fruit.jpg”); ChartUtilities.writeChartAsJPEG( fos_jpg, 100, chart, 400, 300, info); fos_cri = new FileOutputStream(__d:\\fruit.map__); w = new PrintWriter(fos_cri); ChartUtilities.writeImageMap(w, __mapname__, info); w.flush(); }finally{ try{ w.close(); }catch(Exception e){} try{ fos_cri.close(); }catch(Exception e){} try{ fos_jpg.close(); }catch(Exception e){} }
|
打開文件D:\ fruit.map,文件的內(nèi)容就是要寫到頁面上的MAP數(shù)據(jù)。把生成的圖像文件以及MAP數(shù)據(jù)文件寫到頁面上即可完成熱點圖表的功能。至于怎么結(jié)合兩者 之間的關(guān)系例如圖像的useMap屬性值必須與MAP對象的名稱結(jié)合起來,必須根據(jù)實際的應(yīng)用情況進(jìn)行相應(yīng)的處理。筆者建議把二者通過標(biāo)簽庫封裝起來,圖 像文件的名稱以及MAP對象的名稱由標(biāo)簽庫統(tǒng)一進(jìn)行控制,這樣可以保證二者的一致性。