可視化一路走來,體會很多;博客一路寫來,收獲頗豐;代碼一路碼來,思路越來越清晰。終究還是明白了一句古話:紙上得來終覺淺,絕知此事要躬行。
跌跌撞撞整合了個可視化小tool,零零碎碎結交了眾多的志同道合之人,迷迷糊糊創建了我"可視化/Prefuse"的QQ群,詳情可查看左側的公告部分。此群旨在結實更多的可視化領域的同仁,也希望可以成為一個開源項目分享與閱讀的平台。源於開源,開源為我們提供了助我們快速成長的養分,開源的每一行代碼都是前輩們心血的結晶,開源是智慧的濃縮。
在博客園寫博客也是如此,從起初只是一味的平鋪直敘,直抒胸臆,從來沒有估計到還要格式調整,也沒有想到要布局美觀,更不用提什么言簡意賅、一針見血。總之,看看優秀的博客,就如同接觸到一個開源項目,其開篇布局、文章內容、文本樣式都是學習的教材。最近體會比較深的就是:寫博客也體現出博客的特性,博客,不是論文,過多或者過於復雜的理論闡述(除非必不可少)會讓大部分讀者兩眼昏花、四肢無力;博客,也不是小學造句子,過短或過少缺乏整體的連貫性,剛讀起來有點感覺,好吧,還沒高潮就結束了,讓人摸不着頭腦。所以,我發現,但凡獲得點贊多的文章除了內容含金量高以外,還贏在表述以及篇幅的控制,真正站在Reader的角度去闡述問題、解決問題,Reader就會控制不住要點個贊。In a Word,路很長,要學的東西很多,慢慢體味。
上期回顧:《漫談可視化Prefuse(五)---一款屬於我自己的可視化工具》主要介紹了自己開發的一款可視化工具,包含了可視化工具中一些必不可少的功能,有些網友反應期待web版,其實Prefuse是支持applet版本的,這里沒有走web的原因可能還是根據我們的需求走。要知道在以js為堅實后盾的各種可視化庫大行其道的情況下,類似於Gephi、IBM i2等產品仍然具有不可撼動的地位的原因,在於需求催生產品,不同的需求就會對應着不同的產品形態。還有些網友反映披露工具相關細節,下面正文將會有所體現。
本篇主要立足於Prefuse框架講述:1.可視化工具如何根據需求調整邊的粗細;2.try...catch的巧用
1.如何通過邊的粗細來反映邊的權重
Prefuse框架默認邊的粗細是統一的,都為1。但是我們往往面臨的需求是給出數據格式如下
Source | Target | Weight |
1 | 2 | 0.5 |
那么我們就可以通過邊的粗細來體現這個Weight屬性,用來表明節點1和節點2之間的親密程度。
所以,我們針對Prefuse的源碼做一下改動,在此之前,我們先了解有關類的繼承關系:
類LabelRenderer:
public class LabelRenderer extends AbstractShapeRenderer { ...... public void render(Graphics2D g, VisualItem item) { RectangularShape shape = (RectangularShape) getShape(item); if (shape == null) return; ...... } ...... }
類EdgeRenderer:
public class EdgeRenderer extends AbstractShapeRenderer { ...... public void render(Graphics2D g, VisualItem item) { // render the edge line super.render(g, item); ...... } ...... }
抽象類AbstractShapeRenderer:
public abstract class AbstractShapeRenderer implements Renderer { ...... public void render(Graphics2D g, VisualItem item) { Shape shape = getShape(item); ...... } ...... }
從以上三個類可以發現,類LabelRenderer和EdgeRenderer都是繼承自抽象類AbstractShapeRenderer,並且LabelRenderer和EdgeRenderer類都覆寫了父類 render方法。
兩個類的render方法的最大不同之處在於,EdgeRenderer類顯式調用了抽象父類AbstractShapeRenderer的render方法,並且通過在AbstractShapeRenderer類的render方法中加入:
System.out.println("該item屬於:" + item.getGroup())
得到的打印信息都是"該item屬於:graph.edges",即表明都是對於邊的渲染。
了解了類的繼承關系,下面給出修改源碼的步驟以實現通過配置文件實現邊粗細的賦值:
(1)在PrefuseConfig類中添加:
setProperty("data.edge.weight", "weight");
用於xml文件中確定邊粗細的標示,這里是weight;
(2)在Graph類中添加:
public static final String WEIGHT = PrefuseConfig.get("data.edge.weight");
用於在GraphMLReader獲取常量;
(3)在GraphMLReader中添加:
public static final String WEIGHT = Graph.WEIGHT; public static final String WEIGHTID = WEIGHT + '_' + ID;
方法startDocument()中添加:
m_esch.addColumn(WEIGHT, String.class);//邊粗細相關 m_esch.addColumn(WEIGHTID, String.class);
方法startElement()中添加:
m_edges.setString(m_row, WEIGHT, atts.getValue(WEIGHT)); m_edges.setString(m_row, WEIGHTID, atts.getValue(WEIGHTID));
(4)在AbstractShapeRenderer中添加:
EdgeItem edge = (EdgeItem)item; VisualItem vi = (VisualItem)edge.getSourceNode(); item.setStrokeColor(vi.getFillColor()); String weight = null; try { weight = item.get(GraphMLHandler.WEIGHT).toString(); } catch (Exception e) { weight = "1"; } item.setSize(Double.parseDouble(weight)*10);
使用的配置文件為xml格式,內容為:
<?xml version="1.0" encoding="UTF-8"?> <!-- An excerpt of an egocentric social network --> <graphml xmlns="http://graphml.graphdrawing.org/xmlns"> <graph edgedefault="undirected"> <!-- data schema --> <key id="name" for="node" attr.name="name" attr.type="string"/> <key id="gender" for="node" attr.name="gender" attr.type="string"/> <key id="id" for="node" attr.name="id" attr.type="string"/> <!-- nodes --> <node id="1"> <data key="name">Jeff</data> <data key="gender">M</data> <data key="id">1</data> </node> <node id="2"> <data key="name">Ed</data> <data key="gender">M</data> <data key="id">2</data> </node> <node id="3"> <data key="name">Christiaan</data> <data key="gender">M</data> <data key="id">3</data> </node> <node id="4"> <data key="name">Emily</data> <data key="gender">F</data> <data key="id">4</data> </node> <node id="5"> <data key="name">Adam</data> <data key="gender">M</data> <data key="id">5</data> </node> <node id="6"> <data key="name">Cynthia</data> <data key="gender">F</data> <data key="id">6</data> </node> <node id="7"> <data key="name">Joylette</data> <data key="gender">F</data> <data key="id">7</data> </node> <node id="8"> <data key="name">Amanda</data> <data key="gender">F</data> <data key="id">8</data> </node> <node id="9"> <data key="name">Nathaniel</data> <data key="gender">M</data> <data key="id">9</data> </node> <!-- edges --> <edge source="1" target="2" directed="true" weight="0.1"></edge> <edge source="2" target="3" weight="0.2"></edge> <edge source="3" target="1" weight="0.5"></edge> <edge source="4" target="5" weight="0.2"></edge> <edge source="5" target="6" weight="0.1"></edge> <edge source="6" target="4" weight="0.2"></edge> <edge source="7" target="8" weight="0.3"></edge> <edge source="8" target="9" weight="0.1"></edge> <edge source="9" target="7" weight="0.4"></edge> <edge source="1" target="4" weight="0.1"></edge> <edge source="4" target="7" weight="0.2"></edge> <edge source="7" target="1" weight="0.1"></edge> </graph> </graphml>
最終的圖形顯示為:
2.try....catch的巧用
巧用之處見1(4)中所寫:
EdgeItem edge = (EdgeItem)item; VisualItem vi = (VisualItem)edge.getSourceNode(); item.setStrokeColor(vi.getFillColor()); String weight = null; try { weight = item.get(GraphMLHandler.WEIGHT).toString(); } catch (Exception e) { weight = "1"; } item.setSize(Double.parseDouble(weight)*10);
因為在實際情況中,會遇到有些xml文件中沒有weight屬性,或者xml部分邊有部分沒有的情況,這時就會面臨在AbstractShapeRenderer中無法獲取weight值的情形,即
item.get(GraphMLHandler.WEIGHT)
此值不存在或為空,起初通過添加語句:
if(item.get(GraphMLHandler.WEIGHT).toString().equals("") || item.get(GraphMLHandler.WEIGHT).toString() == null){ weight = "1"; }else{ weight = item.get(GraphMLHandler.WEIGHT).toString(); }
等類似手段還是無法捕獲item.get()取不到值的所有情況,后面轉而想到,鑒於要捕獲的是為空異常,索性使用try...catch直接進行捕獲,捕獲后在進行處理。從而有了上面的處理,即當item.get()函數獲取不到值時就捕獲異常,轉入catch部分執行,在這里我們編寫我們想要實現的功能,也就是獲取為空時就將weight設置默認值為1,從而巧妙的解決了考慮所有為空的情況。
從該源碼的經歷發現,需要明確自己想要的功能,明確需要觸及哪些類,明確如何保證修改后的源碼運行的完整性,不能因為修改一處而到處報錯;另外,還需要經常思考,靈活運行一些語句的特性為自己所用。今天就到這吧,如果覺得有用,記得點贊^_^,對可視化(gephi、prefuse、分布式計算、開源)感興趣的可以加群討論。
本文鏈接:《漫談可視化Prefuse(六)---改動源碼定制邊粗細》
友情贊助
如果你覺得博主的文章對你那么一點小幫助,恰巧你又有想打賞博主的小沖動,那么事不宜遲,趕緊掃一掃,小額地贊助下,攢個奶粉錢,也是讓博主有動力繼續努力,寫出更好的文章^^。
1. 支付寶 2. 微信