0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

IGVの内部構造入門【生成記事】

0
Last updated at Posted at 2026-06-02

この記事の全文は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-distigv.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 ディレクトリを作り直せる

コード探索には grepripgrep が便利です。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 を見ます。ゲノム上のデータ本体を変えたいなら DataPanelTrack.render() を見ます。トラックの並びや高さを変えたいなら TrackPanelTrackPanelScrollPane を見ます。

触りたい場所 最初に見るクラス 典型的な改造
起動処理 org.igv.ui.Main 起動オプション、初期化処理、デバッグ用ログ
メイン画面全体 IGV / IGVContentPane パネル構成、初期表示、全体状態
上部メニュー IGVMenuBar メニュー項目、ツール起動、設定画面への導線
トラック名の左側 TrackNamePanel ラベル、アイコン、クリック操作
データ本体の描画 DataPanel / DataPanelPainter / Track.render() 表示内容、色、座標変換
トラックの高さや並び TrackPanel / TrackPanelScrollPane layout、スクロール、折りたたみ

IGV では、ひとつのトラックはモデルであり、同時に描画単位でもあります。Swing の JComponent がトラックそのものなのではありません。TrackPanel という UI コンテナが Track オブジェクトを持ち、その Track に描画を依頼します。ここは初心者が混乱しやすい点です。

ChatGPT Image 2026年6月2日 15_41_26 (1).png

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 派生クラスを真似るほうが安全です。

ChatGPT Image 2026年6月2日 15_41_26 (2).png

4. 代表的なトラック型を読む

IGV には多くのトラック型があります。最初に読む対象は、FeatureTrackAlignmentTrackVariantTrack の三つで十分です。

FeatureTrack は、BED、GFF、GTF などのゲノム区間 feature を表示する基本的なトラックです。遺伝子アノテーションや BED 的なデータを理解したい場合は、ここが入口になります。

ここで重要なのは、FeatureTrack がファイルを直接読み続けるわけではない点です。FeatureTrackFeatureSource という問い合わせ口を通して、現在見えている範囲の 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 の表示です。VariantTrackFeatureTrack を継承していますが、VCF 固有の処理を追加しています。genotype 表示、sample ごとの表示、allele frequency 色分け、filter 表示などです。VCF の INFO/FORMAT を使った独自表示を足したい場合は、VariantTrackVariantRenderer を読むのが自然です。

トラック種別 主なクラス 対応データの例 改造の入口
区間 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 表示、サンプル別表示

ChatGPT Image 2026年6月2日 15_41_28 (4).png

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 の描画で最も重要なのは、ゲノム座標と画面座標の変換です。ここでは ReferenceFrameRenderContext をセットで理解します。

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 bpscale = 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 の違いに注意します。

ChatGPT Image 2026年6月2日 15_41_27 (3).png

7. セッション、設定、状態管理

IGV には多くの状態があります。現在どのゲノムを見ているか、どのトラックを開いているか、どの locus にいるか、ROI は何か、トラックの色や高さはどうか、といった状態です。

このうち、セッションとして保存される状態は org.igv.session.Session が中心です。Session は現在の locus、history、regions of interest、session 内 preference、color scale などを持ちます。また、IGVEventBusViewChange を購読し、表示位置の履歴を記録します。セッション読み書きは SessionReader や session writer 系のクラスを追うと分かります。

ユーザー設定は org.igv.prefs.PreferencesManagerIGVPreferences が中心です。デフォルト値は src/main/resources/defaults_2.3.tabsrc/main/resources/preferences.tab と関係します。BAM 表示に関する設定は、ConstantsSAM.* として多数定義されています。

新しい設定項目を足す場合、定数を増やすだけでは足りません。デフォルト値、設定画面、保存と読み込み、実際に参照する描画・読み込み側コードをつなぐ必要があります。最初は既存の設定を grep で追うとよいです。たとえば SAM.SHOW_SOFT_CLIPPEDDEFAULT_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;
}

FeatureTrackAlignmentTrackVariantTrack などはこれを override します。そして、トラック種別ごとのメニューを返します。たとえば「この BAM トラックだけに解析メニューを足したい」なら、IGVMenuBar より AlignmentTrack.getPopupMenuItems() や関連する menu helper を見るほうが自然です。

最初の改造としては、右クリックメニューに小さな項目を足すのがよいです。選択中の領域やトラック名をログに出すだけでも十分です。これで TrackClickEventReferenceFrameIGV.getInstance()MessageUtils、ログ出力の使い方を確認できます。

9. イベントの仕組みを理解する

IGV には簡単なイベントバス IGVEventBus があります。subscribe(eventClass, observer) で購読し、post(event) で通知します。SessionViewChange を購読しています。トラックによっては 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 を始めないようにします。描画は頻繁に呼ばれるため、ここに重い処理を入れるとスクロールやズームのたびに固まります。

ChatGPT Image 2026年6月2日 15_41_28 (5).png

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 のラベル表示などです。この段階では、新しいデータ形式を作るより、既存形式の見え方を変えるほうが学びやすいです。

最後に、新しいファイル形式や新しいトラック型を追加します。この段階で TrackLoaderFeatureSourceAbstractTrack、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_MODIFICATIONmods package を見ます。soft clip なら SOFT_CLIP、splice junction なら SpliceJunction を探します。

alignment 表示では、読み込み、フィルタ、ダウンサンプリング、packing、描画が絡みます。見た目だけ変えたいのに読み込み側を触ると、副作用が大きくなります。最初は renderer や color option 側で完結する改造を選ぶほうが安全です。

15. VCF 改造で見るべき場所

VCF 周辺は org.igv.variantorg.igv.variant.vcf が中心です。VariantTrackFeatureTrack を継承しつつ、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 には COLLAPSEDSQUISHEDEXPANDEDFULL があります。FeatureTrack は、表示モードに応じて row height や packing を変えます。collapsed では問題がなくても、expanded で壊れることがあります。複数の表示モードで確認してください。

ここでいう packing とは、重なり合う feature を複数行に分けて表示する処理です。同じゲノム領域に exon、peak、BED interval が多数重なると、1 行では読めません。そこで PackedFeatures は feature を行に割り当てます。同じ行では、できるだけ feature が重ならないようにします。

COLLAPSED では、多くの feature を 1 行または少数行に押し込みます。EXPANDEDFULL では、複数行を使って個々の 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 です。gotoloadsnapshotsortgroupcolorByexpandcollapse などのコマンドがここで処理されます。

自分用の改造では、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.javaAbstractTrack.java を読みます。ここでトラックの責務を把握します。その後、DataPanel.javaDataPanelPainter.java を読みます。描画が track.render() に到達する流れを確認します。

次に TrackLoader.java を読みます。すべてを理解する必要はありません。拡張子や format によって、どの loader に分岐するかを眺めます。そのうえで、自分が触りたい形式だけ深掘りします。

最後に、自分の対象に応じて FeatureTrackAlignmentTrackVariantTrack のいずれかを読みます。この順序で読むと、巨大なコードベースでも迷いにくくなります。

20. 改造時の実践的な注意

IGV は研究者が日常的に使う可視化ツールです。改造時は、「表示が変わったが、本当に正しいか」を常に確認します。特にゲノム座標、strand、paired-end、supplementary/secondary alignment、VCF genotype、multi-sample 表示、全ゲノム表示、zoom out 時の summary 表示はバグが入りやすいです。

IGV は巨大ファイルを扱うため、性能劣化も目立ちます。描画のたびに重い計算をする実装は避けます。現在表示中の範囲を超えて全データを読む実装も避けます。tooltip のたびにファイルを読む、Swing paint の中でネットワーク I/O をする、といった実装も避けるべきです。

テストは src/testtest/sessionstest/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 はどれか。読み込みを変えるのか、描画を変えるのか、メニューを変えるのか。この三つを分けて考えると、必要な場所だけを掘り下げられます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?