程式扎記: [ JS 實用代碼 ] ExtJS 3.4 + D3js - Tag Cloud

標籤

2014年8月19日 星期二

[ JS 實用代碼 ] ExtJS 3.4 + D3js - Tag Cloud

Preface 
最近被要求去研究一下如何產生 Word/Tag Cloud, 拜完 Google 大神後找到一個不錯的 Solution - From Here 
 

詳細的實作就不多做說明, 直接去看 JavaScript code 吧! 下面針對自己使用方便, 稍微改過上面 Solution 的 JS 代碼, 變成可以從外部讀入 JSon 資料後轉成對應 Tag Cloud. 另外上面的 Solution 有使用到 D3JS (Data-Driven Documents) 的 library, 因此需要去下載. 另外在資料的產生是使用 Java Servlet 來完成; 而 UI 的呈現則是使用 ExtJS 3.4 的 Window 來當作 Container... 

Sample Code: 
首先我們在後端使用 Servlet 來產生測試資料, 並以 JSON 格式包裝後再回傳到前端. 範例代碼如下: 
  1. package servlets;  
  2.   
  3. import java.io.IOException;  
  4. import java.util.ArrayList;  
  5. import java.util.List;  
  6. import java.util.Random;  
  7.   
  8. import javax.servlet.ServletException;  
  9. import javax.servlet.annotation.WebServlet;  
  10. import javax.servlet.http.HttpServlet;  
  11. import javax.servlet.http.HttpServletRequest;  
  12. import javax.servlet.http.HttpServletResponse;  
  13.   
  14. import net.sf.json.JSONObject;  
  15. import servlets.datas.TagBean;  
  16. import servlets.datas.TagBeanWrapper;  
  17.   
  18. /** 
  19. * Servlet implementation class TagData 
  20. */  
  21. @WebServlet("/TagData")  
  22. public class TagDataServlet extends HttpServlet {  
  23.     private static final long serialVersionUID = 1L;  
  24.          
  25.     /** 
  26.      * @see HttpServlet#HttpServlet() 
  27.      */  
  28.     public TagDataServlet() {  
  29.         super();         
  30.     }  
  31.   
  32.     /** 
  33.      * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) 
  34.      */  
  35.     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  
  36.         response.setCharacterEncoding("UTF-8");  
  37.         int loop = 10;  
  38.         int max=18;  
  39.         int min=1;    
  40.         int lvl=3;  
  41.         String loopStr = request.getParameter("loop");  
  42.         try{if(loopStr!=null) loop = Integer.valueOf(loopStr);}  
  43.         catch(Exception e){}  
  44.         String maxStr = request.getParameter("max");  
  45.         try{if(maxStr!=null) max = Integer.valueOf(maxStr);}          
  46.         catch(Exception e){}  
  47.         String lvlStr = request.getParameter("lvl");  
  48.         try{if(lvlStr!=null) lvl=Integer.valueOf(lvlStr);}  
  49.         catch(Exception e){}  
  50.         System.out.printf("\t[Test] Max=%d; Loop=%d; Lvl=%d...\n", max, loop, lvl);  
  51.         TagBeanWrapper tagBeanWrapper = new TagBeanWrapper();         
  52.         Random rdm = new Random();  
  53.         int range = (int)Math.ceil(Double.valueOf(max)/lvl);  
  54.         int u=0;  
  55.         for(int i=0; i2, i);  
  56.         int unit = Math.max(loop/u, 1);       
  57.         int id=1;  
  58.         for(int i=1; i<=lvl; i++)  
  59.         {  
  60.             int r=((int)Math.pow(2, i-1))*unit;  
  61.             for(int j=0; j
  62.             {  
  63.                 String key = String.format("Test%d", id++);  
  64.                 int val=Math.max(1, max-range*i+rdm.nextInt(range+1));  
  65.                 tagBeanWrapper.addTagBean(                        
  66.                         new TagBean(key, val)  
  67.                 );  
  68.                 //System.out.printf("\t[Test] Add %s(%d)...\n", key, val);  
  69.             }  
  70.         }  
  71.         /*for(int i=0; i 
  72.         {            
  73.             tagBeanWrapper.addTagBean( 
  74.                     new TagBean(String.format("Test%d", i+1), 1+rdm.nextInt(max)) 
  75.             ); 
  76.         }*/  
  77.         tagBeanWrapper.sortTagBean();  
  78.         JSONObject jsonObject = JSONObject.fromObject(tagBeanWrapper);  
  79.         System.out.printf("\t[Info] JSonObj:\n%s\n", jsonObject);  
  80.         response.getWriter().write(jsonObject.toString());    
  81.     }  
  82.   
  83.     /** 
  84.      * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) 
  85.      */  
  86.     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  
  87.         doGet(request, response);  
  88.     }  
  89. }  
上面代碼透過 Http Get 參數 loop 來決定 Tag 的數量; 參數 max, min 決定 Tag 值的上/下限; 參數 lvl 決定切成幾個 Level (第一個 level 有 1 個; 第二個 level 有 2^2=2 個; 第三個 Level 有 2^3=8 個 etc). 另外這裡使用類別 TagBean 來存放 Tag 的名稱與對應的值; 而多個 TagBean 物件則由類別 TagBeanWrapper 進行包裝. 而最後返回的 JSON 格式還需要透過套件 JSON-lib 幫我們由 Java 世界的物件 (TagBeanWrapper) 傳成 JSON 格式的字串. 
- TagBean.java 
  1. package servlets.datas;  
  2.   
  3. public class TagBean {  
  4.     private String  key;  
  5.     private int     value;  
  6.       
  7.     public TagBean(){}  
  8.     public TagBean(String key, int val){this.key=key; this.value=val;}  
  9.       
  10.     public String getKey() {  
  11.         return key;  
  12.     }  
  13.     public void setKey(String key) {  
  14.         this.key = key;  
  15.     }  
  16.     public int getValue() {  
  17.         return value;  
  18.     }  
  19.     public void setValue(int value) {  
  20.         this.value = value;  
  21.     }  
  22.       
  23.     @Override  
  24.     public String toString()  
  25.     {  
  26.         return String.format("[key=%s,value=%d]",  key, value);  
  27.     }  
  28. }  
- TagBeanWrapper.java 
  1. package servlets.datas;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Comparator;  
  5. import java.util.List;  
  6. import java.util.PriorityQueue;  
  7.   
  8. public class TagBeanWrapper {  
  9.     List tags = new ArrayList();  
  10.     int totalNum;  
  11.     int min=0;  
  12.     int max=0;  
  13.   
  14.     public TagBeanWrapper(){}  
  15.   
  16.     public void addTagBean(TagBean bean)  
  17.     {  
  18.         tags.add(bean);  
  19.         totalNum++;  
  20.     }  
  21.       
  22.     public int getTotalNum() {  
  23.         return totalNum;  
  24.     }  
  25.   
  26.     public void sortTagBean()  
  27.     {  
  28.         Comparator cmp = new Comparator(){  
  29.   
  30.             @Override  
  31.             public int compare(TagBean o1, TagBean o2) {  
  32.                 return Integer.valueOf(o2.getValue()).compareTo(o1.getValue());  
  33.             }             
  34.         };  
  35.         PriorityQueue pq = new PriorityQueue(10, cmp);  
  36.         for(TagBean bean:tags) pq.add(bean);  
  37.         List tmpTags = new ArrayList();  
  38.         while(!pq.isEmpty()) {  
  39.             TagBean bean = pq.poll();  
  40.             System.out.printf("\t[Test] %s\n", bean);  
  41.             tmpTags.add(bean);        
  42.         }  
  43.         tags = tmpTags;  
  44.         if(tags.size()>0)  
  45.         {  
  46.             min=tags.get(0).getValue();  
  47.             max=tags.get(tags.size()-1).getValue();  
  48.         }  
  49.         System.out.printf("\t[Test] max=%d; min=%d\n", max, min);  
  50.     }  
  51.       
  52.     public List getTags() {  
  53.         return tags;  
  54.     }  
  55.   
  56.     public void setTags(List tags) {  
  57.         this.tags = tags;  
  58.     }  
  59.   
  60.     public int getMin() {  
  61.         return min;  
  62.     }  
  63.   
  64.     public void setMin(int min) {  
  65.         this.min = min;  
  66.     }  
  67.   
  68.     public int getMax() {  
  69.         return max;  
  70.     }  
  71.   
  72.     public void setMax(int max) {  
  73.         this.max = max;  
  74.     }  
  75.   
  76.     public void setTotalNum(int totalNum) {  
  77.         this.totalNum = totalNum;  
  78.     }         
  79. }  
後端的代碼 Ready 了, 接下來是前端的代碼: 
- tagCloudv2.jsp 
(由於代碼 post 上來會造成編輯錯誤, 請直接下載專案回去, 在 D3jsDemo\WebContent\demo\tagCloudv2.jsp  路徑下)

上面 JS 代碼有關 Tag Cloud 的部分是修改 Jason Davies 大大 的 cloud.js 而來, 所以請尊重智慧財產權了... Orz. 有關更多的說明可以參考 - How the Word Cloud Generator Works.. 上面代碼中 Tag Cloud 的資料是透過 Ajax 存取 "/D3jsDemo/TagData?loop=100&max=10&lvl=4" 路徑下的 Servlet 吐出的 JSON 資料, 事實上這就是剛剛 TagDataServlet 負責的工作. 

由於 Tag Cloud 的產生是不可預期的 (字的排列與轉的角度是 random 發生), 因此在 ExtJS Window 的 Container 上我加上了按鈕 "Refresh" 讓你可以重新產生 Tag Cloud; 另外當你點擊某個 Tag 時產生的 Event, 會在方法 draw3(data, bounds) 被捕抓到. 因此你可以在下面代碼片段加入自己的程式邏輯: 
  1. .on("click", function(d) {  
  2.     alert(d.key);  
  3.     // Define your application logic here when tag is clicked.            
  4. })  
上面範例代碼執行結果如下: 
 

目前這個專案是使用 Eclipse 開發, 完整的專案代碼可以在此 下載

Supplement: 
Google Chart API 介紹 
Open Cloud - Java Library to generate tag clouds 
OpenCloud is a Java library for generating and managing tag clouds, similar to those found in many websites. The aim of the project is to provide an easy to use library, but versatile enough to be used in various applications.

ExtJS 3.4 API - TabPanel

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!