SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

Google Visualization APIを使ってWebアプリケーションにグラフィカルデータを表示する

Google Visualization APIの基本事項から高度な内容まで

独自のビジュアライゼーションの作成

 すべてのビジュアライゼーションは同じメソッドシグネチャに従っており、使い方はどれも同じです。そのため、新しいビジュアライゼーションを簡単に作成できます。次に示すシグネチャに従うJavaScriptオブジェクトを作成し、他の開発者にも使えるようにするだけです。

// namespace, to avoid conflicts with other visualizations and javascript objects 
// living in the same page as this visualization.
var my_domain = {};

// constructor for the MyVisualization object. The 'container'
// parameter is the placeholder DOM element into which 
// you will draw your visualization.
my_domain.MyVisualization = function(container) {

  // store the container into an instance variable for later use.
  this.container = container;
};

// The draw() method receives a DataTable and an options object.
my_domain.MyVisualization.prototype.draw = 
    function(data, options) {
  // ... implement your visualization code here.
}

 こうしたスタブが定義できたら、使用するビジュアライゼーションに必要なコードをdraw()メソッドに実装します。

 本稿執筆時のビジュアライゼーションギャラリーには、まだツリーマップ表示用のビジュアライゼーションが見当たりません。ツリーマップは、ツリー構造のデータを入れ子状の矩形で表示する手法で、ツリー内の各ノードにスカラー値を与える場合に特に重宝します。これは新しいGoogleビジュアライゼーションとして実装するのに適した対象と言えます。

 話を簡単にするため、ここでは描画用コードを一から実装するのではなく、David Cowie氏が作成したツリーマップ描画用のJavaScriptライブラリ「Js-Treemap」を再利用して、これをビジュアライゼーションに組み込みます。図2は、組織図(図1を参照)で使ったのと同じデータをツリーマップで表示したものです。ただし、ツリー内のノードごとに数値の設定が必要になります。

図2 ツリーマップの例。先の組織図で用いたデータをツリーマップで表示するとこうなる
図2 ツリーマップの例。先の組織図で用いたデータをツリーマップで表示するとこうなる

 Js-Treemapライブラリを使えば、ツリーマップの描画は比較的簡単です。次の手順を実行するだけで済みます。

  1. 主体となる図表とその操作用コントロールを含むHTML要素を作成します。
  2. 階層構造を表現するために、ビジュアライゼーションのDataTableをネストされた一連のTreeNodeオブジェクトに変換します。TreeNodeクラスは、Js-Treemapライブラリで定義されています。
  3. ツリーマップ自体の描画処理を行うDivTreeMapインスタンスを、指定のコンテナDOM要素内に作成します。DivTreeMapの定義もJs-Treemap内にあります。
  4. 必要に応じて、ツリーマップのクリックに対するイベントリスナを操作用コントロールにバインドします(ツリーマップの各領域の拡大/縮小など)。

 リスト2を見れば、こうした手順のすべてが、先ほどのスタブを利用してどのようにGoogleビジュアライゼーション内に実装されるかが分かります。

リスト2 Js-Treemapを利用したツリーマップ表示用のGoogleビジュアライゼーション
// namespace
var ns = {};

ns.TreeMap = function(container) {
  this.container = container;
};

ns.TreeMap.prototype.draw = function(data, options) {
  var self = this;
  
  // parse options and fallback to defaults for unspecified ones.
  options = options || {};
  options.width = options.width || '50%';
  options.height = options.height || 300;
  
  // create the required DOM elements
  var treeMapContainer = document.createElement('DIV');
  treeMapContainer.style.width = options.width;
  treeMapContainer.style.height = options.height - 20;
  treeMapContainer.style.backgroundColor = '#77A';
  this.container.appendChild(treeMapContainer);
  
  var statusBar = document.createElement('DIV');
  statusBar.style.height = 20;
  
  var backButton = document.createElement('BUTTON');
  backButton.innerHTML = 'Back';
  backButton.style.right = 0;
  statusBar.appendChild(backButton);
  
  var infoBox = document.createElement('SPAN');
  statusBar.appendChild(infoBox);
  
  this.container.appendChild(statusBar);
    
  // Parse the Gviz DataTable and render it.
  var rootNode = this.createTree(data);
  var map = new DivTreeMap(treeMapContainer, 
                         rootNode, 
                         { shader: new TreeNodeShader(rootNode) });
                            
  // Attach events.
  map.onZoomClick = function() { 
    map.zoom(); backButton.disabled = false; 
  };
  map.onMouseOver = function(node) { 
    infoBox.innerHTML =  node.toString();
  };
  backButton.onclick = function() {
    backButton.disabled = ( map.unzoom() === 0 ); 
  };
};

 生成されるビジュアライゼーションの高さと幅を設定するオプションの構文解析を行うコードや、ユーザーがオプションを指定しなかった場合にデフォルト値を使用する部分に目を向けてください。コンストラクタに渡されたcontainerオブジェクトの内部で図がどのように生成されるかにも注目です。draw()関数が受け取るDataTableオブジェクトの解析は、createTree()メソッド内で行われます。リスト3にこのビジュアライゼーションの実装を示します。

リスト3 ツリーマップビジュアライゼーションでのDataTableの解析
// Parses the GViz DataTable and returns a Tree suitable to be used with
// the treemap javascript library.
ns.TreeMap.prototype.createTree = function(data) {
  
  // Get a list of all non-leaf nodes
  var parentsMap = {};
  var parents = data.getDistinctValues(1);
  for (var i=0; i<parents.length; i++) {
    parentsMap[parents[i]] = {name: parents[i], childs: []};
  }
  
  // Populate each parent node with the list of direct childs
  for (var i=0; i < data.getNumberOfRows(); i++) {
    var name = data.getValue(i, 0);
    var parent = data.getValue(i, 1);
    var quantity = data.getValue(i, 2);
    parentsMap[parent].childs.push({name: name, quantity: quantity});
  }
  
  // Assemble the tree
  for (var parent in parentsMap) {
    var childs = parentsMap[parent].childs;
    for (var i = 0; i<childs.length; i++) {
      if (parentsMap[childs[i].name]) {
        childs[i] = parentsMap[childs[i].name];
      }
    }
  }

  // Transform the tree in a set of TreeNode, TreeParentNode objects
  // (the expected input structure for the treemap js library)
  var rootNode = this.populateTree(parentsMap["Root"]);
  return rootNode;  
};

// Recursively populates a TreeNode structure.
ns.TreeMap.prototype.populateTree = function(node) {
  if (!node.childs) {
    // leaf node
    return new TreeNode( node.name, node.quantity );
  } else {
    // tree node
    var treenodes = [];
    for (var i = 0; i < node.childs.length; i++) {
      treenodes.push(this.populateTree(node.childs[i]));
    }
    return new TreeParentNode(node.name, treenodes);
  }
};

 このコードでは、DataTableに用意された各種関数を使ってそのデータを処理しています。データセット内の全セルに対する反復処理にはGetNumberOfRows()getNumberOfColumns()が、各セルの値の読み取りにはgetValue()が使えます。getDistinctValues()といった他の関数は、行および列にわたるデータの集約処理を行います。

 ツリーマップビジュアライゼーションは、DataTableに3つの列が含まれることを前提としています。最初の2つはノードの名前とその親ノードの名前で、組織図の場合と同じです。残る1つは数値型の列で、リーフノードに与えられるスカラー値を表します。

 この場合も、先ほどの組織図の例と同様、drawVisualization()関数の更新によって、DataTableの列を増やしたり、新たに作成したツリーマップビジュアライゼーションをWebページに追加したりできます。リスト4に最終的なコードを示します。

リスト4 修正後のdrawVisualization()関数
function drawVisualization() {                
  // Create and populate a data table.
  var data = new google.visualization.DataTable();      
  data.addColumn('string', 'Name');
  data.addColumn('string', 'Parent');
  data.addColumn('number', 'Quantity');
  data.addRows(10);

  // Add more data rows and cells here      
  var row = 0;
  addRow(data, row++, "Apples", "Root");
  addRow(data, row++, "Braeburn", "Apples", 1);
  addRow(data, row++, "Cox", "Apples", 2.2);
  addRow(data, row++, "Golden Delicious", "Apples");
  addRow(data, row++, "English", "Golden Delicious", 1);
  addRow(data, row++, "French", "Golden Delicious", 2);
  addRow(data, row++, "Bananas", "Root");
  addRow(data, row++, "Caribbean", "Bananas", 2.5);
  addRow(data, row++, "Central American", "Bananas", 1.5);
  addRow(data, row++, "Clementines", "Root", 3);
  
  var orgchart = new google.visualization.OrgChart(
    document.getElementById('orgchartdiv'));
    
  orgchart.draw(data, {
    selectionColor: 'yellow'
  });
        
  // Instantiate our Treemap visualization.
  var treemap = new ns.TreeMap(document.getElementById('treemapdiv'));

  // Draw our treemap with the data we created locally.
  treemap.draw(data, {
    width: '50%',
    height: '300'
  });      
}

 ここで作成したビジュアライゼーションにより、サンプルのWebページは図3のようになります。

図3 Webページにカスタムのツリーマップビジュアライゼーションを埋め込んだ様子
図3 Webページにカスタムのツリーマップビジュアライゼーションを埋め込んだ様子

次のページ
イベント処理

この記事は参考になりましたか?

japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Riccardo Govoni(Riccardo Govoni)

2003年からJ2EE開発者としてイタリアの金融サービス会社に勤務。ソフトウェアアーキテクトとして従来の銀行システムのWebフロントエンドの設計に従事。物理学修士。SCJP(Sun Certified Java Programmer)資格を持つ。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/3660 2009/03/26 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー