用prefuse对owl本体可视化
在上一篇随笔中介绍了用Jena对owl本体片断进行解析存储到mysql数据库中,本文将接上次的内容对存入到mysql中本体片断进行抽取然后可视化化显示。
把owl本体片断导入protage后的可视化的效果:
鼠标移到结点或边对象时,显示语义信息:
我所做的工作就是实现像protage一样用图形交互。我用的图形交互式可视化组件包是prefuse(到官网上下载http://prefuse.org/ beta版或mater版),导入到JAVA工程中即可调用。
由于prefuse从db中读取数据->添充到自已的数据结构->着色、布置位置->渲染->呈现,这个过程是稳定,而且必须一次性完成,而且它的数据结构是稳定。
所以我的思路是,首先,建产稳定的Graph数据表,其有三个字段:url,name,type(用于标记是类还是实体),然后把所有类和对象属性实例从db中提取出来,填入到Graph数据表中,
然后,根据类之间,类与实例,实例与实例之间的语义关系添加边。
最后,可视化后鼠标移到结点或边上显示的语义信息是根据选中结点url(如果是边就是两个端点的url),从数据库中提取相应的语义信息,然后用tooltip(关于tooltip换行显示的方法可在我的随笔中找到)显示出来。
Java代码:
View Code
1 import javax.swing.JFrame; 2 import javax.swing.JPanel; 3 import javax.swing.SwingUtilities; 4 import java.awt.BasicStroke; 5 import java.awt.Shape; 6 import java.awt.Graphics2D; 7 import java.awt.BorderLayout; 8 import java.awt.event.MouseEvent; 9 import java.util.Iterator; 10 import java.lang.String; 11 import prefuse.Constants; 12 import prefuse.Display; 13 import prefuse.Visualization; 14 import prefuse.action.ActionList; 15 import prefuse.action.GroupAction; 16 import prefuse.action.RepaintAction; 17 import prefuse.action.animate.ColorAnimator; 18 import prefuse.action.animate.PolarLocationAnimator; 19 import prefuse.action.animate.QualityControlAnimator; 20 import prefuse.action.animate.VisibilityAnimator; 21 import prefuse.action.assignment.ColorAction; 22 import prefuse.action.assignment.DataColorAction; 23 import prefuse.action.assignment.FontAction; 24 import prefuse.action.assignment.StrokeAction; 25 import prefuse.action.layout.CollapsedSubtreeLayout; 26 import prefuse.action.layout.graph.RadialTreeLayout; 27 import prefuse.activity.SlowInSlowOutPacer; 28 import prefuse.controls.ControlAdapter; 29 import prefuse.controls.DragControl; 30 import prefuse.controls.FocusControl; 31 import prefuse.controls.HoverActionControl; 32 import prefuse.controls.PanControl; 33 import prefuse.controls.WheelZoomControl; 34 import prefuse.data.Graph; 35 import prefuse.data.Node; 36 import prefuse.data.Table; 37 import prefuse.data.io.sql.*; 38 import prefuse.data.tuple.TupleSet; 39 import prefuse.render.DefaultRendererFactory; 40 import prefuse.render.LabelRenderer; 41 import prefuse.render.EdgeRenderer; 42 import prefuse.util.ColorLib; 43 import prefuse.util.FontLib; 44 import prefuse.visual.EdgeItem; 45 import prefuse.visual.VisualItem; 46 import prefuse.visual.expression.InGroupPredicate; 47 48 public class Buyer extends Display { 49 50 //jdbc driver name 51 public static final String driverName= "com.mysql.jdbc.Driver"; 52 // path for database 53 public static final String dbURL= "jdbc:mysql://localhost:3306/ontology?useUnicode=true&characterEncoding=utf8"; 54 //username 55 public static final String userName= "root"; 56 //password 57 public static final String userPwd= "sql123"; 58 59 60 private final String graph= "graph"; 61 private final String graphNodes = "graph.nodes"; 62 private final String graphEdges = "graph.edges"; 63 private final String linear = "linear"; 64 private static String label="name"; 65 private static Graph graphdata=null; 66 private static BasicStroke bsfocus=null,bslosefocus=null; 67 68 //public static Table classnodes=null; 69 //public static Table individualnodes=null; 70 public Buyer() 71 { 72 super(new Visualization()); 73 74 //第二步,创建Visualization把数据映射到abstraction中 75 graphdata=new Graph(true); //true表明有向图 76 bsfocus=new BasicStroke(4); 77 bslosefocus=new BasicStroke(1); 78 } 79 80 public void InitComponent() 81 { 82 m_vis.add(graph,graphdata); 83 //第三步,进行渲染根据abstraction中的数据 84 LabelRenderer labelrender=new LabelRenderer(label); 85 labelrender.setRoundedCorner(5,5); 86 labelrender.setMaxTextWidth(100); 87 EdgeRenderer edgesrender=new MyEdgeRenderer(Constants.EDGE_TYPE_CURVE,Constants.EDGE_ARROW_FORWARD); 88 //设置箭头大小 89 DefaultRendererFactory drf=new DefaultRendererFactory(); 90 drf.add(new InGroupPredicate(graphNodes),labelrender); 91 drf.add(new InGroupPredicate(graphEdges),edgesrender); 92 m_vis.setRendererFactory(drf); 93 int[] palette = new int[]{ColorLib.rgb(10,190,255),ColorLib.rgb(255,10,180)}; 94 95 DataColorAction fill = new DataColorAction(graphNodes, "type",Constants.NOMINAL, VisualItem.FILLCOLOR, palette); 96 ColorAction textColor = new ColorAction(graphNodes,VisualItem.TEXTCOLOR, ColorLib.gray(0)); 97 ColorAction edgesColor = new MyColorAction(graphEdges,VisualItem.STROKECOLOR); 98 FontAction fonts = new FontAction(graph, FontLib.getFont("宋体&新宋体",1,15)); 99 //着色动作 100 ActionList color=new ActionList(); 101 color.add(fill); 102 color.add(textColor); 103 color.add(edgesColor); 104 color.add(fonts); 105 m_vis.putAction("color",color); 106 107 //更新动作序列 108 ActionList update = new ActionList(); 109 update.add(new MyStrokeAction()); 110 update.add(new RepaintAction()); 111 m_vis.putAction("update", update); 112 113 RadialTreeLayout treeLayout = new RadialTreeLayout(graph); 114 //treeLayout.setAngularBounds(-Math.PI/2, Math.PI); 115 m_vis.putAction("treeLayout", treeLayout); 116 117 CollapsedSubtreeLayout subLayout = new CollapsedSubtreeLayout(graph); 118 m_vis.putAction("subLayout", subLayout); 119 120 // create the filtering and layout 121 ActionList filter = new ActionList(); 122 filter.add(new TreeRootAction(graph)); 123 filter.add(color); 124 filter.add(treeLayout); 125 filter.add(subLayout); 126 m_vis.putAction("filter", filter); 127 128 // animated transition 129 ActionList animate = new ActionList(1250); 130 animate.setPacingFunction(new SlowInSlowOutPacer()); 131 132 animate.add(new QualityControlAnimator()); 133 animate.add(new VisibilityAnimator(graph)); //是否显示变动的动画过程 134 animate.add(new PolarLocationAnimator(graph, linear)); 135 animate.add(new ColorAnimator(graphNodes)); 136 animate.add(new RepaintAction()); 137 m_vis.putAction("animate", animate); 138 m_vis.alwaysRunAfter("filter", "animate"); 139 140 141 setSize(1000,700); 142 addControlListener(new DragControl()); // drag items around 143 addControlListener(new PanControl()); // pan with background left-drag 144 addControlListener(new WheelZoomControl()); // zoom with vertical Wheel 145 addControlListener(new FocusControl(1, "filter")); 146 addControlListener(new HoverActionControl("repaint")); 147 addControlListener(new DataMouseControl()); 148 // filter graph and perform layout 149 m_vis.run("filter"); 150 } 151 /** 152 * @param args 153 */ 154 public static void main(String[] args) { 155 // TODO Auto-generated method stub 156 DatabaseDataSource datasrc=null; 157 try 158 { 159 //create and load node of class 160 Table nodes= GetData("SELECT C_Uri,C_Name From ontologyclass"); 161 Table edges = GetData("SELECT x.C_Uri,y.curi FROM ontologyclass as x,tmpclass as y where x.C_Name=y.cvalue"); 162 163 164 Buyer buy=new Buyer(); 165 graphdata.addColumn("uri",java.lang.String.class); 166 graphdata.addColumn("name", java.lang.String.class); 167 graphdata.addColumn("type",java.lang.Character.class); 168 169 //添加类结点 170 buy.AddClassNode(nodes); 171 //添加类之间的父子关系 172 buy.AddEdges(edges,"C_Uri","curi","uri"); 173 174 //添加类之间的对象属性关系 175 edges=GetData("SELECT * FROM property where Proty_Type=\'OP\'"); 176 buy.AddEdges(edges,"Proty_Domain","Proty_Range","name"); 177 178 //添加实例结点 179 nodes=GetData("SELECT * from individual as x,property as y where x.Indiv_PURI=y.Proty_URI and y.Proty_Type='OP'"); 180 buy.AddIndividualNode(nodes); 181 //添加实例和类之间的边 182 edges=GetData("SELECT z.Indiv_ClaURI,z.Indiv_URI from property as y,individual as z where y.Proty_URI=z.Indiv_PURI and y.Proty_Type='OP'"); 183 buy.AddEdges(edges,"Indiv_ClaURI","Indiv_URI","uri"); 184 //添加实例间的关系 185 edges=GetData("SELECT x.Indiv_URI,x.Indiv_PURI,y.iuri from individual as x,tmpindividual as y,property as z where x.Indiv_PVal=y.iname and y.ipuri=z.Proty_URI and z.Proty_Type='OP'"); 186 buy.AddEdges(edges,"Indiv_URI","iuri","uri"); 187 //初始化显示 188 189 buy.InitComponent(); 190 //定制界面 191 JPanel panel= new JPanel(new BorderLayout()); 192 panel.add(buy, BorderLayout.CENTER); 193 JFrame frame = new JFrame("本体展示"); 194 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 195 frame.getContentPane().add(panel); 196 frame.pack(); 197 frame.setVisible(true); 198 } 199 catch(Exception e) 200 { 201 e.printStackTrace(); 202 System.exit(1); 203 } 204 } 205 206 public static Table GetData(String sql) 207 { 208 DatabaseDataSource datasrc =null; 209 Table table=null; 210 try 211 { 212 // declare a datasource 213 datasrc = ConnectionFactory.getDatabaseConnection(driverName, dbURL, userName, userPwd); 214 //create a table of data 215 table= datasrc.getData(sql); 216 } 217 catch (Exception e) 218 { 219 e.printStackTrace(); 220 System.exit(1); 221 } 222 return table; 223 }//end of static function GetData 224 225 public static class TreeRootAction extends GroupAction 226 { 227 public TreeRootAction(String graphGroup) { 228 super(graphGroup); 229 230 } 231 public void run(double frac) { 232 TupleSet focus = m_vis.getGroup(Visualization.FOCUS_ITEMS); 233 if ( focus==null || focus.getTupleCount() == 0 ) return; 234 235 Graph g = (Graph)m_vis.getGroup(m_group); 236 Node f = null; 237 Iterator tuples = focus.tuples(); 238 while (tuples.hasNext() && !g.containsTuple(f=(Node)tuples.next())) 239 { 240 f = null; 241 } 242 if ( f == null ) return; 243 g.getSpanningTree(f); 244 } 245 }//end of class TreeRootAction 246 247 public class DataMouseControl extends ControlAdapter 248 { 249 private String uri=null; 250 String sql=null; 251 private Graph g=null; 252 Node startnode,targetnode; 253 public String GetShowInformation() 254 { 255 String relation=null; 256 257 char sourcetype=(Character) startnode.get("type"); 258 char targettype=(Character) targetnode.get("type"); 259 switch(sourcetype) 260 { 261 case 'c': 262 { 263 if(targettype=='c') 264 //处理都是类 265 relation=GetRelationInfo(1);//表示类间关系 266 else 267 //处理一个类一个实例(肯定是类与实例关系) 268 relation=startnode.getString("name")+"--has Individual-->"+targetnode.get("name"); 269 break; 270 } 271 272 case 's': 273 { 274 //实例间的关系 275 relation=GetRelationInfo(2);//表示实例间关系 276 break; 277 } 278 279 } 280 return relation; 281 } 282 283 public String GetRelationInfo(int type) 284 { 285 String relation=""; 286 287 Table Relationtable; 288 if (type==1) 289 { //处理类间关系 290 sql="SELECT C_Descript from ontologyclass where C_Name=\'"+ 291 targetnode.getString("name")+"\' and C_DescriptVal=\'"+startnode.getString("name")+"\'"; 292 Relationtable=GetData(sql); 293 if(Relationtable.getRowCount()>0) 294 relation=targetnode.getString("name")+"---"+Relationtable.getString(0,"C_Descript")+"--->"+startnode.getString("name"); 295 else 296 { //说明这两个类之间不是父子关系,接下来尝试其它关系 297 sql="SELECT * FROM property where Proty_Type=\'OP\' and Proty_Domain=\'"+startnode.getString("name")+"\' and Proty_Range=\'"+targetnode.getString("name")+"\'"; 298 Relationtable=GetData(sql); 299 for(int i=0;i<Relationtable.getRowCount();++i) 300 { 301 relation+=Relationtable.getString(i,"Proty_Domain")+"---"+Relationtable.getString(i,"Proty_Name")+"(定义域>值域)--->"+Relationtable.getString(i,"Proty_Range"); 302 } 303 304 } 305 } 306 else 307 { 308 //处理实例间关系 309 sql="SELECT x.Indiv_Name,x.Indiv_PURI,y.iname from individual as x,tmpindividual as y,property as z"+ 310 " where x.Indiv_PVal=y.iname and y.ipuri=z.Proty_URI and z.Proty_Type='OP' and x.Indiv_URI=\'"+startnode.getString("uri")+"\' and y.iuri=\'"+targetnode.getString("uri")+"\'"; 311 Relationtable=GetData(sql); 312 if(Relationtable.getRowCount()>0) 313 { 314 relation=Relationtable.getString(0,"Indiv_PURI").substring(Relationtable.getString(0,"Indiv_PURI").lastIndexOf("#")+1); 315 relation=Relationtable.getString(0,"Indiv_Name")+"---"+relation+"--->"+Relationtable.getString(0,"iname"); 316 //System.out.print(relation); 317 } 318 319 } 320 321 return relation; 322 } 323 public void itemEntered(VisualItem item, MouseEvent e) 324 { 325 Display d = (Display)e.getSource(); 326 item.setHighlighted(true); 327 //说明选中的是边 328 if(item.getGroup().equals(graphEdges)) 329 { 330 //起始结点在图中结点号 331 int startid=Integer.parseInt(item.getSourceTuple().getString(0)); 332 startnode=graphdata.getNode(startid); 333 //目标结点在图中结点号 334 int targetid=Integer.parseInt(item.getSourceTuple().getString(1)); 335 targetnode=graphdata.getNode(targetid); 336 //System.out.print(relation); 337 d.setToolTipText(GetShowInformation()); 338 m_vis.run("update"); 339 340 } 341 else 342 { 343 uri=item.getString("uri"); 344 String msg=""; 345 if((Character)item.get("type")=='c') 346 { 347 //类结点处理 348 msg="<html>类名:"+item.getString("name")+"<br>资源URI:"+uri; 349 sql="SELECT x.C_Name,z.Proty_Name,y.R_Type,y.R_Val,z.Proty_Range" 350 +" FROM ontologyclass as x,propertyrestriction as y,property as z"+ 351 " WHERE x.C_Uri=y.R_Curi and y.R_Puri=z.Proty_URI and x.C_Uri=\'"+item.getString("uri")+"\'"; 352 Table restrictioninfo=GetData(sql); 353 for(int i=0;i<restrictioninfo.getRowCount();++i) 354 { 355 msg+="<br>约束条件:"+restrictioninfo.getString(i,"C_Name")+" "+restrictioninfo.getString(i,"Proty_Name")+" "+restrictioninfo.getString(i,"R_Type") 356 +"--"+restrictioninfo.getString(i,"R_Val")+" "+restrictioninfo.getString(i,"Proty_Range"); 357 } 358 msg+="</html>"; 359 } 360 else 361 { 362 msg="<html>实例名:"+item.getString("name")+"<br>资源URI:"+uri; 363 //对象属性定义 364 sql="SELECT x.Proty_Name,y.Indiv_Name,y.Indiv_PVal" 365 +" FROM property as x,individual as y WHERE x.Proty_URI=y.Indiv_PURI " 366 +"and x.Proty_Type='OP' and y.Indiv_URI=\'"+item.getString("uri")+"\'"; 367 Table restrictioninfo=GetData(sql); 368 for(int i=0;i<restrictioninfo.getRowCount();++i) 369 { 370 msg+="<br>对象属性定义:"+restrictioninfo.getString(i,"Indiv_Name")+" "+restrictioninfo.getString(i,"Proty_Name")+" " 371 +restrictioninfo.getString(i,"Indiv_PVal"); 372 } 373 374 sql="SELECT x.Proty_Name,y.Indiv_Name,y.Indiv_PVal,x.Proty_Range" 375 +" FROM property as x,individual as y WHERE x.Proty_URI=y.Indiv_PURI " 376 +"and x.Proty_Type='DP' and y.Indiv_URI=\'"+item.getString("uri")+"\'"; 377 restrictioninfo=GetData(sql); 378 for(int i=0;i<restrictioninfo.getRowCount();++i) 379 { 380 msg+="<br>数据类型属性定义:"+restrictioninfo.getString(i,"Indiv_Name")+" "+restrictioninfo.getString(i,"Proty_Name")+" " 381 +restrictioninfo.getString(i,"Indiv_PVal")+"--"+restrictioninfo.getString(i,"Proty_Range"); 382 } 383 msg+="</html>"; 384 } 385 d.setToolTipText(msg); 386 } 387 388 389 390 } 391 392 public void itemExited(VisualItem item, MouseEvent e) 393 { 394 Display d = (Display)e.getSource(); 395 d.setToolTipText(""); 396 m_vis.run("update"); 397 } 398 399 public void itemClicked(VisualItem item, MouseEvent e) 400 { 401 if (!SwingUtilities.isLeftMouseButton(e)) return; 402 if ( e.getClickCount()==2) 403 { 404 405 } 406 } 407 408 409 public void itemPressed(VisualItem item, MouseEvent e) 410 { 411 412 } 413 414 public void itemReleased(VisualItem item, MouseEvent e) 415 { 416 417 } 418 }//end of class DataMouseControl 419 420 421 //添加当前类的边 422 public void AddEdges(Table edges,String startkey,String endkey,String according) 423 { 424 Node source=null,target=null; 425 426 for(int j=0;j<edges.getRowCount();++j) 427 { 428 String startname=edges.getString(j,startkey); 429 String endname=edges.getString(j, endkey); 430 //System.out.print(startname+"---"); 431 //System.out.println(endname); 432 //找到起始端点 433 for(int i=0;i<graphdata.getNodeCount();++i) 434 { 435 String name=graphdata.getNode(i).getString(according); 436 if(name.equalsIgnoreCase(startname)) 437 { 438 source=graphdata.getNode(i); 439 break; 440 } 441 } 442 443 //找到末端点 444 for(int k=0;k<graphdata.getNodeCount();++k) 445 { 446 String name=graphdata.getNode(k).getString(according); 447 if(name.equalsIgnoreCase(endname)) 448 { 449 target=graphdata.getNode(k); 450 break; 451 } 452 } 453 454 //两结点间加条线 455 //System.out.print(source.get(according)+"--"); 456 //System.out.println(target.get(according)); 457 graphdata.addEdge(source, target); 458 459 } 460 }//end of function AddEdges 461 462 public void AddClassNode(Table nodes) 463 { 464 for(int i=0;i<nodes.getRowCount();++i) 465 { 466 Node node=graphdata.addNode(); 467 //System.out.print(nodes.getString(i,key2)); 468 node.setString("uri", nodes.getString(i,"C_Uri")); 469 node.setString("name", nodes.getString(i,"C_Name")); 470 node.set("type",'c'); 471 } 472 //System.out.print(graphdata.getNode(2).get("name")); 473 } 474 475 public void AddIndividualNode(Table nodes) 476 { 477 for(int i=0;i<nodes.getRowCount();++i) 478 { 479 Node node = graphdata.addNode(); 480 node.set("uri",nodes.getString(i, "Indiv_URI")); 481 node.setString("name",nodes.getString(i, "Indiv_Name")); 482 node.set("type",'s'); 483 484 } 485 System.out.print(nodes.getRowCount()); 486 }//end of function AddIndividualNode 487 488 public class MyColorAction extends ColorAction 489 { 490 private Node startnode,targetnode; 491 public MyColorAction(String group,String field) 492 { 493 super(group,field); 494 } 495 496 public int getColor(VisualItem item) 497 { 498 499 //起始结点在图中结点号 500 int startid=Integer.parseInt(item.getSourceTuple().getString(0)); 501 startnode=graphdata.getNode(startid); 502 //目标结点在图中结点号 503 int targetid=Integer.parseInt(item.getSourceTuple().getString(1)); 504 targetnode=graphdata.getNode(targetid); 505 int color=0; 506 switch((Character)startnode.get("type")) 507 { 508 509 case 'c': 510 if((Character)targetnode.get("type")=='c') 511 color=ColorLib.rgb(246,129,4); 512 else 513 color=ColorLib.rgb(0,0,0); 514 break; 515 case 's': 516 color=ColorLib.rgb(0,140,0); 517 break; 518 } 519 520 return color; 521 } 522 523 } 524 public class MyStrokeAction extends StrokeAction 525 { 526 public BasicStroke getStroke(VisualItem item) 527 { 528 return item.isHover()?(bsfocus):(bslosefocus); 529 } 530 } 531 532 public class MyEdgeRenderer extends EdgeRenderer 533 { 534 public MyEdgeRenderer(int edgeType, int arrowType) 535 { 536 super(edgeType,arrowType); 537 } 538 public void render(Graphics2D g, VisualItem item) 539 { 540 /* 541 //虚线 542 float[] dash1 = {5.0f}; 543 BasicStroke s = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f); 544 item.setStroke( s ); 545 // render the edge line 546 */ 547 super.render(g, item); 548 549 // render the edge arrow head, if appropriate 550 if ( m_curArrow != null ) 551 { 552 g.setPaint(ColorLib.getColor(item.getStrokeColor())); 553 super.setArrowHeadSize(10,10); 554 g.fill(m_curArrow); 555 } 556 557 } 558 559 } 560 }
最后的可视化效果:
蓝色结点是类,紫色结点是实例,类与类之间,类与实例,实例与实例之间用不同的线条颜色表示,鼠标移到边或结点对象时,显示相应的语义信息。
这样就完成owl本体片断的可视化,不过效果和protage的比差了些。
转载本文请标明出处。