ベクトルタイル地図を持ち出してみる：Raspberry Pi で ベクトルタイルサーバー (2)

# はじめに
この記事では、最近試している Raspberry Pi でウェブ地図（ベクトルタイル地図）を持ち出してみた実験を書きます。 昨年の記事（[Raspberry Pi で ベクトルタイルサーバー をつくる](https://qiita.com/T-ubu/items/f5cb639b4c337ef1050d)）と重複するところもありますが、今回はラズベリーパイを使った**3パターン**で実際にデータの持ち出しを試してみようと思います。

こんな感じでスマホからも地図を見られました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/b8e1fdd0-a31f-7566-514a-91b2468d1696.png)

## 基本的な方針
### ベクトルタイル サーバーの作り方についての方針
私が持ち出したいベクトルタイルは数GBになるので、ベクトルタイルはpbfではなくてmbtiles形式で持ち出します。そのため、昨年同様に、nodejs/expressのサーバーで実装します。以下のような機能を持たせます。

* 静的なホスティング（pbfタイル、その他コンテンツ）
* 1つのmbtilesファイルからpbfを返す仕組み（サーバーサイド、nodejsで実現）
* 複数のmbtilesからpbfを返す仕組み（サーバーサイド、nodejsで実現）
    * 内部でベクトルデータ配信を実装するのに使った方法なので開発済み。
* 複数のmbtilesからラスタタイルpng形式を返す仕組み（サーバーサイド、nodejsで実現）
    * 内部でRGB標高タイル（ラスタタイル）配信を実装するのに使った方法なので開発済み

### ラズベリーパイの使い方についての方針
以下の3パターンを試したいと考えています。それぞれの特徴として考えていることもメモしておきます。

* パターン１：　ラズベリーパイをLANケーブルでPCにつなぐ
  * オフライン環境でもデータにアクセスできる。
  * 通信が高速（ケーブルで接続しているため）。
  * データのセキュリティが保たれる（接続しているPCのみ）
  * データへアクセスできるPCが限られる。（接続PCが他PCに共有されていない場合。）
* パターン２：　ラズベリーパイを既存のWifiネットワーク（or ローカルネットワーク）につなぐ
  * Wifiネットワーク（ローカルネットワーク）があることが前提
  * データはローカルネットワーク内に保たれる。
  * 複数の端末が同時にデータにアクセスできる。
  * 既存のWifiネットワークを邪魔しない。（インターネット接続を維持できる。）
* パターン３：　ラズベリーパイのWifiスポットを立ち上げる
  * オフライン環境でもデータにアクセスできる。
  * ラズベリーパイのWifiにアクセスするため、ほかのWifiネットワークから切り替えないといけない。（インターネットへの接続を失う場合も）
  * 複数の端末が同時にデータにアクセスできる。

### 実験するデータの相場観、方針
今回実験するデータは内部のものなので、外の環境におけません。しかし、以下の様な感じのデータです。
* グローバルデータは350GBを超える
  * ベクトルタイルデータ（基本地物）170 GB
  * ベクトルタイルデータ（等高線）　32 GB
  * RGB標高タイル　　185 GB
* 個別地域は数GBに収まる　（例：南スーダンをカバーする範囲）
  * ベクトルタイルデータ（基本地物）1.48 GB
  * ベクトルタイルデータ（等高線）　0.54 GB
  * RGB標高タイル　　2.83 GB

（例）南スーダン全体をカバーする場合：
6-36-30, 6-36-31, 5-18-14（ 標高タイルは6-37-29 ）, 6-37-30, 6-37-31, 6-38-30, 6-38-31 の7区画でOK。



# 私の環境
* Raspberry Pi：　3B (400もありますが、今回は3Bで実験)
* SDカード：　32GB、SDHCカード（V10,U1）
* パソコン： Windows 10 （Windows PowershellでラズベリーパイにSSH接続、たまにWinSCPも使います。）
* LANケーブルでパソコンとラズベリーパイを接続

# 実際の作業
## Step 1. サーバースクリプトなどの準備
まず、nodejs/expressを使ってサーバーを作りました（ https://github.com/ubukawa/albite ）。サンプル用に小縮尺のデータ（[NaturalEarth](https://www.naturalearthdata.com/) 起源）も入れてあります。

まったく新しく作ったわけでなく、以前から使っているものをチョコチョコと集めたり更新して作ったものです。ここではサーバーの詳細説明を書きませんが、基本的な作り方は昨年の記事（「[nodejsでベクトルタイルサーバーを作る（mbtilesからpbfを配信する）](https://qiita.com/T-ubu/items/545d9f995ef7496a2ec4)」）に書いてあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/9bbe9a54-ba17-a7cb-3eef-b0602a96e43e.png)


このサーバーについてもう少し細かく説明すると以下の様な感じです。
* nodejs/expressでうごく。パソコン（nodejs ver. 16）では動作確認済み。(ラズパイはnodejs 18 だが問題なく動いた) 
* 主な機能
  * app.js で httpサービスの実行（keyがあればhttpsにもできます）、ログの記録、静的なホスティングなどをする。
  * routeフォルダの中に、単一のmbtilesからpbfを返すもの（VT.js）、複数のmbtilesからpbfを返すもの(UNVT.js)、複数のmbtilesからpngラスタタイルを返すもの（rgbElev.js）を入れておく。app.jsでルーティングしているので、それぞれ以下のパスでアクセス可能:
    * VT/zxy/{mbtiles name}/{z}/{x}/{y}.pbf
    * UNVT/zxy/{mbtiles group name}/{z}/{x}/{y}.pbf
    * rgb-elev/zxy/{z}/{x}/{y}.pbf
  * 静的にホスティングされる htdocs にMapLibre GL JS などのウェブ地図関連ファイルを格納。特にオフラインで利用する可能性もあるのでマップライブラリ等はローカルにあることが重要
      * htdocs/font --> NotoSansやNotoSansDisplayなどのフォント（pbf形式）をいくつか格納
      * htdocs/maplibre-gl@2.4.0 --> MapLibre GL JS 
      * htdocs/maps --> スタイルファイルと地図用htmlが入っています
      * htdocs/pbf_tile --> pbfタイルを静的にホスティングするときのための場所
      * htdocs/sprite-rev --> 我々用の地図記号
      * htdocs/watergis --> 将来の拡張用に、watergisさんのMapLibreプラグインを2ついれてあります。

## Step 2. Raspberrypiの準備
Raspberry Pi Imager が2022年12月2日時点では1.7.3になっていたので、これをRaspberry Piのページ（[こちら](https://www.raspberrypi.com/software/)）からダウンロードして新しいMicro SDカードにRaspberry Pi OSをインストールしてきます。今回は改めて新しいMicroSDカードに書き込んでみます。

### 2-1. OSの準備
Imagerをインストールします。インストールしたRaspberry Pi Imagerを使って、OSと書き込み対象ストレージを選びます。今回もRaspberry Pi OSを使います。そして、書き込みを押す前にctrl + shift + x を押して細部の設定をします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/b9e9bf01-acbb-d0c3-8342-1c8a686fcfc3.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/15b92918-99dd-0d57-4454-68e9a7413f6a.png)
今回もホスト名、SSH有効化（パスワード）、ユーザー名とパスワード指定、wifi設定を行いました。その後、元の画面に戻るので書き込みをします。無事に終わったらMicro SDカードをRaspberry Piに差し替えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/35295276-2d60-b540-8fec-0a6f690072ed.png)

### 2-2. 電源オンとSSHでアクセス
LANケーブルをつないで、パソコンからRaspberry Pi に sshで接続します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/9bac6601-eed7-1e1f-6cae-7734d1423b3d.png)

wifiのため、/etc/wpa_supplicant/wpa_supplicant.conf　を編集しておきます。ssidとパスワードを変更したのと、国コードがなかったので足しておきました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/7532c06c-3abb-953d-f9d3-82480bdfb856.png)

更新したら再起動してWifiにつなぎます。
（しかし、これは職場のwifiにつなげませんでした。なぜなら、私のラズパイが3Bで職場のwifiが 5 GHzだったからです。3B+から5GHzでも大丈夫になったようです。（以前ラズパイの400でやったときはネットワークに繋げました。））

### 2-3. ツールのインストール
nodejsなどの今回の作業に必要なツールと、サーバー関係のレポジトリ（ https://github.com/ubukawa/albite ）をインストールします。unvt/equinox のインストーラーから ベクトルタイルの生産などに関係するものを除いて、私のサーバーのcloneをくっつけた以下の install.sh を使います。このインストーラーはGitHubにあるので、https://ubukawa.github.io/albite/install.sh でアクセスできます。

```sh:install.sh
##############
# This is for raspberry pi implementation (not for CentOS/RHEL)
# This is a subset of the tools installed by https://github.com/unvt/equinox/blob/master/install.sh with my server repository 
##############
cd $HOME
sudo apt update
sudo apt -y upgrade
curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash -
curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | sudo tee /usr/share/keyrings/yarnkey.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update
sudo apt install -y autoconf automake cmake code git gpsd gpsd-clients gpsd-tools libsqlite3-dev libtool raspberrypi-kernel-headers nodejs parallel sqlite3 tmux unar vim xrdp yarn zip #gdal-bin nginx pdal ruby 
sudo service xrdp restart
sudo yarn global add browserify hjson pm2 rollup @mapbox/mapbox-gl-style-spec @pushcorn/hocon-parser @unvt/charites
git clone https://github.com/ubukawa/albite
cd albite; npm install; cd ..
```
curl で見られるのを確認したら、パイプ（|）でつないで実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/8aef6c4e-c4a4-d8f6-4d15-be386c187eca.png)
```bash
curl -sL https://ubukawa.github.io/albite/install.sh
curl -sL https://ubukawa.github.io/albite/install.sh | bash -
```

私はRaspberry Pi 3Bでやって30分くらい待ちましたが、無事に終わったようです。nodejsやpm2もインストールされている模様です。サーバー関係の albite というフォルダも出来ています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/9248c94a-ffea-563e-4ff2-aa275b2160c8.png)


### 2-4. 実験用のデータを入れる
データはGitHubレポジトリにはいれていないので、地図データをUNmbtilesフォルダの下にアップロードしておきます。簡単のために今回は WinSCP でやりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/1893060f-7265-531c-bfde-db21ce245490.png)

今回は南-スダンのJuba近辺だけにするので、6-37-30.mbtilesと6-37-31.mbtilesをアップロードしています。small-scale.mbtilesにはZL0-5までのグローバルなものが入っていて、ZL6-15までは地域別に6-x-y.mbtilesに入っています（地区によっては5-x-yとか4-x-yです）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/1bb726ec-d82f-7fe3-e8e6-523e1becb08e.png)

まだ3D Terrainは試しませんが、標高RGBタイルも当該地区をアップロードしておきます。0-0-0にはZL5までの標高RGBタイルが入っていて、ZL6～ZL12までのタイルは6-x-y.mbtilesというものに地区ごとに入れてあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/1da2b85a-e74d-c5c7-d4ba-2f740c8b89b6.png)

ここまで出来たのでラズベリーパイの準備は完了です。

### 2-5. スタイルファイル中のパスを少し調整する。

他PCからラズベリーパイにアクセスするときのために、スタイルJSON中でtileのURLとspriteのURLを直さないといけません。glyphsは相対パスでも大丈夫なのですが、spriteは相対パスでは上手くいきませんでした。LANケーブルのときとWifiのときでホスト名を変えないといけないと思います。htdocs/mapsにあるスタイルファイルを3つ修正しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/944a9f5f-7dbd-fa86-721a-c434fa2d2f56.png)

* LANケーブル：　unvt-us-p01.local
* Wifi： unvt-us-p01 

### 2-6. サーバーの実行
いきなりpm2でやらずに、まずはnodejsの普通のコマンドでサーバーを立ち上げてみます。
```
node app.js
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/2db13dae-4702-cfae-069a-eb7e25e08092.png)
app.jsが大丈夫だったことが確認出来たので、pm2を立ち上げてデーモンプロセスとして実行してもOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/84e049c4-212d-abbf-7085-0aaff5591b7d.png)


## Step 3. いろいろな経路で地図を見てみます。
### 3-1. LAN ケーブルでラズパイをパソコンにつないで地図をみる (unvt-us-p01.local:8836)
LANケーブル経由のときは、ホスト名に".local"をつける必要があるようです。ブラウザで開いてみるとindexページが返ってくるので、サーバーは動いています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/9de60a9e-8094-95bc-e5b0-aceb2783b247.png)

下にあるサンプルマップへのリンクも確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/7f8cc7fe-dbed-042f-4121-44064725adb7.png)

map1とmap2はOK。サーバーが動きながら地図を返しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/f6d41615-9db5-d61b-75d9-a6d3388b4e6c.png)

map3も快調です（map3はレポジトリにデータを入れないとみられません）。小縮尺ではグローバルに見られますし、縮尺をあげるとデータをアップロードした所定の区画だけになります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/69886284-43e0-12a2-a170-81406a3ab23e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/49d46700-86c7-8cdb-7ea7-0736be0ad1e9.png)

建物やストリートレベルまでズーム出来ます。オフラインでもオンラインでもラズベリーパイをパソコンのつなげばここまで出来ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/e67c9629-098a-e2e2-669f-e05f894acb3d.png)


### 3-2. Wifi 経由（ローカルネットワーク内）でみる (unvt-us-p01:8836)
ラズベリーパイがWifiにつながっていれば、そのネットワークの中で他の端末から地図を見ることが出来ます。他のWindows PC からだと、unvt-us-p01.local:8836 でも unvt-us-p01:8836 でも大丈夫だったのですが、スマホからだと unvt-us-p01:8836 で指定してあげないとみられませんでした。スタイルファイル中のタイルやspritesのURLを調整します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/bf63dec3-60f1-1daa-471f-d8e77a29ec94.png)

そしてまたサーバーをスタートします。同じwifi（ローカルネットワーク）に接続している端末からならば、unvt-us-p01:8836 から地図にアクセスできることが確認出来ました。携帯からでもベクトルタイル地図を見られるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/b8e1fdd0-a31f-7566-514a-91b2468d1696.png)

### 3-3. Raspberry Pi をWifi アクセスポイントにしてwifi接続してから見る (unvt-us-p01:8836)
今度はラズベリーパイをアクセスポイントにします。以前の記事（[こちら](https://qiita.com/T-ubu/items/906a8970da5dbec73cec)）のやり方でWifiアクセスポイントにしました。

作ったWifiネットワークに接続して、地図にアクセスすると見られました。気のせいかもしれませんが、Wifi経由でアクセスしたときよりもウェブ地図が軽快に動くような気がしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/4af7d3eb-32af-d6ce-ab79-f5d5296edf06.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/31988511-11d8-3cdb-8660-39dfdd92d201.png)

# 結果の考察
以下の3パターンとも、ウェブ地図を見ることが出来ました。表にして考えてみます。
1. LAN ケーブルでラズパイをパソコンにつないで地図をみる
1. Wifi 経由（ローカルネットワーク内）でみる
1. Raspberry Pi をWifi アクセスポイントにしてwifi接続してから見る

| 方法 | つなげる台数 | インターネットアクセス | 速さ |
| --- | --- | --- | --- |
| LAN ケーブルでラズパイをパソコンにつないで地図をみる | 1 | OK | OK |
| Wifi 経由（ローカルネットワーク内）でみる | 複数OK | OK | 比較的遅い（？） |
| Raspberry Pi をWifi アクセスポイントにしてwifi接続してから見る | 複数OK | なし | OK |

1と2だと、ユーザーはインターネットへのアクセスを維持したままラズベリーパイを使える点のは魅力的だと思います。1は地図のレスポンスも早いですが、アクセス出来る人がだいたい1名（ラズベリーパイをつないだパソコンの持ち主）という欠点があります。また1であればWindows PC 中に仮想環境を作って（dockerなど）サーバーを立ち上げても同様の効果が期待出来ると思います。2はWifiなので同じローカルネットワークにいる比較的多くの人がアクセスできますが、反応は1や3に比べて遅いかもしれません。
3は、インターネットへの接続を失いますが、オフラインでもマップが提供できますし、通信や地図の描画が2より早いかもしれません。

ということで、それおぞれの方法に特徴があるので、引き続き調べて行きたいと思います。ただし、どの手法にしても、私の実験ではみなhttpです。あと、今回は3通りの方法を試してみましたが、ラズベリーパイは永続的なオペレーションに使うのではなく、オフライン環境での利用やキャパビル教材などのユースケースの方が合うかもしれないと思いました。。


# まとめ
ラズベリーパイを使ってウェブ地図を作りました。3パターンは無事に動作を確認することができました。

# 参考
https://qiita.com/T-ubu/items/f5cb639b4c337ef1050d

https://qiita.com/T-ubu/items/906a8970da5dbec73cec

https://www.raspberrypi.com/software/