GUNMA GIS GEEK

群馬県の片隅でオープンデータとデータビジュアライゼーションとGIS(地理情報システム)に戯れるエンジニアのブログ。

*

【D3.js】チャートをSVGやPNGでダウンロードできるようにするまでの長い道のり

     - D3v4

はてなブックマーク - 【D3.js】チャートをSVGやPNGでダウンロードできるようにするまでの長い道のり
Pocket

自分用のメモ書きですが、何かの参考になるかもしれないので掲載しておきます。


結論

先に結論だけ書いておきます。
画像にしてダウンロードさせるのは、サーバーサイドでSVGをラスタに変換する方がずーと楽。

目標

・D3.jsで出力したチャートをSVGやPNGでダウンロードできるようにする。
・ダウロードしたチャートにはCSSの内容も反映されていること。

SVG・PNG共通

チャートに適用されているCSSの内容がダウンロードしたファイルにも反映されるように、すべてのパラメータをSVGの属性(atribute)に変換します。
その際、元のチャートには手をつけたくないので、ダウンロード実行時にいったんコピーしgetComputedStyleメソッドを使ってチャートを構成する要素の算出スタイルをatributeに変換します。
そのままでは数が多すぎるので、空のSVGを作成して必要なスタイルを抽出するためのフィルターとして使います。

ぶっちゃけこの辺はSVG Crowbarのコードを丸パクリしてます。

また、svgの中に画像などをimageタグで挿入している場合、data URI スキーム化してsvg内に埋め込む必要があります。

download SVG

SVG要素をXMLSerializerを使ってシリアライズし、さらにblobオブジェクトに変換してダウンロードできる形にしています。
IEの場合はmsSaveBlob関数を使い、それ以外のブラウザの場合はdownload属性を付けたa要素にblobオブジェクトを渡しています。

・問題点
safariでは、a要素のdownload属性が実装されていないため、この方法だとページ遷移が発生してダウンロードされません。(ファイルがブラウザ上で開かれるます)

downloadPNG

SVGとしてダウンロードさせるより一手間かかります。
まず、シリアライズしたSVGをData URI schemeに変換しimgオブジェクトに読み込ませ、それをcanvasにdrawImage使って転写することでラスタライズします。
その後、canvasのtoDataURLメソッドを使って再度Data URI schemeに変換し、画像としてダウンロードさせます。

・問題点 safari
downloadSVGと同じく、ダウンロードされずブラウザ上で開かれてしまう。

・問題点 IE
svgをData URI schemeに変換しimgオブジェクトに読み込ませる際、base64に変換する必要があります。
(他ブラウザではそのまま渡せる)
base64に変換するwindow.btoaは、ユニコードに対応していないため元svgに日本語が含まれているとエラーとなります。従って、別途base64 encoderを実装する必要があります。

さらにimgオブジェクトに読み込ませることに成功したとしても、canvasのdrawImageメソッドを使ってimgオブジェクトを転写する際にSecurity Errorがでます。
これについては回避策が無いため、IEのみ別のアプローチが必要となります。
今回はサードパーティライブラリのcanvg.jsを使いIEの時だけSVGをパースしてcamvasに一から描画するという方法を使いました。
以下はIE対策を施したdownloadPNG

また、imgオブジェクトのonloadイベントが発火しなかったり、データの読み込みが終わる前に発火してしまったりと不安定な部分も。

サンプル

bl.ocks.orgにサンプルを掲載してあります。

Chart Downloader α – bl.ocks.org

はてなブックマーク - 【D3.js】チャートをSVGやPNGでダウンロードできるようにするまでの長い道のり
Pocket

 - D3v4

  関連記事

RESAS-APIを使ってみた。

RESAS(地域経済分析システム)に掲載しているデータをプログラムから取得できる …

javascript Proxy Object
【D3.js】proxyを使ってデータセットが変更されたらチャートを再描画する。

D3.jsを使ってデータビジュアライゼーションを作成するときにありがちな処理とし …

d3.js logo
D3.js version.4を学ぶための学習リソースの紹介。

この記事は「D3.js Advent Calendar 2016」 参加記事です …

D3.js ver.4 example
D3.js ver.4 始めました。

http://bl.ocks.org/shimizu D3.js のver.4を …

d3.js ver.4 axis update example
【D3.js】Axis(軸)を可変させる2種類の方法。

D3で作成した軸の値を変更してアップデートする方法です。 基本的には、スケールの …

駅すぱあと路線図API Hacks! D3.jsを使ってSVGをオーバーレイする。

新しい地図サービスが始まったとき、誰もが気になるのは「この地図サービスでD3.j …