テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

平安京ビューの簡易可視化プログラムをsvgwriteを使って実装しました

今朝, 平安京ビューの速報で, ツリーを表現するエッジリストを入力として, 各ノードのジオメトリ情報を返すところまで実装したという紹介をしました.

しかし, 出力のジオメトリ情報を見ても, 各ノードがどうやって配置されているかピンと来ません. 例のようにノード数4程度であればなんとかなるかも知れませんが, 100や1000となった時になった時には, 頭の中の平安京は, きっと秩序のないものになってしまいます.

大丈夫, 私たちはコードが書けます. コードを書いて可視化しましょう. 今回, 私が作ったのはあくまでもデバッグ用の可視化プログラムであり, 実際に可視化アプリとして活躍することは出来ません. しかし, 簡単に可視化が出来てデバッグが出来ることは必要です.

どうやって可視化するか

平安京ビューは幾何情報を数学のxy座標系で持っています. これをディスプレイ座標系に直すことが必要になりそうですが(OpenGLならば, xy座標系でやれるのですが, OpenGLはここではやりすぎコージー), これはそこまで難しいことではありません.

では問題は, 何のライブラリを使って可視化するかということになりますが, とりあえずフォーマットとしてはSVGを選びました. SVGの良さは何と言ってもXMLファイルで出力出来ることです. SSHでログインしたLinux開発マシン上でSVGを吐いて, それをローカルのマックにscpしてファイルを開いて確認するという作業に落とし込めます. ガチのグラフィックライブラリでは, DISPLAY環境変数を要求してくることもあります. これはなんとも鬱陶しいことです. また, SVG浮動小数点で座標を指定出来ます. 実際の描画はピクセル単位に落とし込められてしまいますが, 整数に圧縮されるタイミングを遅延出来ることは利点です. 他には, ブラウザで表示出来ることも利点でしょう.

PythonSVGライブラリ

SVGを吐くに当たって, どういうライブラリを使うかですが, Pythonはこういうツールには困らない言語です. メジャーなのは, svgwriteとpysvgがあります. 私はこの2つを比較して, svgwriteを使うことにしました. これらの比較については, http://florian-berger.de/en/articles/creating-simple-svg-from-python に書いてあります. pysvgの作者はJava系の人であり, 設計も極めて糞Java的です(先の文書には, not Pythonicと書いてあります. I agreeです). また, svgwriteの方が開発が健全な気がしました. svgwriteはbitbucket上でMercurialで管理されていますが, pysvgはgooglecode上でsvnで管理されています. svgwriteならば, バグがあってもなんとか修正出来そうです(GitとMercurialは概念的に似ているため)が, pysvgはちょっと無理そうです. GitかMercurialで開発されていないととても不健全そうに見えるのは私がまだ若造だからでしょうか?

SVGにしてもsvgwriteにしても設計はとても自然なので, 学習に苦労はしませんでした. https://svgwrite.readthedocs.org/en/latest/にドキュメントがあります. これと, sampleコードを見て, 適当に理解した気になったので, どれどれ試してみようと思いました. しかしsvgwriteのインストールが出来ない!なぜだ!エラーメッセージから, 糞みたいなバグだとわかったので, 速攻で修正してプルリクを投げました. Mercurialはカンで使いました. https://bitbucket.org/mozman/svgwrite/pull-request/3/fix-etreepy/diff

コード

コードは以下のような感じです. とてもシンプルと思います.

akira@Lyle> cat ez-visualize.py
import heiankyoview as HV
import svgwrite
import sys

fn = sys.argv[1]

dwg = svgwrite.Drawing(size=('100%', '100%'), profile='tiny')

g = HV.EdgeList.read(fn)
L = HV.BFS(g)

tp = HV.TreePacking(g)
tp.pack()

rr = g.getRect( g.getRoot() )
mvx, mvy = 0.5 * rr.w - rr.x, (-0.5) * rr.h - rr.y
W, H = float(rr.w + 1), float(rr.h + 1)

def toperc(n):
        return "%f" % (100.0 * n) + "%"

for n in L:
        rect = g.getRect(n)
        rect.translate( (mvx, mvy) )

        # 0.0-1.0
        x, y = rect.left() / W, -rect.up() / H
        w, h = rect.w / W, rect.h / H

        dwg.add(dwg.rect(
                insert=(toperc(x), toperc(y)),
                size=(toperc(w), toperc(h)),
                stroke="black",
                stroke_width=1,
                fill_opacity=0))

print(dwg.tostring())

可視化結果

今朝の記事で紹介した4ノードの簡単なエッジリストに対する可視化は, 以下の通りです. 可視化によって, 長方形の間隔が均等ではないことが分かりました. 本質的な問題ではありませんが, 今後修正しようと思います.

平安京ビューはこのように, 階層関係をネストによって表現します. ツリーマップに対する明らかな優位点は, ツリーマップでは, リーフノードが小さくなりすぎてほぼ消滅したり, aspect ratioが極端になりすぎて見えにくくなることがありますが, 平安京ビューはリーフノードからボトムアップで処理を実行していくため, このようなことにはならない点です. 一長一短はありますが, ツリーマップは広く応用されているのに比べ, 平安京ビューは極めてマイナー, 日本の中では知っている人も微々いますが, 海外では認知度がたぶんほぼゼロでしょう. これはあまりにもやられすぎだと思うので, 私のソフトウェアを突破口にして, 平安京ビューが少しでも知れ渡れば良いなと思います.

f:id:akiradeveloper529:20130616200139p:plain

課題

次は, ディレクトリ階層など, 比較的大きなツリー構造を可視化してみたいと思います. バグが見つかる可能性があります. 今の考えでは, Pythonスクリプトで閉じるのではなく, 一度エッジリストとしてファイル出力してから, 今回紹介した可視化プログラムにfeedしようと思います. どなたかが書いてくださればとても嬉しいです. mkel <dirname>と言った感じでしょうか?