指定した領域のラスタータイルをダウンロードしたりGeoTIFFに変換してくれるPythonパッケージをPyPIで公開しました！

:::note info
これは [MIERUNE AdventCalendar 2022](https://qiita.com/advent-calendar/2022/mierune)　22日目の記事です。
昨日は @asahina820 さんによる [Cesium for Unityを使って3D Tiles形式のPLATEAUデータを表示する](https://qiita.com/asahina820/items/14fb41cf00690d2dd15f) でした。
:::

## 初めに

**ラスタータイル**、というデータ形式をご存知でしょうか？

WebGISではとてもよく利用される概念ですが、以下のように背景となる地図（画像）をズームレベルという概念に沿って倍率を変え、ズームレベルが上がるごとに地図の詳細度が上がっていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/7b320925-2ba2-6b73-2c7c-5820fae61e1d.png)
（[地理院タイルについて](https://maps.gsi.go.jp/development/siyou.html)より）

表示区画があらかじめ決まっているため、地図画像を事前に作成しておくことが可能で、**軽量かつ高速に全世界分の背景地図をブラウザで表示**することができます。

こんなラスタータイルですが、通常は`https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png`のように`{ズームレベル}/{X座標}/{Y座標}.png（.jpg）`のような**ZXYのタイル座標**を指定する形式で配信され、**ブラウザなどから利用する**ことを想定しています。

ただ、国土地理院が配信する[標高タイル](https://maps.gsi.go.jp/development/demtile.html)のようにピクセルの値そのものに意味があるラスタータイルも存在していたり、航空写真などであればブラウザ以外でも一部の箇所だけ取得し、単なる背景画像以外の用途で利用したいなぁと思う場面はちょこちょこあります。

ラスタータイル自体は、それ自体に地理的な意味を全く持たないただの画像データなんで、そんな時にいちいち緯度経度からズームレベルごとのタイル座標を計算し、ダウンロードして、地理情報を埋め込んで〜みたいなことはしたくないわけです。

こういう悩みを持つ人は多そうなんで、みなさんが利用できるようにPythonでパッケージを作成して、PyPIに公開しました！

## 使い方

今回作ったのは、[tile-operator](https://github.com/nokonoko1203/tile-operator)というツールです。

PyPIでも公開しているので、Python実行環境があれば「pip」などを利用して誰でも簡単に利用することができます。
https://pypi.org/project/tile-operator/0.0.6/

```bash
pip install tile-operator
```

CLIツールとしても利用できますが、多少めんどくさい設定が必要になります

### CLIとして利用するための準備

CLIとして利用する場合には[こちら](https://github.com/nokonoko1203/tile-operator/releases/tag/v0.0.6)からSource code(zip)をクリックしてコード一式をダウンロードしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/655f4c17-c6da-3574-3326-43320126fc52.png)

その後、zipを解凍し、ターミナルなどを利用してtile-operatorディレクトリの中に入ってください。

```bash
$ cd ~/Downloads/tile-operator-0.0.6
$ ls -al
total 224
drwxrwxr-x@ 10 satoru  staff    320 12 17 16:11 ./
drwx------@  9 satoru  staff    288 12 17 21:44 ../
-rw-rw-r--@  1 satoru  staff   3277 12 17 16:11 .gitignore
-rw-rw-r--@  1 satoru  staff   1069 12 17 16:11 LICENSE
-rw-rw-r--@  1 satoru  staff   1904 12 17 16:11 README.md
-rw-rw-r--@  1 satoru  staff  91048 12 17 16:11 poetry.lock
-rw-rw-r--@  1 satoru  staff   1023 12 17 16:11 pyproject.toml
drwxrwxr-x@  5 satoru  staff    160 12 17 16:11 tests/
drwxrwxr-x@  4 satoru  staff    128 12 17 16:11 tile_operator/
-rw-rw-r--@  1 satoru  staff   1338 12 17 16:11 to.py
```

次に、poetryをインストールします。

```bash
curl -sSL https://install.python-poetry.org | python -
echo 'export PATH="/Users/satoru/.local/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
poetry --version
```

バージョンが表示されれば、正常にインストールできています。できていない場合はググってください！！！

Pythonの仮想空間の管理をしやすくさせるために、ディレクトリ内に仮想環境を作成されるよう変更しておきましょう。

```bash
poetry config virtualenvs.in-project true
```

ここまで終了したら、以下のコマンドを入力して、tile-operatorの依存パッケージをインストールします。

```bash
poetry install
poetry shell
```

ここまでの準備が終わったら、ようやく指定範囲のラスタータイルをダウンロードしていきます。

初めに画像を取得したい範囲の矩形を示すGeoJSONを作成しましょう。

[geojson.io](https://geojson.io/)なんかを利用すると以下のように手軽に作成できます。

範囲が広すぎるとデータ数も増えてしまうので、札幌駅周辺のみを取得することとしましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/152708dc-5a13-52d3-709c-17779b28d809.png)

右に表示されているGeoJSONを`aoi.geojson`など適当な名前をつけて作成し、書き込んでおきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/0f00d656-8b89-8f40-0e6e-7669c763ae51.png)

次に、取得したいラスタータイルのURLを設定します。

今回は[OpenStreetMap](https://openstreetmap.jp/)のデータを利用していきましょう。

URLは以下のようになります。
`https://tile.openstreetmap.jp/{z}/{x}/{y}.png`

最後に取得したいズームレベルを決めましょう。

今回は18を指定します。

ズームレベル18はとても詳細度の高いズームレベルですので、広い範囲で取得するときはお気をつけください。

**東京都全域とか指定すると、10万ファイルは超えると思います**。

それではコマンドを実行してみましょう。

```bash
$ python to.py -v download https://tile.openstreetmap.jp/{z}/{x}/{y}.png aoi.geojson 17

Tile Download

 Options:
  tile_url=https://tile.openstreetmap.jp/{z}/{x}/{y}.png
  file_path=aoi.geojson
  zoom_level=17


100%|█████████████████████████████████████████████████████████████████████████████████████| 20/20 [00:10<00:00,  1.94it/s]
```

プログレスバーの表示から、20枚の画像がダウンロードされたことがわかりますね。

ダウンロードされた画像はこのようにoutputフォルダが生成され、そこに格納されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/67dd3712-ed3f-3058-d5b4-30f14b190892.png)

ね、簡単でしょ？

## Pythonのパッケージとして利用する

こちらはPyPIからインストールすればすぐに使える上、CLIツールより高機能です。

まずは以下のようにクラスとユーティリティ関数をインポートします。

```python
from tile_operator.operate import TileOperate, file_to_bounds
```

次に、インポートしたfile_to_boundsを利用して、先ほど作成したaoi.geojsonのバウンディングボックスを作成しましょう。

```python
file_path = "./aoi.geojson"
bbox = file_to_bounds(file_path).bounds()
bbox
# (141.34524527865244, 43.06547110473289, 141.3568432889649, 43.07128825219701)
```

その後、作成したbboxとデータを取得したいズームレベルのみ指定してインスタンス化します。

```python
to = TileOperate(
    bbox=bbox,
    zoom_level=17,
)
```

そしてset_tile_listメソッドを利用し、その後、geo_tile_listメソッドを呼び出してセットされたタイル座標の一覧を取得しましょう。

```python
to.set_tile_list()
tile_list = to.get_tile_list()
print(len(tile_list))
```

ダウンロードされるタイルが、CLIを利用した時と同様に20個であることがわかりました。

```text
20
```

さらに、タイル座標の一覧も取得できます。

```python
to.get_tile_list()
```

```text
[(116998, 48126),
 (116998, 48127),
 (116998, 48128),
 (116998, 48129),
 (116999, 48126),
 (116999, 48127),
 (116999, 48128),
 (116999, 48129),
 (117000, 48126),
 (117000, 48127),
 (117000, 48128),
 (117000, 48129),
 (117001, 48126),
 (117001, 48127),
 (117001, 48128),
 (117001, 48129),
 (117002, 48126),
 (117002, 48127),
 (117002, 48128),
 (117002, 48129)]
```

set_tile_listメソッドを呼び出して、タイル座標の一覧が生成された後であれば、download_all_tilesメソッドを呼び出し、タイルをダウンロードすることが可能です。

```python
tile_url = "https://tile.openstreetmap.jp/{z}/{x}/{y}.png"
to.download_all_tiles(tile_url)
```

```text
100%|██████████| 20/20 [00:05<00:00,  3.70it/s]
```

## その他便利そうな機能

タイル座標に応じたグリッド（ポリゴン）を作成し、geopandasのGeoDataFrameで返すメソッドも用意されています。

```python
to.create_tile_grid_from_bbox_list()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/3ee91b60-8f91-e657-d7b1-5f462c8d57cc.png)

ディレクトリ構成が{z}/{x}/{y}.png(.jpg)になっている必要がありますが、タイル画像へのパスを与えて、GeoTIFFに変換するメソッドもあります。

```python
tile_path = "./output/17/116998/48126.png"
to.tile_to_geotiff(tile_path)
```

指定ファイルと同じディレクトリに48126.tifというファイルが生成されるので、QGISで表示させてみましょう。

背景地図とぴったり重なります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/f1f87432-f01e-da1b-2dd5-e1d53147407d.png)

## 終わりに

ということで、ありそうでなかった（実はあるかも）手軽にタイルをローカルマシンにダウンロードしたり、色々できるツールを作ってみました！！

これからも機能を追加していこうと思いますんで、使ってみてください！

:::note info
明日は@soramiさんです！お楽しみにー！
:::