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 swing._
2: import org.apache.commons.collections15.Factory
3: import edu.uci.ics.jung.algorithms.layout.{Layout,FRLayout}
4: import edu.uci.ics.jung.graph.{Graph,UndirectedSparseGraph}
5: import edu.uci.ics.jung.io.GraphMLReader
6: import edu.uci.ics.jung.visualization.VisualizationViewer
7: object Sample7a extends SimpleSwingApplication {
8: def top = new MainFrame {
9: title = "Graph View: Reading GraphML"
10: val filename: String = "data/sample-data1.xml"
11: val graph = readFile(filename)
12: val layout: Layout[MyNode,MyEdge] = new FRLayout[MyNode,MyEdge](graph)
13: val panel = new VisualizationViewer[MyNode,MyEdge](layout)
14: contents = Component.wrap(panel)
15: }
16: def readFile(filename: String): Graph[MyNode,MyEdge] = {
17: val nodeFactory = new Factory[MyNode] {
18: override def create(): MyNode = new MyNode("")
19: }
20: val edgeFactory = new Factory[MyEdge] {
21: override def create(): MyEdge = new MyEdge("")
22: }
23: val graphMLReader =
new GraphMLReader[Graph[MyNode,MyEdge],MyNode,MyEdge](nodeFactory, edgeFactory)
24: val graph: Graph[MyNode,MyEdge] = new UndirectedSparseGraph[MyNode,MyEdge]
25: graphMLReader.load(filename, graph)
26: graph
27: }
28: }
23行目でGraphMLReader[Graph[MyNode,MyEdge], MyNode,MyEdge]クラスのインスタンスgraphMLReaderを生成しています。コンストラクタは引き数として二つのファクトリオブジェクトを取ります。ノードを生成するFactory[MyNode]型のオブジェクトとエッジを生成するFactory[MyEdge]型のオブジェクトです。それぞれのファクトリは17行目から19行目、20行目から22行目で定義されています。25行目で、graphMLReaderオブジェクトのload()メソッドが呼び出され、GraphMLのファイルが読まれて、そのグラフデータによってgraphにノードやエッジが追加されます。
エラー処理を何もしていないので、main()関数も例外を投げます。
上のプログラムを実行するとすると、たとえば下のようなグラフが描かれます。場合によって配置は異なります。
上の例では、GraphMLで記述されたグラフの構造だけを読み込みました。実際には、ノードやエッジに付随する情報も必要になります。次の例ではノードのidを取り出す方法を紹介します。
まず、MyNode.scalaを下のように変更します。labelをvalからvarに変えています。一旦ノードを作成したあとで、labelを変更するためです。
1: class MyNode2(var label: String) {
2: override def toString: String = label
3: }
ノードのidをlabelとして設定するには下のようにします。
2: import org.apache.commons.collections15.{BidiMap,Factory}
(中略)
21: def readFile(filename: String): Graph[MyNode2,MyEdge] = {
(中略)
30: graphMLReader.load(filename, graph)
31: val nodeIds: BidiMap[MyNode2,String] = graphMLReader.getVertexIDs
32: graph.getVertices.foreach((node: MyNode2) => {
33: node.label = nodeIds.get(node)
34: })
35: graph
36: }
37: }
graphMLReaderオブジェクトのload()メソッドを呼び出したあと、getVertexIDs()メソッドで、ノードと文字列のマップを取り出します。マップはBidiMap[MyNode2,String]型のオブジェクトとして定義されます。31行目でBidiMap[MyNode2,String]型のオブジェクトnodeIdsを取り出しています。nodeIdsオブジェクトのget()メソッドを、ノードを引数として呼び出すとそのノードのidを取り出せます(33行目)。これを利用して、32行目から34行目ですべてのノードのlabelを設定し直しています。あとはノードのラベル表示と同じです。
上のプログラムを実行するとすると、下のように各ノードのidが表示されます。
idは通常内部的な表現に使用されるもので、ユーザに見せるものではありません。応用の面からはノードやエッジの属性の方が重要です。次の例ではノードやエッジの属性を取り出す方法を紹介します。
2: import java.util.Map;
(中略)
6: import edu.uci.ics.jung.io.{GraphMLMetadata,GraphMLReader}
(中略)
31: graphMLReader.load(filename, graph)
32: val nodeMetadata: Map[String,GraphMLMetadata[MyNode2]] =
graphMLReader.getVertexMetadata
33: val metadata: GraphMLMetadata[MyNode2] = nodeMetadata.get("label")
34: graph.getVertices.foreach((node: MyNode2) => {
35: node.label = metadata.transformer.transform(node)
36: })
37: graph
38: }
39: }
graphMLReaderオブジェクトのload()メソッドを呼び出したあと、getVertexMetadata()メソッドで、ノードとメタデータのマップを取り出します。マップはMap[String, GraphMLMetadata[MyNode2]]型のオブジェクトとして定義されます。32行目でMap[String, GraphMLMetadata[MyNode2]]型のnodeMetadataを取り出しています。オブジェクトnodeMetadataのget()メソッドを、属性名を表す文字列を引数として呼び出すと、ノードから属性値への写像を取り出せます(33行目)。その写像のtransformerフィールドがTransformer[MyNode2,String]型のオブジェクトになっています。これを利用して、34行目から36行目ですべてのノードのlabelを設定し直しています。あとはノードのラベル表示と同じです。
上のプログラムを実行するとすると、下のように各ノードのlabel属性が表示されます。
JUNG2.0でグラフを描くために使用するBasicVisualizationServer[V,E]クラスはjavax.swing.JComponentのサブクラスなので、paint()メソッドを利用して、BufferedImageに描画することができます。それをファイルに書き出すことで、PNG形式で(あるいは他の形式で)画像を保存できます。
下はプログラムの要点を取り出したものです。
val panel = new BasicVisualizationServer[MyNode,MyEdge](layout, viewArea)
(中略:グラフを描く)
val bufferedImage =
new BufferedImage(panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_INT_RGB)
val g2: Graphics2D = bufferedImage.createGraphics
panel.paint(g2)
g2.dispose()
ImageIO.write(bufferedImage, "png", file)
画像データを直接プリンタに出力することもできます。下のプログラムを参考にして下さい。