(Node.js) @mapbox/mbtilesモジュールを理解する

# はじめに
node.jsのコードの中で@mapbox/mbtilesモジュールを使用する機会があったので、どのようなモジュールであるのか簡単にまとめたいと思います。

# @mapbox/mbtilesモジュールとは
Mapbox社 によって提供されている、Node.js 用のライブラリで、MBTiles 形式の地図タイルファイルを扱うためのツールです。MBTiles は、地図タイルデータを SQLite データベースにパッケージ化したファイル形式で、オフラインで地図データを提供する際にも使用できます。

## 主な特徴
MBTiles ファイルの読み込み・アクセス: 
.mbtiles ファイルに保存された地図タイルデータにアクセスできます。これにより、地図アプリケーションでタイルを効率的に表示したり、オフライン地図を提供することが可能です。

タイルデータの取得: 
ズームレベル (z)、x座標 (x)、y座標 (y) を指定して、対応するタイルデータを取得します。

非同期処理に対応: 
非同期でタイルファイルを開いたり、タイルデータを取得したりできるため、Node.js アプリケーションの流れにスムーズに組み込むことができます。

# 使用方法

## インスタンス化
```javascript
new MBTiles('.mbtilesDir/example.mbtiles?mode=ro', function(err, mbtiles) {
    // 関数の中身を記載
});
```
mode パラメータは、MBTiles を開く際のフラグで、「ro」は読み取り専用モードを意味します。この場合、MBTiles が存在しない場合はエラーをスローします。mbtilesが作成されたインスタンス（オブジェクト）です。

## 読み込み
### getTile(z, x, y, callback)

MBTilesテーブルから個々のタイルを取得します。タイルは、ラスタ形式（画像データ）か、gzipで圧縮されたベクトルタイルです。また、HTTP経由で提供するために重要なヘッダー情報も返されます。

```javascript
mbtiles.getTile(z, x, y, function(err, tile, headers) {
  // コールバック関数の中身を記載
});
```

プロセスの典型的な流れは以下のようになります。
1.	タイルのリクエスト: クライアントが特定のズームレベル（z）、X座標、Y座標に対応するタイルをサーバーにリクエストします。
2.	MBTilesデータベースからの取得: サーバーは、MBTilesファイル内で指定された座標に対応するタイルを探し、取得します。
3.	タイルの形式: タイルはラスタデータ（JPEGやPNGなどの画像形式）または圧縮されたベクトルデータ（PBFなど）として保存されています。
4.	HTTPヘッダーの追加: サーバーは、タイルに適切なHTTPヘッダー（例えば、コンテンツタイプやコンテンツエンコーディングなど）を付加し、クライアントに返します。これにより、クライアントは正しくデータを解釈できるようになります。


### getInfo(callback)
MBTilesファイルの情報を取得します。取得される情報には、ズームレベル、境界（bounds）、ベクトルレイヤーなど、タイル生成時に作成された内容が含まれます。

```javascript
mbtiles.getInfo(function(err, info) {
  console.log(info);
});
```

# サンプルコード
```javascript:mapbox-mbtiles.js
const MBTiles = require('@mapbox/mbtiles');

new MBTiles('./mbtiles/VTpracticegzip.mbtiles?mode=ro', (err, mbtiles) => {
  if (err) {
    console.error('MBTiles file could not be opened:', err);
  } else {
    mbtiles.getTile(1, 1, 1, (err, tile, headers) => {
      if (err) {
        console.error('Tile could not be found:', err);
      } else {
        console.log('Tile data: ', tile);
        console.log('Headers data: ', headers);
      }
    });
    mbtiles.getInfo((err, info) => {
      if (err) {
        console.error('Could not get info data:', err);
      }
      console.log('Info data: ', info);
      // console.log('Info data: ', JSON.stringify(info, null, 2));
    });
  }
});

```

## コードの解説
特定のMBTilesファイルからタイルデータとメタデータを取得する処理を示しています。

```javascript
new MBTiles('./mbtiles/VTpracticegzip.mbtiles?mode=ro', (err, mbtiles) => {
```
指定されたパス（./mbtiles/VTpracticegzip.mbtiles?mode=ro）のMBTilesファイルを開こうとしています。ここで、mode=roは「読み取り専用」モードを指定しています。コールバック関数がerrとmbtilesオブジェクトを引数として受け取ります。

```javascript
mbtiles.getTile(1, 1, 1, (err, tile, headers) => {
```
getTileメソッドを使って、ズームレベル1、X座標1、Y座標1のタイルデータを取得しています。このメソッドのコールバック関数には、エラーオブジェクト、タイルデータ、HTTPヘッダーが引数として渡されます。


```javascript
mbtiles.getInfo((err, info) => {
```
getInfoメソッドを使用して、MBTilesファイルに関するメタデータ（ズームレベル、境界、ベクターレイヤーなど）を取得します。

```javascript
// console.log('Info data: ', JSON.stringify(info, null, 2));
```
この行はコメントアウトされていますが、もし有効にすると、infoオブジェクトをJSON形式に整形して出力します。第二引数にnullを指定すると、すべてのプロパティが含まれるようになります。第三引数の2は「スペース数」を指定し、生成されるJSON文字列のインデントを決定します。この場合、2を指定することで、各レベルのインデントが2つのスペースで行われます。これにより、出力が視覚的に整形され、読みやすくなります。


# 実行結果

![スクリーンショット 2024-10-02 16.59.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3684675/67adac8d-4e7f-422b-94fb-4654d11a5902.png)

ここで、[Object]となっているものについては、JSON.stringify()を使用すれば出力されますが、出力されるものが非常に長くなります。

タイルデータ('Tile data: '）は、バイナリ形式で出力されています。
ヘッダーデータ（'Headers data: '）は、以下の４つが出力されています。
  'Content-Type': 'application/x-protobuf',
  'Content-Encoding': 'gzip',
  'Last-Modified': 'Thu, 25 Jul 2024 18:16:44 GMT',
  ETag: '290816-1721931404748'

getInfoメソッドにより、Info dataが表示されており、以下のような情報が表示されています。
- MBTilesのファイルサイズ
- ベクトルタイル作成時の設定の最小・最大ズームレベル
- 地図境界(boudns）
- ファイルが生成される際に使用されたtippecanoeコマンド
- Tippecanoeによる生成の際の設定（ベースズームレベルやドロップ率など）
- ベクトルタイルのレイヤ（id、minzoom、maxzoomなど）（これが重要！）
- タイル統計情報（tilestats)（レイヤー数など）


# まとめ
node.jsの@mapbox/mbtilesモジュールの使用方法についてまとめました。
getInfoメソッドは有用だと思われます。なぜなら、MBTilesファイルがtippecanoeのどのようなコマンド（オプション設定など）で作成されたのかや、含まれるレイヤなどの情報を得ることができるため、スタイリング時の参考にすることが出来るからです。
Mablibreでも同じようなモジュールがあるかと思い調べましたが、見つけられませんでした。

# Reference

https://www.npmjs.com/package/@mapbox/mbtiles

https://qiita.com/k96mz/items/668bee0c6d751a444b5c