deck.glを使って、D3.jsで計算したネットワークグラフをWebGLで描画する
概要
deck.glは、Uber社が巨大なデータセットを地図上に可視化するために作ったフレームワークですがバージョンアップのごとに進化を遂げ、現在では地理空間データに関わらず、様々な座標系に対応したビジュアライゼーションフレームワークになっています。
ここでは、D3を使って計算したネットワークグラフをDeck.glを用いてWebGLに描画します。
ネットワークグラフをWebGLを使って描画する
サンプルコード
解説
deck.glのビューシステム
deck.glでは、デフォルトで地理空間データを描画するためのビュー(Mapview
)が設定されています。
deck.glにはMapview
の他にも、さまざまな座標データに対応したviewが用意されおり、開発者が地理空間データ以外のデータでもwebGLを用いて描画することができます。
(各viewには、データの座標をスクリーン上の座標に変換する投影法と、カメラのコンントローラー設定がワンセットとなってパッケージ化されています)
ここでは、3D(x,y,z)座標の投影法と、指定した座標を中心として回転するカメラコントローラーが定義された(OrbitView
)を使って、D3で生成したネットワークグラフをwebGL上に描画します。
d3.forceSimulationの計算結果をdeck.glに反映させる
ネットワークグラフの座標計算について詳しい仕組みは d3-force のドキュメントを参照してください。
ここでのポイントは、forceSimulation
の内部タイマーによって送出されるtick
イベントを捕まえて、シュミレーターによって計算されたノードとリンクの座標をdeck.glのScatterplotLayer
とLineLayer
に反映している部分になります。
基本的には、ノードとリンク用のステートを生成し、tick
イベント内で、計算された座標をレイヤーに反映するわけですが、この時重要なポイントとして計算結果が収められたデータオブジェクトを別オブジェクトとして複製する必要があります。
D3.jsはシミュレーターに渡されたデータオブジェクトを破壊的に更新しますが、これをそのままステートに渡してもアップデートが検知されずレンダーが走らないためです。
そのため、JSON.stringify
とJSON.parse
を使ったあまりスマートでないディープコピーを行なっています。これはもうちょっと改善の余地があるかも。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
//ノードとリンク用のstateを設定 const [nodes, setNodes] = useState(data.nodes); const [links, setLinks] = useState(data.links); //ネットワークグラフの座標計算を行うシミュレーターを生成 const simulation = forceSimulation() .force( "link", forceLink().id(function (d) { return d.id; }) ) .force("charge", forceManyBody()) .force("center", forceCenter(0, 0)); //ノードとリンクのデータをシミュレーターに束縛 simulation.nodes(data.nodes); simulation.force("link").links(data.links); //ループイベント時に呼ばれるコールバックを設定 const ticked = () => { //renderを走らせる為に座標計算済みのデータを別オブジェクトとして複製 const newData = JSON.parse(JSON.stringify(data)); //react stateを更新 setNodes(newData.nodes); setLinks(newData.links); }; //シミュレーターのループイベントにコールバックを束縛 simulation.on("tick", ticked); |
ネットワークグラフ自体も3Dにする
せっかくなので、z軸も含めてネットワークグラフの座標計算を行うd3-force-3dプラグインを使って3Dのネットワークグラフも作ってみました。
基本的な内容は変わっていません、d3-forceライブラリを使っていたところを、d3-force-3dライブラリに変え、ノードを描画するのにScatterplotLayer
ではなくPointCloudLayer
を使うように変更しただけです。(ScatterplotLaye
にはz軸を描画する機能がなかったので、PointCloudLayer
に変更しました)
サンプルコード
deck.glには、エレメントにテクスチャーを貼ったりマテリアルを細かく設定する機能備わっているので、より細かくカスタマイズしたネットワークグラフを描画することもできます。