
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には、エレメントにテクスチャーを貼ったりマテリアルを細かく設定する機能備わっているので、より細かくカスタマイズしたネットワークグラフを描画することもできます。