「東京ドーム１個分の大きさ」を可視化してみた

# はじめに
誰もがテレビで「東京ドーム〇〇個分の大きさ」という表現を耳にしたことがあると思います。
しかしこれは、東京ドームを見たことがない人にはあまりピンと来ない表現です。

そこで、東京ドームの大きさを身近に感じられる地図コンテンツを作りました。


# 何を作ったか
現在地を東京ドームと(大体)同じ大きさのポリゴンで覆う地図コンテンツを作りました。
<a href='https://bl.ocks.org/Hirosaji/raw/788653f2ce96f46c18d3ed5ca3daf82d'><img width='350px' src='https://qiita-image-store.s3.amazonaws.com/0/162655/e75699e5-0978-df7d-3526-a4185a82d848.png'></a>
[デモページ](https://bl.ocks.org/Hirosaji/raw/ed4443946608237eaddcf1207b3776cb/)
[差分（航空写真 var.）](https://bl.ocks.org/Hirosaji/raw/788653f2ce96f46c18d3ed5ca3daf82d/)

このコンテンツでは、ボタンを押すだけで、現在地を中心としたポリゴンが載ったWeb地図を生成することができます。
（※閲覧はスマホ推奨、PCでも可）

## 東京ドーム型のポリゴンについて

東京ドーム型のポリゴンの大きさは、東京ドームを円形と仮定して、[公式HP](https://www.tokyo-dome.co.jp/dome/about/)の情報をもとに計算しました。
大きさの設定に用いたパラメータは、以下のとおりです。

- 半径: 122m（※建築面積から算出）
- 最高部の高さ: 56.190m

# どう作ったか
地図描画ライブラリ[Mapbox GL JS](https://www.mapbox.com/mapbox-gl-js/)と、地理空間データ分析ライブラリ[Turf.js](http://turfjs.org/)を用いて実装しました。
ただし、現在地の取得にはMapbox GL JSのメソッドを使わず、Geolocation APIを利用しています。

# ソースコード
この記事では要点を絞って、下記の２つの機能のスクリプトを紹介します。

1. 現在地の緯度経度を受け渡す機能
2. 渡された緯度経度を中心とした東京ドームポリゴン付きの地図を生成する機能

ちなみに、コンテンツ全体のソースコードは次のページに掲載しています。
https://bl.ocks.org/Hirosaji/ed4443946608237eaddcf1207b3776cb
https://gist.github.com/Hirosaji/ed4443946608237eaddcf1207b3776cb

## 1. 現在地の緯度経度を受け渡す機能
ボタンのクリックイベントを受け取ってGeolocation APIの呼び出し、呼び出し成功時に緯度経度を <B>2.</B> の関数に渡すスクリプトです。

```js
var successCallBackFunction, errorCallBackFunction;

// API option
var option = {
    enableHighAccuracy: true, // Whether to use GPS to improve accuracy
    maximumAge: 0, // To hold cache ms
    timeout: 30000 // Timeout ms
};

/**
* Processing at the end of HTML loading
*/
window.addEventListener('load', function () {

    // Check whether API is enabled/disabled
    if (!navigator.geolocation) {
        alert('Geolocation API is disabled');
        return;
    }

    // Set button to get position info from API
    document.getElementById('button_id').addEventListener('click', function () {
    //　API call
        navigator.geolocation.getCurrentPosition(successCallBackFunction, errorCallBackFunction, option);
    }, false);
}, false);

/**
* Callback func at successful getting position info
*/
successCallBackFunction = function (position) {
    var lng = position.coords.longitude;
    var lat = position.coords.latitude;
    // Pass lng & lat to creating polygon function (for 2.)
    latlng2polygon(lng, lat);
};

/**
* Callback func at faild getting position info
*/
errorCallBackFunction = function (positionError) {
    alert(positionError.code + ': ' + positionError.message);
};
```

### 参考リンク
- [Geolocatoion APIで現在地を取得しGoogleマップに表示する方法](http://jbelltree.hatenablog.com/entry/2016/01/13/230235) - ゆとり世代プログラマの備忘録


## 2. 取得した緯度経度を中心とした東京ドームポリゴン付きの地図を生成する機能
取得した緯度経度を中心とした東京ドーム型のポリゴンをTruf.jsで生成し、Mapboxの地図上に載せるスクリプトです。

```js
function latlng2polygon(lng, lat) {

    mapboxgl.accessToken = 'Your token';
    var center = [lng, lat];
    var radius = 0.122;
    var precision = 0.001525;
    var epsilon = 0.0001;

    // Create new Mapbox object
    var map = new mapboxgl.Map({
        container: 'map',
        style: 'mapbox://styles/mapbox/light-v9',
        center: center,
        zoom: 16,
        pitch: 60
    });

    /**
    * Create Tokyo Dome Polygon using Turf.js
    */
    var grid = turf.hexGrid(turf.bbox(turf.circle(center, radius)), precision);

    // Calculate parameters of each element in turf circular bbox
    var dome = turf.featureCollection(grid.features.map(function (feature) {
        var point = turf.centroid(feature);
        var distance = turf.distance(center, point);
        if (distance > radius) { return; }

        // sphere r^2 = x^2 + y^2 + z^2
        // therefore z = Math.sqrt(r^2 - x^2 - y^2)
        //             = Math.sqrt(r^2 - (x^2 + y^2))
        // but distance^2 = x^2 + y^2
        // so z = Math.sqrt(r^2 - distance^2)
        var z = Math.sqrt(Math.pow(radius, 2) - Math.pow(distance, 2));
        var normHeight = 56.190 / 122; // to normalize height
        z = isNaN(z) ? 0 : z * normHeight;

        // Adapt parameters to Tokyo dome polygon data
        return turf.feature(feature.geometry, {
            base_height: z * 1000, // adopt z / m from z / km
            height: (z * 1000) + (distance * 1000 + epsilon) * 0.1
        });
    }).filter(function (feature) { return feature; }));

    // add Tokyo Dome polygon Layer on Mapbox
    map.on('load', function () {
        map.addSource('dome', {
            type: 'geojson',
            data: dome
        });

        map.addLayer({
            id: 'dome',
            type: 'fill-extrusion',
            source: 'dome',
            paint: {
                'fill-extrusion-color': 'red',
                'fill-extrusion-base': {
                    type: 'identity',
                    property: 'base_height'
                },
                'fill-extrusion-height': {
                    type: 'identity',
                    property: 'height'
                },
                'fill-extrusion-opacity': 0.5
            }
        });
    });
};
```

### 参考リンク
- [Add dome in Mapbox GL JS using a hex grid and polygon extrusions](https://bl.ocks.org/andrewharvey/c1fdbebafe3fc46d743ba9514e4d64b2) - Andrew Harvey’s Block

# おわりに
この記事では、東京ドームの大きさを可視化した地図コンテンツを紹介し、その要点となるスクリプトをコメントアウト付きで掲載しました。
地図系のライブラリは、組み合わせ次第で様々な表現ができるので、使っていてとても楽しいです。

特にMapbox GL JSは、3D表現が可能なWebGLを用いたライブラリの中でも、とにかく多様な表現ができることで有名です。
Mapbox GL JSをご存知ない方は、ぜひ[公式のExamples](https://www.mapbox.com/mapbox-gl-js/example/simple-map/)を覗いてみてください。
その表現の幅広さに、エンジニアなら誰もが気持ちを高ぶらせてしまうと思います。

またTurf.jsは、地理空間上で数値計算やデータ分析をする際には欠かせないライブラリです。
まだ参考サイトは少ないですが、今後リッチなWeb地図を作りたい方は、覚えておいて損はないと思います。

以上、ぜひ皆さんも自分だけの新たな地図を作ってみてください。