COMTiles(Cloud Optimized Map Tiles)をAmazon S3にホスティングしてMapLibre GL JSで表示してみた


![img](https://day-journal.com/memo/images/try-098_00.gif)
![img](https://day-journal.com/memo/images/try-098_23.png)

<br>

この記事は、「[FOSS4G Advent Calendar 2022](https://qiita.com/advent-calendar/2022/foss4g)」の23日目の記事です。

<br>

### COMTiles(Cloud Optimized Map Tiles)をAmazon S3にホスティングしてMapLibre GL JSで表示してみました :tada:

<br>

## Cloud Optimized

![img](https://day-journal.com/memo/images/try-098_01.png)
※JAWS DAYS 2022 - Satellites 発表資料 「[AWSではじめる位置情報アプリケーション](https://speakerdeck.com/dayjournal/awsdehazimeruwei-zhi-qing-bao-apurikesiyon)」

Cloud Optimizedとは、これまで地図配信サーバーやタイル分割処理が必要だった巨大な位置情報データのリクエスト処理を、単一ファイルで実現するクラウド最適化フォーマットのことです。

個人的に衝撃的な特徴は下記です。

- 巨大なファイルの一部分だけをリクエスト可能
- 単一ファイルをストレージに置くだけでフロントエンドから読み込み可能
- 地図配信サーバーが必要ない
- 事前タイル分割処理(ベクトルタイル・ラスタタイル)が必要ない
- レンダリングも高速

ラスターは  
「[COG(Cloud Optimized GeoTIFF)](https://www.cogeo.org)」  
ポイントクラウドは  
「[COPC(Cloud Optimized Point Cloud)](https://copc.io)」  
で安定している印象です。

ベクトル系は、フォーマットがたくさんありまだ定まっていない印象です。

- [FlatGeobuf](http://flatgeobuf.org)  
- [Zarr](https://zarr.readthedocs.io)
- [GeoParquet](https://github.com/opengeospatial/geoparquet)
- [PMTiles](https://github.com/protomaps/PMTiles)
- [COMTiles(Cloud Optimized Map Tiles)](https://github.com/mactrem/com-tiles)

<br>

## COMTiles(Cloud Optimized Map Tiles)

![img](https://day-journal.com/memo/images/try-098_24.png)  

COMTilesの仕組み: [com-tiles-spec](https://github.com/mactrem/com-tiles/tree/main/packages/spec)

今回は「[COMTiles(Cloud Optimized Map Tiles)](https://github.com/mactrem/com-tiles)」について検証しました。COMTilesはPMTilesと比較されることが多いですが、最近はPMTilesの人気が上がってきている印象です。COMTilesの技術自体は素晴らしいと思うのですが、ライブラリがnpmに公開されていないことや、ドキュメントがあまり充実していないため、個人的にはまだ導入が手軽ではない印象です。PMTilesは、ドキュメントがしっかりしているのも人気の理由なのかなと思っています。今回は、COMTilesについてひととおり検証したので、導入を検討しているかたの参考に少しでもなれば嬉しいです！

## 事前準備

COMTilesの変換用に、SQLiteで構築されている地図タイルを格納するための「[MBTiles](https://github.com/mapbox/mbtiles-spec)」を準備します。

一般的には、下記のツールを利用することでOpenStreetMapや独自データで地図タイル(MBTiles)を作成可能です。

- [Tilemaker](https://github.com/systemed/tilemaker)
- [Planetiler](https://github.com/onthegomap/planetiler) 
- [OpenMapTiles](https://github.com/openmaptiles/openmaptiles)
- [tippecanoe](https://github.com/felt/tippecanoe)

今回は手軽にデータを準備したかったので、指定範囲の地図タイル(MBTiles)をダウンロードできる[MapTiler](http://maptiler-affiliate-program.sjv.io/BXrxx9)のMapTiler DATAを利用しました。  

OpenStreetMap basemapsのDOWNLOAD DATAをクリック。
![img](https://day-journal.com/memo/images/try-098_02.png)

検索窓に任意の地域を入力し選択 → DOWNLOADをクリック。今回はJAPANを選択しました。
![img](https://day-journal.com/memo/images/try-098_03.png)

個人検証用に利用するので、non-commercial personal projectをクリック → FREE DOWNLOADをクリック。
![img](https://day-journal.com/memo/images/try-098_04.png)

<br>

これで利用するMBTilesをダウンロードできます。ダウンロードしたデータを「sample.mbtiles」にリネームします。

<br>
<br>

実行環境

- node v18.1.0  
- npm v8.19.2  

<br>

詳細として下記について説明します。

- [環境構築](https://qiita.com/dayjournal/items/b2d9553f9db7574ae7a8#%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89)
- [MBTilesをCOMTilesに変換](https://qiita.com/dayjournal/items/b2d9553f9db7574ae7a8#mbtiles%E3%82%92comtiles%E3%81%AB%E5%A4%89%E6%8F%9B)
- [style.jsonとtiles.json作成](https://qiita.com/dayjournal/items/b2d9553f9db7574ae7a8#stylejson%E3%81%A8tilesjson%E4%BD%9C%E6%88%90)
- [Amazon S3に配信用データをホスティング](https://qiita.com/dayjournal/items/b2d9553f9db7574ae7a8#amazons3%E3%81%AB%E9%85%8D%E4%BF%A1%E7%94%A8%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E3%83%9B%E3%82%B9%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0)
- [MapLibre GL JSでCOMTilesを表示](https://qiita.com/dayjournal/items/b2d9553f9db7574ae7a8#maplibre-gl-js%E3%81%A7comtiles%E3%82%92%E8%A1%A8%E7%A4%BA)

## 環境構築
はじめに、MBTilesをCOMTilesに変換するための環境構築をします。  

環境はオリジナルをForkした、「[zimmermannpeter/com-tiles](https://github.com/zimmermannpeter/com-tiles)」をForkまたはダウンロードし利用します。「zimmermannpeter/com-tiles」は、2022年12月現在オリジナルの「[mactrem/com-tiles](https://github.com/mactrem/com-tiles)」にプルリクエストを出している状態で、ドキュメントの改善やMapLibre GL JSの表示環境も整備されているため採用しました。

全体構成
![img](https://day-journal.com/memo/images/try-098_05.png)

com-tilesのルートで実行します。

```sh
npm install
```

packages/providerで実行します。

```sh
npm run build
```

![img](https://day-journal.com/memo/images/try-098_06.png)
![img](https://day-journal.com/memo/images/try-098_07.png) 

packages/mbtiles-converterで実行します。

```sh
npm run build
```

![img](https://day-journal.com/memo/images/try-098_08.png)  

com-tilesのルートで実行します。

```sh
npm audit fix --force
npm install -g ./packages/mbtiles-converter
```

<br>

これで変換用のコマンドが利用可能になります！

<br>

## MBTilesをCOMTilesに変換
次に、MBTilesをCOMTilesに変換する方法を紹介します。  

事前準備でダウンロードしたMBTilesを、com-tilesのルートにコピーします。
![img](https://day-journal.com/memo/images/try-098_09.png)

ヘルプでコマンドの内容が確認できます。

```sh
convert-comtiles -h
```

![img](https://day-journal.com/memo/images/try-098_10.png)

convert-comtilesコマンドを実行し変換します。

```sh
convert-comtiles -i sample.mbtiles -o sample.comt
```

![img](https://day-journal.com/memo/images/try-098_11.png)

コマンドを実行すると、MBTilesからCOMTilesが作成されます。
![img](https://day-journal.com/memo/images/try-098_12.png)

これでMBTilesからCOMTilesへの変換は完了になります！

<br>

## style.jsonとtiles.json作成
次に、style.jsonとtiles.jsonを作成する方法を紹介します。  

事前にAmazon S3にバケットを作成しURLを決めておきます。  
![img](https://day-journal.com/memo/images/try-098_13.png)

下記のstyle.jsonとtiles.jsonを修正します。 
![img](https://day-journal.com/memo/images/try-098_14.png)

<br>

/packages/maplibre-provider/demo/assets
#### style.json
「url」と「glyphs」にアップロード予定のAmazon S3のURLを指定します。「url」はstyle.jsonのURLで、「glyphs」はフォントのURLです。

```json
{
  "version": 8,
  "name": "Basic",
  "sources": {
    "openmaptiles": {
      "type": "vector",
      "url": "https://com-tiles-202212.s3.ap-northeast-1.amazonaws.com/assets/tiles.json"
    }
  },
  "glyphs": "https://com-tiles-202212.s3.ap-northeast-1.amazonaws.com/assets/fonts/{fontstack}/{range}.pbf",
  "layers": [
    {
      "id": "background",
      "type": "background",
      "paint": {"background-color": "hsl(47, 26%, 88%)"}
    },
.....
```

<br>
<br>

/packages/maplibre-provider/demo/assets
#### tiles.json
「tiles」にアップロード予定のAmazon S3のURLを指定します。「tiles」はCOMTilesのURLです。「comt://」はCOMTilesを指定する場合に必要です。

```json
{
  "tilejson": "2.0.0",
  "name": "maplibre",
  "attribution": "<a href=\"https://www.maptiler.com/copyright/\" target=\"_blank\">&copy; MapTiler</a> <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\">&copy; OpenStreetMap contributors</a>",
.....
  "extent": [
    -20037508.342789244,
    -20037508.6269291,
    20037508.342789244,
    20037508.626929108
  ],
  "center": [
    0,
    0,
    0
  ],
  "tiles": [
    "comt://https://com-tiles-202212.s3.ap-northeast-1.amazonaws.com/data/sample.comt/{z}/{x}/{y}"
  ]
}
```

<br>

これでstyle.jsonとtiles.jsonの作成は完了になります！

<br>

## Amazon S3に配信用データをホスティング
次に、Amazon S3にCOMTiles・style.json・tiles.json・fontsディレクトリをホスティングします。  

COMTiles・style.json・tiles.jsonは前の作業で作成したものをアップロードします。fontsディレクトリは「/packages/maplibre-provider/debug/assets/fonts」をアップロードします。  
![img](https://day-journal.com/memo/images/try-098_15.png)

Amazon S3の対象バケットを公開設定にします。今回はサンプル用の公開設定にしましたが、本番運用時は限定した公開設定をおすすめします。  
[Amazon S3の公開設定を色々とためしてみた](https://day-journal.com/memo/try-078)
![img](https://day-journal.com/memo/images/try-098_16.png)

対象バケットのディレクトリ構成です。今回は「assets」と「data」ディレクトリを作成しました。  
![img](https://day-journal.com/memo/images/try-098_17.png)

assetsディレクトリに、style.json・tiles.json・fontsディレクトリをアップロードします。  
![img](https://day-journal.com/memo/images/try-098_18.png)

dataディレクトリに、COMTilesをアップロードします。  
![img](https://day-journal.com/memo/images/try-098_19.png)

これでAmazon S3に配信用データをホスティングは完了になります！

<br>

## MapLibre GL JSでCOMTilesを表示
最後に、MapLibre GL JSでCOMTilesを表示します。デモの「/packages/maplibre-provider/demo/index.html」のみを修正します。  

全体構成
![img](https://day-journal.com/memo/images/try-098_20.png)

<br>

/packages/maplibre-provider/demo
#### index.html
「style」にアップロードしたAmazon S3のURLを指定します。「url」はstyle.jsonのURLです。「center」と「zoom」を表示したい領域に変更します。今回は東京周辺に設定しました。

```html
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>MapLibre COMT Demo</title>

        <script src="dist/maplibre-gl.js"></script>
        <link href="dist/maplibre-gl.css" rel="stylesheet" />
        <script src="dist/maplibreComtProvider.js"></script>

        <style>
            body,
            #map {
                height: 100vh;
                margin: 0px;
            }
        </style>
    </head>
    <body>
        <div id="map"></div>
        <script>
            comtiles.MapLibreComtProvider.register();
            const map = new maplibregl.Map({
                container: "map",
                style: "https://com-tiles-202212.s3.ap-northeast-1.amazonaws.com/assets/style.json",
                center: [139.767, 35.681],
                zoom: 11,
            });
            map.addControl(new maplibregl.NavigationControl({}));
        </script>
    </body>
</html>
```

COMTiles表示用のライブラリを読み込みます。

```html
<script src="dist/maplibreComtProvider.js"></script>
```

MapLibreComtProviderを読み込みます。

```javascript
comtiles.MapLibreComtProvider.register();
```

スタイルにstyle.jsonのURLを指定します。

```javascript
style: "https://com-tiles-202212.s3.ap-northeast-1.amazonaws.com/assets/style.json",
```

<br>

今回の表示環境は、ローカル簡易サーバーを実行し確認します。実行後「<http://localhost:8080>」にアクセスします。

/packages/maplibre-provider/demo

```sh
python -m http.server 8080
```

![img](https://day-journal.com/memo/images/try-098_21.png)

<br>

COMTilesを利用することで、地図配信サーバー無しで巨大な単一ファイル(ベクトルタイル)をMapLibre GL JSで表示できました！レンダリングも高速です！
![img](https://day-journal.com/memo/images/try-098_22.png)
![img](https://day-journal.com/memo/images/try-098_23.png)

<br>
<br>

MapLibre GL JSとAmazon S3について、他にも記事を書いています。よろしければぜひ :bow:
<a href="https://day-journal.com/memo/tags/MapLibreGLJS" target="_blank">tags - MapLibre GL JS</a>
<a href="https://day-journal.com/memo/tags/Amazon-S3" target="_blank">tags - Amazon S3</a>

やってみたシリーズ :grinning:
<a href="https://day-journal.com/memo/tags/Try" target="_blank">tags - Try</a>

https://qiita.com/dayjournal/items/8fe865169e89c8bad343

https://qiita.com/dayjournal/items/2200ce37472c3b4cd6f3

https://qiita.com/dayjournal/items/c827a17917127bff3906

<br>
<br>

<a href="https://dayjournal.booth.pm" target="_blank"><img src="https://day-journal.com/memo/images/etc/book.png" alt="book"></a>