最近作的一個在線氣象觀測網站要實現一個需求:使用圖表展現最近五天溫溼度等氣象要素的曲線變化javascript
具體效果參考:http://www.weatherobserve.com/showInfoIndex.jsphtml
圖示以下(2016-5-25日的數據):java
下面就詳述一下實現過程吧(注:相較於原網頁我隱去了不少內容,本實現過程就只專一於Echarts圖表實現)jquery
一:web
HTML頁面部分,代碼以下:ajax
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html lang="zh-CN"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- 在IE運行最新的渲染模式 --> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 初始化移動瀏覽顯示 --> <meta name="Author" content="Dreamer-1."> <script type="text/javascript" src="js/jquery-1.12.3.min.js"></script> <script type="text/javascript" src="js/echarts.common.min.js"></script> <title>- 觀測數據 -</title> </head> <body> <!-- 顯示Echarts圖表 --> <div style="height:410px;min-height:100px;margin:0 auto;" id="main"></div> <script type="text/javascript"> // 基於準備好的dom,初始化echarts實例 var myChart = echarts.init(document.getElementById('main')); // 指定圖表的配置項和數據 var option = { title: { //圖表標題 text: '過去五天數據圖表' }, tooltip: { trigger: 'axis', //座標軸觸發提示框,多用於柱狀、折線圖中 /* 控制提示框內容輸出格式 formatter: '{b0}<br/><font color=#FF3333> ● </font>{a0} : {c0} ℃ ' + '<br/><font color=#53FF53>● </font>{a1} : {c1} % ' + '<br/><font color=#68CFE8> ● </font>{a3} : {c3} mm ' + '<br/><font color=#FFDC35> ● </font>{a4} : {c4} m/s ' + '<br/><font color=#B15BFF> ● </font>{a2} : {c2} hPa ' */ }, dataZoom: [ { type: 'slider', //支持鼠標滾輪縮放 start: 0, //默認數據初始縮放範圍爲10%到90% end: 100 }, { type: 'inside', //支持單獨的滑動條縮放 start: 0, //默認數據初始縮放範圍爲10%到90% end: 100 } ], legend: { //圖表上方的類別顯示 show:true, data:['溫度(℃)','溼度(%)','雨量(mm)','風速(m/s)','壓強(hPa)'] }, color:[ '#FF3333', //溫度曲線顏色 '#53FF53', //溼度曲線顏色 '#B15BFF', //壓強圖顏色 '#68CFE8', //雨量圖顏色 '#FFDC35' //風速曲線顏色 ], toolbox: { //工具欄顯示 show: true, feature: { saveAsImage: {} //顯示「另存爲圖片」工具 } }, xAxis: { //X軸 type : 'category', data : [] //先設置數據值爲空,後面用Ajax獲取動態數據填入 }, yAxis : [ //Y軸(這裏我設置了兩個Y軸,左右各一個) { //第一個(左邊)Y軸,yAxisIndex爲0 type : 'value', name : '溫度', /* max: 120, min: -40, */ axisLabel : { formatter: '{value} ℃' //控制輸出格式 } }, { //第二個(右邊)Y軸,yAxisIndex爲1 type : 'value', name : '壓強', scale: true, axisLabel : { formatter: '{value} hPa' } } ], series : [ //系列(內容)列表 { name:'溫度(℃)', type:'line', //折線圖表示(生成溫度曲線) symbol:'emptycircle', //設置折線圖中表示每一個座標點的符號;emptycircle:空心圓;emptyrect:空心矩形;circle:實心圓;emptydiamond:菱形 data:[] //數據值經過Ajax動態獲取 }, { name:'溼度(%)', type:'line', symbol:'emptyrect', data:[] }, { name:'壓強(hPa)', type:'line', symbol:'circle', //標識符號爲實心圓 yAxisIndex: 1, //與第二y軸有關 data:[] }, { name:'雨量(mm)', type:'bar', //柱狀圖表示 //barMinHeight: 10, //柱條最小高度,可用於防止某數據項的值太小而影響交互 /* label: { //顯示值 normal: { show: true, position: 'top' } }, */ data:[] }, { name:'風速(m/s)', type:'line', symbol:'emptydiamond', data:[] } ] }; myChart.showLoading(); //數據加載完以前先顯示一段簡單的loading動畫 var tems=[]; //溫度數組(存放服務器返回的全部溫度值) var hums=[]; //溼度數組 var pas=[]; //壓強數組 var rains=[]; //雨量數組 var win_sps=[]; //風速數組 var dates=[]; //時間數組 $.ajax({ //使用JQuery內置的Ajax方法 type : "post", //post請求方式 async : true, //異步請求(同步請求將會鎖住瀏覽器,用戶其餘操做必須等待請求完成才能夠執行) url : "ShowInfoIndexServlet", //請求發送到ShowInfoIndexServlet處 data : {name:"A0001"}, //請求內包含一個key爲name,value爲A0001的參數;服務器接收到客戶端請求時經過request.getParameter方法獲取該參數值 dataType : "json", //返回數據形式爲json success : function(result) { //請求成功時執行該函數內容,result即爲服務器返回的json對象 if (result != null && result.length > 0) { for(var i=0;i<result.length;i++){ tems.push(result[i].tem); //挨個取出溫度、溼度、壓強等值並填入前面聲明的溫度、溼度、壓強等數組 hums.push(result[i].hum); pas.push(result[i].pa); rains.push(result[i].rain); win_sps.push(result[i].win_sp); dates.push(result[i].dateStr); } myChart.hideLoading(); //隱藏加載動畫 myChart.setOption({ //載入數據 xAxis: { data: dates //填入X軸數據 }, series: [ //填入系列(內容)數據 { // 根據名字對應到相應的系列 name: '溫度', data: tems }, { name: '溼度', data: hums }, { name: '壓強', data: pas }, { name: '雨量', data: rains }, { name: '風速', data: win_sps } ] }); } else { //返回的數據爲空時顯示提示信息 alert("圖表請求數據爲空,可能服務器暫未錄入近五天的觀測數據,您能夠稍後再試!"); myChart.hideLoading(); } }, error : function(errorMsg) { //請求失敗時執行該函數 alert("圖表請求數據失敗,多是服務器開小差了"); myChart.hideLoading(); } }) myChart.setOption(option); //載入圖表 </script> </body> </html>
二:sql
Servlet部分,客戶端請求提交到 ShowInfoIndex 處,先在 web.xml 裏配置一下Servlet映射:數據庫
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>BlogExample</display-name> <servlet> <servlet-name>ShowInfoIndexServlet</servlet-name> <servlet-class>EchartsExample.ShowInfoIndexServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ShowInfoIndexServlet</servlet-name> <url-pattern>/ShowInfoIndexServlet</url-pattern> </servlet-mapping> </web-app>
關於ShowInfoIndexServlet,簡單說一下請求-響應中間的過程:apache
客戶端發送圖表請求給Servlet,Servlet接收到請求後先獲取客戶端請求查看的氣象站名稱,而後從數據庫(SqlServer2005 Express版)中獲取最近五天內該氣象站全部的採集數據(裝在List中),再用Gson工具將該List轉換成Json對象返回給客戶端,客戶端接收到返回的Json對象後對其進行解析並將相應數據填入Echarts中,而後做顯示;json
其中Record.java是隻對外提供get/set方法的用於封裝數據的普通實體類,DBUtil.java是JDBC方式下專門提供Connection、Statement、ResultSet等的數據庫工具類。
(本來的鏈接數據庫並獲取數據過程需通過業務邏輯層與數據訪問層,較爲複雜,這裏隱去這兩層,直接在Servlet內鏈接數據庫並拿取數據)
Ps:牆裂建議使用PreparedStatement進行參數化查詢,這樣能夠有效避免SQL注入!
ShowInfoIndexServlet代碼以下:
其中用到了Google的Gson工具包(提供java-json對象間的轉換,下載後放在 WRB-INF/lib 目錄下便可)
下載地址:http://mvnrepository.com/artifact/com.google.code.gson/gson
package EchartsExample; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.text.SimpleDateFormat; import java.util.*; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.gson.Gson; /** * 響應觀測記錄展現頁的Echarts圖表數據請求(使用json格式返回客戶端須要的數據) * @author zhong * */ public class ShowInfoIndexServlet extends HttpServlet { /** * */ private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); //設定客戶端提交給servlet的內容按UTF-8編碼 response.setCharacterEncoding("UTF-8"); //設定servlet傳回給客戶端的內容按UTF-8編碼 response.setContentType("text/html;charset=UTF-8"); //告知瀏覽器用UTF-8格式解析內容 String name = request.getParameter("name"); //獲取臺站名參數 //獲取當天在內的五天之前的0點格式字符串(用於數據庫查詢) Calendar cal = Calendar.getInstance(); cal.add(Calendar.DATE, -4); //獲取當天在內的五天之前的日期時間 SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd 00:00:00"); //設定日期格式 String fiveDaysAgoStr = sdf1.format(cal.getTime()); //將五天前的日期時間按指定格式轉換成字符串 //獲取當前時間並將其轉換成指定格式的字符串(用於數據庫查詢) Date now = new Date(); SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String nowStr = sdf2.format(now); //System.out.println(nowStr); //======================================鏈接數據庫操做============================================================================================ /* * 鏈接數據庫並獲取五天內該名稱的氣象站的全部採集數據 */ List<Record> records = new ArrayList<Record>(); //用一個ArrayList來盛裝封裝了各氣象數據的對象 Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { conn = DBUtil.getConnection(); //獲取與數據庫的鏈接 String sql = "select * from alldata where data_taizhan_num = ? and data_date >= ? and data_date <= ? order by data_date asc"; //初始化SQL查詢語句 pstmt = conn.prepareStatement(sql); //建立preparedStatement語句對象 pstmt.setString(1, name); //設定查詢參數 pstmt.setString(2, fiveDaysAgoStr); pstmt.setString(3, nowStr); rs = pstmt.executeQuery(); //獲取查詢到的結果集 while (rs.next()) { //封裝Record對象 Record r = new Record(); r.setTaizhan_num(rs.getString(1)); r.setDate(rs.getTimestamp(2)); r.setTem(rs.getString(3)); r.setHum(rs.getString(4)); r.setPa(rs.getString(5)); r.setRain(rs.getString(6)); r.setWin_dir(rs.getString(7)); r.setWin_sp(rs.getString(8)); //將時間轉換成給定格式便於echarts的X軸日期座標顯示 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String str = sdf.format(rs.getTimestamp(2)); r.setDateStr(str); //System.out.println(r.getTem()+" | "+r.getHum()+" | "+r.getPa()+" | "+r.getRain()+" | "+r.getWin_dir()+" | "+r.getWin_sp()); records.add(r); //將封裝好的Record對象放入列表容器中 } } catch (SQLException e) { System.out.println("查詢出錯,操做未完成!"); e.printStackTrace(); } finally { //查詢結束後釋放資源 DBUtil.close(rs); DBUtil.close(pstmt); DBUtil.close(conn); } //======================================鏈接數據庫操做(完)============================================================================================ //將list中的對象轉換爲Json格式的數組 Gson gson = new Gson(); String json = gson.toJson(records); //System.out.println(json); //將json數據返回給客戶端 response.setContentType("text/html; charset=utf-8"); response.getWriter().write(json); } }
alldata表部分數據截圖:
Record類代碼以下:
package EchartsExample; import java.sql.*; /** * 封裝氣象數據信息 * @author zhong * */ public class Record { private String taizhan_num; //臺站名 private String tem; //溫度 private String hum; //溼度 private String pa; //壓強 private String rain; //雨量 private String win_dir; //風向 private String win_sp; //風速 private String dateStr; //觀測日期(用於Echarts顯示格式) private Timestamp date; //觀測日期(原始格式) /** * 獲取觀測日期(用於echarts圖表展現); * @return 觀測日期值 */ public String getDateStr() { return dateStr; } /** * 設置觀測日期(用於echarts圖表展現); * @param dateStr 待設置觀測日期值 */ public void setDateStr(String dateStr) { this.dateStr = dateStr; } /** * 獲取產生該觀測記錄的臺站名稱; * @return 臺站名稱 */ public String getTaizhan_num() { return taizhan_num; } /** * 設置產生該觀測記錄的臺站名稱; * @param taizhan_num 待設置臺站名稱 */ public void setTaizhan_num(String taizhan_num) { this.taizhan_num = taizhan_num; } /** * 獲取溫度; * @return 溫度值 */ public String getTem() { return tem; } /** * 設置溫度; * @param tem 待設置溫度值 */ public void setTem(String tem) { this.tem = tem; } /** * 獲取溼度; * @return 溼度值 */ public String getHum() { return hum; } /** * 設置溼度; * @param hum 待設置溼度值 */ public void setHum(String hum) { this.hum = hum; } /** * 獲取壓強; * @return 壓強值 */ public String getPa() { return pa; } /** * 設置壓強; * @param pa 待設置壓強值 */ public void setPa(String pa) { this.pa = pa; } /** * 獲取雨量; * @return 雨量值 */ public String getRain() { return rain; } /** * 設置雨量; * @param rain 待設置雨量值 */ public void setRain(String rain) { this.rain = rain; } /** * 獲取風向; * @return 風向值 */ public String getWin_dir() { return win_dir; } /** * 設置風向; * @param win_dir 待設置風向值 */ public void setWin_dir(String win_dir) { this.win_dir = win_dir; } /** * 獲取風速; * @return 風速值 */ public String getWin_sp() { return win_sp; } /** * 設置風向; * @param win_sp 待設置風向值 */ public void setWin_sp(String win_sp) { this.win_sp = win_sp; } /** * 獲取觀測日期; * @return 觀測日期 */ public Timestamp getDate() { return date; } /** * 設置觀測日期; * @param date 觀測日期值 */ public void setDate(Timestamp date) { this.date = date; } }
DBUitl類(數據庫工具類)代碼以下:
package EchartsExample; import java.sql.*; import org.apache.tomcat.jdbc.pool.DataSource; import org.apache.tomcat.jdbc.pool.PoolProperties; /** * 數據庫工具類(採用了tomcat jdbc pool) * @author zhong * */ public class DBUtil { private static DataSource ds; static { //配置tomcat jdbc pool (鏈接池) PoolProperties p = new PoolProperties(); p.setUrl("jdbc:sqlserver://localhost:1433; DatabaseName=weather"); //設置鏈接的url p.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); //載入數據庫驅動 p.setUsername("sa"); //用於遠程鏈接的用戶名 p.setPassword("2003NianDeDiYiChangXue"); //密碼 p.setJmxEnabled(true); p.setTestWhileIdle(false); p.setTestOnBorrow(true); p.setValidationQuery("SELECT 1"); p.setTestOnReturn(false); p.setValidationInterval(30000); p.setTimeBetweenEvictionRunsMillis(30000); p.setMaxActive(100); p.setInitialSize(10); p.setMaxWait(10000); p.setRemoveAbandonedTimeout(60); p.setMinEvictableIdleTimeMillis(30000); p.setMinIdle(10); p.setLogAbandoned(true); p.setRemoveAbandoned(true); p.setJdbcInterceptors( "org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"+ "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"); ds = new DataSource(); ds.setPoolProperties(p); } private DBUtil() {} /** * 獲取一個數據庫鏈接(Connection); * @return Database Connection */ public static Connection getConnection() { Connection conn = null; try { conn = ds.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return conn; } /** * 關閉傳入的Connection; * @param conn 待關閉的Connection */ public static void close(Connection conn) { try { if (conn != null) { conn.close(); conn = null; } } catch (SQLException e) { e.printStackTrace(); } } /** * 關閉傳入的Statement; * @param stmt 待關閉的Statement */ public static void close(Statement stmt) { try { if (stmt != null) { stmt.close(); stmt = null; } } catch (SQLException e) { e.printStackTrace(); } } /** * 關閉傳入的ResultSet; * @param rs 待關閉的ResultSet */ public static void close(ResultSet rs) { try { if (rs != null) { rs.close(); rs = null; } } catch (SQLException e) { e.printStackTrace(); } } }
——————————————————————————我是小小分割線————————————————————————————————————
關於遠程鏈接(包括使用Eclipse鏈接)SqlServer2005我再多嘴兩句:
當確認鏈接的URL,驅動加載,用戶名,密碼都配置正確時,仍然拋出 java.lang.NullPointerException 的話,請打開開始菜單Microsoft SQL Server 2005軟件目錄下的SQL Server配置管理器:
①:啓用SQL Server 2005網絡配置 下的 SQLEXPRESS協議 內的 TCP/IP 協議,並右鍵TCP/IP協議選擇屬性,確保IP地址一欄最末的TCP端口爲1433,具體見下圖
②:啓用 SQL Native Client配置 下的 客戶端協議 內的 TCP/IP 協議,並右鍵查看TCP/IP 屬性,確保端口爲1433,具體見下圖
③:從新啓動 SQL Server 2005服務 下的 SQL Server服務,詳情見下圖
至此,應該就解決了java.lang.NullPointerException這個錯誤了。
————————————————————————我是小小分割線————————————————————————————————————
讓咱們再回到原來的Echarts圖表顯示過程上,你能夠試着在後臺打印看看轉換出的Json字符串,關於Json的使用這裏我再也不多言解釋,具體可參考簡單易懂的官方教程:http://www.w3school.com.cn/json/
源碼下載:http://files.cnblogs.com/files/Dreamer-1/BlogExample.rar