サイト内におけるページ間のリンク構造を可視化する
戻る
::::::::::::::
GraphViewer.bat
::::::::::::::
java GraphViewer
::::::::::::::
CommonDrawer.java
::::::::::::::
import java.awt.*;
import java.util.*;
/**
* $Id: GraphViewer.html,v 1.1 2009/06/22 16:11:45 kishi Exp kishi $
* @author KISHI Yasuhiro
* 2次元座標をリスト化したものを線分で繋いで描画する汎用クラス
*/
public class CommonDrawer implements Paintable {
private java.util.Map vertexMap;
private Color color;
private double cx;
private double cy;
public void setVertexMap( java.util.Map vertexMap, Color color, double cx, double cy ) {
this.vertexMap = vertexMap;
this.color = color;
this.cx = cx;
this.cy = cy;
}
public void paint( Graphics g ) {
g.setColor( color );
Iterator iterator = vertexMap.keySet().iterator();
while ( iterator.hasNext() ) {
String url = ( String ) iterator.next();
Vertex vertex = ( Vertex ) vertexMap.get( url );
double x = vertex.getX();
double y = vertex.getY();
java.util.List links = vertex.getLinks();
for ( int i = 0;i < links.size();i++ ) {
Vertex link = ( Vertex ) links.get( i );
g.drawLine( ( int ) ( x + cx ), ( int ) ( y + cy ), ( int ) ( link.getX() + cx ), ( int ) ( link.getY() + cy ) );
}
}
}
}
::::::::::::::
CrossReferenceContainer.java
::::::::::::::
import java.util.*;
import java.io.*;
/**
* $Id: GraphViewer.html,v 1.1 2009/06/22 16:11:45 kishi Exp kishi $
* @author KISHI Yasuhiro
*/
public class CrossReferenceContainer extends HashMap {
private Reader reader;
/** クローリングした結果が格納されるMAP */
private Map crawlingResult;
public CrossReferenceContainer() {
super();
crawlingResult = new HashMap();
}
public void setReader( Reader reader ) {
this.reader = reader;
}
public void parse() throws Throwable {
try {
// クロール結果の内容を全て読み込む
setCrawlingResult();
// クロスレファレンスを求める
getCrossReference();
} catch ( Throwable e ) {
throw e;
}
}
private void getCrossReference() {
Iterator iterator = this.keySet().iterator();
while ( iterator.hasNext() ) {
String url = ( String ) iterator.next();
seekLinks( url );
}
}
private void seekLinks( String url ) {
System.err.print( "*" );
// System.out.println( url );
Iterator iterator = crawlingResult.keySet().iterator();
while ( iterator.hasNext() ) {
String key = ( String ) iterator.next();
String value = ( String ) crawlingResult.get( key );
if ( url.equals( value ) ) {
if ( key.length() == 0 ) {
// ルートノードである
continue;
}
String parentKey = getParentKey( key );
// System.out.println( "*** " + key + " --> " + parentKey);
addLinks( url, parentKey );
}
}
}
private String getParentKey( String key ) {
if ( !key.contains( "-" ) ) {
return "";
} else {
String[] arr = key.split( "-" );
StringBuilder sb = new StringBuilder();
sb.append( arr[ 0 ] );
for ( int i = 1;i < arr.length - 1;i++ ) {
sb.append( "-" );
sb.append( arr[ i ] );
}
return sb.toString();
}
}
private void addLinks( String url, String parentKey ) {
String linkURL = ( String ) crawlingResult.get( parentKey );
// System.out.println( " " + linkURL );
HashSet links = ( HashSet ) this.get( url );
if ( !links.contains( linkURL ) ) {
links.add( linkURL );
}
}
private void setCrawlingResult() throws Throwable {
BufferedReader br = new BufferedReader( reader );
try {
String line = null;
while ( ( line = br.readLine() ) != null ) {
// System.out.println( line);
String token[] = line.split( "\t" );
// System.out.println(token[0]);
if ( token.length != 2 ) {
System.err.println( "不正な行です-> " + line );
continue;
}
String path = token[ 0 ];
String url = token[ 1 ];
crawlingResult.put( path, url );
this.put( url, new HashSet() );
}
br.close();
} catch ( Throwable e ) {
throw e;
}
}
public void dumpAll() {
System.out.println();
Iterator iterator = this.keySet().iterator();
int i = 0;
while ( iterator.hasNext() ) {
String url = ( String ) iterator.next();
HashSet links = ( HashSet ) this.get( url );
// それぞれのURLとそれにリンクしているURLの数をダンプする
System.out.printf( "%s", url );
System.out.printf( "\t%4d\n", links.size() );
}
}
/** 単体試験 */
static public void main( String[] args ) throws Throwable {
if ( args.length != 1 ) {
System.out.println( "Usage: java CrossReferenceContainer [inputFileName]" );
System.exit( 1 );
}
CrossReferenceContainer container = new CrossReferenceContainer();
try {
container.setReader( new FileReader( args[ 0 ] ) );
container.parse();
container.dumpAll();
} catch ( Throwable e ) {
throw e;
}
}
}
::::::::::::::
DifferentiationManager.java
::::::::::::::
import java.util.*;
/**
* $Id: GraphViewer.html,v 1.1 2009/06/22 16:11:45 kishi Exp kishi $
* @author KISHI Yasuhiro
* それぞれのVertexが持っているリンクの数をもとに表示位置を決める
*/
public class DifferentiationManager {
/** グループの個数 */
private int divisionCount;
/** グループ分けしたVertexを格納したHASH */
private Map divisionMap;
/** 最大半径 */
private double maxRadius;
private Map vertexMap;
public DifferentiationManager( Map vertexMap ) {
this.vertexMap = vertexMap;
}
public void setDivisionCount( int divisionCount ) {
this.divisionCount = divisionCount;
}
public void setMaxRadius( double maxRadius ) {
this.maxRadius = maxRadius;
}
/** 各々のVertexが持つリンク数の最大値を求める */
private int getMaxNumberOfLinks() {
int max = 0;
Iterator iterator = vertexMap.keySet().iterator();
while ( iterator.hasNext() ) {
String url = ( String ) iterator.next();
Vertex vertex = ( Vertex ) vertexMap.get( url );
List list = vertex.getLinks();
if ( max < list.size() ) {
max = list.size();
}
}
return max;
}
/** 度数分布を求める */
private void getFrequencyDistribution() {
int maxNumberOfLinks = getMaxNumberOfLinks();
Iterator iterator = vertexMap.keySet().iterator();
while ( iterator.hasNext() ) {
String url = ( String ) iterator.next();
Vertex vertex = ( Vertex ) vertexMap.get( url );
List list = vertex.getLinks();
// 保持するリンク数ごとにグループ分けするためのIDを求める
int division = ( int ) ( list.size() * 1.0 / maxNumberOfLinks * divisionCount );
// System.out.println( vertex.getName() + "\t" + list.size() + "\t" + division);
if ( divisionMap == null ) {
divisionMap = new TreeMap();
}
List vertexList = ( List ) divisionMap.get( division );
if ( vertexList == null ) {
vertexList = new LinkedList();
vertexList.add( vertex );
divisionMap.put( division, vertexList );
} else {
vertexList.add( vertex );
}
}
}
/** 得られた度数分布をダンプする */
private void dumpFrequencyDistribution() {
Iterator iterator = divisionMap.keySet().iterator();
while ( iterator.hasNext() ) {
Integer division = ( Integer ) iterator.next();
List vertexList = ( List ) divisionMap.get( division );
double radius = maxRadius * ( divisionCount + 1 - division.intValue() ) / divisionCount;
System.out.println( division + "\t" + vertexList.size() + "\t" + radius );
}
}
// グループ化したVertexに座標を与える
public void differentiate() {
getFrequencyDistribution();
//======================================
// DEBUG用
//======================================
dumpFrequencyDistribution();
// 各グループごとに座標をセットしていく
Iterator iterator = divisionMap.keySet().iterator();
while ( iterator.hasNext() ) {
Integer division = ( Integer ) iterator.next();
List vertexList = ( List ) divisionMap.get( division );
double radius = maxRadius * ( divisionCount + 1 - division.intValue() ) / divisionCount;
// 同一グループの場合は、同一円周上に均等に配置する
for ( int i = 0;i < vertexList.size();i++ ) {
double radian = 2.0 * Math.PI * i / vertexList.size();
double x = radius * Math.cos( radian );
double y = radius * Math.sin( radian );
// X座標、Y座標を与える
Vertex vertex = ( Vertex ) vertexList.get( i );
vertex.setPosition( x, y );
}
}
}
}
::::::::::::::
DrawingPanel.java
::::::::::::::
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
/**
* $Id: GraphViewer.html,v 1.1 2009/06/22 16:11:45 kishi Exp kishi $
* @author KISHI Yasuhiro
*/
public class DrawingPanel extends JPanel implements Runnable {
private GraphModel model;
private CommonDrawer drawer;
public DrawingPanel() {
// モデルのインスタンス化
try {
// --- 後でUIからパラメータを設定できるように変更する予定
model = new GraphModel( "myData.tsv" );
} catch ( Throwable e ) {
e.printStackTrace();
}
// 各Vertexが保有するリンクの数から、それぞれの座標を設定する
// --- 後でUIからパラメータを設定できるように変更する予定
DifferentiationManager manager = new DifferentiationManager( model.getVertexMap() );
manager.setDivisionCount( 10 );
manager.setMaxRadius( 150.0 );
manager.differentiate();
// 描画するクラスをインスタンス化
drawer = new CommonDrawer();
drawer.setVertexMap( model.getVertexMap(), Color.white, 400.0, 300.0 );
// スレッド開始
Thread thread = new Thread( this );
thread.start();
}
public void run() {
while ( true ) {
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
repaint();
}
}
);
try {
Thread.sleep( 100 );
} catch ( Exception e ) {
e.printStackTrace();
}
}
}
public void paintComponent( Graphics g ) {
int width = getWidth();
int height = getHeight();
// 画面クリア
g.setColor( Color.black );
// このコンポーネントのサイズを取得する
g.fillRect( 0 , 0 , width , height );
// 各頂点を結ぶ線分を描画
drawer.paint( g );
// 各頂点を移動させる(収縮させる)
VertexDisplacer.deflate( model.getVertexMap() );
// 各頂点を移動させる(膨張させる)
VertexDisplacer.inflate( model.getVertexMap() );
}
/** あとで別のユーティリティクラスに移動する */
static String sprintf( String format, Object obj ) throws Exception {
String value = null;
try {
// C言語のsprinfもどき
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter( baos );
pw.printf( format, obj );
pw.flush();
pw.close();
value = baos.toString( "UTF8" );
baos.close();
} catch ( Exception e ) {
throw e;
} finally {
return value;
}
}
}
::::::::::::::
GraphModel.java
::::::::::::::
import java.io.*;
import java.util.*;
/**
$Id: GraphViewer.html,v 1.1 2009/06/22 16:11:45 kishi Exp kishi $
@author KISHI Yasuhiro
*/
public class GraphModel {
/** VertexクラスのオブジェクトとURLの関係性を格納したMAP */
private Map vertexMap;
/** 入力TSVファイル */
private String inputFileName;
/** URL同士のクロスレファレンス情報を格納するMAP */
private CrossReferenceContainer container;
public GraphModel( String inputFileName ) throws Throwable {
this.inputFileName = inputFileName;
try {
init();
} catch ( Throwable e ) {
throw e;
}
// VertexクラスのオブジェクトとURLの関係性を構築
createVertexMap();
// Vertexクラスのオブジェクト同士の関連性を構築
createRelationshipAmongVertices();
}
/** 指定したURLに対応するリンクをVertexレベルで求める */
public List getVertexLinks( String url ) {
Vertex vertex = ( Vertex ) vertexMap.get( url );
return vertex.getLinks();
}
/** Vertexクラスのオブジェクト同士の関連性を構築 */
private void createRelationshipAmongVertices() {
Iterator iterator = container.keySet().iterator();
while ( iterator.hasNext() ) {
String url = ( String ) iterator.next();
// このURLにリンクされているURLのリストが取得される
Set links = ( HashSet ) container.get( url );
Vertex vertex = ( Vertex ) vertexMap.get( url );
linkVertices( vertex, links );
}
}
private void linkVertices( Vertex vertex, Set links ) {
Iterator iterator = links.iterator();
while ( iterator.hasNext() ) {
String url = ( String ) iterator.next();
Vertex counterVertex = ( Vertex ) vertexMap.get( url );
// Vertex同士をリンクする
vertex.addLink( counterVertex );
}
}
/** VertexクラスのオブジェクトとURLの関係性を構築 */
private void createVertexMap() {
Iterator iterator = container.keySet().iterator();
vertexMap = new TreeMap();
while ( iterator.hasNext() ) {
String url = ( String ) iterator.next();
// System.out.println( url );
vertexMap.put( url, new Vertex( url ) );
}
}
/** クロスレファレンスを求める */
private void init() throws Throwable {
container = new CrossReferenceContainer();
try {
container.setReader( new FileReader( inputFileName ) );
container.parse();
} catch ( Throwable e ) {
throw e;
}
}
public Map getVertexMap() {
return vertexMap;
}
public void dumpVertexPosition() {
// セットされた座標をダンプしてみる
Iterator iterator = vertexMap.keySet().iterator();
while ( iterator.hasNext() ) {
String url = ( String ) iterator.next();
Vertex vertex = ( Vertex ) vertexMap.get( url );
double x = vertex.getX();
double y = vertex.getY();
System.out.printf( "%s %5.2f %5.2f\n",
vertex.getName(), x, y );
}
}
/** 単体試験 */
static public void main( String[] args ) throws Throwable {
if ( args.length != 1 ) {
System.out.println( "Usage: java GraphModel [inputFileName]" );
System.exit( 1 );
}
GraphModel model;
try {
model = new GraphModel( args[ 0 ] );
} catch ( Throwable e ) {
throw e;
}
// テストコード→リンクの確認
System.out.println();
List list = model.getVertexLinks( "http://www.yodobashi.com/enjoy_more/cm/cat_8427434/66.html" );
Iterator iterator = list.iterator();
while ( iterator.hasNext() ) {
Vertex vertex = ( Vertex ) iterator.next();
System.out.println( "\t" + vertex.getName() );
}
// 各Vertexが保有するリンクの数から、それぞれの座標を設定する
DifferentiationManager manager = new DifferentiationManager( model.getVertexMap() );
manager.setDivisionCount( 10 );
manager.setMaxRadius( 200.0 );
manager.differentiate();
// 頂点の座標をダンプしてみる
model.dumpVertexPosition();
}
}
::::::::::::::
GraphViewer.java
::::::::::::::
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/**
<pre>
サイト内におけるページ間のリンク構造を可視化する
</pre>
$Id: GraphViewer.html,v 1.1 2009/06/22 16:11:45 kishi Exp kishi $
@author KISHI Yasuhiro
*/
public class GraphViewer extends JFrame {
private DrawingPanel dp = new DrawingPanel();
public static void main( String args[] ) {
new GraphViewer().setVisible( true );
}
public GraphViewer() {
//----------------------------------------------------
// タイトル
//----------------------------------------------------
setTitle( "サイト内におけるページ間のリンク構造を可視化する" );
setDefaultCloseOperation( EXIT_ON_CLOSE );
//----------------------------------------------------
// 描画用パネルを追加
//----------------------------------------------------
dp.setPreferredSize( new Dimension( 800 , 600 ) );
this.add( dp );
this.pack();
}
}
::::::::::::::
Paintable.java
::::::::::::::
import java.awt.*;
/**
$Id: GraphViewer.html,v 1.1 2009/06/22 16:11:45 kishi Exp kishi $
@author KISHI Yasuhiro
*/
public interface Paintable {
public void paint( Graphics g );
}
::::::::::::::
Position.java
::::::::::::::
public class Position {
double x;
double y;
public Position( double x, double y ) {
this.x = x;
this.y = y;
}
public void setXY( double x, double y ) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
}
::::::::::::::
Vertex.java
::::::::::::::
import java.util.*;
/**
* $Id: GraphViewer.html,v 1.1 2009/06/22 16:11:45 kishi Exp kishi $
* @author KISHI Yasuhiro
* グラフ構造における頂点を表現するクラス
*/
public class Vertex {
private List links;
private String name;
private Position position;
public Vertex( String name ) {
this.name = name;
}
/** XY座標を与える */
public void setPosition( double x, double y ) {
if ( position == null ) {
position = new Position( x, y );
} else {
position.setXY( x, y );
}
}
public double getX() {
return position.getX();
}
public double getY() {
return position.getY();
}
public String getName() {
return name;
}
public void addLink( Vertex vertex ) {
if ( links == null ) {
links = new LinkedList();
}
links.add( vertex );
}
public List getLinks() {
return links;
}
public void deflateNext() {
//=========================================================
// リンクしている頂点の重心をもとめてそれに近づく(距離1.0だけ)
//=========================================================
double x = this.position.getX();
double y = this.position.getY();
List list = this.getLinks();
Iterator iterator = list.iterator();
double avgX = 0.0;
double avgY = 0.0;
while ( iterator.hasNext() ) {
Vertex link = ( Vertex ) iterator.next();
avgX += link.getX();
avgY += link.getY();
}
// 重心の位置を取得
avgX /= list.size();
avgY /= list.size();
double diffX = avgX - x;
double diffY = avgY - y;
double distance = Math.sqrt( diffX * diffX + diffY * diffY );
x += 1.0 / distance * diffX;
y += 1.0 / distance * diffY;
position.setXY( x, y );
}
public void inflateNext( double cx, double cy ) {
//=========================================================
// 全体の中心点をもとめてそこから離れる(距離1.0だけ)
//=========================================================
double x = this.position.getX();
double y = this.position.getY();
List list = this.getLinks();
Iterator iterator = list.iterator();
double diffX = cx - x;
double diffY = cy - y;
double distance = Math.sqrt( diffX * diffX + diffY * diffY );
x -= 1.0 / distance * diffX;
y -= 1.0 / distance * diffY;
position.setXY( x, y );
}
}
::::::::::::::
VertexDisplacer.java
::::::::::::::
import java.util.*;
/**
$Id: GraphViewer.html,v 1.1 2009/06/22 16:11:45 kishi Exp kishi $
*/
public class VertexDisplacer {
static public void deflate( Map vertexMap ) {
Iterator iterator = vertexMap.keySet().iterator();
while ( iterator.hasNext() ) {
String url = ( String ) iterator.next();
Vertex vertex = ( Vertex ) vertexMap.get( url );
vertex.deflateNext();
}
}
static public void inflate( Map vertexMap ) {
Iterator iterator;
// 全体の重心を求める
double wx = 0.0;
double wy = 0.0;
iterator = vertexMap.keySet().iterator();
while ( iterator.hasNext() ) {
String url = ( String ) iterator.next();
Vertex vertex = ( Vertex ) vertexMap.get( url );
wx += vertex.getX();
wy += vertex.getY();
}
wx /= vertexMap.size();
wy /= vertexMap.size();
// 全体の重心から膨張させる
iterator = vertexMap.keySet().iterator();
while ( iterator.hasNext() ) {
String url = ( String ) iterator.next();
Vertex vertex = ( Vertex ) vertexMap.get( url );
vertex.inflateNext( wx, wy );
}
}
}
戻る