うーん これは困りますねえ

CDNというのをご存知でしょうか?  Content Delivery Networkの略だと思います これは、インターネットで皆が使用する可能性の高いライブラリなどを配信専用に最適化されたサーバーから素早く皆に供給するために色々な会社がサービスとして行っているものです 有名なものとしては、Javascriptのライブラリである jQueryを配信する CDNがありますし、Twitter bootstrapというものも無くてはならいなサービスです

これらのサービスを利用して皆がインターネットのサイトを構築しているのです

ところが本日北京で更新したサイトを uploadした後、アクセスすると、どうも jQueryが動いていない、という驚くべき現象に遭遇したのです 自分で書いたコードを見なおしてみるとどうやら 以下の部分が関係しているようです

<?php
	if ($_SERVER['HTTP_HOST'] == 'localhost') {
?>
		<link rel="stylesheet" type="text/css" href="twitter_bootstrap/css/bootstrap.min.css">
        <link rel="stylesheet" type="text/css" href="twitter_bootstrap/css/bootstrap-theme.min.css">
		<script src = "javascriptlib/jquery-1.10.2.min.js"></script>
		<script src="twitter_bootstrap/js/bootstrap.min.js"></script>
		<script src="d3/d3.min.js"></script>
<?php
	} else {
?>
        <link rel="stylesheet" type="text/css" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
  		<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css" />
		<script src = "//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
		<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
        <script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<?php
	}
?>

これは何をしているのか? と言えば、テスト用の local serverで作動させる時には、localのファイルを読みに行かせ、インターネットにつながっている時には、CDNを読みに行かせ作動を最適化させようという工夫なのです

ところが、どうも else以下の部分、つまりインターネット接続時に CDNを読みに行っていない、というか読み込めないようなのです この国では Googleが接続できないのは有名な話なのですが、どうやら

//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js

で、CDNからの読み込みが弾かれているようなのです うーん困った問題です 従って、急遽ライブラリの読み込みを localから行うように変更しました まあ、それで作動はきちんとするのですが・・・

そして再び D3.jsを用いて登録症例数週毎推移をグラフとして表示する部分も uploadしました こんな風になるのです グラフをクリックしてね

graph.gif
graph.gif

ついに本番ページに実装

これまでの積み重ねを経て、各種テストを行いました そして ついに RAP and BEATのトップページに登録症例数のグラフ表示を実装できました

動画で表示するようにし、自施設に関しては、赤グラフで表示するようにしました もちろん、ログインが誰により行われ、その人が所属する施設が自施設となります ここで問題だったのは、$_SESSION変数を如何にして Javascriptライブラリである D3.jsに変数として渡すか? でしたが これに関しては少し調べて簡単にできることが分かりました

ここに記載されていたように json_encodeを用いて jsonオブジェクトとして渡すのだそうです これは securityが深く関係しているようです 実際の javascript codeはかくも簡単です

// json_encodeを使用して$_SESSION変数を安全に javascript変数に変換する
    var hp_tbl_id = <?php echo json_encode($_SESSION['hp_tbl_id'], JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); ?>;

この関数の第二引数はオプションであり、security上非常に重要だそうです

更に進化中

グラフの特定のものを赤色にし、しかもその列が描き出し開始を遅らせることにしました こんな具合です

graph
graph

しかも、自動的に動画再生するように gifに変換しました

これで d3.js の威力が分かりますよね

大分と理解が進んできたぞーっ

朝6:00AM前に起床し、新古賀病院にPCIで訪問するまでの そうですね実質 1.5時間ぐらい懸案のプログラミングに挑みました

必要要件としては

  1. サーバー側のデータベースから施設あたり登録症例数を取得する
  2. 取得したデータを JSON(JavaScript Object Notation)形式でクライアントに ajax通信する
  3. クライアント側では ajax通信により JSON形式データを取得する
  4. 取得した JSON形式データを加工する
  5. それをグラフとして出力する

というものです まずは1.ですね ここは MySQL database serverに対して SQL文で問い合わせを行います 具体的には

 	try {
    // MySQLサーバへ接続
   	$pdo = new PDO("mysql:host=$db_host;dbname=$db_name;charset=utf8", $db_user, $db_password);
	} catch(PDOException $e){
    	    die($e->getMessage());
	}
	$sql = "SELECT `hp_tbl_id` as `ID`, COUNT(*) as `ENROLL` FROM `pt_tbls` GROUP BY `hp_tbl_id`;";
	$stmt = $pdo->query($sql);
	$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);	

これは、SELECT `hp_tbl_id` as `ID`, COUNT(*) as `ENROLL` FROM `pt_tbls` GROUP BY `hp_tbl_id`; という SQL文により、MySQLに対して、`hp_tbl_id`毎にカウントした数を数えさせているのです

そして、その結果は、$rowsという配列オブジェクトに格納されます しかし、ここでのプログラミング言語は PHPですので、これを JSON形式に変換しないと、その後のデータ処理ができませんので

$json = json_encode($rows);
	
header('Content-type: application/json; charset=UTF-8');
	
echo($json);
exit;

によって、$rowsという php配列オブジェクトを JSONに変換しています そして、 echo($json); という文により、JSON形式のオブジェクトがクライアントに対して通信されているのです ここまでを行うプログラムはサーバー側のものであり、言語は PHPを用いています このプログラム・ファイルを get_each_h_enroll.php としました

次に、これを取得する方のプログラムですが、これはクライアント側のものであり、あくまでも htmlで書かれています ここではファイル名を each_hp_enroll.htmlとしました

ここでのキモは、d3.jsにより JSONデータ取得の ajax通信です それが

d3.json("get_each_hp_enroll.php", function(dataSet){
	var dataE = [];
	for (var key in dataSet) {
		dataE.push(dataSet[key].ENROLL);
	}

というものなのですが、d3.jsで定義されたJSON読み込み関数に従って、get_each_hp_enroll.phpより Ajax通信で渡された JSONデータを dataSetというオブジェクトに読み込みました

そして、それの中からデータとして使用する部分 – ここでは登録症例数 == ENROLLを配列 dataEに入れ込んでいます

後は、このdataEをもちいてグラフを描くだけですが、ここでは d3.jsの強力なデータ描画機能を用いるのです そのために、描画部分も d3という名前空間の中に閉じ込められています 従って さっきの部分も含めると

d3.json("get_each_hp_enroll.php", function(dataSet){
	var dataE = [];
	for (var key in dataSet) {
		dataE.push(dataSet[key].ENROLL);
	}
	
	d3.select("#myGraph")
		.selectAll("rect")
		.data(dataE)
		.enter()
		.append("rect")
		.attr("class", "bar")
		.attr("width", function(d, i) {
			return d;
		})
		.attr("height", 20)
		.attr("x", 0)
		.attr("y", function(d, i) {
			return i*25;
		})
});

ということになります

これを実行する = ブラウザで each_hp_enroll.htmlを開く と、以下のグラフが描画されます

DBから読み取り描かれたグラフ
DBから読み取り描かれたグラフ

これて見栄えは良くありませんが、とりあえずきちんと意図通りに作動するグラフが、SQL, HTML, Javascript, D3.jsを用いて描くことができました

ちなみに、一番重要な基幹技術は SVGなのです SVGというのは Scalable Vector Graphicsの頭文字ですが、その言葉通りに、 scaleできる == サイズを自由に変えることができる ベクトル化された描画 というものであり、HTML5の最新の技術なのです これが今使用されているブラウザで用いることができるようになり世界が変わったのですよっ

その話はまたね

心を鎮め清らかに

心が荒れるのは良くない それでも人間が未熟だから どうしても他者との関係の中で心が荒れる そんな時に人は色々な手段を使って心を鎮めるものです それが、宗教の領域にまで高められれば、座禅という型をとる場合もあるでしょう あるいは一心不乱に神に祈ることもあるでしょう あるいは五家荘の神楽のように神に捧げて一心不乱に踊る場合もあるでしょう

人それぞれ その人にあった方法で心を鎮めれば良いのです またその方法は一人にとって一つである必要は無く、色々な局面で色々な方法を適用すれば良いのです

などと言う前書きが長かったですが、今回僕はその方法として、「この際、理解不十分な部分を習得しよう」と思い立ったのです

実は RAP and BEATという国際共同多施設無作為化医師主導型臨床試験を運営しています 総登録症例数は 1,900例という規模のものです これはインターネット Web Siteを通じて無作為化および症例登録が行われるのですが、そのプログラム全体を僕一人で作成し、そして運用・改良しているのです

既に1,800例以上が登録され、もうすぐ閉じるのですが、大きなトラブルも無く運用されてきています もちろん securityには気をつけ、適宜データベースを backupしながら運用しているのです

前々から思っていて実現できていなかった interfaceとして、「ログインすれば、各施設毎の登録症例数一覧がグラフ表示される」という課題があります

どうすれば良いかは最初から分かっているのです

  1. クライアントがloginする
  2. クライアントの所属する病院IDをサーバーに投げる
  3. DB (database)を検索し、その病院IDおよび全参加病院ID毎の登録症例数をSQLで問い合わせる
  4. サーバーはDB問い合わせ結果をクライアントに投げる
  5. クライアントはそのデータ(Object)を解析し、グラフにする

ということです 3に関しては簡単です SQLさえ知っていれば簡単に一瞬で問い合わせ可能です 問題は4なのです ここではクライアントの画面が遷移しないように、ajax通信をせねばなりません その時には、通常 JSONというデータ形式を使うのが最近の流行りなのです

そして5ではグラフに描くのですが、ここでは javascript librariesを使用しますが、最近の流行りは D3.jsという libraryを用いるのです 問題はもう一方の Javascript librariesの雄である jQueryのようにふんだんに資料が転がっている訳ではないのです

問題点ははっきりしました 当初の課題は「D3.jsを用いて Ajax通信を如何に行うか?」という問題に帰着されたのです あるいは「D3.jsを用いて JSONをどのように扱うか?」と言い換えても良いです

そして苦闘24時間 ほぼ解決しました これで大きな一歩を踏み出せたのです 実際のコードは次回にでも掲載しましょう 何れにしても僕の心は鎮まりました

夢は広がる

さて、先ほどの SQLを使用すればたとえば以下のような出力が得られます もちろんデータは dummyですし、<table></table>を用いてhtmlで整形しています

登録数 病院名 年月
3 ShonanKamakura General Hospital 202410
15 ShonanKamakura General Hospital 202411
22 ShonanKamakura General Hospital 202412

これを用いれば、データをデータベースより出力し、現在勉強中の d3.jsを用いてグラフ化することも近いですね これは夢が拡がります

d3.js

ここ一ヶ月ぐらい d3.jsという Javascript Libraryというか Frameworkを勉強しています この歳で新たなものを勉強・習得するには大変な努力が必要です 何しろ今までと全く違う世界を生きる、ということに等しいからです

しかも d3.jsはとても癖がある Frameworkなのです それでも頑張っているのは、このライブラリは今後ものすごく発展しそうだからなのです 何故 d3.jsと呼ばれるか、と言えば 後ろの .jsというのはこれが Javascriptで書かれたプログラムであることを示します 要するに OSが認識できる拡張子なのです

そして d3というのは Data-Driven Documentsの頭3つをとったものらしいのです 何をするのか、と言えば ファイルやサーバーからデータを読み込み、それに応じてブラウザ上に動的にグラフを描く、そのようなソフトウェアなのです

これまでと全く違う世界に入るためには、要するに馴染まなければならないのです そのためには数多くプログラムを眺め、書き、理解しようとせねばなりません なかなか前に進まないのですが頑張るしかないでしょう

d3.jsを用いて interactiveなグラフ表示を実現する

ここ数週間遅々にではありますが、確実に勉強してきている d3.jsです

ついに、参考書を参考に、interactive graphを作成しました 忘れないようにそのコードを以下に記載しましょう

 	d3.selectAll("button").on("click", function() {
		var csvFile = this.getAttribute("data-src");
		var barElements;
		
		d3.csv(csvFile, function(error, data) {
			var dataSet = [];
			for (var i=0; i<data.length; i++) {
				dataSet.push(data[i]["商品A"], data[i]["商品B"] , data[i]["商品C"]);
				console.log("商品A = " + data[i]["商品A"] + "; " + "商品B = " + data[i]["商品B"] + "; " + "商品C = " + data[i]["商品C"]);
			}
		
			barElements = d3.select("#myGraph")
				.selectAll("rect")
				.data(dataSet);
			
			barElements
				.enter()
				.append("rect")
				.attr("class", function(d, i) {
   					if (i % 3 == 0) return "bar";
					if (i % 3 == 1) return "bar_red";
					if (i % 3 == 2) return "bar_green";
				})
	//			.attr("class", "bar")
				.attr("width", function(d, i) {
					return d;
				})
				.attr("height", 20)
				.attr("x", 0)
				.attr("y", function(d, i) {
					if ((i + 3) % 3 == 0) return Math.floor(i/3) * 65;
					if ((i + 3) % 3 == 1) return Math.floor(i/3) * 65 + 20;
					if ((i + 3) % 3 == 2) return Math.floor(i/3) * 65 + 40;
				})
		
			barElements
				.attr("class", function(d, i) {
   					if (i % 3 == 0) return "bar";
					if (i % 3 == 1) return "bar_red";
					if (i % 3 == 2) return "bar_green";
				})
				.attr("width", function(d, i) {
					return d;
				})
		
			barElements
				.exit()
				.remove();
	
			var old_class;
			barElements
				.on("mouseenter", function() {
					old_class = this.getAttribute("class");
					d3.select(this)
						.attr("class", "bar_red");
					return old_class
				})
				.on("mouseleave", function() {
						d3.select(this)
							.attr("class", old_class)
				})	
		})
	})

 

これにより csv dataをボタンで読み込み 以下のようなグラフが描けるのです

グラフ1(ボタン1を押したところ)
グラフ1(ボタン1を押したところ)

そして次のボタンを押すとこうなります

グラフ2(ボタン2を押したところ)
グラフ2(ボタン2を押したところ)

そしてカーソルをグラフの上に移動すると バーの色が赤にかわります

グラフ3(バーの色が変わっているのに注目)
グラフ3(バーの色が変わっているのに注目)

 

いやあここまで来るのに苦労しました