QGIS データを Web で表示 #4 - QWC2

QGIS で作成したデータ（"プロジェクト"）を Web ページで表示する方法について、ノウハウを共有します。

前回は QGIS Server をセットアップしました。

- [QGIS データを Web で表示 #3 - QGIS Server](https://qiita.com/softark/items/c1da4f9a52e6727a5e97)

今回は QWC2 (QGIS Web Client 2) をセットアップして、QGIS Server と連携する Web 地図アプリを作成します。

マニュアルとして、`QWC2 / QWC Services` のドキュメントを参照します。

- [QWC2](https://qwc-services.github.io/master/) > [Quick Start](https://qwc-services.github.io/master/QuickStart/)

このマニュアルには、QGIS Server と QWC2 を中核として含む `qwc-services` という統合環境を Docker でインストールする場合と、QWC2 を単独でインストールする場合の説明が混ざり合っています。今回は QWC2 を単独でインストールする場合の説明を読んでいくことになります。

## 1. 環境整備

### 1-1. 開発ツール

`git`, `nodejs`, そして `yarnpkg` が必要になりますので、それぞれ `dnf` でインストールします。

#### 1-1-1. git

```bash
sudo dnf install git
```

#### 1-1-2. nodejs

AlmaLinux の標準レポジトリからインストール出来るのは Version 16.20.2 ですが、必要とされるバージョンは 18.12.0 以上ですので、`dnf install nodejs` の前に一作業必要です。

root にスイッチして作業します。

```bash
su -
curl -fsSL https://rpm.nodesource.com/setup_20.x | bash -
dnf install nodejs
```

参照 : [NODESOURCE > Red Hat Based Linux Installation (Node.js 20 lTS)](https://downloads.nodesource.com/#RHEL-v20)

#### 1-1-3. yarnpkg

```bash
sudo dnf install yarnpkg
```

### 1-2. Web 地図アプリの環境設定

#### 1-2-1. ルート・ディレクトリ

ここでは、Web 地図アプリのルート・ディレクトリとして '/var/www/qwc2' というディレクトリを作ります。

```bash
sudo mkdir /var/www/qwc2
sudo chown my-account:my-accout /var/www/qwc2
sudo chmod 755 /var/www/qwc2
```

#### 1-2-2. nginx のルーティング

`/etc/nginx/conf.d/qwc2.conf` を作成して、Web 地図アプリへのルーティングを設定します。

```nginx
# webgis.mydomain ... QGIS Web Client 2
server {
    listen 80;
    server_name webgis.mydomain;
    root /var/www/qwc2;

    location / {
        index index.html;
    }

    error_log /var/log/nginx/qwc2-error.log;
    access_log /var/log/nginx/qwc2-access.log;
}
```

`nginx` を再起動しておきます。

```bash
sudo systemctl restart nginx
```

## 2. qwc2-demo-app

マニュアルによると、デモ・アプリをインストールする（そして、自分の環境とニーズに合わせてカスタマイズする）のが QWC2 を導入する最も簡単な方法だということですので、それに従って進めていきます。

### 2-1. qwc2-demo-app のインストール

まず、`qwc2-demo-app` を `git` で取得します。

```bash
cd
git clone --recursive https://github.com/qgis/qwc2-demo-app.git
```

次に、アプリが依存するパッケージをすべてインストールします。

```bash
cd qwc2-demo-app
yarn install
```

### 2-2. 開発版アプリのビルドと動作確認

そして、開発版アプリをビルドして動作確認をします。

```bash
yarn start
```

開発版アプリは既定では `http://localhost:8081/` で動作します。外部からアクセスする場合は、ホスト名を使うとエラーになりますので、 IP アドレスを使用します。例えば、`http://webgis.mydomain:8081/` ではなく、`http://192.168.0.9:8081/` とします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12310/f3116567-2a4b-6103-c35b-f7da0feeed8f.png)

このスクリーンショットのような画面が表示されたら `qwc2-demo-app` のインストールとビルドは成功裏に完了しています。

開発版アプリは `Ctrl + C` を2回押して終了します。

### 2-3. 運用版アプリのビルドと配備（デプロイ）

開発版アプリが無事に動作したら、運用版アプリを作成して運用環境に配備します。

次のコマンドで運用版アプリをビルドします。

```bash
yarn run prod
```

これで、`./prod` ディレクトリ以下に、運用版アプリの全てのファイルが作成されます。

`./prod` ディレクトリ以下のファイルとフォルダを全てアプリのルート・ディレクトリにコピーします。

```bash
cp -r ./prod/* /var/www/qwc2/.
```

ブラウザで以下のアドレスにアクセスして、運用版と同じようにデモ版の地図が表示されることを確認します。

```url
http://webgis.mydomain/
```

以上で `qwc2-demo-app` の一連のインストール作業は完了です。

## 3. テーマ "world" を追加する

作成した直後の `qwc2-demo-app` は、前回セットアップした QGIS Server とはまだ連携していません。

何はともあれ、QGIS Server が提供している筈の "world" というデモ・データを QWC2 で表示して、二つが連携できることを確認したい所です。

### 3-1. テーマ設定に "world" を追加する

`$HOME/qwc2-demo-app/thenesConfig.json` の冒頭部分は現在以下のようになっている筈です。

```json
{
    "themes":{
        "items": [
            {
                "url":"http://qwc2.sourcepole.ch/ows/uster/bauprojekte",
                "attribution":"Stadt Uster",
                "attributionUrl":"https://gis.uster.ch/",
                "scales": [
                    ...
```

この冒頭部分に "world" というテーマ・アイテムを追加します。

```json
{
    "themes":{
        "items": [
            {
                "id": "world",
                "url": "http://webgis.mydomain:8082/world",
                "mapCrs": "EPSG:4326",
                "scales": [
                    200000000,
                    80000000,
                    40000000,
                    20000000,
                    8000000,
                    4000000,
                    2000000,
                    800000,
                    400000,
                    200000,
                    80000
                ]
            },
            {
                "url":"http://qwc2.sourcepole.ch/ows/uster/bauprojekte",
                "attribution":"Stadt Uster",
                "attributionUrl":"https://gis.uster.ch/",
                "scales": [
                    ...
```

- `"url": "http://webgis.mydomain:8082/world"` ... 設置した QGIS Server から "world" というプロジェクトをテーマ・アイテムとして取得することを意味します。
- `"mapCrs": "EPSG:4326" ... プロジェクトの CRS が `EPSG:4326 WGS 84` であることを示しています。
- `"scales"` ... 選択可能な表示倍率を指定しています。

### 3-2. 開発版アプリのビルドと動作確認

`themesConfig.json` を書き換えたら、開発版アプリをビルドして動作確認をします。

```bash
yarn start
```

ブラウザで `http://192.168.0.9:8081/` にアクセスして、以下のような地図が表示されば、思惑通りに QGIS Server との連携が出来たことになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12310/44c7c948-d99f-387e-22c7-7b03813c6266.png)

### 3-3. 運用版アプリのビルドと配備（デプロイ）

開発版アプリで確認が取れたら、運用版アプリを作成して配備します。

```bash
yarn run prod
rm -rf /var/www/qwc2/*
cp -r ./prod/* /var/www/qwc2/.
```

## 4. テーマ "isg" を追加する

実のところ、"world" というテーマ（QGIS のプロジェクト）は、ちょっと扱いづらいところがあります。何故か分かりませんが、`qwc2-demo-app` では地図全体が表示されるところまで表示倍率を下げることが出来ません。また、それほど面白い地図ではなく、今後の参考になるとも思えません。

そこで、自分で作成した QGIS プロジェクト `"isg"` をテーマとして追加することにします。

### 4-1. ダウンロード

下記に `"isg"` プロジェクトの一式を置いています。ダウンロードして自由にお使い下さい。

- https://isarigami.net/map/isg.zip

### 4-2. プロジェクトを設置する

zip ファイルを解凍し、前回、[2-3. QGIS プロジェクトを設置する](https://qiita.com/drafts/c1da4f9a52e6727a5e97/edit#2-3-qgis-%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E8%A8%AD%E7%BD%AE%E3%81%99%E3%82%8B) で説明したところに従って、書庫に入っているファイルとディレクトリを設置します。

結果として、次のようなディレクトリ構成になります。

```
基準ディレクトリ (例えば "/home/my-account/qgis-projects")
    - world.qgs
    - naturalearth.sqlite
    + common
        + rasters
        + shpfiles
    - isg.qgs
    + isg
        + rasters
        + shpfiles
```

### 4-3. socket と service を構成する

```bash
sudo systemctl enable qgis-server@isg.socket
sudo systemctl enable qgis-server@isg.service
sudo systemctl start qgis-server@isg.socket
sudo systemctl start qgis-server@isg.service
```

### 4-4. nginx を再構成する

`/etc/nginx/conf.d/qgis-server.conf` に `isg` 用のエントリを追加します。

```nginx
server {
    listen       8082;
    server_name  webgif.mydomain;

    gzip off;
    include fastcgi_params;
    
    # CORS を許可
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
    add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';

    location /world/ {
        fastcgi_pass unix:/run/qgis-server-world.sock;
    }
    location /isg/ {
        fastcgi_pass unix:/run/qgis-server-isg.sock;
    }

    error_log /var/log/nginx/qgis-server-error.log;
    access_log /var/log/nginx/qgis-server-access.log;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
}
```

そして、`nginx` を再起動しておきます。

```bash
systemctl restart nginx
```

### 4-5. テーマ設定に "isg" を追加する

`$HOME/qwc2-demo-app/thenesConfig.json` の冒頭部分に "isg" というテーマ・アイテムを追加します。

```json
{
    "themes":{
        "items": [
            {
                "id": "isg",
                "url": "http://webgis.mydomain:8082/isg"
            },
            {
                "id": "world",
                "url": "http://webgis.mydomain:8082/world",
                "mapCrs": "EPSG:4326",
                "scales": [
                    200000000,
                    80000000,
                    40000000,
                    20000000,
                    8000000,
                    4000000,
                    2000000,
                    800000,
                    400000,
                    200000,
                    80000
                ]
            },
            ...
```

- `"id"` と `"url"` しか設定していませんが、これで大丈夫です。

### 4-6. 開発版アプリのビルドと動作確認

`themesConfig.json` を書き換えたら、開発版アプリをビルドして動作確認をします。

```bash
yarn start
```

ブラウザで `http://192.168.0.9:8081/` にアクセスして、以下のような地図が表示されば、テーマ "isg" の追加は成功裏に完了しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12310/91075353-6da9-1d8d-c967-7910cf3e3d5b.png)

### 4-7. 運用版アプリのビルドと配備（デプロイ）

そして、運用版アプリを作成して配備します。

```bash
yarn run prod
rm -rf /var/www/qwc2/*
cp -r ./prod/* /var/www/qwc2/.
```

## 5. カスタマイズ作業の流れ

`qwc2-demo-app` のカスタマイズ作業は、これまでの説明から分かるように、以下のような一連の流れになっています。

1. `themesConfig.json` を編集する
2. `yarn start` で開発版アプリをビルドし、動作確認をする
3. `yarn run prod` で運用版アプリをビルドする
4. ビルドされた運用版アプリを運用環境に配備する

この流れをもう少し詳しく知っておくと役に立ちます。

### 5-1. `yarn start` と `yarn run prod`

`package.json` の `"scripts"` セクションを見ると、`yarn start` と `yarn run prod` の動作内容を推測することが出来ます。それによると、これら二つのコマンドは、実際には次のような動作をしています。

- `yarn start`
    1. `tsupdate` ... 翻訳の更新
    2. `themesconfig` ... テーマの構成
    3. `iconfont` ... アイコンとフォントの構成
    4. `webpack serve --mode development` ... 開発モードで走らせる
- `yarn prod`
    1. `tsupdate` ... 翻訳の更新
    2. `themesconfig` ... テーマの構成
    3. `iconfont` ... アイコンとフォントの構成
    4. `webpack --mode production` ... 運用モードでアプリを作成する

最後だけが異なっているのですね。

何だ、それなら共通の3つの段階を行うコマンドと最後の段階だけを別々に行うコマンドを合計3つ作ったら効率的だぞ、と思われるかも知れません。

私もそう思ったのでやってみました。初めの3段階はほとんど時間を消費せずすぐに完了します。遅いのは最後の段階です。従って、コマンドを再編成しても、ビルドの速度はあまり変りません。

ただし、共通部分の3つの段階だけを行うコマンドは作っておくと後で役に立ちますので、`package.json` の `"scripts"` セクションに "make" というコマンドを追加しておきます。

```diff_json
{
    ...
    "scripts": {
        "prod": "npm run tsupdate && npm run themesconfig && npm run iconfont && webpack --mode production --progress",
        "start": "npm run tsupdate && npm run themesconfig && npm run iconfont && webpack serve --mode development --progress --host 0.0.0.0 --port 8081",
+       "make": "npm run tsupdate && npm run themesconfig && npm run iconfont",
        "iconfont": "node qwc2/scripts/makeIconkit.js",
        ...
    }
}
```

それよりも重要なのは、`themesconfig`（テーマの構成）が実際に何をやっているかです。

### 5-2. `themesconfig`

`themesconfig` では、おおむね、次のように作業が進みます。

1. `themesConfig.json` を読み込む
    - アプリで何個のテーマを扱うかが決まる
    - 各テーマについて、`url` によってサーバが特定される
2. 各テーマの詳細についてサーバに問い合せを行う
    - タイトル、概要説明、キーワード、作成者など、一般的な情報
    - サポートされているサービスの種類（WMS, WMTS, WFS, WCS）とその詳細な内容
    - CRS、地図の範囲、初期表示範囲
    - 各レイヤの「短い名前（≒ ID）」とタイトル、その他の詳細情報
3. `themesConfig.json` の情報と問い合せて得た情報を合成する
    - `themesConfig.json` に記載されている情報が優先される
    - 指定のない項目については、`themesConfig.json` に記載されている各テーマ共通のデフォルト値が適用される
4. 合成したテーマ情報を `static/themes.json` に書き出す

そして、開発版アプリも運用版アプリも、テーマについては `static/themes.json` を参照してビルドされます。

## And so, what's next?

ここまでの作業で実際にやったことは、テーマを追加するということだけで、アプリの外観や機能のカスタマイズについては、まったく手が着いていない状態です。変更したい所が数多くあるのに、その設定方法がよく分からない場合が多くて、前途遼遠です。今現在の `themesConfig.json` には自分にとって無関係と思われる記述が初期状態のまま残っており、不要なものをばっさりと削除してすっきりさせたいところですが、それすらも出来ていません。

しかし、ここまで来ると、QWC2 でいろいろと遊ぶことが出来ます。そして、遊んでいるうちに、どうしてもその重さが気になってきます。同じ内容の地図なのに `qgis2web` で作成したものに比べると、表示更新速度がカタツムリのように遅いのです。この速度の問題を放置してアプリの見た目にこだわっても、あまり意味が無いと思われます。

ですので、外観や機能のカスタマイズは一先ず後回しにして、表示速度の問題に手を着けることにします。

既に述べたようにパフォーマンス改善のために `MapCache` を導入するのですが、その前に、QWC2 だけで対応できる事がいくつかあります。次回は QWC2 において背景地図の取扱いを最適化する方法を説明します。

- [QGIS データを Web で表示 #5 - QWC2（続）](https://qiita.com/softark/items/9d172c9cd6447d13ff0d)