hsuetsugu’s diary

ITの技術的なことに関して主に書きます。Rとpythonとd3.jsとAWSとRaspberryPiあたりを不自由なく使いこなせるようになりたいです。

(35連休)3日目:AWS(MACでのAWS上のd3.jsを使った画面構築)

本日は、RaspberryPiから少し離れて、かなり前にアカウント作ったAWS上でd3.jsを使ったページを構築しました。*1
まずはこれまでWindowsからしかやってこなかったAWSへの接続を、MACから実施する必要がありました。特につまづいたところはないですが、念のためメモ。その上で、OpenLayorを使ってd3.jsで放射線の線量観測データを可視化しました。

AWSの初期設定はここに記載しています。
http://hsuetsugu.hatenablog.com/entry/2014/07/09/095336

MACからAWSへの接続(SFTP)

Raspberry Piで使ったのと同じCyberduckを用いる。SFTPを選択して、pemキーを指定して、ユーザ名はec2-userで問題なく接続できた。

MACからAWSへの接続(SSH

AWSのマネジメントコンソールのEC2の画面にて、"Connect"をみると、どうすればいいか書いてくれている。
ターミナルにて、ログインする前に、書かれている通り、下記を実施しないとうまくいきませんでした。

chmod 400 [pemキー名称].pem

これをしたうえで、下記をうって、無事SSHで接続ができました。

ssh -i [pemキーのpath] ec2-user@[elastic ip]

html,js,cssなどの配置

何もしなければ、ドキュメントルートが /var/www/html になっているので、ここにおけば、ブラウザからみれます。設定は/etc/httpd/conf/httpd.confに記載されています。
SFTPで何も考えず上記フォルダにファイルをおこうとしたらできなかったため、/home/ex2-user に一旦おいてから、SSHでmvコマンドにてディレクトリごと移動させています。(これが普通かはわかりませんが・・)

d3.jsとOpenLayorsを用いた地図描画

試しに、一度作ってみた放射線線量データの地図上での可視化をやってみました。

線量データの取得

データはcsv形式にはなりますが、東日本大震災関連情報 放射線モニタリング測定結果等 | 原子力規制委員会からダウンロードができます。
このサイトも各種オープンデータをまとめてくれているオープンデータのまとめ - NAVER まとめにも記載がありました。

線量データcsvファイルをgeojsonへ変換

d3.jsで地図上にポイントデータを表示するには、geojsonにしておく必要があります。単純なポイントデータとはいえ、csvからgeojsonへの変換プログラムをかくのも面倒なので、QuantumGISを使います。
QGISを起動して、メニューの「レイヤ」→「デリミティッドテキストレイヤの追加」から上記でダウンロードしたcsvを読み込みます。デリミタをカンマ、XとYに経度と緯度を指定すると、読み込めます。あとはこれを「名前をつけて保存」でgeojsonで保存すると、下記のようなgeojsonができあがります。

{ "type": "Feature", "id": 6, "properties": { "area": "いわき市", "y": 37.03515, "x": 140.85048, "qty": 0.117 }, "geometry": { "type": "Point", "coordinates": [ 140.85048, 37.03515 ] } }
,

htmlファイル

読み込ませる線量データは上記でできあがるので、あとはhtml,javascriptです。
d3.jsとOpenLayorsを使います。

<script src="http://d3js.org/d3.v2.min.js"></script>
<script src="http://openlayers.org/dev/OpenLayers.js"></script>

csvから変換して作ったgeojsonを"nuclearMonitor.geojson"という名前にして、htmlと同じディレクトリに配置して、読み込みます。

d3.json("nuclearMonitor.geojson", function (geoData) {
    main(geoData);
});

mainは下記のような感じです。

function main(geoData) {

    //地図における最大の空間範囲と地図スクロール範囲。
    var extent = [
        -20037508.34, -20037508.34,
        20037508.34, 20037508.34
    ];


    var map = new OpenLayers.Map('map', {
        numZoomLevels: 20,
        projection: new OpenLayers.Projection("EPSG:3857"), // 地図の投影(真球メルカトル投影)
        displayProjection: new OpenLayers.Projection("EPSG: 4326"),  // 等経緯度投影を定義
        maxExtent: extent, //最大の空間範囲
        restrictedExtent: extent, //地図スクロール範囲
        controls: [
            new OpenLayers.Control.Navigation(),
            //new OpenLayers.Control.Zoom(),
            new OpenLayers.Control.PanZoomBar(),
            new OpenLayers.Control.ScaleLine(),
            new OpenLayers.Control.LayerSwitcher(),
            new OpenLayers.Control.MousePosition(),
            new OpenLayers.Control.KeyboardDefaults()
        ]
    });

    //open streat map レイヤを指定
    var ol_wms = new OpenLayers.Layer.OSM();

    //OpenStreetMapレイヤーを追加
    map.addLayers([ol_wms]);


    //初期位置指定
    map.setCenter(new OpenLayers.LonLat(140.29, 37.45).transform("EPSG:4326", "EPSG:900913"), 8);

    //svgを表示するオーバーレイオブジェクトを作成
    //var overlay = new OpenLayers.Layer.Vector("stations");
    var overlay = new OpenLayers.Layer.Vector("states");

    // 地図にオーバーレイを追加する際のコンテナを作成
    overlay.afterAdd = function () {
        //ベクターレイヤーのdivエレメントを取得
        var div = d3.selectAll("#" + overlay.div.id);
        //既存のsvgレイヤを削除して新たなsvg要素を追加
        div.selectAll("svg").remove();
        var svg = div.append("svg");

        g = svg.append("g");

         //地図の左下と右上の緯度経度取得
        var bounds = d3.geo.bounds(geoData);

         //緯度経度->パスジェネレーター関数作成
        var path = d3.geo.path().projection(project);


	var maxQty = d3.max(geoData.features, function(d) {return d.properties.qty} ); //追加
	var minQty = d3.min(geoData.features, function(d) {return d.properties.qty} ); //追加

        //geojsonデータからpathを作成
        var feature = g.selectAll("path")
                .data(geoData.features)
                .enter()
                .append("path")
		  .attr("fill","red") //追加
		  .attr("d", path.pointRadius(function(d) {
			return 10*Math.sqrt(d.properties.qty)}) ) //値に応じて半径を変化させる
		 .sort(order)
		//ツールチップ追加
                            .on("mouseover", function (d) {
                            var mousePosition = d3.svg.mouse(this);
                            //var format = d3.time.format("%Y-%m-%d %HH:%MM:%SS");
                            $("#pop-up").fadeOut(100, function () {
                                // Popup content
                                $("#pop-up-title").html(d.properties.area);
                                $("#pop-img").html(d.properties.qty + "(μSv/h)");
                                $("#pop-desc").html("");

                                // Popup position
                                var popLeft = mousePosition[0] + 300 > screen.width ?
                                    mousePosition[0] - 400 : mousePosition[0];
                                var popTop = mousePosition[1];
                                $("#pop-up").css({
                                    "left": popLeft + 50,
                                    "top": popTop
                                });
                                $("#pop-up").fadeIn(100);
                            });
                        }).
                        on("mouseout", function () {
                            $("#pop-up").fadeOut(50);
                        });

	  // Defines a sort order so that the smallest dots are drawn on top.
	  function order(a, b) {
	    return radius(b) - radius(a);
	  }

        //MapにMove Event登録
        map.events.register("moveend", map, update);

        update();

        function update() {
            var bottomLeft = project(bounds[0]); //地図左下緯度経度→ピクセル
            var topRight = project(bounds[1]);//地図右上緯度経度→ピクセル
            console.log(topRight)

            //svgのサイズを更新
            svg.attr("width", topRight[0] - bottomLeft[0])
                .attr("height", bottomLeft[1] - topRight[1])
                .style("margin-left", bottomLeft[0] + "px")
                .style("margin-top", topRight[1] + "px");

            g.attr("transform", "translate(" + -bottomLeft[0] + "," + -topRight[1] + ")");

            //描画したsvgのアップデート
            feature.attr("d", path);
        }

        //位置情報→ピクセル変換
        function project(x) {
                var point = map.getViewPortPxFromLonLat(new OpenLayers.LonLat(x[0], x[1]).transform("EPSG:4326", "EPSG:900913"));
                return [point.x, point.y];
        }
    }

    map.addLayer(overlay);
}

以上で、こんな感じでOpenLayorにポイントデータを重ねることができました。
f:id:hsuetsugu:20140812150151p:plain

*1:ジャンパワイヤなど、電子工作に必要な部品群がそろっていないため。