JFreeChart
是一個開源的
JAVA
項目,它主要用來開發各種各樣的圖表,這些圖表包括:餅圖、柱狀圖
(
普通柱狀圖以及堆棧柱狀圖
)
、線圖、區域圖、分布圖、混合圖、甘特圖以及一些儀表盤等等。在這些不同式樣的圖表上可以滿足目前商業系統的要求。
JFreeChart
是一種基于
JAVA
語言的圖表開發技術。
JFreeChart
可用于
Servlet
、
JSP
、
Applet
、
Java Appication
環境中,通過
JDBC
可動態顯示任何數據庫數據,結合
Itext
可以輸出至
PDF
文件。
JFreeChart
主要是由三個類構成:
A
)
org.jfree.chart.servlet.ChartDeleter
繼承自
HttpSessionBindingListener
,用于實現當
Session
關閉時,刪除臨時目中的圖象文件。
B
)
org.jfree.chart.servlet.DisplayChart
繼承自
Httpservlet
用于處理顯示圖象。
C
)
org.jfree.chart.servlet.ServletUtilities
有一系列方法,例如,
saveChartAs*;saveChartAs*
是把圖表按照不同的形式存儲為圖象;
sendTempFile
方法被重載了很多次,用于把文件流發送
response
。
下面以柱狀圖和餅圖為例,介紹圖形創建方法。
1 柱狀圖
org.jfree.chart.ChartFactory
這個工廠類有
createBarChart
、
createStackedBarChart
、
createBarChart3D
、
createStackedBarChart3D
,這幾個工廠方法創建不同類型的柱狀圖,比較重要的是
PlotOrientation.VERTICAL
讓平行柱垂直顯示,而
PlotOrientation.HORIZONTAL
則讓平行柱水平顯示。對柱狀圖影響較大的幾個類包括:
org.jfree.chart.axis.CategoryAxis
、
org.jfree.chart.axis.ValueAxis
、
org.jfree.chart.renderer.BarRenderer
、
org.jfree.chart.renderer. BarRenderer3D
。
具體實現步驟:
1
)創建用于圖形生成所要的數據集對象。
CategoryDataset dataset = DatasetUtilities.createCategoryDataset(rowKeys, columnKeys, data)
;
其中:
rowKeys
表示
X
軸數據,
columnKeys
表示
Y
軸數據,
data
表示填充柱狀圖所要的實際數據(來自于數據庫)。
2
)創建圖形對象。
JFreeChart chart = ChartFactory.createBarChart3D("
標題
",?null
,
null
,
dataset
,
PlotOrientation.VERTICAL,
,
true
,
false
,
false)
;
createBarChart3D
方法是
ChartFactory
工廠類里的一個方法,用于
3D
柱狀圖的生成,該類繼承自
JFreeChart
。其中的八個參數分別代表:圖形的標題、
X
軸標題、
Y
軸標題、
dataset
就是
CategoryDataset
類的實例對象、顯示標題、啟用熱鍵、啟用超鍵接。
3
)設置圖形顯示的屬性。
a ) ValueAxis
類,設置柱到圖上下邊的距離。實現方法是:
ValueAxis rangeAxis = plot.getRangeAxis();
設置最高的一個柱與圖片頂端的距離:
rangeAxis.setUpperMargin(0.15)
設置最低的一個柱與圖片底端的距離:
rangeAxis.setLowerMargin(0.15)
b
)
org.jfree.chart.renderer.BarRenderer3D
類,設置圖形上顯示的數值。實現方法如下:
BarRenderer3D renderer = new BarRenderer3D();
renderer.setBaseOutlinePaint(Color.BLACK);
設置
Wall
的顏色:
renderer.setWallPaint(Color.gray);
設置每個柱的顏色:
renderer.setSeriesPaint(0, new Color(0, 0, 255));
renderer.setSeriesPaint(1, new Color(0, 100, 255));
renderer.setSeriesPaint(2, Color.GREEN);
設置每個柱的
Outline
顏色
renderer.setSeriesOutlinePaint(0, Color.BLACK);
renderer.setSeriesOutlinePaint(1, Color.BLACK);
renderer.setSeriesOutlinePaint(2, Color.BLACK);
設置每個地區所包含的平行柱之間的距離
renderer.setItemMargin(0.1);
顯示每個柱的數值,并修改該數值的字體屬性
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
包,包含創建餅形圖的所有方法和屬性。
筆者根據業務需求創建了
setURLGenerator(PieURLGenerator generator)
方法,在圖片上建立連接,就是圖片不同部分連接不同的資源。
setSectionLabelType(int type)
方法:
指定
section
標簽的類型,共有
7
種類型。如果不指定,默認是
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
輪廓線的顏色,如果不指定,默認值為
NULL
。
setDefaultOutlineStroke(java.awt.Stroke stroke)
方法,指定
section
輪廓線的厚度。
setRadius(double percent)
和
setExplodePercent(int section, double percent)
方法,抽離
section
,就是把某一
section
從餅形圖剝離出來,需要兩個方法一起使用。
setStartAngle(double angle)
方法,設置第一個
section
開始位置,默認從
12
點鐘方向開始。
setPaint(int section, java.awt.Paint paint)
方法指定
section
的顏色。
setDirection(int direction)
方法指定
section
順序,默認是順時針方向。順時針:
PiePlot.CLOCKWISE
;逆時針:
PiePlot.ANTICLOCKWISE
。
具體實現步驟:
1
)創建用于圖形生成所要的數據集對象。
首先實例化類
DefaultPieDataset dataset = new DefaultPieDataset()
。然后利用
DefaultPieDataset
類提供的
setValue
(
value1,value2
)方法,把從數據庫里提取的數據存入
DefaultPieDataset
對象。其中
value1
是數據名稱、
value2
是數據值。
2
)創建圖形對象。
首先實例化JFreeChart chart = ChartFactory.createPieChart3D(title, dataset, true, true, false)createPieChart3D方法是用于餅圖生成的主要方法。其中title代表圖形的標題、dataset就是DefaultPieDataset對象的實例。
3
)設置圖形顯示的屬性。
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
)方法用于把保存的圖片文件以字節流的形式寫入用戶界面。
其中pw是java.io包的PrintWriter類的實例對象,該對象創建一個圖形輸出流。Filename是輸出圖片的文件名。該文件名來自ServletUtilities.saveChartAsPNG方法創建。
參數info用于圖形信息的顯示。
用ChartRenderingInfo info=new ChartRenderingInfo(new StandardEntityCollection())創建。
最后輸出完成圖形,調用pw.flush()方法關閉IO流。
------------------------------------------------------------------------------------------
使用JFreeChart生成各種樣式的圖表
限于篇幅的問題我們在這里只實現兩種常用的圖表,其他類型圖表讀者可以觸類旁通。我們先給出柱狀圖的實現,餅圖的實現再來跟柱狀圖進行比較。
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( "水果產量圖", // 圖表標題 "水果", // 目錄軸的顯示標簽 "產量", // 數值軸的顯示標簽 dataset, // 數據集 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) {} } } /** * 獲取一個演示用的簡單數據集對象 * @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; } /** * 獲取一個演示用的組合數據集對象 * @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; } }
|
程序運行結束后生成的圖片文件效果如下圖所示:
圖4
如果是使用簡單的數據即使用getDataSet方法獲取數據集時產生的圖片文件如下:
圖5
2 餅圖
對于餅圖而言,數據集的獲取用的不是同一個數據集類,另外餅圖不支持同一個類別的項目中還有子項目這樣的數據。我們只給出創建餅圖的代碼,至于寫圖表到一個文件則與柱狀圖一致,無需重復。
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("水果產量圖", // 圖表標題 data, true, // 是否顯示圖例 false, false ); //寫圖表對象到文件,參照柱狀圖生成源碼 } /** * 獲取一個演示用的簡單數據集對象 * @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對象獲取到的輸出流,詳細代碼清單如下:
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("水果產量圖", data, true, false, false );
ChartUtilities.writeChartAsJPEG(res.getOutputStream(), 100,chart,400,300,null); } /** * 獲取一個演示用的簡單數據集對象 * @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; } }
|
高級主題
很多情況我們不僅僅要求可以在瀏覽器上顯示一個圖表,我們更需要客戶可以直接在圖表上做一下交互的操作,例如獲取信息提示,點擊圖表某個部分進行更詳細信 息的展示等等。例如前面生成的簡單柱狀圖,用戶需要在看到柱狀圖后點擊某種水果例如是蘋果即可看到各個地區蘋果產量的情況。為此就要求該圖形具有交互操作 的功能。在HTML中為了讓一個圖像具有可交互的功能就必須給該圖像定義一個Map對象。下表節選一段具有該功能的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>
|
由此就產生了一個問題:如果 根據一個圖像來生成對應的MAP對象。我們回頭看看剛才的代碼,在創建一個圖表對象時候有兩個參數,我們舉柱狀圖的例子來講這兩個參數就是 ChartFactory. createBarChart3D方法中的最后兩個參數,這兩個參數的類型都是布爾值。這兩個參數意思分別是:是否創建工具提示(tooltip)以及是 否生成URL。這兩個參數分別對應著MAP中一個AREA的title屬性以及href屬性。
可是我想知道的是怎么來產生這個MAP啊!哈哈,不要著急,JFreeChart已經幫我們做好生成MAP對象的功 能。為了生成MAP對象就要引入另外一個對象:ChartRenderingInfo。因為JFreeChart沒有直接的方法利用一個圖表對象直接生成 MAP數據,它需要一個中間對象來過渡,這個對象就是ChartRenderingInfo。下圖是生成MAP數據的流程圖:
圖7
如 上圖所示,ChartUtilities類是整個流程的核心,它周圍的對象都是一些例如數據對象或者是文件等。這個流程簡單描述如下:首先創建一個 ChartRenderingInfo對象并在調用ChartUtilities的writeChartAsJPEG時作為最后一個參數傳遞進去。調用該 方法結束后將產生一個圖像文件以及一個填充好MAP數據的ChartRenderingInfo對象,有了這個對象我們還是沒有辦法獲取具體的MAP數 據,我們還必須借助于ChartUtilities的writeImageMap方法來將ChartRenderingInfo對象讀取出來,獲取MAP 數據的代碼片斷如下:
PrintWriter w = null; FileOutputStream fos_jpg = null; FileOutputStream fos_cri = null; try{ //根據不同類型的圖表使用不同類,以下是針對餅圖的操作 PiePlot plot = (PiePlot) chart.getPlot(); plot.setURLGenerator(new StandardPieURLGenerator(url)); //設置工具提示 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,文件的內容就是要寫到頁面上的MAP數據。把生成的圖像文件以及MAP數據文件寫到頁面上即可完成熱點圖表的功能。至于怎么結合兩者 之間的關系例如圖像的useMap屬性值必須與MAP對象的名稱結合起來,必須根據實際的應用情況進行相應的處理。筆者建議把二者通過標簽庫封裝起來,圖 像文件的名稱以及MAP對象的名稱由標簽庫統一進行控制,這樣可以保證二者的一致性。