這個雙12,別人都在搶紅包、逛淘寶、上京東,我選擇再續我的“漫談可視化”系列(好了,不裝了,其實是郎中羞澀。。。)
上篇《漫談可視化Prefuse(三)---Prefuse API數據結構閱讀有感》主要介紹了可視化工具Prefuse API中主要解讀的是prefuse.data包中的主要接口,並利用《漫談可視化Prefuse(一)---從SQL Server數據庫讀取數據》中例子,將參數配置模塊剝離出來,實現界面傳值,繪制圖形。
本篇決定不再貼API,實在沒啥意思,還占篇幅(但是不容置疑的是API確實很重要,想了解API點這里)。那今天我們主要講講Prefuse比較常用的包以及如何使用這些包做出自己想要的展示效果:
Demo1.解決圖形元素形狀單一化問題——DataShapeAction的使用
Prefuse對於節點的形狀默認是使用Constants.SHAPE_RECTANGLE,這里使用DataShapeAction類完成一個圖形中根據性別不同展示不同的節點形狀,具體代碼如下:
1 public class Demo1 { 2 3 /** 4 * @param args 5 */ 6 public static void main(String[] args) { 7 8 Graph graph = null; 9 try { 10 graph = new GraphMLReader().readGraph("socialnet.xml"); 11 } catch (Exception e) { 12 e.printStackTrace(); 13 System.out.println("Error loading"); 14 System.exit(1); 15 } 16 17 Visualization vis = new Visualization(); 18 vis.add("graph", graph); 19 vis.setRendererFactory(new DefaultRendererFactory()); 20 21 int[] palette = new int[]{ColorLib.rgb(255, 180, 180),ColorLib.rgb(190, 190, 255)}; 22 DataColorAction fill = new DataColorAction("graph.nodes" , "gender" , Constants.NOMINAL, VisualItem.FILLCOLOR,palette); 23 ColorAction text = new ColorAction("graph.nodes", VisualItem.TEXTCOLOR, ColorLib.gray(0)); 24 ColorAction edges = new ColorAction("graph.edges", VisualItem.STROKECOLOR, ColorLib.gray(200)); 25 26 int[] shapes = new int[]{ Constants.SHAPE_RECTANGLE, Constants.SHAPE_DIAMOND}; 27 DataShapeAction shape = new DataShapeAction("graph.nodes", "gender", shapes); 28 29 ActionList color = new ActionList(); 30 color.add(fill); 31 color.add(text); 32 color.add(edges); 33 color.add(shape); 34 35 ActionList layout = new ActionList(Activity.INFINITY); 36 layout.add(new ForceDirectedLayout("graph")); 37 layout.add(new RepaintAction()); 38 39 vis.putAction("color", color); 40 vis.putAction("layout", layout); 41 42 Display display = new Display(vis); 43 display.setSize(750, 700); 44 display.pan(250, 250); 45 display.addControlListener(new DragControl()); 46 display.addControlListener(new PanControl()); 47 display.addControlListener(new ZoomControl()); 48 display.addControlListener(new WheelZoomControl()); 49 display.addControlListener(new FocusControl(1)); 50 display.addControlListener(new ZoomToFitControl()); 51 52 JFrame jf = new JFrame(); 53 jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 54 jf.add(display); 55 jf.pack(); 56 jf.setVisible(true); 57 58 vis.run("color"); 59 vis.run("layout"); 60 61 } 62 63 }
DataShapeAction中的第一個參數為指定數據范圍,這里指定針對“graph.nodes”點集;第二個參數為屬性域,這里針對性別不同賦予不同的形狀;第三個參數是形狀數組,這里定義了Constants.SHAPE_RECTANGLE, Constants.SHAPE_DIAMOND,除此之外還有Constants.SHAPE_CROSS、Constants.SHAPE_ELLIPSE、Constants.SHAPE_STAR等。同理prefuse.action.assignment包下的DataColorAction用法與DataShapeAction類似。圖形展示效果如下:
Demo2:打破輸入輸出流的壟斷親自繪制圖形——Graph的使用
之前幾篇介紹的例子主要依賴於prefuse.io中的讀入寫出操作類進行數據的導入,這里我們決定甩開膀子,自己豐衣足食,打造自己的圖形,這里主要使用Graph類的添加節點以及添加邊的方法,構建一個互連的三個三角形的形狀。具體代碼如下:
1 public class Demo2{ 2 3 public static void main(String[] argv) { 4 Visualization vis = new Visualization(); 5 Graph g = new Graph(); 6 for(int i = 0; i<3; i++){ 7 Node n1 = g.addNode(); 8 Node n2 = g.addNode(); 9 Node n3 = g.addNode(); 10 g.addEdge(n1, n2); 11 g.addEdge(n2, n3); 12 g.addEdge(n3, n1); 13 } 14 g.addEdge(0, 3); 15 g.addEdge(3, 6); 16 g.addEdge(6, 0); 17 18 vis.add("graph", g); 19 ShapeRenderer renderer = new ShapeRenderer(10); 20 vis.setRendererFactory(new DefaultRendererFactory(renderer)); 21 22 ColorAction nodeFill = new ColorAction("graph.nodes", VisualItem.FILLCOLOR, ColorLib.rgb(10, 150, 220)); 23 ColorAction edgesStroke = new ColorAction("graph.edges", VisualItem.STROKECOLOR, ColorLib.rgb(100, 80, 180)); 24 ColorAction nodeHighlight = new ColorAction("graph.nodes", VisualItem.HIGHLIGHT, ColorLib.rgb(10, 150, 220)); 25 26 ActionList color = new ActionList(); 27 color.add(nodeFill); 28 color.add(edgesStroke); 29 30 ActionList layout = new ActionList(Activity.INFINITY); 31 layout.add(color); 32 layout.add(new ForceDirectedLayout("graph")); 33 layout.add(new RepaintAction()); 34 35 Display display = new Display(vis); 36 display.setSize(400, 500); 37 display.pan(250, 250); 38 display.addControlListener(new DragControl()); 39 display.addControlListener(new PanControl()); 40 display.addControlListener(new ZoomControl()); 41 display.addControlListener(new WheelZoomControl()); 42 display.addControlListener(new FocusControl(1)); 43 display.addControlListener(new ZoomToFitControl()); 44 45 vis.putAction("color", color); 46 vis.putAction("layout", layout); 47 48 49 JFrame frame = new JFrame(); 50 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 51 frame.pack(); 52 frame.setSize(600, 700); 53 frame.add(display); 54 frame.setVisible(true); 55 56 vis.run("color"); 57 vis.run("layout"); 58 } 59 60 }
從代碼可以看出,對於創建節點、添加邊,Graph處理的游刃有余,只有你想不到的圖形,沒有創建不了的Graph。當然只是簡單創建了node和edge只是有了骨架,還需要血肉的填充,所以可以看到有很多的Action紛紛來助陣,最終的眾志成城之作如下:
Demo3:誰說點點之間只能“直腸子”——EdgeRenderer的使用
看不慣點與點之間的“直腸子”?可以,Prefuse了解你,知道你喜新厭舊,這不使用EdgeRenderer就可以實現曲線連接,具體代碼與Demo1相近,只是多了一下幾行代碼:
1 DefaultRendererFactory rf = new DefaultRendererFactory(label); 2 EdgeRenderer edgeRenderer = new EdgeRenderer(Constants.EDGE_TYPE_CURVE); 3 rf.add(new InGroupPredicate("graph.edges"), edgeRenderer);
這里采用EdgeRenderer邊渲染器,將邊渲染為曲線,並統一應用到組“graph.edges”中的元素上,除了Constants.EDGE_TYPE_CURVE,還有Constants.EDGE_ARROW_FORWARD、Constants.EDGE_ARROW_NONE等。話不多說,看效果:
Demo4:Prefuse沒節操?不知道篩選?No!——Predicate的使用
Prefuse自帶過濾器,可以完成十分強大的過濾操作,視個別情況個別對待。拿Demo2中的圖形來說,處在中間的節點可以不收節點控制(無法拖拽,不能交互),人家兄弟姐妹多,就拿那些勢力若的邊緣小弟拖過來拽過去^_^。具體代碼如下:
1 public class Demo4{ 2 3 public static void main(String[] argv) { 4 Visualization vis = new Visualization(); 5 Graph g = new Graph(); 6 for(int i = 0; i<3; i++){ 7 Node n1 = g.addNode(); 8 Node n2 = g.addNode(); 9 Node n3 = g.addNode(); 10 g.addEdge(n1, n2); 11 g.addEdge(n2, n3); 12 g.addEdge(n3, n1); 13 } 14 g.addEdge(0, 3); 15 g.addEdge(3, 6); 16 g.addEdge(6, 0); 17 vis.add("graph", g); 18 19 ShapeRenderer renderer = new ShapeRenderer(10); 20 EdgeRenderer edgeRenderer = new EdgeRenderer(Constants.EDGE_TYPE_CURVE); 21 DefaultRendererFactory drf = new DefaultRendererFactory(); 22 drf.add(new InGroupPredicate("graph.edges"), edgeRenderer); 23 vis.setRendererFactory(drf); 24 25 ColorAction nodeFill = new ColorAction("graph.nodes", VisualItem.FILLCOLOR, ColorLib.rgb(10, 150, 220)); 26 ColorAction edgesStroke = new ColorAction("graph.edges", VisualItem.STROKECOLOR, ColorLib.rgb(100, 80, 180)); 27 ColorAction nodeHighlight = new ColorAction("graph.nodes", VisualItem.HIGHLIGHT, ColorLib.rgb(10, 150, 220)); 28 29 ShapeAction shape = new ShapeAction("graph.nodes",Constants.SHAPE_CROSS); //設置節點形狀 30 31 ActionList color = new ActionList(); 32 color.add(nodeFill); 33 color.add(edgesStroke); 34 color.add(shape); 35 36 ActionList layout = new ActionList(Activity.INFINITY); 37 layout.add(color); 38 layout.add(new ForceDirectedLayout("graph")); 39 layout.add(new RepaintAction()); 40 41 Point p = new Point(100,200); 42 p.move(300, 400); 43 44 45 Predicate pCount =(Predicate)ExpressionParser.parse("degree()>2"); 46 47 Display display = new Display(vis); 48 display.setSize(400, 500); 49 display.pan(250, 250); 50 display.animatePanAbs(230, 220, 2000); 51 display.addControlListener(new DragControl()); 52 display.addControlListener(new PanControl()); 53 display.addControlListener(new ZoomControl()); 54 display.addControlListener(new WheelZoomControl()); 55 display.addControlListener(new FocusControl(1)); 56 display.addControlListener(new ZoomToFitControl()); 57 58 vis.removeGroup("graph"); 59 VisualGraph vg = vis.addGraph("graph", g); 60 Iterator nodes = vg.nodes(); 61 display.addControlListener(new ControlAdapter() { 62 public void itemEntered(VisualItem item, MouseEvent e) { 63 System.out.println("倫家已經是:" + item.getGroup()+"的人了"); 64 } 65 public void itemExited(VisualItem item, MouseEvent e) { 66 System.out.println("哦,那傑哥再找找-_-"); 67 } 68 });//為組件添加監控並作相應的響應 69 70 vis.setInteractive("graph.nodes", pCount, false); 71 72 vis.putAction("color", color); 73 vis.putAction("layout", layout); 74 75 76 JFrame frame = new JFrame(); 77 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 78 frame.pack(); 79 frame.setSize(600, 700); 80 frame.add(display); 81 frame.setVisible(true); 82 83 vis.run("color"); 84 vis.run("layout"); 85 } 86 87 }
這里Predicate pCount =(Predicate)ExpressionParser.parse("degree()>2")表示過濾出節點度數大於2的,該過濾條件在vis.setInteractive("graph.nodes", pCount, false)中執行,即在組“graph.nodes”元素中節點度數大於2的不能交互(即鼠標拖拽無效)。
另外從代碼中可以看出當鼠標經過和離開VisualItem(點或邊)時會執行相應的操作,下圖展現了部分結果:
Demo5:個性化定制,我有我個性——VisualItem的領悟
要知道,在數據中心Visualizaiton里,每一個node或是edge都可以看成一個VisualItem,每個VisualItem都可以有屬於自己的造型、尺寸,這里我們還是用Demo2中的圖形繼續玩,直到玩壞為止。具體代碼如下:
1 public class Demo5{ 2 public static Visualization vis = new Visualization(); 3 4 public static void main(String[] argv) { 5 6 Graph g = new Graph(); 7 for(int i = 0; i<3; i++){ 8 Node n1 = g.addNode(); 9 Node n2 = g.addNode(); 10 Node n3 = g.addNode(); 11 g.addEdge(n1, n2); 12 g.addEdge(n2, n3); 13 g.addEdge(n3, n1); 14 } 15 g.addEdge(0, 3); 16 g.addEdge(3, 6); 17 g.addEdge(6, 0); 18 19 vis.add("graph", g); 20 ShapeRenderer renderer = new ShapeRenderer(10); 21 vis.setRendererFactory(new DefaultRendererFactory(renderer)); 22 23 vis.removeGroup("graph"); 24 VisualGraph vg = vis.addGraph("graph", g); 25 VisualItem nodeI = (VisualItem)vg.getEdge(7).getSourceNode(); 26 nodeI.setShape(Constants.SHAPE_STAR); 27 nodeI.setSize(4); 28 nodeI.setFixed(true); 29 VisualItem edgeI = (VisualItem)vg.getEdge(5); 30 edgeI.setSize(8); 31 32 ColorAction nodeFill = new ColorAction("graph.nodes", VisualItem.FILLCOLOR, ColorLib.rgb(10, 150, 220)); 33 ColorAction edgesStroke = new ColorAction("graph.edges", VisualItem.STROKECOLOR, ColorLib.rgb(100, 80, 180)); 34 ColorAction nodeHighlight = new ColorAction("graph.nodes", VisualItem.HIGHLIGHT, ColorLib.rgb(10, 150, 220)); 35 36 ActionList color = new ActionList(); 37 color.add(nodeFill); 38 color.add(edgesStroke); 39 40 ActionList layout = new ActionList(3000); 41 layout.add(color); 42 layout.add(new ForceDirectedLayout("graph")); 43 layout.add(new RepaintAction()); 44 45 Display display = new Display(vis); 46 display.setSize(400, 500); 47 display.pan(250, 250); 48 display.addControlListener(new DragControl()); 49 display.addControlListener(new PanControl()); 50 display.addControlListener(new ZoomControl()); 51 display.addControlListener(new WheelZoomControl()); 52 display.addControlListener(new FocusControl(1)); 53 display.addControlListener(new ZoomToFitControl()); 54 display.addControlListener(new ControlAdapter(){ 55 public void itemEntered(VisualItem item, MouseEvent e) { 56 57 System.out.println("倫家已經是:" + item.getGroup()+"的人了"); 58 vis.run("color"); 59 vis.run("layout");//重繪三秒 60 } 61 }); 62 63 vis.putAction("color", color); 64 vis.putAction("layout", layout); 65 66 67 JFrame frame = new JFrame(); 68 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 69 frame.pack(); 70 frame.setSize(600, 700); 71 frame.add(display); 72 frame.setVisible(true); 73 74 vis.run("color"); 75 vis.run("layout"); 76 } 77 78 }
可以看出代碼中nodeI.setShape(Constants.SHAPE_STAR);nodeI.setSize(4);nodeI.setFixed(true);是將節點的形狀設為星形,大小放大為4並固定此節點;edgeI.setSize(8);將邊的粗細設為8。這里還具有一個功能,就是代碼跑起來顯示圖形運行三秒,當鼠標經過某點或邊是,整個圖形在運行三秒,為展示這種效果,首次使用ps做gif,想想也是醉了。。。
Prefuse就先玩到這吧,真材實料送上5個Demo,雖然搶不到紅包,搶點Demo補補腦也不錯哦,走到這里,似乎我已經拉近了與Prefuse的距離,下一步就是力求親密接觸,碰撞出可視化狂拽炫酷的展示效果。覺得有用,記得點贊哦。
本文鏈接:《漫談可視化Prefuse(四)---被玩壞的Prefuse API》http://www.cnblogs.com/bigdataZJ/p/VisualizationSoloShow4.html
友情贊助
如果你覺得博主的文章對你那么一點小幫助,恰巧你又有想打賞博主的小沖動,那么事不宜遲,趕緊掃一掃,小額地贊助下,攢個奶粉錢,也是讓博主有動力繼續努力,寫出更好的文章^^。
1. 支付寶 2. 微信