データ可視化プラグインの開発(2)

この記事は1年以上前に書かれました。
内容が古くなっている可能性がありますのでご注意下さい。


SPARQLクエリの結果をSgvizlerでビジュアライズしてWordPressの投稿や固定ページに貼り付けるプラグイン、WP Sgvの開発の第2回では、より実用的なプラグインをめざして機能拡張します。

●バージョン0.4
以下のような囲み型ショートコードを使用てSPARQLクエリを記述し、

[wp_sgv endpoint="http://ja.dbpedia.org/sparql" chart="google.visualization.BarChart" height="600" width="600"]
SELECT DISTINCT ?kenmei COUNT(?shi) AS ?cities WHERE {
  ?ken rdf:type schema:AdministrativeArea ;
  dbpedia-owl:country dbpedia-ja:日本 ;
  prop-ja:name ?kenmei .
  ?shi rdf:type dbpedia-owl:City ;
  dbpedia-owl:location ?ken . }
GROUP BY ?kenmei
ORDER BY DESC(?cities)
LIMIT 10
[/wp_sgv]

Sgvizlerでビジュアライズするという点では変わりませんが、ショートコードをすべて手書きするのではなく、簡易なショートコードジェネレータを提供します。

投稿や固定ページの編集画面に、以下のようなメタボックスを追加します。

チャートの種類とSPARQLエンドポイントをリストから選択し、チャートのオプションとSPARQLクエリを記述して[Generate shortcode]ボタンをクリックすると、ボタンの下にあるテキストエリアにショートコードが生成されますので、それを編集画面にコピペして使用します。
チャート表示領域の幅と高さはデフォルト値が表示されますので、必要に応じて変更します。
このデフォルト値は、プラグインのオプション設定画面で変更することができます。

メタボックスに表示されるSPARQLエンドポイントのリストの内容は、このオプション設定画面で変更することができます。

プラグインの完全なソースコードは以下のとおりです。

<?php
/*
  Plugin Name: WP Sgv
  Plugin URI: http://midoriit.com/works/wp-sgv.html
  Description: Visualize SPARQL query result using Sgvizler
  Version: 0.4
  Author: Midori IT Office LLC
  Author URI: http://midoriit.com/
  License: GPL3
*/

$wpsgv = new WPSgv();

class WPSgv {

  public function __construct() {
    register_activation_hook(__FILE__, array(&$this,'wpsgv_activate'));
    register_uninstall_hook(__FILE__, 'WPSgv::wpsgv_uninstall');
    add_action('admin_init', array(&$this, 'wpsgv_init'));
    add_action('admin_menu', array(&$this, 'wpsgv_menu'));
    add_shortcode('wp_sgv', array(&$this, 'wp_sgv_handler'));
  }

  public function wpsgv_activate() {
    add_option('wpsgv_width', '600');
    add_option('wpsgv_height', '600');
    add_option('wpsgv_endpoints', "http://ja.dbpedia.org/sparql\nhttp://datameti.go.jp/sparql");
  }

  public static function wpsgv_uninstall() {
    delete_option('wpsgv_width');
    delete_option('wpsgv_height');
    delete_option('wpsgv_endpoints');
  }

  public function wpsgv_init() {
    add_meta_box('sparql', 'SPARQL visualization shortcode', array(&$this, 'wpsgv_box'), 'post');
    add_meta_box('sparql', 'SPARQL visualization shortcode', array(&$this, 'wpsgv_box'), 'page');
  }

  public function wpsgv_box() {

    $width = get_option('wpsgv_width');
    $height = get_option('wpsgv_height');
    $endpoints = get_option('wpsgv_endpoints');
    $charts = array(
      'google.visualization.AnnotatedTimeLine',
      'google.visualization.AreaChart',
      'google.visualization.BarChart',
      'google.visualization.BubbleChart',
      'google.visualization.CandlestickChart',
      'google.visualization.ColumnChart',
      'google.visualization.Gauge',
      'google.visualization.GeoChart',
      'google.visualization.GeoMap',
      'google.visualization.ImageSparkLine',
      'google.visualization.LineChart',
      'google.visualization.Map',
      'google.visualization.MotionChart',
      'google.visualization.OrgChart',
      'google.visualization.PieChart',
      'google.visualization.ScatterChart',
      'google.visualization.SteppedAreaChart',
      'google.visualization.Table',
      'google.visualization.TreeMap',
      'sgvizler.visualization.DefList',
      'sgvizler.visualization.D3ForceGraph',
      'sgvizler.visualization.DraculaGraph',
      'sgvizler.visualization.List',
      'sgvizler.visualization.Map',
      'sgvizler.visualization.MapWKT',
      'sgvizler.visualization.Table',
      'sgvizler.visualization.Text'
    );

    echo '<script type="text/javascript">';
    echo 'function sgv_genshortcode() {
      sgv_shortcode.value = "[wp_sgv endpoint =\"" + sgv_endpoint.value + "\"" + 
        " chart=\"" + sgv_chart.value + "\"" +
        " options=\"" + sgv_options.value + "\"" +
        " width=\"" + sgv_width.value + "\"" +
        " height=\"" + sgv_height.value + "\"]]\n" + 
        sgv_query.value +
        "\n[[/wp_sgv]";
      sgv_shortcode.select();
    }';
    echo '</script>';
    echo 'Chart : <select id="sgv_chart">';
    foreach($charts as $chart) {
      echo '<option value="'.$chart.'">'.$chart.'</option>';
    }
    echo '</select><br />';
    echo 'Width : <input type="text" id="sgv_width" value="'.$width.'" size="10" />';
    echo ' Height : <input type="text" id="sgv_height" value="'.$height.'" size="10" /><br />';
    echo 'Endpoint : <select id="sgv_endpoint">';
    $array = explode("\n", $endpoints);
    $array = array_map('trim', $array);
    foreach($array as $endpoint) {
      echo '<option value="'.$endpoint.'">'.$endpoint.'</option>';
    }
    echo '</select><br />';
    echo 'Options : <textarea id="sgv_options" rows="2" style="max-width:100%;min-width:100%"></textarea><br />';
    echo 'Query : <textarea id="sgv_query" rows="3" style="max-width:100%;min-width:100%"></textarea><br />';
    echo '<a class="button" onClick="sgv_genshortcode();">Generate shortcode</a><br />';
    echo '<br /><textarea id="sgv_shortcode" rows="4" style="max-width:100%;min-width:100%" onClick="this.select();" readonly></textarea><br />';
  }

  public function wp_sgv_handler($atts, $content) {

    extract( shortcode_atts(array(
      'endpoint' => '',
      'chart' => 'sgvizler.visualization.Text',
      'options' => '',
      'height' => '100',
      'width' => '100'),
        $atts ) );

    $query = str_replace(array("\r\n","\r","\n","<br />","<br>","</br>","<p>","</p>"), ' ', htmlspecialchars_decode($content));

    if( empty($endpoint) ) {
      return 'no endpoint';
    } else if( !trim($query) ) {
      return 'no query';
    }

    $uniq = uniqid('',1);

    return 
    '<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.js"></script>
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
    <script type="text/javascript" src="http://beta.data2000.no/sgvizler/release/0.6/sgvizler.js"></script>
    <div id="sgvdiv'.$uniq.'"></div>
    <script type="text/javascript">
      var sparqlQueryString = "'.$query.'";
      q = new sgvizler.Query(null, {'.$options.'});
      q.query(sparqlQueryString)
        .endpointURL("'.$endpoint.'")
        .endpointOutputFormat("json")
        .chartFunction("'.$chart.'")
        .chartHeight("'.$height.'")
        .chartWidth("'.$width.'")
      .draw("sgvdiv'.$uniq.'");
    </script>';
  }

  function wpsgv_menu() {
    add_options_page('WP Sgv Options', 'WP Sgv', 'manage_options',
      'wp_sgv', array(&$this, 'wpsgv_options'));
  }

  function wpsgv_options() {
    if ( !current_user_can('manage_options')) {
      wp_die( __('insufficient permissions.') );
    }

    if (isset($_POST['update_option'])) {
      check_admin_referer('wpsgv_options');
      $width = $_POST['wpsgv_width'];
      if(is_numeric($width)){
        update_option('wpsgv_width', $width);
      }
      $height = $_POST['wpsgv_height'];
      if(is_numeric($height)){
        update_option('wpsgv_height', $height);
      }
      $endpoints = $_POST['wpsgv_endpoints'];
      update_option('wpsgv_endpoints', $endpoints);
    }

    $width = get_option('wpsgv_width');
    $height = get_option('wpsgv_height');
    $endpoints = get_option('wpsgv_endpoints');

    echo '<div><h2>WP Sgv Options</h2>';
    echo '<form name="wpsgv_form" method="post" action="">';
    wp_nonce_field('wpsgv_options');
    echo '<table class="form-table"><tbody>';
    echo '<tr><td>Default Width</td>';
    echo '<td><input type="text" name="wpsgv_width" value="'.$width.'" size="20"></td></tr>';
    echo '<tr><td>Default Height</td>';
    echo '<td><input type="text" name="wpsgv_height" value="'.$height.'" size="20"></td></tr>';
    echo '<tr><td>Endpoints</td>';
    echo '<td><textarea name="wpsgv_endpoints" rows="3" style="max-width:100%;min-width:100%">'.$endpoints.'</textarea></td></tr>';
    echo '</tbody></table>';
    echo '<input type="submit" name="update_option" class="button button-primary" value="'.esc_attr__('Save Changes').'" />';
    echo '</form>';
    echo '</div>';
  }

}
?>

今回の機能拡張にあたり、クラス化をしました。プラグインをクラス化することによって、他のプログラムとの関数名の重複を防ぐことができます。

追加した関数は以下のとおりです。
__construct()はコンストラクタです。
wpsgv_activate()は、プラグインを有効化する際に実行される関数です。
wpsgv_uninstall()は、プラグインを削除する際に実行される関数です。
wpsgv_init()は、管理画面の初期化時に実行される関数です。
wpsgv_box()は、メタボックスを表示します。
wpsgv_menu()は、管理メニューの初期化時に実行される関数です。
wpsgv_options()は、プラグインのオプション設定を行います。

それでは、順番に見て行きましょう。

はじめにコンストラクタでは、アクションに対するフック関数を登録します。

public function __construct() {
  register_activation_hook(__FILE__, array(&$this,'wpsgv_activate'));
  register_uninstall_hook(__FILE__, 'WPSgv::wpsgv_uninstall');
  add_action('admin_init', array(&$this, 'wpsgv_init'));
  add_action('admin_menu', array(&$this, 'wpsgv_menu'));
  add_shortcode('wp_sgv', array(&$this, 'wp_sgv_handler'));
}

プラグインを有効化する際には、オプション設定値の初期値を登録します。

public function wpsgv_activate() {
  add_option('wpsgv_width', '600');
  add_option('wpsgv_height', '600');
  add_option('wpsgv_endpoints', "http://ja.dbpedia.org/sparql\nhttp://datameti.go.jp/sparql");
}

SPARQLエンドポイントとして dbpedia と datameti をあらかじめ登録しています。

プラグインを削除する際には、オプション設定値を削除します。

public static function wpsgv_uninstall() {
  delete_option('wpsgv_width');
  delete_option('wpsgv_height');
  delete_option('wpsgv_endpoints');
}

管理画面の初期化時に呼ばれる、admin_initアクションのフックメソッドでは投稿と固定ページの編集画面にメタボックスを追加します。

public function wpsgv_init() {
  add_meta_box('sparql', 'SPARQL visualization shortcode', array(&$this, 'wpsgv_box'), 'post');
  add_meta_box('sparql', 'SPARQL visualization shortcode', array(&$this, 'wpsgv_box'), 'page');
}

wpsgv_box()はメタボックスを表示します。この関数が出力しているJavaScriptコードがブラウザ上で動作し、メタボックス内から情報を集めてショートコードを生成します。ショートコードの出力後のコピペに便利なように、生成したショートコード全体を選択状態にします。

  public function wpsgv_box() {

    $width = get_option('wpsgv_width');
    $height = get_option('wpsgv_height');
    $endpoints = get_option('wpsgv_endpoints');
    $charts = array(
      'google.visualization.AnnotatedTimeLine',
      'google.visualization.AreaChart',
      'google.visualization.BarChart',
      'google.visualization.BubbleChart',
      'google.visualization.CandlestickChart',
      'google.visualization.ColumnChart',
      'google.visualization.Gauge',
      'google.visualization.GeoChart',
      'google.visualization.GeoMap',
      'google.visualization.ImageSparkLine',
      'google.visualization.LineChart',
      'google.visualization.Map',
      'google.visualization.MotionChart',
      'google.visualization.OrgChart',
      'google.visualization.PieChart',
      'google.visualization.ScatterChart',
      'google.visualization.SteppedAreaChart',
      'google.visualization.Table',
      'google.visualization.TreeMap',
      'sgvizler.visualization.DefList',
      'sgvizler.visualization.D3ForceGraph',
      'sgvizler.visualization.DraculaGraph',
      'sgvizler.visualization.List',
      'sgvizler.visualization.Map',
      'sgvizler.visualization.MapWKT',
      'sgvizler.visualization.Table',
      'sgvizler.visualization.Text'
    );

    echo '<script type="text/javascript">';
    echo 'function sgv_genshortcode() {
      sgv_shortcode.value = "[wp_sgv endpoint =\"" + sgv_endpoint.value + "\"" + 
        " chart=\"" + sgv_chart.value + "\"" +
        " options=\"" + sgv_options.value + "\"" +
        " width=\"" + sgv_width.value + "\"" +
        " height=\"" + sgv_height.value + "\"]]\n" + 
        sgv_query.value +
        "\n[[/wp_sgv]";
      sgv_shortcode.select();
    }';
    echo '</script>';
    echo 'Chart : <select id="sgv_chart">';
    foreach($charts as $chart) {
      echo '<option value="'.$chart.'">'.$chart.'</option>';
    }
    echo '</select><br />';
    echo 'Width : <input type="text" id="sgv_width" value="'.$width.'" size="10" />';
    echo ' Height : <input type="text" id="sgv_height" value="'.$height.'" size="10" /><br />';
    echo 'Endpoint : <select id="sgv_endpoint">';
    $array = explode("\n", $endpoints);
    $array = array_map('trim', $array);
    foreach($array as $endpoint) {
      echo '<option value="'.$endpoint.'">'.$endpoint.'</option>';
    }
    echo '</select><br />';
    echo 'Options : <textarea id="sgv_options" rows="2" style="max-width:100%;min-width:100%"></textarea><br />';
    echo 'Query : <textarea id="sgv_query" rows="3" style="max-width:100%;min-width:100%"></textarea><br />';
    echo '<a class="button" onClick="sgv_genshortcode();">Generate shortcode</a><br />';
    echo '<br /><textarea id="sgv_shortcode" rows="4" style="max-width:100%;min-width:100%" onClick="this.select();" readonly></textarea><br />';
  }

チャートの幅と高さのデフォルト値はオプション設定値から取得して表示し、SPARQLエンドポイントのリストはオプション設定値から取得して作成します。

管理メニューの初期化時に呼ばれる、admin_menuアクションのフックメソッドではプラグインの設定画面をメニューに追加します。

function wpsgv_menu() {
  add_options_page('WP Sgv Options', 'WP Sgv', 'manage_options',
    'wp_sgv', array(&$this, 'wpsgv_options'));
}

wpsgv_options()は、プラグインのオプション設定を行います。はじめにユーザの権限をチェックし、権限がない場合には終了します。このページにPOSTされて来た場合には設定値を更新します。

  function wpsgv_options() {
    if ( !current_user_can('manage_options')) {
      wp_die( __('insufficient permissions.') );
    }

    if (isset($_POST['update_option'])) {
      check_admin_referer('wpsgv_options');
      $width = $_POST['wpsgv_width'];
      if(is_numeric($width)){
        update_option('wpsgv_width', $width);
      }
      $height = $_POST['wpsgv_height'];
      if(is_numeric($height)){
        update_option('wpsgv_height', $height);
      }
      $endpoints = $_POST['wpsgv_endpoints'];
      update_option('wpsgv_endpoints', $endpoints);
    }

    $width = get_option('wpsgv_width');
    $height = get_option('wpsgv_height');
    $endpoints = get_option('wpsgv_endpoints');

    echo '<div><h2>WP Sgv Options</h2>';
    echo '<form name="wpsgv_form" method="post" action="">';
    wp_nonce_field('wpsgv_options');
    echo '<table class="form-table"><tbody>';
    echo '<tr><td>Default Width</td>';
    echo '<td><input type="text" name="wpsgv_width" value="'.$width.'" size="20"></td></tr>';
    echo '<tr><td>Default Height</td>';
    echo '<td><input type="text" name="wpsgv_height" value="'.$height.'" size="20"></td></tr>';
    echo '<tr><td>Endpoints</td>';
    echo '<td><textarea name="wpsgv_endpoints" rows="3" style="max-width:100%;min-width:100%">'.$endpoints.'</textarea></td></tr>';
    echo '</tbody></table>';
    echo '<input type="submit" name="update_option" class="button button-primary" value="'.esc_attr__('Save Changes').'" />';
    echo '</form>';
    echo '</div>';
  }

後半のコードは、設定画面を表示するためのものです。

以上で、ショートコードジェネレータ付きのプラグインの完成です。

●バージョン0.5
国際化対応し、ショートコードジェネレータのメタボックス

と、プラグインのオプション設定画面

で日本語を表示できるようにしました。国際化対応の方法については別の記事で解説していますので参考にして下さい。

今回作成したプラグインはGitHubからダウンロードできます。
https://github.com/midoriit/wp-sgv

データ可視化プラグインの開発 12