GraphMLはグラフを記述するためのXMLベースのファイルフォーマットです。下はGraphMLで記述された無向グラフの例です。GraphMLに関する詳細情報はこちらにあります。
<?xml version="1.0" encoding="UTF-8"?> <graphml xmlns="http://graphml.graphdrawing.org/xmlns"> <key id="label" for="node" attr.name="label" attr.type="string"/> <key id="count" for="edge" attr.name="count" attr.type="int"/> <graph edgedefault="undirected" id="G"> <node id ="a190292"> <data key="label">Kazuo</data> </node> <node id ="a57781"> <data key="label">Kozo</data> </node> (中略) <edge id="e1" source="a57781" target="a190292"> <data key="count">6</data> </edge> <edge id="e2" source="a137438" target="a190292"> <data key="count">1</data> </edge> (中略) </graph></graphml>
GraphMLでは、ノードやエッジに様々な属性を設定できます。上のサンプルデータではノードにはstring型のlabelを、エッジにはint型のcountを属性として設定しています。id="a19292"のノードはlabel属性の値が"Kazuo"になっています。またid="e1"のエッジはid="a57781"のノードとid="a190292"のノードを接続していて、count属性の値が6になっています。
JUNG2.0にはGraphMLを読み込むためのGraphMLReader<Graph<V,E>,V,E>クラスが用意されています。下のプログラムはこのクラスを利用してGraphMLで記述されたデータを読み込んで表示します。
1: import java.io.IOException; 2: import javax.swing.JFrame; 3: import javax.xml.parsers.ParserConfigurationException; 4: import org.apache.commons.collections15.Factory; 5: import org.xml.sax.SAXException; 6: import edu.uci.ics.jung.algorithms.layout.FRLayout; 7: import edu.uci.ics.jung.graph.Graph; 8: import edu.uci.ics.jung.graph.UndirectedSparseGraph; 9: import edu.uci.ics.jung.io.GraphMLReader; 10: import edu.uci.ics.jung.visualization.VisualizationViewer; 11: public class Sample7a { 12: protected static Graph<MyNode,MyEdge> readFile(String filename) throws ParserConfigurationException, SAXException, IOException { 13: Factory<MyNode> nodeFactory = new Factory<MyNode>() { 14: @Override 15: public MyNode create() { 16: return new MyNode(""); 17: } 18: }; 19: Factory<MyEdge> edgeFactory = new Factory<MyEdge>() { 20: @Override 21: public MyEdge create() { 22: return new MyEdge(""); 23: } 24: }; 25: GraphMLReader<Graph<MyNode,MyEdge>, MyNode,MyEdge> graphMLReader = new GraphMLReader<Graph<MyNode,MyEdge>, MyNode,MyEdge>(nodeFactory, edgeFactory); 26: Graph<MyNode,MyEdge> graph = new UndirectedSparseGraph<MyNode,MyEdge>(); 27: graphMLReader.load(filename, graph); 28: return graph; 29: } 30: public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException { 31: String filename = "data/sample-data1.xml"; 32: Graph<MyNode,MyEdge> graph = readFile(filename); 33: VisualizationViewer<MyNode,MyEdge> panel = new VisualizationViewer<MyNode,MyEdge>(new FRLayout<MyNode,MyEdge>(graph)); 34: JFrame frame = new JFrame("Graph View: Reading GraphML"); 35: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 36: frame.getContentPane().add(panel); 37: frame.pack(); 38: frame.setVisible(true); 39: } 40: }
25行目でGraphMLReader<Graph<MyNode,MyEdge>, MyNode,MyEdge>クラスのオブジェクトgraphMLReaderを生成しています。コンストラクタは引き数として二つのファクトリオブジェクトを取ります。ノードを生成するFactory<MyNode>型のオブジェクトとエッジを生成するFactory<MyEdge>型のオブジェクトです。それぞれのファクトリは13行目から18行目、19行目から24行目で定義されています。27行目で、graphMLReaderオブジェクトのload()メソッドが呼び出され、GraphMLのファイルが読まれて、そのデータによってgraphにノードやエッジが追加されます。
エラー処理を何もしていないので、main()関数も例外を投げます。
上のプログラムを実行するとすると、たとえば下のようなグラフが描かれます。場合によって配置は異なります。
上の例では、GraphMLで記述されたグラフの構造だけを読み込みました。実際には、ノードやエッジに付随する情報も必要になります。次の例ではノードのidを取り出す方法を紹介します。
4: import org.apache.commons.collections15.BidiMap; (中略) 15: protected static Graph<MyNode,MyEdge> readFile(String filename) throws ParserConfigurationException, SAXException, IOException { (中略) 30: graphMLReader.load(filename, graph); 31: final BidiMap<MyNode,String> nodeIds = graphMLReader.getVertexIDs(); 32: for (MyNode node : graph.getVertices()) { 33: node.label = nodeIds.get(node); 34: } 35: return graph; 36: }
graphMLReaderオブジェクトのload()メソッドを呼び出したあと、getVertexIDs()メソッドで、ノードと文字列のマップを取り出します。マップはBidiMap<MyNode,String>クラスのオブジェクトとして定義されます。31行目でBidiMap<MyNode,String>クラスのオブジェクトnodeIdsを取り出しています。nodeIdsオブジェクトのget()メソッドを、ノードを引数として呼び出すとそのノードのidを取り出せます(33行目)。これを利用して、32行目から34行目ですべてのノードのlabelを設定しなおしています。あとはノードのラベル表示と同じです。
上のプログラムを実行するとすると、下のように各ノードのidが表示されます。
idは通常内部的な表現に使用されるもので、ユーザに見せるものではありません。応用の面からはノードやエッジの属性の方が重要です。次の例ではノードやエッジの属性を取り出す方法を紹介します。
2: import java.util.Map; (中略) 10: import edu.uci.ics.jung.io.GraphMLMetadata; (中略) 16: protected static Graph<MyNode,MyEdge> readFile(String filename) throws ParserConfigurationException, SAXException, IOException { (中略) 30: Graph<MyNode,MyEdge> graph = new UndirectedSparseGraph<MyNode,MyEdge>(); 31: graphMLReader.load(filename, graph); 32: final Map<String, GraphMLMetadata<MyNode>> nodeMetadata = graphMLReader.getVertexMetadata(); 33: GraphMLMetadata<MyNode> metadata = nodeMetadata.get("label"); 34: for (MyNode node : graph.getVertices()) { 35: node.label = metadata.transformer.transform(node); 36: } 37: return graph; 38: }
graphMLReaderオブジェクトのload()メソッドを呼び出したあと、getVertexMetadata()メソッドで、ノードとメタデータのマップを取り出します。マップはMap<String, GraphMLMetadata<MyNode>>クラスのオブジェクトとして定義されます。32行目でMap<String, GraphMLMetadata<MyNode>>クラスのオブジェクトnodeMetadataを取り出しています。nodeMetadataオブジェクトのget()メソッドを、属性名を表す文字列を引数として呼び出すと、ノードから属性値への写像を取り出せます(33行目)。その写像のtransformerフィールドがTransformer<MyNode,String>オブジェクトになっています。これを利用して、34行目から36行目ですべてのノードのlabelを設定しなおしています。あとはノードのラベル表示と同じです。
上のプログラムを実行するとすると、下のように各ノードのlabel属性が表示されます。
JUNG2.0でグラフを描くために使用するBasicVisualizationServer<V,E>クラスはjavax.swing.JComponentのサブクラスなので、paint()メソッドを利用して、BufferedImageに描画することができます。それをファイルに書き出すことで、PNG形式で(あるいは他の形式で)画像を保存できます。
下はプログラムの要点を取り出したものです。
panel = new BasicVisualizationServer<MyNode,MyEdge>(layout); (中略:グラフを描く) BufferedImage bufferedImage = new BufferedImage(panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics2D g2 = bufferedImage.createGraphics(); panel.paint(g2); ImageIO.write(bufferedImage, "png", file);
画像データを直接プリンタに出力することもできます。下のプログラムを参考にして下さい。