D3.jsを用いてUSの州昇格を時系列でグラフ化してみた その2(スライダーバーとか導入)


はじめに

D3.jsを用いてUSの州昇格を時系列でグラフ化してみたを作成したときにまだやらない夫が南北戦争を戦いぬくようですを未読でした。ですが、読み終わってみると単にアメリカの準州と州昇格しかグラフにしか組み込みでは作りが甘いことに理解できました。

例えば、南北戦争時に合衆国を離脱しアメリカ連合を形成した州や各種戦場とかのも表示されればよりアメリカが州をどのように組み込んできたかわかるというものではないかと思い修正することにしました。

また、前回コメントでプログレスバーのようなものが必要でないかとコメントを頂いたのでそれも一緒に組み込みました。

さらに、毎回マップを再描画する頭の悪い方法を採用していたので、それも修正してみました。

準備

今回、jQuery UIのスライダーを使用して時間の遷移を表現してみました。一応はタイマーの速度も変更可能にしています。

また、独立戦争、米墨戦争、南北戦争の主な戦場に関してwikipediaを調べて記載しました。と言っても、年で動くため一斉に表示されてしまい見やすくはありませんが。けど、主な南北戦争の主な戦場はどのように選ばれているのでしょうかね?

ついでに、アメリカ側が勝利した場合は青色で、敵(イギリス、メキシコ、CSA)が勝った場合は赤色、イーブンの場合は黒で戦場を記載しています。

ただし、戦場の場所はだいたいですので参考程度にしてください。

イメージ

実際に動くのはバージョンアップした州昇格マップがあります。

⏵ボタンを押すとスタートします。⏵⏵を押すと早く動き、⏴⏴押すと遅くなります。

スライダーを動かすと好きな箇所に移動できます。ただし、スライダーを動かすとアニメーションがストップしますので、続きを見たい場合は⏵ボタンを押してください。

ソース

ソースはhttps://github.com/rakichiki/us_map_states.gitにありますので、git cloneしていただければと思います。まぁ、index.htmlだけですし。

今回作成するにあたり一番困ったのはD3.jsでsvgを描くのですが、どうしてもidを付与できませんでした。WEBで調べるとできるようなサンプルもあるのですが、やってみてもダメでした。

このため、あきらめてDOM操作でidを付与しました。

id付与
var elements = document.getElementsByTagName('g')[0].children;
  for (var i = 0; i < elements.length; i++) {
    elements[i].setAttribute('id', 'state_' + us.objects.states.geometries[i].id);
  }

理由がわかりませんが、DOM操作のほうが得意なので、そちらに逃げてしまいました。

スライダーはよくあるサンプルです。ただし、スライダーを動かしたときに、タイマーの停止、スタート・ストップボタンの変更、年の変更とマップの描画を行っています(この辺りはスライダーを使う場合はよくあること機能だと思います)。

スライダー
$(function(){
  $("#slider1").slider({
    range: "min",
    min: start_year,
    max: end_year,
    value: start_year,
    step: 1,
    slide: function(event, ui) {
      change_slide(ui.value);
    }
 });
});

// スライダー変更
// value: スライダーの値
function change_slide(value) {
  var element_main = document.getElementById('button_main');
  button_status = 0;
  element_main.innerHTML = '?';
  clearTimeout(requestID);
  sleep_count = (value - start_year) * year_bin;
  if (value != end_year) {
    var element_main = document.getElementById('button_main');
    element_main.disabled = false;
  }
  draw_us_map(false);
}

また州の色を変更する箇所はDOMで操作してしまいました。D3.jsライクに書くべきなのかもしれませんが...

色変更箇所
  var element = document.getElementById('state_' + state_info_array[i].id);
  省略
  element.style.fill = '#88f';

この修正は割と効果的でした。前のように毎回描画したときはCPUの使用率5%近くいっていたのですが、該当の箇所だけ色を変更することで2%近くまで落ちました。以下が修正後のCPU使用率です。定期的に上がっているのが描画している箇所ですが、この山が低くなりました。

SVGはやっぱり重いなと思うのですよね。この辺りベンチにすると少し面白いかもしれませんが、SVGはな...

あと戦争イベントは以下のようにまとめました。色をそのまま

戦争イベントデータ
// 戦争イベント
// year: 勃発年
// name: 戦場名
// x: x位置
// y: y位置
// color: 表示色(青はUSAが勝利した場合、赤は敵、黒はイーブン)
var war_array = [
  {year: 1775, name: 'レキシントン・コンコード', x:888, y:160, color:'#009'}
 ,{year: 1775, name: 'バンカーヒル',      x:892, y:160, color:'#900'}

あとは更新スピードを変更できるようにしたのですが、スピードの量をスライダーの色で表現してみました。

スライダーの色の変更箇所
  var element = document.getElementById('slider1');
  element.children[0].style.background = time_out_color_array[count];

このUIがわかりやすいのか、そうでないのかわかりませんが、単にタイマーの数字を描くよりは見た目上いいかなと思ったりしました。(バーの色ではなく、スライダーのボタンの箇所のほうがよかったかなと思ったりはしていますが)

作成するにあたり問題点

いくつか問題がありました。

1つ目は、上でも書きましたがSVGにidを付与できなかったところです。やり方はあるのかもしれませんが、私は解決できませんでした。一回しか描くことだけを考えれば不要な機能ではあるのですが、該当項目だけ変更するときはどうしても必要だと思うのですが...classで一斉に変更というのはまた別のケースだと思うのですけどね。

2つ目は戦場イベントもマップ上に記載したのですが、場所を調べるのが非常に大変でした。wikipediaとかで調べても、場所が何々州何々郡と言われてもそれがus_mapのどこに位置するかさっぱりわかりません。まだ緯度・軽度があれば変換して使用可能なのですが、それが揃っているものはありませんでした。

じゃ、どうしたのかというと、郡名が書いてあったので、郡表記の且つマウスを合わせるととx,yが表示が出るマップを別途作成して、それを使ってusマップの座標をアバウトに調べました。それを使って戦場のx,yを決めました。

原始的でかつ力業という面白くない方法を用いるしかありませんでした。このため、正確性はとても乏しいでしょう。この辺りマップ系でこの世に存在しない情報をプロットする場合はなかなか難しいなと思うのです。

3つ目は、同じく戦場のところですが、表示すると思いのほか重なって面白くありませんでした。年更新にしたため仕方ないところですが、月ごとに表示して、時間がたつと薄くしていけばもしかするとよりわかりやすかったかもしれません。また、文字だけでは面白くないので被害の状況を円グラフなどで表現するとより大変さが際立ったかもしれません。

反省

上でも書いてある通りD3.jsでSVGにidを付与することができなかった問題です。これを解決する方式はわかりませんでした。やり方がわかればもっとD3.jsライクにソースを書けたのにと思わなくもありません。

あとライブラリを増やしたくなかったのでスライダーにはメモリなどを入れませんでした。スライダーの進みぐらいがわかりづらいところがあると思いますが、もう少しわかりやすくできればいいのにと思わなくもありません。

この辺りはjQueryが得意じゃなかったり、D3.jsの経験不足からきている感じがしないでもありません。

最後に

だいたいの目的のものが作成できました。これを見て、リンカーン大統領が焦って北軍総司令官をころころ変えていますが、少しはその気持ちがわかるというものでしょうか。ワシントンDCの近くが戦場されたらそらこまりますわな。

この辺りは”やらない夫が南北戦争を戦いぬくようです”を読むことで、マップが赤く塗りつぶされたり戦場が近かったりとかをマップで見るとリンカーン大統領の苦悩が少しでも理解度が増すとうれしいです(まぁAAでマップ描くのはすごく大変だと思うんですよね。D3.jsではマップ普通に公開されているから簡単にできていますが...)。

これを作成するともう少し動きがあるグラフのほうが面白い気がしなくもないなぁと思います。けどヨーロッパの国の領域とかは面白いかもしれませんが、マップ系は元データがないと本当に大変でして。戦場の位置程度でさえ手で調べるのがこんなに大変だとマップ系を一から作れと言われると私はちょっと無理かなと思わなくもありません。

ゲームとかこういうのとかもそうですが、画像や元データが簡単に手に入らないとそう簡単に作れないだろうと思うのですよ。

リンク

D3.jsを用いてUSの州昇格を時系列でグラフ化してみた