Google Mapの上にD3.jsでグラフをのせる


[WebAPIでとったデータをGoogle Mapにのせる](http://qiita.com/itagakishintaro/items/b0561d053241dc3f10a9)の続き的に。

![fusho.png](https://qiita-image-store.s3.amazonaws.com/0/50720/5bb2c91f-3eed-fcc7-997a-e0e5e86a92d1.png "fusho.png")


# 流れ

1. 府省ごとの情報システム予算をWebAPIからとってくる
2. 府省名から住所をとってくる
3. Google Mapを描画
4. Google Mapの上に府省ごとに予算額に応じたグラフをD3.jsで描く

# 使うもの

* IT Dashboard WebAPI

情報システム予算があるので、それを使います。
http://www.itdashboard.go.jp/DataFeeds/webapi#300

* Google Geocoding API

住所を取得するのに使います。
https://developers.google.com/maps/documentation/webservices/?hl=ja

* Google Map API

地図に使います。
https://developers.google.com/maps/?hl=ja

# 府省ごとの情報システム予算をWebAPIからとってくる

```
fusho_array{
	府省名{
		budget: 予算,
		lat: 緯度,
		lng: 経度
	}
}
```
の形式でデータを入れたいと思います。

まずは、緯度経度は空で府省名だけを入れます。
IT Dashboardの以下のページにサンプルコードもあります。
ポイントは、同じ府省でもシステムごとに予算がとれるので合計してやる必要があるところです。if-elseで分岐しているところがそうです。

http://www.itdashboard.go.jp/DataFeeds/webapi#300

あまりよくないかもしれませんが、上記流れをシーケンシャルに実行したいため、`async: false`を入れて、同期処理にしています。

```js
function getFushoITBudget() {
    $.ajax({
        url: "http://www.itdashboard.go.jp/PublicApi/getData.json",
        data: {
            dataset: "Budget",
            field: "organization,sum_budget"
        },
        async: false
    }).done(function(data) {
        $.each(data.raw_data, function(i, item) {
            if (fusho_array[item.organization] === undefined) {
                fusho_array[item.organization] = {
                    "budget": item.sum_budget
                };
            } else {
                fusho_array[item.organization].budget = Number(fusho_array[item.organization].budget) + item.sum_budget;
            }
        });
    });
}
```

# 府省名から住所をとってくる

どうも、府省名によっては、住所が取れないものもあるようです。
そこで、分岐を入れて、とれなかったものはログに吐いています。

こちらも、上記流れをシーケンシャルに実行したいため、`async: false`を入れて、同期処理にしています。

```js
function getFushoAddress() {
    $.each(fusho_array, function(name, object) {
        var geocoding = "http://maps.googleapis.com/maps/api/geocode/json";
        $.ajax({
            url: geocoding,
            data: {
                address: name
            },
            async: false
        }).done(function(data) {
            if (data.results[0] === undefined) {
                console.log(name);
            } else {
                fusho_array[name]["lat"] = data.results[0].geometry.location.lat;
                fusho_array[name]["lng"] = data.results[0].geometry.location.lng;
            }
        });
    });
}
```

# Google Mapを描画

idがmap-canvasの要素にGoogle Mapを表示します。
mapのオプション(mapOptions)の中心地点は、とりあえず、霞が関の緯度経度をハードコーディングしています。ｓ

```js
function handleLoadGoogleMap() {
    var mapOptions = {
        center: new google.maps.LatLng(35.673838, 139.750899),
        zoom: 16,
        mapTypeId: google.maps.MapTypeId.ROADMAP,
        mapTypeControl: false,
        disableDefaultUI: true
    };
    mymap = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
}
```

# Google Mapの上に府省ごとに予算額に応じたグラフをD3.jsで描く

次のサイトを参考にさせてもらいました。
http://shimz.me/blog/d3-js/3733

`overlay`を使うところ以外は通常のd3.jsです。
ポイントは、次の緯度経度をpx座標に変換しているところ。

```js
// 緯度経度をpx座標に変換
var point = this.getProjection().fromLatLngToDivPixel(new google.maps.LatLng(object.lat, object.lng));
```

```css
<style>
.SvgOverlay {
    position: relative;
}
.SvgOverlay svg {
    position: absolute;
    top: 0;
    left: 0;
    width: 8000px;
    height: 8000px;
}
</style>
```

```js
function overlayITBudget() {
    $.each(fusho_array, function(name, object) {
        // D3jsでグラフを表示
        if (object.lat !== undefined) {
            var overlay = new google.maps.OverlayView(); //OverLayオブジェクトの作成
            overlay.onAdd = function() {
                //オーバーレイ設定
                var layer = d3.select("#map-canvas").append("div").attr("class", "SvgOverlay");
                var svg = layer.append("svg");

                // 緯度経度をpx座標に変換
                var point = this.getProjection().fromLatLngToDivPixel(new google.maps.LatLng(object.lat, object.lng));
                overlay.draw = function() {
                    // 予算で棒グラフで表示
                    svg.selectAll(".node")
                        .data([point]).enter()
                        .append("line")
                        .attr("class", "node")
                        .attr("x1", function(d) {
                            return d.x;
                        })
                        .attr("x2", function(d) {
                            return d.x;
                        })
                        .attr("y1", function(d) {
                            return d.y - object.budget / 1000000000;
                        })
                        .attr("y2", function(d) {
                            return d.y;
                        })
                        .style("stroke", "gray")
                        .style("stroke-width", "10px");
                    // 府省名をテキストで表示
                    svg.selectAll("text")
                        .data([point]).enter()
                        .append("text")
                        .attr("x", function(d) {
                            return d.x;
                        })
                        .attr("y", function(d) {
                            return d.y;
                        })
                        .attr("fill", "blue")
                        .attr("font-size", "12px")
                        .text(name);
                }
                overlay.draw();
            }
            overlay.setMap(mymap);
        }
    });
}
```

# 完成品

```
<!DOCTYPE html>
<html lang='ja'>

<head>
    <meta charset='utf-8'>
    <title>府省map</title>

    <script type='text/javascript' src='https://maps.googleapis.com/maps/api/js?sensor=false'>
    </script>
    <script src='js/d3.min.js'></script>
    <script src='js/markerwithlabel_packed.js'></script>
<style>
#map-canvas {
    width: 100%;
    height: 600px;
}
.labels {
    color: black;
    background-color: white;
    font-family: 'Lucida Grande', 'Hiragino Kaku Gothic ProN', 'ヒラギノ角ゴ ProN W3', Meiryo, メイリオ, sans-serif;
    font-size: 10px;
    text-align: center;
    border: 1px solid black;
    white-space: nowrap;
}
.SvgOverlay {
    position: relative;
}
.SvgOverlay svg {
    position: absolute;
    top: 0;
    left: 0;
    width: 8000px;
    height: 8000px;
}
</style>
</head>

<body>
    <h1>府省情報システム予算</h1>
    <div id='map-canvas'></div>

    <script src='js/jquery-2.1.1.min.js'></script>
    <script type="text/javascript">
    var mymap;
    var fusho_array = {};
    google.maps.event.addDomListener(window, 'load', draw);

    function draw() {
        getFushoITBudget();
        getFushoAddress();
        handleLoadGoogleMap();
        overlayITBudget();
    }

    function getFushoITBudget() {
        $.ajax({
            url: "http://www.itdashboard.go.jp/PublicApi/getData.json",
            data: {
                dataset: "Budget",
                field: "organization,sum_budget"
            },
            async: false
        }).done(function(data) {
            $.each(data.raw_data, function(i, item) {
                if (fusho_array[item.organization] === undefined) {
                    fusho_array[item.organization] = {
                        "budget": item.sum_budget
                    };
                } else {
                    fusho_array[item.organization].budget = Number(fusho_array[item.organization].budget) + item.sum_budget;
                }
            });
        });
    }

    function getFushoAddress() {
        $.each(fusho_array, function(name, object) {
            var geocoding = "http://maps.googleapis.com/maps/api/geocode/json";
            $.ajax({
                url: geocoding,
                data: {
                    address: name
                },
                async: false
            }).done(function(data) {
                if (data.results[0] === undefined) {
                    console.log(name);
                } else {
                    fusho_array[name]["lat"] = data.results[0].geometry.location.lat;
                    fusho_array[name]["lng"] = data.results[0].geometry.location.lng;
                }
            });
        });
    }

    function handleLoadGoogleMap() {
        var mapOptions = {
            center: new google.maps.LatLng(35.673838, 139.750899),
            zoom: 16,
            mapTypeId: google.maps.MapTypeId.ROADMAP,
            mapTypeControl: false,
            disableDefaultUI: true
        };
        mymap = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
    }

    function overlayITBudget() {
        $.each(fusho_array, function(name, object) {
            // D3jsでグラフを表示
            if (object.lat !== undefined) {
                var overlay = new google.maps.OverlayView(); //OverLayオブジェクトの作成
                overlay.onAdd = function() {
                    //オーバーレイ設定
                    var layer = d3.select("#map-canvas").append("div").attr("class", "SvgOverlay");
                    var svg = layer.append("svg");

                    // 緯度経度をpx座標に変換
                    var point = this.getProjection().fromLatLngToDivPixel(new google.maps.LatLng(object.lat, object.lng));
                    overlay.draw = function() {
                        // 予算で棒グラフで表示
                        svg.selectAll(".node")
                            .data([point]).enter()
                            .append("line")
                            .attr("class", "node")
                            .attr("x1", function(d) {
                                return d.x;
                            })
                            .attr("x2", function(d) {
                                return d.x;
                            })
                            .attr("y1", function(d) {
                                return d.y - object.budget / 1000000000;
                            })
                            .attr("y2", function(d) {
                                return d.y;
                            })
                            .style("stroke", "gray")
                            .style("stroke-width", "10px");
                        // 府省名をテキストで表示
                        svg.selectAll("text")
                            .data([point]).enter()
                            .append("text")
                            .attr("x", function(d) {
                                return d.x;
                            })
                            .attr("y", function(d) {
                                return d.y;
                            })
                            .attr("fill", "blue")
                            .attr("font-size", "12px")
                            .text(name);
                    }
                    overlay.draw();
                }
                overlay.setMap(mymap);
            }
        });
    }
    </script>
</body>

</html>
```