PythonでGoogleStreetViewから画像をダウンロードする


# はじめに
GoogleStreetViewのAPIを利用してパノラマ画像をダウンロードする際に、いい感じの記事が見つからず少し悩んだのでメモ。

<details><summary>ソースコードの全体像はこちら</summary><div>

```python:./street_view_download.py
import argparse
import requests

import google_streetview.api
from bs4 import BeautifulSoup

# デバック用パッケージ
from icecream import ic


class StreetViewRepository:
    def __init__(self, api_key, size=(600, 300)):
        self.api_key = api_key
        self.size = str(size[0]) + "x" + str(size[1])

    def _create_param(
        self, location: str, pitch: int, heading: int, fov: int = 90
    ) -> {}:
        params = [
            {
                "size": self.size,  # max 640x640 pixels
                "location": location,
                "pitch": str(pitch),  # 上下の角度 -90~90 (-90: 真下, 90: 真上)
                "heading": str(heading),  # 方位 0~360 (0:北, 90:東, 180:南, 270:西)
                "fov": str(fov),  # ズーム 0~120
                "key": self.api_key,
            }
        ]
        return params

    def get_pano_for_address(
        self, address: str, pitch: int, heading: int, fov: int = 90
    ):
        """住所からGoogleStreetViewの画像を返すプログラム

        Args:
            address (str): 検索対象の住所
            pitch (int): 上下方向の角度
            heading (int): 水平方向の角度
            fov (int): ズーム
        """
        target_coodinate = self._coordinate(address)
        location = str(target_coodinate[0]) + "," + str(target_coodinate[1])

        ic(address, location)
        params = self._create_param(location, pitch, heading)

        results = google_streetview.api.results(params)
        ic(results.preview())
        results.download_links("downloads")

    def _coordinate(self, address) -> []:
        """
        addressに住所を指定すると緯度経度を返す。

        >>> coordinate('東京都文京区本郷7-3-1')
        ['35.712056', '139.762775']
        """
        URL = "http://www.geocoding.jp/api/"  # Geocording Apiを利用する。
        payload = {"q": address}
        html = requests.get(URL, params=payload)
        soup = BeautifulSoup(html.content, "html.parser")
        if soup.find("error"):
            raise ValueError(f"Invalid address submitted. {address}")
        latitude = soup.find("lat").string
        longitude = soup.find("lng").string
        return [latitude, longitude]


def download_photo(
    api_key: str, address: str, pitch: int = 0, heading: int = 0, fov: int = 90
):
    """住所からGoogleStreetViewの画像を保存する
    Args:
        api_key (str): GoogleStreetViewから取得するAPIキー
        address (str): 検索対象の住所
        pitch (int): 上下方向の角度
        heading (int): 水平方向の角度
        fov (int): ズームの割合
    """
    # 初期化
    sr = StreetViewRepository(api_key=api_key)

    # 画像のダウンロード処理
    sr.get_pano_for_address(
        address=address,
        pitch=pitch,
        heading=heading,
        fov=fov,
    )


if __name__ == "__main__":
    # 引数の設定
    parser = argparse.ArgumentParser()

    parser.add_argument("api_key", help="GoogleStreetViewから取得するAPIキー")
    parser.add_argument("address", help="検索対象の住所")
    parser.add_argument("--pitch", default=0, help="上下方向の角度")
    parser.add_argument("--heading", default=0, help="水平方向の角度")
    parser.add_argument("--fov", default=0, help="ズームの割合")

    args = parser.parse_args()

    download_photo(
        api_key=args.api_key,
        address=args.address,
        pitch=args.pitch,
        heading=args.heading,
        fov=args.fov,
    )

```

</div></details>

実行コマンドを以下に示す。`${APIキー}`の箇所には、各自で取得したAPIキーを入力する。

```sh:
$ python street_view_download.py ${APIキー} 東京都千代田区
ic| address: '東京都千代田区', location: '35.694031,139.753772'

[0] UdOQn3fvD27X6L7Mr48uog
===========================
date: 2020-02
location: 
  lat: 35.6942363
  lng: 139.7538972
pano_id: UdOQn3fvD27X6L7Mr48uog
status: OK
ic| results.preview(): None
$ 
```

# APIの有効化
GoogleStreetViewApiを利用する認証キーの取得方法は、以下の公式のドキュメントを参照。APIの利用にはクレジットカードの登録が必要になるので注意。

- [Google Maps Platform スタートガイド  \|  Google Developers](https://developers.google.com/maps/gmp-get-started#create-project)

# 実装

## google-streetview
GoogleStreetViewをPythonで利用する際には、非公式のライブラリの`google-streetview`を利用するのが一番簡単。

- [google-streetview · PyPI](https://pypi.org/project/google-streetview/)

インストールは、以下のコマンドで実施する。

```sh:
pip install google_streetview
```

## 指定可能なオプション

GoogleStreetViewのAPIを利用して画像をダウンロードする際に、指定可能なオプションを以下に示す。
ズームや上下の角度の調整など、かなり細かい範囲まで指定できる。

| パラメータ名 | 意味                                    | フォーマット         |
| ------------ | --------------------------------------- | -------------------- |
| size         | ダウンロードする画像のサイズ            | {縦}x{横}            |
| location     | 緯度経度                                | {緯度},{経度}        |
| heading      | 方位(0:北, 90:東, 180:南, 270:西)       | [0-360]の範囲の数字  |
| fov          | ズームの量                              | [0-120]の範囲の数字  |
| pitch        | 上下の角度 -90~90 (-90: 真下, 90: 真上) | [-90-90]の範囲の数字 |

ダウンロードする画像のサイズを、`{縦}x(エックスの小文字){横}`で指定するなど、少し引っ掛かりポイントがあるので注意。筆者も最初はエックスの小文字を算術記号の`×`を入力していたため、ダウンロードできず少し悩んだ。

## 住所->緯度経度に変換

APIは緯度経度を入力することで、その地点の画像をダウンロードできると言う仕組みである。しかし、日本のある地点の画像をダウンロードしたいときは、緯度経度を入力するより、住所の入力からその地点の画像をダウンロードできるほうが簡単。その為、入力した日本の住所を緯度経度に変換するコードを作成した。
実装には、以下の方のコードを参考にした。

[住所から緯度経度を取得するpythonスニペット - Qiita](https://qiita.com/paulxll/items/7bc4a5b0529a8d784673)

```python:
    def _coordinate(self, address) -> []:
        """
        addressに住所を指定すると緯度経度を返す。

        >>> coordinate('東京都文京区本郷7-3-1')
        ['35.712056', '139.762775']
        """
        URL = "http://www.geocoding.jp/api/"  # Geocording Apiを利用する。
        payload = {"q": address}
        html = requests.get(URL, params=payload)
        soup = BeautifulSoup(html.content, "html.parser")
        if soup.find("error"):
            raise ValueError(f"Invalid address submitted. {address}")
        latitude = soup.find("lat").string
        longitude = soup.find("lng").string
        return [latitude, longitude]
```

# 実行

作成したプログラムで、試しにスカイツリーの地点のStreetViewの画像をダウンロードしてみる。実行コマンドは以下の通り。

```sh:
$ python street_view_download.py ${APIキー} 東京都墨田区押上１丁目１−２n
ic| address: '東京都墨田区押上１丁目１−２', location: '35.710256,139.810795'

[0] CAoSLEFGMVFpcE5ZVERCVHVLaHhsYkxtdXN5S25NTk5XOWtjaWFydGNoaHlMbWFR
=====================================================================
date: 2020-09
location: 
  lat: 35.71023864335756
  lng: 139.8107896372676
pano_id: CAoSLEFGMVFpcE5ZVERCVHVLaHhsYkxtdXN5S25NTk5XOWtjaWFydGNoaHlMbWFR
status: OK
ic| results.preview(): None
```

ダウンロードされた画像はこちら。なんとスカイツリーの中に進入できた。

![5d6d0ce9.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163680/078f7ccb-39f9-3d14-1cdf-73cdee53a7c9.jpeg)



# おわりに

いろんな地点の画像を簡単にダウンロードできて便利。APIの呼び出し回数だけには注意。

[GitHub - sey323/google-streetview-downloader: Download Image from Google Streetview in Japan](https://github.com/sey323/google-streetview-downloader)


# 参考資料
- [Street View Static API overview  \|  Google Developers](https://developers.google.com/maps/documentation/streetview/overview)
- [google_streetview — google_streetview 1.2.9 documentation](https://rrwen.github.io/google_streetview/)