Overture Mapsの建物データ（7億件）からDuckDBのPython APIで日本のデータだけ取得してみた！

https://www.itmedia.co.jp/news/articles/2307/27/news089.html

Overture Maps Foundationから、とうとうオープン地図データがリリースされましたね！

世間一般ではどんな反応なのかわかりませんが、界隈では割と盛り上がっている様子です。

Overture Mapsは構造化され、品質が保証された使いやすい地図データをオープンにするべく、整備しているとんでもない規模のプロジェクトで、以下のようなビッグテックが開発を行なっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/f3dd515c-0593-72bf-2631-89dea2cfab18.png)

2023/07/26に「Overture 2023-07-26-alpha.0」という名称で初めてデータがリリースされました。
データは4種類に分かれており、個別にライセンスが適用されていますが、基本的には「概ね自由に利用できる」ライセンスになっており、開発者目線だととてもありがたいデータになっています。
（とはいえ、ライセンスはちゃんと確認するようにしましょう。）

- 場所のテーマ:   CDLA Permissive v 2.0
- 建物のテーマ:   ODbL
- 交通テーマ:  ODbL
- 管理境界のテーマ:  CDLA Permissive v 2.0

（[https://overturemaps.org/download/](https://overturemaps.org/download/)より）

色々なデータがありますが、今回は分かりやすい「建物データ」を利用していきましょう。

## DuckDBのダウンロード

早速データをダウンロードしていきたいですが、データへのアクセス方法を見ると、どうやらAWSやAzureに保存されているようです。
https://github.com/OvertureMaps/data/blob/main/README.md#how-to-access-overture-maps-data

色々書かれていますが、ちょろっとデータを見るためだけにクラウドを使うのも手間なので、今回は[3. DuckDB (SQL)
](https://github.com/OvertureMaps/data/blob/main/README.md#3-duckdb-sql)を利用していきましょう。

DuckDBはOLAP（オンライン上のデータを分散処理で分析すること）に特化したDBであり、OSSです。
軽量かつSQLを利用して分析ができ、Python APIも整備されています。

Pythonによる地理データ分析といえば、GeoPandasなどが挙げられるかと思いますが、より高速に動作するのが特徴のようですね。

詳しくは、以下の記事などが分かりやすかったです。

- [🦆🦆🦆🦆🦆🦆DuckDB入門🦆🦆🦆🦆🦆🦆](https://zenn.dev/notrogue/articles/1193d0ab8d8eda#%E6%A9%9F%E8%83%BD%E3%83%BB%E7%89%B9%E5%BE%B4)
- [DuckDBに入門してみた](https://zenn.dev/kyami/articles/0189f25846bbba)

ということでダウンロードしましょう。

以下でダウンロード可能で、すぐにPythonから利用可能になります。

```bash
pip install duckdb
```

ついでに、その他利用するパッケージも入れておきましょう。

```bash
pip install geopandas pyarrow
```

## DuckDBでデータを触ってみる

[こちら](https://github.com/OvertureMaps/data/blob/main/README.md#3-duckdb-sql)の手順に従っていきます。

今回はJupyter notebookを利用していきます。
Jupyterの構築方法などは、ググってみてください。

Macユーザーであればこんな記事も書いていますので、参考にしてみてください。

- [MacでGISデータ分析を始めるためにサクッとAnacondaとjupyter labをインストールしてみる](https://qiita.com/nokonoko_1203/items/3b5ceb911af6b22a449f)

まずは以下のようにDuckDBのDBを構築します。

```Python
import duckdb
import geopandas as gpd
import os

# DB の作成
conn = duckdb.connect("../data/overture.duckdb")
c = conn.cursor()
```

これで`../data/overture.duckdb`というファイルが作成されました。

この.duckdb（拡張子は`.db`などでもいいっぽい）自体が、SQLiteのようなデータベース本体になっているようです。

次に、DBにGIS拡張機能をインストールします。。

```Python
c.execute(
    """INSTALL spatial;
    INSTALL httpfs;
    LOAD spatial;
    LOAD parquet;
"""
)
```

データはParquetという、データ分析を高速に行うために特化したファイル形式で提供されています。
（GIS拡張である`GeoParquet`についてはこちらで言及しています。
[GeoPandasをやるならFlatGeobufより10倍早いGeoParquetを使おう！](https://qiita.com/nokonoko_1203/items/a01168096c2d4c2d6914)）

このParquetにはDuckDBから以下のようにアクセスできます。
まずはデータの総数を確認してみましょう。

```Python
sql = """
SELECT
    count(*)
FROM
    read_parquet('s3://overturemaps-us-west-2/release/2023-07-26-alpha.0/theme=buildings/type=*/*')
"""

print(c.execute(sql).fetchall())
# [(785524851,)]
```

7億レコード！！！やっぱり結構多いですね！！

## データをダウンロードする

どこに書いていたか忘れてしまいましたが、全データをダウンロードすると150GBを超えるようで、流石にそのまま取り扱うのはめんどくさいので、以下のように、地域を日本のみに絞ってダウンロードしてみましょう。

```python
c.execute(
    """COPY (
    SELECT
           theme,
           type,
           version,
           updateTime,
           JSON(sources) AS sources,
           JSON(names) AS names,
           height,
           numFloors,
           class,
           ST_GeomFromWkb(geometry) AS geometry
      FROM read_parquet('s3://overturemaps-us-west-2/release/2023-07-26-alpha.0/theme=buildings/type=*/*')
     WHERE ST_Within(ST_GeomFromWkb(geometry), ST_Envelope(ST_GeomFromText('POLYGON((122.934570 20.425378, 122.934570 45.551483, 153.986672 45.551483, 153.986672 20.425378, 122.934570 20.425378))')))
) TO '../data/buildings.parquet'
WITH (FORMAT PARQUET);
"""
)
```

僕の環境では43分かかりましたが、なんとかダウンロードすることができました。

が、これをGeoPandasで読もうとすると…

```Python
gdf = gpd.read_parquet("../data/buildings.parquet")
gdf.head()
```

こんなエラーが出ました。

```Python
ValueError: Missing geo metadata in Parquet/Feather file.
            Use pandas.read_parquet/read_feather() instead.
```

こんなツイートをしている人もいて、どうやらGeoParquetに準拠した形式ではないため、よみこむことができないようです。

https://twitter.com/opencholmes/status/1684221063843008512?s=20

仕方ないのでパフォーマンスは落ちますが、FlatGeobufでダウンロードしていきましょう。

```Python
c.execute(
    """COPY (
    SELECT
           theme,
           type,
           version,
           updateTime,
           JSON(sources) AS sources,
           JSON(names) AS names,
           height,
           numFloors,
           class,
           ST_GeomFromWkb(geometry) AS geometry
      FROM read_parquet('s3://overturemaps-us-west-2/release/2023-07-26-alpha.0/theme=buildings/type=*/*')
     WHERE ST_Within(ST_GeomFromWkb(geometry), ST_Envelope(ST_GeomFromText('POLYGON((122.934570 20.425378, 122.934570 45.551483, 153.986672 45.551483, 153.986672 20.425378, 122.934570 20.425378))')))
) TO '../data/buildings.fgb'
WITH (FORMAT GDAL, DRIVER 'FlatGeobuf');
"""
)
```

6時間ほどかかりました…
データ取得方法自体は同じなので、ParquetとFlatGeobufでは書き込み性能に大きな違いがあることが分かります。

ともあれダウンロードができたので、ファイルサイズを確認しましょう。

```Python
os.path.getsize("../data/buildings.fgb")
# 7872148968
```

かなり減りましたが、まだ7GBもあるので、GeoPandasで読み込むにはかなり時間がかかります。
（時間がかかりすぎて今回は諦めました。）

```Python
gdf = gpd.read_file("../data/buildings.fgb")
gdf.head()
```

## QGISで読んでみる

今回は大した分析をするつもりはなく、とりあえずデータを取得して閲覧してみよう、くらいの感じですので、ダウンロードしたFlatGeobufはQGISに載せてみましょう！

7GBあるので流石に時間がかかりますが、読み込むと2000万地物まで減っていることが分かります！

だいぶ絞れましたね！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/15fceaee-5b7f-6ee5-684b-627b9ffcf7db.png)

表示するとこんな感じになっていました！

https://twitter.com/nokonoko_1203/status/1684723594922999808?s=20

見てみると、東京都でも歯抜けが多いように見えます。

とはいえ、この規模のデータがオープンで公開されるのはとても良いことですね！

## おわりに

ということでOverture MapsのデータとDuckDBを使ってデータ取得してみました。

GIS界隈はオープンマインドな方が多く、ツールやデータなど大量に公開されていて、めちゃくちゃ開発がしやすくなっていますし、どんどん便利になってますね。

まだ最初のリリースなので、諸々の問題はあるかもしれませんが、そこはビッグテックの皆様に期待ですね。