この記事の全文はChatGPTによって生成されました。
人間による内容の確認は一切行っていませんのでご注意ください。
IGV 内部構造入門
この文書は、IGV を「使う」側から「改造する」側へ進むための入門です。対象は、Java と Swing を少し知っているが、IGV のコードベースにはまだ慣れていない人です。
目的は、IGV の全体を一度に理解することではありません。まず、起動、画面、トラック、描画、データ読み込み、設定がどうつながるかを把握します。その地図があると、自分に必要な機能を足す場所を探しやすくなります。
IGV は長く使われてきた Java アプリケーションです。小さな MVC アプリのように、すべてが整理されているわけではありません。最初は設計図を探すより、実際に中心になっているクラスを追うほうが理解しやすいです。
0. 開発環境とよく使うコマンド
IGV を改造する前に、まずローカルでビルドと起動ができる状態にします。igv は Gradle プロジェクトです。build.gradle では Java toolchain として Java 21 が指定されています。
開発中は、最初から配布物を作る必要はありません。まず ./gradlew classes でコンパイルを確認します。次に ./gradlew run で GUI を起動します。IDE から起動する場合は、メインクラスに org.igv.ui.Main を指定します。
| 目的 | macOS / Linux | Windows | 確認できること |
|---|---|---|---|
| Java の確認 | java -version |
java -version |
Java 21 を使っているか |
| Gradle wrapper の確認 | ./gradlew --version |
gradlew.bat --version |
Gradle と JVM の組み合わせ |
| コンパイル確認 | ./gradlew classes |
gradlew.bat classes |
Java コードが通るか |
| アプリ起動 | ./gradlew run |
gradlew.bat run |
org.igv.ui.Main から起動できるか |
| jar 作成 | ./gradlew jar |
gradlew.bat jar |
build/libs/igv.jar を作れるか |
| 配布用ディレクトリ作成 | ./gradlew createDist |
gradlew.bat createDist |
build/IGV-dist を作れるか |
| テスト実行 | ./gradlew test |
gradlew.bat test |
既存テストが通るか |
| クリーンビルド | ./gradlew clean classes |
gradlew.bat clean classes |
生成物を消して再確認できるか |
最初は、次の確認で十分です。
cd igv
java -version
./gradlew --version
./gradlew classes
./gradlew run
配布用に近い形で確認したい場合は createDist を使います。build/IGV-dist に igv.jar、依存 jar、起動スクリプトが集められます。
./gradlew createDist
ls build/IGV-dist
開発中は、毎回フルビルドしなくても構いません。小さな Java 修正なら ./gradlew classes で十分です。画面を見たいときは ./gradlew run を使います。配布物の構成まで確認したいときだけ ./gradlew createDist を使います。
| 開発中の状況 | まず使うコマンド | 理由 |
|---|---|---|
| メソッド名や型を変えた | ./gradlew classes |
最短でコンパイルエラーを見つける |
| UI の挙動を見たい | ./gradlew run |
開発中のクラスパスで起動できる |
| jar の中身を見たい | ./gradlew jar |
成果物を確認できる |
| 配布構成を見たい | ./gradlew createDist |
起動スクリプトや依存 jar も確認できる |
| 変更の副作用を見たい | ./gradlew test |
parser や batch の既存テストを確認できる |
| 古い生成物が残っていそう | ./gradlew clean classes |
build ディレクトリを作り直せる |
コード探索には grep や ripgrep が便利です。IGV は大きいので、IDE のジャンプ機能だけで追うと迷いやすいです。まず用語で入口を探すと、読み始める場所を決めやすくなります。
# mainClass の確認
grep -n "mainClass" build.gradle
# Track の render 実装を探す
rg "void render\(RenderContext" src/main/java
# BAM 表示設定の参照箇所を探す
rg "SHOW_SOFT_CLIPPED|COLOR_BY_TAG|BASE_MODIFICATION" src/main/java
# VCF / VariantTrack 周辺を探す
rg "class VariantTrack|VariantRenderer|VCFVariant" src/main/java
# メニュー作成箇所を探す
rg "new JMenu|JMenuItem|getPopupMenuItems" src/main/java/org/igv
大事なのは、コマンドを暗記することではありません。改造のたびに、コンパイル確認、起動確認、対象クラスの探索、必要ならテスト、という小さなループを回せるようにすることです。
1. ビルドと起動の入口を見る
IGV は Gradle プロジェクトです。ルートの build.gradle では、application プラグインのメインクラスが次のように指定されています。
application {
mainClass = 'org.igv.ui.Main'
}
つまり、GUI アプリとしての入口は src/main/java/org/igv/ui/Main.java です。互換用に org.igv.ui.IGVMainFrame もありますが、これは Main.main(args) を呼ぶだけです。起動処理を追う場合は、まず Main を読みます。
起動時の流れは、おおむね次の通りです。Main.main() がコマンドライン引数を Main.IGVArgs に変換します。その後、IGV 用ディレクトリ、ログ、HTTP/プロキシ、Look and Feel、Swing の初期化を行います。最後に JFrame を作り、Main.open(frame, igvArgs) に渡します。その先で、実際のアプリ本体である org.igv.ui.IGV が作られます。
最初に覚えるべき点は、IGV クラスがアプリケーション全体の中心的な状態を持つことです。IGV はシングルトンに近い形で使われます。多くの場所から IGV.getInstance() で参照されます。現代的な依存注入の設計ではありませんが、既存コードを読むうえでは重要です。全体状態が必要な処理は、IGV に聞いていることが多いと考えると追いやすくなります。
2. 画面は Swing コンポーネントの階層でできている
IGV の UI は Java Swing で作られています。IGV のコンストラクタでは、メインウィンドウに IGVContentPane をセットします。メニューバーとして IGVMenuBar も作ります。
起動後の大まかな構造は次のように考えます。
Main
-> JFrame
-> IGV
-> IGVContentPane
-> MainPanel
-> TrackPanelScrollPane
-> TrackPanel
-> TrackNamePanel
-> AttributePanel
-> DataPanelContainer
-> DataPanel
-> IGVMenuBar
この階層を丸暗記する必要はありません。ただし、改造したい場所によって見るクラスは変わります。メニューを増やしたいなら IGVMenuBar を見ます。トラック名の左側表示を変えたいなら TrackNamePanel を見ます。ゲノム上のデータ本体を変えたいなら DataPanel と Track.render() を見ます。トラックの並びや高さを変えたいなら TrackPanel や TrackPanelScrollPane を見ます。
| 触りたい場所 | 最初に見るクラス | 典型的な改造 |
|---|---|---|
| 起動処理 | org.igv.ui.Main |
起動オプション、初期化処理、デバッグ用ログ |
| メイン画面全体 |
IGV / IGVContentPane
|
パネル構成、初期表示、全体状態 |
| 上部メニュー | IGVMenuBar |
メニュー項目、ツール起動、設定画面への導線 |
| トラック名の左側 | TrackNamePanel |
ラベル、アイコン、クリック操作 |
| データ本体の描画 |
DataPanel / DataPanelPainter / Track.render()
|
表示内容、色、座標変換 |
| トラックの高さや並び |
TrackPanel / TrackPanelScrollPane
|
layout、スクロール、折りたたみ |
IGV では、ひとつのトラックはモデルであり、同時に描画単位でもあります。Swing の JComponent がトラックそのものなのではありません。TrackPanel という UI コンテナが Track オブジェクトを持ち、その Track に描画を依頼します。ここは初心者が混乱しやすい点です。
3. IGV の主役は Track である
IGV を改造するなら、最初に読むべきインターフェイスは org.igv.track.Track です。これは IGV における「表示できるデータ」の共通インターフェイスです。
Track には多くの責務があります。名前、ID、色、高さ、表示モード、データ範囲、ポップアップメニュー、クリック処理、検索、セッション保存用の入出力などです。その中でも特に重要なのは次のメソッドです。
void render(RenderContext context);
IGV の描画は、最終的には各トラックの render() に到達します。Swing から DataPanel.paintComponent() が呼ばれます。そこで RenderContext が作られ、DataPanelPainter.paint() を経由して track.render(context) が呼ばれます。
Swing repaint
-> DataPanel.paintComponent(Graphics)
-> new RenderContext(...)
-> DataPanelPainter.paint(track, context)
-> track.render(context)
したがって、表示を少し変えたい場合は、まず対象トラックの render() を探します。マウス位置に応じた情報を変えたい場合は、getValueStringAt() や handleDataClick() を見ます。右クリックメニューを変えたい場合は、getPopupMenuItems() 周辺を見ます。
多くのトラックは AbstractTrack を継承しています。AbstractTrack は、名前、色、高さ、表示モード、サンプル属性、オートスケール、データレンジなどの共通処理を持ちます。新しいトラックを作る場合も、Track を直接実装するより、既存の AbstractTrack 派生クラスを真似るほうが安全です。
4. 代表的なトラック型を読む
IGV には多くのトラック型があります。最初に読む対象は、FeatureTrack、AlignmentTrack、VariantTrack の三つで十分です。
FeatureTrack は、BED、GFF、GTF などのゲノム区間 feature を表示する基本的なトラックです。遺伝子アノテーションや BED 的なデータを理解したい場合は、ここが入口になります。
ここで重要なのは、FeatureTrack がファイルを直接読み続けるわけではない点です。FeatureTrack は FeatureSource という問い合わせ口を通して、現在見えている範囲の feature を受け取ります。
この設計は、データロードと描画の責務を分けるためのものです。Track は「何をどう描くか」を担当します。FeatureSource は「chr/start/end を指定されたら、そこに重なる feature を返す」ことを担当します。この分離により、同じ FeatureTrack で、ローカル BED、tabix 付き GFF、リモート URL、キャッシュ済みデータなどを扱いやすくなります。
巨大なゲノムファイルでは、画面に見えている範囲だけを読むことが重要です。そのため、FeatureSource のような境界は性能面でも意味があります。
Iterator<T> getFeatures(String chr, int start, int end) throws IOException;
FeatureSource のコメントでは、start / end は UCSC 方式です。つまり 0-based start、end exclusive です。IGV 内部の区間表現は、基本的にこの考え方で動きます。一方、ユーザーに表示する locus 文字列は chr1:1-100 のような 1-based 表示に変換されます。
実際に Locus は、文字列を読むときに start から 1 を引きます。表示するときには start に 1 を足します。内部では 0-100、画面表示では 1-100 という対応になります。
| 層 | 座標の考え方 | 例 | 注意点 |
|---|---|---|---|
| 内部の feature/source | 0-based, end exclusive | 0-100 |
Java オブジェクトや FeatureSource の問い合わせで使う |
| ユーザー向け locus 表示 | 1-based, end inclusive 的に見える | chr1:1-100 |
検索窓、履歴、画面表示で使う |
| 画面ピクセル | 左端を 0 pixel とする | x = 0..width |
ReferenceFrame / RenderContext で変換する |
改造中に「1 塩基ずれている」問題が出たら、まず座標系を分けて確認します。入力ファイルの規約、内部 feature の start/end、表示用 locus 文字列の変換を別々に見ます。FeatureSource は、その境界にあたる重要な場所です。
AlignmentTrack は、BAM/CRAM/SAM などのアラインメント表示です。ここは大きく複雑です。read の色分け、soft clip、base modification、ロングリード、splice junction、挿入表示などが絡みます。
初心者が最初から AlignmentTrack 全体を読む必要はありません。目的を決めて読むほうがよいです。BAM の右クリックメニューを足したいなら getPopupMenuItems() 周辺を見ます。read の色を変えたいなら ColorOption や renderer 周辺を見ます。表示される read の条件を変えたいなら data manager や filter 周辺を追います。
VariantTrack は VCF/BCF/gVCF の表示です。VariantTrack は FeatureTrack を継承していますが、VCF 固有の処理を追加しています。genotype 表示、sample ごとの表示、allele frequency 色分け、filter 表示などです。VCF の INFO/FORMAT を使った独自表示を足したい場合は、VariantTrack と VariantRenderer を読むのが自然です。
| トラック種別 | 主なクラス | 対応データの例 | 改造の入口 |
|---|---|---|---|
| 区間 feature | FeatureTrack |
BED, GFF, GTF, gene annotation | ラベル、色、packing、tooltip |
| alignment | AlignmentTrack |
BAM, CRAM, SAM | read の色分け、tag 表示、soft clip、base modification |
| variant | VariantTrack |
VCF, BCF, gVCF | INFO/FORMAT 表示、genotype 表示、色分け |
| signal | wig/bigWig 系 track | bigWig, wig, bedGraph | coverage や signal の表示形式 |
| segmentation | seg 系 track | copy number segment | CNA 表示、サンプル別表示 |
5. データ読み込みは TrackLoader から始まる
ファイルを開いたとき、IGV は拡張子や format を見て適切なトラックを作ります。この中心が org.igv.track.TrackLoader です。
IGV.loadTracks(Collection<ResourceLocator>) は、各 ResourceLocator を処理します。実際にファイル形式を判定してトラックを作るのは、IGV.load(locator) の先にある TrackLoader.load(locator, genome) です。
TrackLoader.load() は長い分岐を持っています。format に応じて、VCF、BAM/CRAM、BED、bigWig/bigBed、TDF、seg、GWAS、BEDPE、MAF などへ振り分けられます。
IGV.loadTracks(...)
-> IGV.load(locator)
-> new TrackLoader().load(locator, genome)
-> loadAlignmentsTrack / loadVCF / loadWigFile / loadTribbleFile / ...
-> Track オブジェクトを作る
-> IGV.addTracks(tracks)
新しいファイル形式を IGV に読ませたい場合は、まず TrackLoader に分岐を足すことを考えます。ただし、最初から独自形式を深く統合する必要はありません。BED-like、VCF-like、bigWig-like など、既存の枠に乗せられるなら、そのほうが安全です。
IGV は htsjdk/Tribble に強く依存しています。ゲノム区間データなら、Tribble codec として載せる選択肢もあります。
| 調べたいこと | 探す文字列の例 | よく行き着く場所 |
|---|---|---|
| どこで形式判定しているか |
TrackLoader / locator.getFormat()
|
org.igv.track.TrackLoader |
| BAM/CRAM の読み込み |
loadAlignmentsTrack / AlignmentTrack
|
org.igv.sam |
| VCF の読み込み |
loadVCF / VariantTrack
|
org.igv.variant |
| BED/GFF/GTF の読み込み |
loadTribbleFile / FeatureTrack
|
org.igv.feature / org.igv.track
|
| bigWig/bigBed |
bigWig / bigBed
|
reader / track loader 周辺 |
rg "loadVCF|loadAlignmentsTrack|loadTribbleFile" src/main/java/org/igv/track
rg "class FeatureTrack|class AlignmentTrack|class VariantTrack" src/main/java
6. 描画座標の考え方:ゲノム座標から画面座標へ
IGV の描画で最も重要なのは、ゲノム座標と画面座標の変換です。ここでは ReferenceFrame と RenderContext をセットで理解します。
ReferenceFrame は、今どの染色体のどの範囲を、どのズームで見ているかを持ちます。RenderContext は、その ReferenceFrame を含みます。さらに Graphics2D、描画対象の矩形、visible rect、clip bounds なども持ちます。つまり RenderContext は、Track.render() が 1 回の描画で必要とする情報をまとめたものです。
ReferenceFrame: どこを見ているか
chrName / origin / end / scale / zoom / widthInPixels
RenderContext: 今この track をどう描くか
Graphics2D / ReferenceFrame / trackRectangle / visibleRect / clipBounds
| クラス | 役割 | 代表的な値 | 読む場面 |
|---|---|---|---|
ReferenceFrame |
表示中のゲノム範囲を保持する |
chrName, origin, scale, zoom
|
移動、ズーム、マウス座標変換 |
RenderContext |
1 回の描画に必要な情報を束ねる |
Graphics2D, trackRectangle, visibleRect
|
Track.render() 内の描画 |
DataPanel |
Swing の描画イベントを受ける | paintComponent() |
repaint から track 描画へ入る場所 |
RenderContext.getChr() は現在の染色体です。getOrigin() は表示範囲の左端です。getScale() は 1 pixel あたり何 bp かを表します。つまり scale の単位は bp/pixel です。
ゲノム上の距離をピクセル距離に直すには、scale で割ります。たとえば origin = 1000 bp、scale = 10 bp/pixel とします。このとき genomicPosition = 1050 は、左端から 50 bp 右にあります。1 pixel が 10 bp なので、画面上では 50 / 10 = 5 pixel 右に描きます。
int x = (int) ((genomicPosition - context.getOrigin()) / context.getScale());
逆に、マウスの x pixel からゲノム座標を得るときは、origin + x * scale になります。ReferenceFrame.getChromosomePosition(int screenPosition) はこの考え方で実装されています。ゲノム座標から画面座標に戻すときは、ReferenceFrame.getScreenPosition(double chromosomePosition) を使えます。
改造コードでは、可能なら自前計算より既存メソッドを使います。expanded insertion などの特殊ケースに対応しやすくなるためです。
| 変換 | 直感 | 式の形 | 既存メソッド |
|---|---|---|---|
| ゲノム座標 → pixel | bp の距離を bp/pixel で割る |
(pos - origin) / scale |
ReferenceFrame.getScreenPosition(pos) |
| pixel → ゲノム座標 | pixel 数に bp/pixel を掛ける |
origin + x * scale |
ReferenceFrame.getChromosomePosition(x) |
| 表示範囲の右端 | 幅 pixel 分だけ bp を進める | origin + width * scale |
ReferenceFrame.getEnd() |
実際には、expanded insertion、multi-frame 表示、clip、visibleRect なども関係します。それでも、最初の理解としてはこの対応で十分です。IGV の描画バグは、座標系の取り違えで起きがちです。特に 0-based / 1-based、inclusive / exclusive、画面左端の origin、スクロール時の visibleRect、複数 locus 表示時の frame の違いに注意します。
7. セッション、設定、状態管理
IGV には多くの状態があります。現在どのゲノムを見ているか、どのトラックを開いているか、どの locus にいるか、ROI は何か、トラックの色や高さはどうか、といった状態です。
このうち、セッションとして保存される状態は org.igv.session.Session が中心です。Session は現在の locus、history、regions of interest、session 内 preference、color scale などを持ちます。また、IGVEventBus で ViewChange を購読し、表示位置の履歴を記録します。セッション読み書きは SessionReader や session writer 系のクラスを追うと分かります。
ユーザー設定は org.igv.prefs.PreferencesManager と IGVPreferences が中心です。デフォルト値は src/main/resources/defaults_2.3.tab や src/main/resources/preferences.tab と関係します。BAM 表示に関する設定は、Constants に SAM.* として多数定義されています。
新しい設定項目を足す場合、定数を増やすだけでは足りません。デフォルト値、設定画面、保存と読み込み、実際に参照する描画・読み込み側コードをつなぐ必要があります。最初は既存の設定を grep で追うとよいです。たとえば SAM.SHOW_SOFT_CLIPPED や DEFAULT_VISIBILITY_WINDOW が、どこで定義され、どこで読まれ、どの UI から変更されるかを確認します。
8. メニューや右クリックメニューを追加する
上部メニューは IGVMenuBar が作っています。createMenus() の中で File、Genomes、Track Hubs、Sessions、View、Regions、Tools、Extras、Help などが追加されます。全体メニューに項目を追加したい場合は、まずここを見ます。
一方、トラック固有の操作は、右クリックメニューとして実装されていることが多いです。Track には次の default method があります。
default List<Component> getPopupMenuItems(final TrackClickEvent te) {
return null;
}
FeatureTrack、AlignmentTrack、VariantTrack などはこれを override します。そして、トラック種別ごとのメニューを返します。たとえば「この BAM トラックだけに解析メニューを足したい」なら、IGVMenuBar より AlignmentTrack.getPopupMenuItems() や関連する menu helper を見るほうが自然です。
最初の改造としては、右クリックメニューに小さな項目を足すのがよいです。選択中の領域やトラック名をログに出すだけでも十分です。これで TrackClickEvent、ReferenceFrame、IGV.getInstance()、MessageUtils、ログ出力の使い方を確認できます。
9. イベントの仕組みを理解する
IGV には簡単なイベントバス IGVEventBus があります。subscribe(eventClass, observer) で購読し、post(event) で通知します。Session は ViewChange を購読しています。トラックによっては IGVEventObserver を実装し、表示変更や設定変更に反応します。
このイベントバスは大規模な reactive framework ではありません。表示位置が変わった、ゲノムが変わった、データが読み込まれた、refresh したい、といった局所的な通知に使う仕組みです。
改造時は、イベント購読したオブジェクトを不要になったときに unsubscribe することを意識します。IGVEventBus は weak map を使っていますが、不要な購読を残さないほうが安全です。トラックを消す処理では、IGV.removeTracks() が IGVEventObserver であるトラックを unsubscribe しています。
10. バックグラウンド処理と Swing スレッド
IGV は GUI アプリです。重い処理を Swing の Event Dispatch Thread、つまり EDT で実行すると画面が固まります。Swing のボタンクリック、メニュー選択、paintComponent() などは基本的に EDT で動きます。
一方、ファイル読み込み、リモート URL へのアクセス、BAM/VCF の大きな走査、セッション読み込みなどは時間がかかります。このような処理は LongRunningTask.submit() を通じてバックグラウンドで実行します。
| 処理 | 使うもの | 理由 | 例 |
|---|---|---|---|
| 重い計算・I/O | LongRunningTask.submit() |
EDT を止めない | track load、session load、URL download |
| Swing UI の更新 |
UIUtilities.invokeOnEventThread() / SwingUtilities.invokeLater()
|
Swing は EDT で触る必要がある | label 更新、dialog 表示、menu visible 変更 |
| 結果を待つ UI 更新 | UIUtilities.invokeAndWaitOnEventThread() |
EDT 上の処理完了を待つ | 初期化中に UI 状態を確実に作る |
| 描画 |
paintComponent() → Track.render()
|
Swing repaint の流れ |
DataPanel から各 track へ |
典型的な形は、バックグラウンドで読み、最後だけ EDT に戻って UI を更新することです。
LongRunningTask.submit(() -> {
// 重い処理をここで行う。ファイルを読む、計算する、外部ツールを呼ぶ。
Result result = loadOrCompute();
// Swing コンポーネントを触る処理だけ EDT に戻す。
UIUtilities.invokeOnEventThread(() -> {
updateTrackOrPanel(result);
IGV.getInstance().repaint();
});
});
実際の IGV でも、IGV.loadTracks(...) や session 読み込み系は LongRunningTask.submit() を使います。メニューの表示変更やダイアログ操作では UIUtilities.invokeOnEventThread() が使われます。
UIUtilities.invokeOnEventThread() は、すでに EDT ならそのまま実行します。EDT でなければ SwingUtilities.invokeLater() に回します。そのため、IGV 内では生の SwingUtilities.invokeLater() より、既存の UIUtilities を使う場面が多くなります。
改造時の基本ルールは単純です。ファイル I/O、ネットワーク、BAM/VCF の重い走査は UI スレッドで実行しません。逆に、Swing コンポーネントの追加、削除、表示更新は UI スレッドを意識します。また、paintComponent() や Track.render() の中で重い I/O を始めないようにします。描画は頻繁に呼ばれるため、ここに重い処理を入れるとスクロールやズームのたびに固まります。
11. どこから改造を始めるべきか
IGV を自分用に改造する場合、最初から大きな機能を入れないほうが安全です。小さい改造を積み上げると、コードの場所と副作用を理解しやすくなります。
まず、ビルドして起動できる状態を作ります。./gradlew run または IDE から org.igv.ui.Main を起動します。JDK は Java 21 を使います。
次に、ログを出す小改造をします。たとえば IGVMenuBar に一時的なメニュー項目を足します。クリック時に MessageUtils.showMessage() で現在の locus を表示します。これで UI のイベント処理に慣れます。
その次に、既存トラックの右クリックメニューに項目を足します。対象が BAM なら AlignmentTrack、VCF なら VariantTrack、BED/GFF なら FeatureTrack を見ます。TrackClickEvent からクリック位置やトラックを取れるようになると、改造の幅が広がります。
さらに進んだら、既存データの表示ロジックを少し変えます。たとえば BAM の色分け、VCF の genotype 表示、FeatureTrack のラベル表示などです。この段階では、新しいデータ形式を作るより、既存形式の見え方を変えるほうが学びやすいです。
最後に、新しいファイル形式や新しいトラック型を追加します。この段階で TrackLoader、FeatureSource、AbstractTrack、renderer、session 保存をつなげます。
12. 新しい Track を足すときの考え方
新しいトラックに必要な要素は、最小限で考えると四つです。データを持つこと、高さを持つこと、描画すること、必要ならクリックやメニューに反応することです。
単純化すると、次のような形になります。
public class MyTrack extends AbstractTrack {
public MyTrack(ResourceLocator locator) {
super(locator);
setHeight(40);
}
@Override
public void render(RenderContext context) {
Graphics2D g = context.getGraphics();
Rectangle rect = context.getTrackRectangle();
g.drawString("Hello IGV", 10, rect.y + 20);
}
}
実際には、これだけではファイル読み込み、session 保存、全ゲノム表示には対応できません。それでも、IGV のトラック描画の最小単位はこの程度です。最初は固定文字列を描くトラックを作るだけでも、流れを理解できます。
実装時は、Track を直接実装するより AbstractTrack を継承するほうが現実的です。Track インターフェイスが要求するメソッドが多いためです。
また、ゲノム区間データなら、完全な独自トラックを作る前に FeatureTrack + 独自 FeatureSource にできないかを検討します。そのほうが packing、検索、ラベル、表示モード、右クリックメニューなどの既存機能を利用できます。
13. 新しいファイル形式を追加するときの考え方
新しいファイル形式を追加する入口は TrackLoader.load() です。ここで locator.getFormat() や拡張子に応じて分岐し、parser、source、track を作ります。
ただし、「ファイル形式を追加すること」と「トラック型を追加すること」は別です。新しい TSV ファイルを読みたいだけなら、独自 parser で BasicFeature 的な feature に変換し、FeatureTrack として表示する方法があります。これは比較的簡単です。
一方、ロングリードのメチル化、複雑な SV、クローン構造、複数サンプル行列などは、既存の feature 表示に乗りにくい場合があります。その場合は独自 Track や独自 renderer が必要になります。
設計時は、次の三つに分けて考えると整理しやすいです。
parser: ファイルを読む
source: chr/start/end の問い合わせに答える
track/renderer: 画面に描く
IGV では巨大ファイルを扱います。そのため、「全部メモリに読む」設計は避けるべきです。BAM/CRAM、VCF、bigWig/bigBed などは、インデックスを使って現在表示中の範囲だけを読みます。独自形式でも、大きくなる可能性があるなら、最初から indexed access を意識したほうがよいです。
14. BAM/CRAM 改造で見るべき場所
BAM/CRAM 周辺は org.igv.sam にまとまっています。主役は AlignmentTrack です。ただし、読み込みや管理は AlignmentDataManager、reader は org.igv.sam.reader 以下、表示用の alignment や packing、coverage、splice junction、base modification などは別クラスに分かれています。
BAM 表示を改造するときは、目的ごとに grep するのがよいです。read の色分けなら ColorOption を探します。タグによる色分けなら SAM.COLOR_BY_TAG を探します。base modification なら BASE_MODIFICATION や mods package を見ます。soft clip なら SOFT_CLIP、splice junction なら SpliceJunction を探します。
alignment 表示では、読み込み、フィルタ、ダウンサンプリング、packing、描画が絡みます。見た目だけ変えたいのに読み込み側を触ると、副作用が大きくなります。最初は renderer や color option 側で完結する改造を選ぶほうが安全です。
15. VCF 改造で見るべき場所
VCF 周辺は org.igv.variant と org.igv.variant.vcf が中心です。VariantTrack は FeatureTrack を継承しつつ、variant site と genotype 表示を扱います。VCF の中身は VCFVariant などで表現されます。
VCF 改造では、site レベルの表示を変えるのか、sample/genotype レベルの表示を変えるのかを分けて考えます。INFO フィールドに基づいて variant の色やラベルを変えるなら site 側です。FORMAT フィールドに基づいて sample ごとの色を変えるなら genotype 側です。
IGV には、allele frequency や methylation rate などの表示モードがすでにあります。新しい FORMAT キーを使った表示を足す場合は、既存の color mode や renderer の分岐を参考にします。
16. BED/GFF/GTF 改造で見るべき場所
BED/GFF/GTF のような区間 feature は FeatureTrack が中心です。ファイル読み込みは、htsjdk Tribble codec や IGV 独自 parser を経由します。描画は feature renderer が担当します。
この領域の改造は比較的始めやすいです。独自のラベル表示、feature の色、strand 表示、row の詰め方、クリック時の tooltip などは FeatureTrack 周辺を読めば追いやすいです。
注意点は表示モードです。Track.DisplayMode には COLLAPSED、SQUISHED、EXPANDED、FULL があります。FeatureTrack は、表示モードに応じて row height や packing を変えます。collapsed では問題がなくても、expanded で壊れることがあります。複数の表示モードで確認してください。
ここでいう packing とは、重なり合う feature を複数行に分けて表示する処理です。同じゲノム領域に exon、peak、BED interval が多数重なると、1 行では読めません。そこで PackedFeatures は feature を行に割り当てます。同じ行では、できるだけ feature が重ならないようにします。
COLLAPSED では、多くの feature を 1 行または少数行に押し込みます。EXPANDED や FULL では、複数行を使って個々の feature を見やすくします。
| DisplayMode | packing の見え方 | 向いている用途 | 改造時の注意 |
|---|---|---|---|
COLLAPSED |
できるだけ少ない行にまとめる | 全体の存在確認 | ラベルや重なりは見えにくい |
SQUISHED |
行は分けるが高さを詰める | 多数 feature の概観 | 文字や細い図形が潰れやすい |
EXPANDED |
重なりを避けて複数行表示 | 個々の feature 確認 | track 高が増えやすい |
FULL |
詳細表示寄り | gene model など | 描画コストと高さに注意 |
PackedFeatures.pack() は、display mode が COLLAPSED かどうかで行の作り方を変えます。strand ごとに分けるかどうかも関係します。FeatureTrack の描画を変えるときは、feature 単体の start/end だけでなく、その feature が何行目に packed されたかも確認します。
17. コマンド・バッチ機能も改造ポイントになる
IGV には GUI 操作だけでなく、batch command があります。中心は org.igv.batch.CommandExecutor です。goto、load、snapshot、sort、group、colorBy、expand、collapse などのコマンドがここで処理されます。
自分用の改造では、GUI にボタンを足すより、batch command を一つ足すほうが便利な場合があります。たとえば、現在の locus と開いている BAM/VCF の状態を特定形式で出力する機能です。特定の表示設定をまとめて適用する機能も batch command に向いています。スクリーンショットを研究用フォーマットで保存する機能も自動化しやすくなります。
GUI と batch の両方から呼びたい処理は、UI クラスに直接書かないほうがよいです。別の utility や service 的なクラスに分けると、あとで使い回しやすくなります。IGV 本体には密結合な部分もありますが、自分の追加コードはなるべく薄く保つのが安全です。
18. 開発中の確認ループを作る
IGV の改造では、ひとつの修正を入れたらすぐに小さく確認します。Swing の UI 変更は、コンパイルが通っても、実際にクリックするまで壊れていることがあります。一方、parser や command の変更は、GUI を起動しなくてもテストや batch で確認できることがあります。
| 変更内容 | 最低限の確認 | 追加で見るべきこと |
|---|---|---|
| メニュー項目追加 |
./gradlew classes → ./gradlew run
|
クリック時に EDT で例外が出ていないか |
| Track の描画変更 | ./gradlew run |
zoom in/out、collapsed/expanded、複数 locus |
| BAM/VCF 読み込み変更 |
./gradlew test の一部、実ファイル load |
index あり/なし、remote URL、大きなファイル |
| batch command 追加 | batch 関連テスト、手動 batch 実行 | GUI 操作と同じ状態変化になるか |
| preference 追加 | 起動→設定変更→再起動 | default 値、保存、既存セッションとの互換性 |
手元でよく使う確認コマンドは、シェル履歴に残しておくと便利です。
# 速い確認
./gradlew classes
# GUI で確認
./gradlew run
# テスト。失敗時は --info や --stacktrace を付ける
./gradlew test --stacktrace
# 配布物に入るか確認
./gradlew createDist
find build/IGV-dist -maxdepth 2 -type f | head
IDE では、実行構成の main class を org.igv.ui.Main にします。作業ディレクトリはプロジェクトルートにします。JDK は Java 21 にします。Gradle import がうまくいっていれば、IDE からの実行でも Gradle の run でも同じ入口に入ります。
| IDE 設定項目 | 推奨値 |
|---|---|
| Main class | org.igv.ui.Main |
| Working directory |
igv のルート |
| JDK | Java 21 |
| VM options | 最初は空でよい。重いデータでは -Xmx を調整する |
| Program arguments | 最初は空でよい。必要なら読みたいファイルや locus 関連引数を試す |
19. 初心者向けのコードリーディング順序
最初の 1 日で読むなら、次の順序がおすすめです。
まず build.gradle を読みます。Java 21、依存ライブラリ、mainClass を確認します。次に Main.java を読み、起動から IGV 作成までを追います。続いて IGV.java のコンストラクタと loadTracks()、load()、addTracks() 周辺を読みます。ここで、アプリ本体、データ読み込み、トラック追加がつながります。
次に Track.java と AbstractTrack.java を読みます。ここでトラックの責務を把握します。その後、DataPanel.java と DataPanelPainter.java を読みます。描画が track.render() に到達する流れを確認します。
次に TrackLoader.java を読みます。すべてを理解する必要はありません。拡張子や format によって、どの loader に分岐するかを眺めます。そのうえで、自分が触りたい形式だけ深掘りします。
最後に、自分の対象に応じて FeatureTrack、AlignmentTrack、VariantTrack のいずれかを読みます。この順序で読むと、巨大なコードベースでも迷いにくくなります。
20. 改造時の実践的な注意
IGV は研究者が日常的に使う可視化ツールです。改造時は、「表示が変わったが、本当に正しいか」を常に確認します。特にゲノム座標、strand、paired-end、supplementary/secondary alignment、VCF genotype、multi-sample 表示、全ゲノム表示、zoom out 時の summary 表示はバグが入りやすいです。
IGV は巨大ファイルを扱うため、性能劣化も目立ちます。描画のたびに重い計算をする実装は避けます。現在表示中の範囲を超えて全データを読む実装も避けます。tooltip のたびにファイルを読む、Swing paint の中でネットワーク I/O をする、といった実装も避けるべきです。
テストは src/test、test/sessions、test/batch にあります。GUI アプリなので完全な単体テストは難しいです。それでも、batch command、loader、parser、session のテストは比較的書きやすいです。自分用改造でも、少なくとも「起動する」「対象ファイルを load できる」「snapshot を作れる」程度の確認スクリプトを作っておくと安心です。
# 通常のテスト
./gradlew test
# 詳細なスタックトレース付き
./gradlew test --stacktrace
# 大きなテストデータを別ディレクトリに置いた場合
./gradlew -DLARGE_DATA_DIR="/path/to/largedata/" test
| テストしやすい領域 | 理由 |
|---|---|
| parser / codec | 入力ファイルと期待値で比較しやすい |
| batch command | GUI を直接触らずに状態変化を確認できる |
| session 読み書き | XML/JSON 的な入出力を比較しやすい |
| preference | default 値と保存値を確認しやすい |
| Swing 描画 | 自動テストは難しいので、snapshot や目視確認に寄りやすい |
21. 最小の理解モデル
IGV の内部構造を一文でまとめると、次のようになります。
IGV は、Main から起動し、IGV が全体状態を持ち、TrackLoader がファイルから Track を作り、TrackPanel/DataPanel が Swing の画面領域を提供し、最後に各 Track.render(RenderContext) が現在の ReferenceFrame に基づいてゲノム座標を画面に描くアプリケーションです。
この理解があれば、多くの改造は分解できます。入口はどこか。対象 Track はどれか。読み込みを変えるのか、描画を変えるのか、メニューを変えるのか。この三つを分けて考えると、必要な場所だけを掘り下げられます。




