Google Earth でシンゴジラと同じ高さのビルを探す


シンゴジラ，すごい迫力ですね．118.5m もあるそうです．ただ一方で，高層ビルが立ち並ぶ東京においては，それを超える高さのビルは沢山あります．東京ミッドタウンなんかは 248m あるようで，もはやシンゴジラはその半分の大きさすらありません．

シンゴジラは果たして大きいのか小さいのか．何だかよく分からなくなってきたので，どれくらい大きいのかイメージをわかせるために，Google Earth API と Selenium を用いて東京 23 区内で同じ高さのビルを探してみました．

## Google Earth 

![midtown.png](https://qiita-image-store.s3.amazonaws.com/0/7113/d6fc0a51-ee1d-8354-ab99-c73a82cdc72f.png "midtown.png")


上の画像は Google Earth にて，東京ミッドタウンにカーソルをあわせた際のものです．下部に，緯度経度に加えて標高 (269m) が表示されており，これから海面高度を引いたものが，その建物の高さになります．Google Earth では，航空写真測量という技術を用いて世界中の建物の高度を求めています．

Google Earth には Web API はありませんが [Javascript API](https://developers.google.com/earth/) が提供されており，イベント経由でその地点の情報を取得することができます．本 API は現在 Depricated であり，2016 年末で提供終了されるので，データを利用される方は早めに取得した方がいいかもしれません．建物の高度の情報なんかはオープンデータが存在せず，Google Earth 以外で取得するのは難しいように思います．

## Javascript API でクリックした地点の高度を取得

まずはじめに Javascript API を用いて，ブラウザに Google Earth を表示し，クリックしたらその地点の情報を取得できるようにします．今回は，```/path/to/index.html?lat=35.820&lon=139.555``` のようにクエリパラメータに緯度経度を指定すると，その地点を左上とした 5km × 5km の地図が表示されるようにしてみました．これが 1,000px × 1,000px 上にのるようにしたので，1px あたり 5m となります．

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Google Earth API</title>
    <style>
      body { width: 1000px; height: 1000px; margin: 0; }
      #map3d { height: 100%; }
    </style>
  </head>
  <body>
    <div id="map3d" />
    <script src="https://www.google.com/jsapi"></script>
    <script src="map.js"></script>
  </body>
</html>

```

```javascript:map.js
google.load('earth', '1', { other_params: 'sensor=false' })

const results = []

google.setOnLoadCallback(() => {
  google.earth.createInstance('map3d', earth => {
    earth.getWindow().setVisibility(true)
    earth.getLayerRoot().enableLayerById(earth.LAYER_BUILDINGS, true)

    // query string を parse  e.g., /path/to/index.html?lat=35.820&lon=139.555
    const params = location.search.split('?')[1].split('&')
    const [lat, lng] = params.map(param => parseFloat(param.split('=')[1]))
    
    const globe = earth.getGlobe()
    const lookAt = earth.createLookAt('')

    // 画面表示範囲 (m)
    lookAt.setRange(5000)

    // lat, lng が画面左上に位置するように修正 (デフォルトだとセンター)
    lookAt.setLatitude(lat - 0.025844962203145)
    lookAt.setLongitude(lng + 0.03188911376511783)

    earth.getView().setAbstractView(lookAt)

    // クリックイベントを設定
    google.earth.addEventListener(globe, 'click', event => {
      const info = {
        latitude: event.getLatitude(),
        longitude: event.getLongitude(),
        altitude: event.getAltitude()  // 高度
      }
      info.groundAltitude = globe.getGroundAltitude(info.latitude, info.longitude)  // 海面高度
      console.log(info)
      results.push(info)
    })
  }, () => {
    console.error('fail!')
  })
})
```

Google Earth のプラグインをブラウザにインストールした上で表示させてみると，下図のように，指定した緯度経度を起点とした地図が表示されます．また，地図のどこかをクリックしてみると，その地点の情報がコンソールに出力されているのが分かると思います．

![スクリーンショット 2016-08-21 0.30.27.png](https://qiita-image-store.s3.amazonaws.com/0/7113/b7ea9ac9-8e0d-e10d-5dd9-efd2acd3f4ab.png "スクリーンショット 2016-08-21 0.30.27.png")

## Selenium によるクリックイベントの自動化

クリックにより情報が取得できるようになったので，次は Selenium に 1px ずつポチポチさせていこうと思います．今回だと 5m ごとに高度を取得することになり，1,000px × 1,000px なので 100 万クリックとなります．

```py:getAltitude.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-

from selenium import webdriver
import argparse
import time

def main():
    url = parse_args()
    browser = webdriver.Firefox()
    browser.get(url)
    map_element = browser.find_element_by_id('map3d')

    time.sleep(20)  # Allow google earth plugin while this time

    for x in xrange(1000):
        for y in xrange(1000):
            print x, y
            action = webdriver.common.action_chains.ActionChains(browser)
            action.move_to_element_with_offset(map_element, x, y).click().perform()
        results = browser.execute_script("return results;")
        with open('{}.csv'.format(url.split('?')[1]), 'a') as f:
            for d in results:
                f.write('{latitude},{longitude},{altitude},{groundAltitude}\n'.format(**d))
        browser.execute_script("results = [];")

def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument('-u', '--url', type=str, required=True, help='url path')
    args = parser.parse_args()
    return args.url

if __name__ == '__main__':
    main()
```

以下のように実行すると，Firefox が立ち上がり，20 秒後に Selenium がクリックを開始します．この 20 秒の間に，Google Earth プラグインの許可を済ませて下さい．本当は PhantomJS などのヘッドレスなブラウザ用いてサーバ上で実行させたいところですが，それらには Google Earth プラグインを入れることができないため，ローカルで頑張りました．

```
python getAltitude.py --url /path/to/index.html?lat=35.820\&lon=139.555
```

実行が完了すると以下のような csv ファイルが生成され，緯度，経度，高度，海面高度の列が 100 万行ずらっと並んでいます．

```
35.8201681437,139.554803147,11.4957876807,11.4957887253
35.8201160743,139.55480317,11.495894997,11.4958948098
35.8200640214,139.554803208,11.4960015882,11.4960015454
35.8200119787,139.55480324,11.4961081794,11.4961079175
35.8199599214,139.554803282,11.4962147706,11.4962156907
...
```

東京 23 区は 5km × 5km の正方形 36 個 (30km × 30km の正方形) でカバーできることが分かったので，この手順を各区画でおこない，計 3,600 万の地点情報 (約2GB) を得ました．

![23wards.png](https://qiita-image-store.s3.amazonaws.com/0/7113/6320a316-ea82-f555-5e08-f5ad8599348a.png "23wards.png")

## シンゴジラと同じ高さのビルを取得

最後に，取得したデータからシンゴジラと同じ高さ (118m ~ 119m) のビルの緯度経度を取得しました．

```py
In [1]: import csv

In [2]: for l in csv.reader(open('/path/to/csv')):
   ...:     if 118 < float(l[2]) - float(l[3]) < 119:
   ...:         print l[0], l[1]
```

約 1,000 行の出力が得られました．5m 間隔でクリックしているため，同じビルを何回もポチポチしているものも多く，ビルの数は行数よりもずっと少ないです．

```
35.5497718126 139.67314824
35.5499246984 139.673900422
35.5498233084 139.673962201
35.549719274 139.676315323
35.5498629383 139.676696309
...
```

取得したデータをサンプリングし，いくつか Google Map で確認してみました．

```35.6704993 139.7551047```: 富国生命ビルという，日比谷にある 29 階建の建物を指していました．Wikipedia によると高さ 120m と書かれていたので，だいたいシンゴジラと同じ高さですね．

```35.6884934,139.6947538```: モノリスビルという，新宿にある 30 階建の建物でした．Wikipedia には 123.35m と書かれており，シンゴジラより少し大きめでした．また Wikipedia には以下のような記述もあり，すでにゴジラに破壊されているようです．

>1991年に公開された『ゴジラvsキングギドラ』では、ゴジラによって破壊されるシーンが描かれた。

東京に来てまだ日が浅く，知らない建物が多かったため，結果を見てもあまりイメージは深まりませんでしたが，だいたい 30 階建のビルと同じ高さであることは分かり，シンゴジラは「そこそこでかい」という知見が得られました．