MapLibre GL JS を使ってウェブ地図ページを作る方法（一例）

# はじめに
この記事は、[Smart Maps Advent Calendar 2022](https://qiita.com/advent-calendar/2022/smart-maps) にポストする記事として書いています。

国連事務局では、UN Clear Mapという小縮尺の地図を使っています。これに関する検討・実験を進める中で、**最近、既存のベクトルタイルデータとスタイルから、MapLibreを使ってウェブ地図ページを作るという実験をしたので、その記録をここにメモします**。ベクトルタイルの**利用やホスティング**という分野に関係するかなと思います。

すでにベクトルタイルとそのスタイルを作成していて、自分でウェブ地図ページを作りたいという人の参考になるとよいと思います。さらに、この地図を作る前段の作業ですが、[UNVT/charites](https://github.com/unvt/charites)を使って、Esriのベクトルタイルスタイルをインポートし、MapLibre用に加工する際のポイントもつけておきますので、こちらも興味がある人の役に立てればよいと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/4afcf92d-2b0d-7259-b0ea-989f00f35a46.png) ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/e47a1efe-ebd1-9b95-2eae-b137a5902d84.png)


# 私の環境
* Windows 10 enterprise
* Windows Powershell
* Google Chrome

# イントロ： UN Clear Map について（今回の実験対象）
国連の地理空間情報グループでは、国連事務局での利用などに供するために、Clear Mapという小縮尺ウェブ地図を提供しています。簡単にいうと、これはもともと国連事務局や関係部局での利用のために作成されたものですが、その他のコミュニティにも所定のルールに従ってオープンになっているものです。細かい説明は次のリンクを参照してください。
* ClearMapの説明：　https://www.un.org/geospatial/mapsgeo/webservices
* ClearMap（ラスタ）のページ：　https://geoservices.un.org/Html5Viewer/index.html?viewer=clearmap
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/f22e8486-2eee-daf2-bde5-3bab471c419e.png)

公開されている地図なので、実際に上のリンクからアクセスできますが、私の経験上では、たまにレスポンスが遅くて表示されないこともあります。下図のように、Google Chromeの開発者モードで見てみるとラスタタイルの地図であることがわかります。このページのタイルでは、http/1.1プロトコルを使っているようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/875c49f5-9a2a-0f2a-96e5-1b11314dc241.png)

Clear MapはWMSとしてもサービスを行っていました（マニュアルは[こちら](https://geoportal.un.org/arcgis/sharing/rest/content/items/541557fd0d4d42efb24449be614e6887/data)）。（当時の）マニュアルにはWMSはhttps://geoservices.un.org/arcgis/services/ClearMap_topo/MapServer/WMSServer でアクセスできることが示されていますが、2022年11月末現在、https://geoservices.un.org/arcgis/services/ClearMap_topo/MapServer/WMSServer?service=wms&request=GetCapabilities&version=1.0.0 と試してもダメでした。WMSは負荷が大きいですし、ラスタタイルで配信できているので置き換えたのかもしれませんね。

ラスタタイルは、例えば、https://geoservices.un.org/arcgis/rest/services/ClearMap_WebPlain/MapServer/tile/5/15/12 でアクセスできます。直接ラスタタイルを利用することについては説明されていないですし、もしかしたらほかのドメインからのタイル利用は奨励されていないのかもしれませんね。（CORS設定などどうなっているのか？？）


## UN Clear Map （ベクトル版）
Clear Mapはベクトル版もあり、用務に使うこともあります。主にEsri製品群での利用を想定しているようで、これらのベクトルタイル地図についてはArcGIS JavaScriptベースでの描画になっており、私が知る限り、MapLibreなどのオープンソースではまだ利用されていません。

例えば、プレインなデザインであれば、こんな感じのスタイルになっています（**注： データを自由に利用ができるかは明示されていませんので、まだ一般の方による利用は想定していないのだと思います。**）。
* スタイル：　https://pro-ags2.dfs.un.org/arcgis/rest/services/Hosted/Clearmap_Webplain/VectorTileServer/resources/styles/root.json
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/b914cfdf-64ba-bf45-9b2c-2d86dd4fbd66.png)
（↑ 行数が多いので、画像は一部のみ）

現状では、スタイルが違うとベクトルタイルソースが違います。ArcGIS Proを使って、ベクトルタイルのデータとスタイルをセットで出力する方法が一般的なのでそのようにしているのかもしれません。細部で利用しているデータが違うかもしれません（海の深さの塗分けの有無など）が、ほとんどは同じデータを使っていると思いますので、そういう点も将来改善できるかもしれませんね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/bc5fbae7-a0e3-237c-1566-cbfb39bf2fa1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/962a6d5f-8bf3-0f1a-4132-50ccc0f7fd07.png)

なお、こちらのベクトルタイルもhttp/1.1プロトコルを使っていました。サーバーの設定なのかもしれません。

# MapLibreでのWeb地図作成作業
さて、それでは実際の作業をメモしていこうと思います。今回は以下のステップでおこないました。

## Step 1. GitHubのレポジトリを作る
できたウェブ地図はGitHubページでホストしようと思うので、レポジトリを作ってその中にdocsフォルダを作っておきます。

## Step 2. MapLibre GL JSとプラグインを準備する
インターネットから直接参照してもよいですが、MapLibre GL JS と使いたいプラグインをダウンロードしてきます。

### 2-1. MapLibre GL JS をダウンロード
2022年11月現在、最新版は2.4.0のようです。Windows Powershellのcurl.exeを使ってダウンロードしていきます。unpkg.comさんのところからダウンロードすると便利です。

```
mkdir docs
cd docs
mkdir maplibre-gl@2.4.0
cd maplibre-gl@2.4.0
curl.exe -O https://unpkg.com/maplibre-gl@2.4.0/dist/maplibre-gl.js
curl.exe -O https://unpkg.com/maplibre-gl@2.4.0/dist/maplibre-gl.js.map
curl.exe -O https://unpkg.com/maplibre-gl@2.4.0/dist/maplibre-gl.css
curl.exe -O https://raw.githubusercontent.com/maplibre/maplibre-gl-js/main/LICENSE.txt
cd ..
```

### 2-2. ExportプラグインとLegendプラグインをダウンロード
watergisで開発された２つのプラグイン、[watergis/maplibre-gl-export](https://github.com/watergis/maplibre-gl-export)と[watergis/maplibre-gl-legend](https://github.com/watergis/maplibre-gl-legend)を使いたいと思いますので、ダウンロードしてきます。2022年11月29日現在、それぞれ1.3.8と1.2.8です。マップの出力ができたり、レイヤのオンオフができるようになります。

```
mkdir watergis
cd watergis
mkdir maplibre-gl-export@1.3.8
cd maplibre-gl-export@1.3.8
curl.exe -O  https://raw.githubusercontent.com/watergis/maplibre-gl-export/main/LICENSE
curl.exe -O  https://watergis.github.io/maplibre-gl-export/maplibre-gl-export.css
curl.exe -O  https://watergis.github.io/maplibre-gl-export/maplibre-gl-export.js
cd ..
mkdir maplibre-gl-legend@1.2.8
cd maplibre-gl-legend@1.2.8
curl.exe -O https://raw.githubusercontent.com/watergis/maplibre-gl-legend/main/LICENSE
curl.exe -O https://watergis.github.io/maplibre-gl-legend/maplibre-gl-legend.js
curl.exe -O https://watergis.github.io/maplibre-gl-legend/maplibre-gl-legend.css
```

フォルダの中身はこんな感じになっていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/62be9e6e-b273-cea2-d0b7-aef935992187.png)




## Step 3. スタイルファイルを準備
MapLibreで使うために準備してあったスタイルファイルをdocsフォルダに入れておきます。なお、スタイルとして参照しているspriteやglyphsについても、自分のページに持っておきたい場合は適宜の場所に置いておきましょう。

スタイルファイルを持っていないけど実験をしたい人は、例えば、私がNaturalEarthから作ったベクトルタイル用のスタイル https://ubukawa.github.io/vt-test/maps/test-simple.json を使って実験してもいいと思います。

### （参考）Esri のスタイルファイルをMapLibre用に加工した方法
このセクションのここから先はかなり細かい話になるので、**作業に直接関係しない場合はStep 4にいきましょう！**

#### 使ったツール
[unvt/charites](https://github.com/unvt/charites)を使うと加工が楽なので使いました。~~現在はまだプルリクエスト中なのですが、レイヤIDに/（スラッシュ）が入っているときにフォルダ構造を維持してくれるバージョン、[keichan34/charites](https://github.com/keichan34/charites) のsupport-slash-in-layer-idブランチがいいです~~ 12月1日現在、ブランチがマージされたようです。最新のcharitesを使いましょう（まだバージョン0.2.0なので npmはまだ更新されていないかも・・・）。
ここではcharitesが使える前提で説明する（インストール方法を書きません）のですが、docker上で動かしても楽だと思います。

#### スタイルjsonのインポート
Esriのベクトルタイルサーバーだと、サーバーの下の VectorTileServer/resources/styles/root.json にスタイルファイルがあります。Esriさん用のスタイルですが、こちらをコピーしてきて、charitesが利用できる形（YAML）にインポートします。

```
curl.exe -O https://（アドレス）/VectorTileServer/resources/styles/root.json
```
```bash:charitesを使える状態で
charites import root.json esri/style.yml
```

なお、インポートすると最上位でスタイルを取りまとめるYAMLファイル（上の例だとstyle.yml）ができます。その中に、レイヤの一覧があるので確認しましょう。
Esri用のスタイルだと、往々にしてスタイルレイヤ名が長くなります。unvt/charitesは一行が長いときは、YAMLの">"（折り返し形式のブロックスカラー）を使って見やすくしてくれるのですが、これがあるとレイヤ一覧としてうまく働かないので">"があるところは消して、一行に戻しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/c3307705-62b1-cfe4-a511-3acefd981b7d.png)

#### 修正すべき点
* Esriさんのスタイルでは、タイルサービスやリソース（spriteやglyphs）のパスを相対パスで書いています。MapLibreで使うにあたり、参照しやすいようにURLを直します。spriteとglyphsは経路があっていれば相対パスでもよいと思います。
* ソースの場所、ベクトルタイルについてはesriのスタイルではVectorTileSeverのindex.jsonを返すパスを指定していると思うので、VectorTileServer/tile/{z}/{y}/{x}.pbf とパスを指定してあげる必要があります（urlでなく、tileとして書く）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/e36ea66d-8694-6aa3-c353-b82f465605c1.png)

上記の2点を直すと、unvt/charitesのライブプレビューでも作業ができるようになります。ライブプレビューはたまに止まっているときなどもあるので、ctrl + c をして再度立ち上げたり、ウェブブラウザで再読み込みしたりして調整するとよいです。

```bash:charitesのライブブレビュー
charites serve esri/style.yml
```

* これでEsriのスタイルが見られるはずですが、見られない場合は各レイヤのテキストフォントなども確認してみるといいと思います。
    * 最上位のYAMLファイルにあるスタイルレイヤ一覧の前に適宜コメントアウト（#）をつけてレイヤをつけたり消したりしながら作業する、問題のあるレイヤが見つけやすいです。
    * text-fontの値が文字列として複数並んでいると、MapLibreなどでは見られません。（スタイル仕様上では使うフォントをArrayとして書いておいていいはずなんですが、MapLibreではどうもうまくいきません。Esriさんはここに上手く対応しているので、フォントが複数並んでいる場合はEsriさんのStyleをそのままMapLibreには持ち込めません。）

 
#### YAMLからjsonへの出力
ライブプレビューをみて作業しながら、もうよいなと思ったら、JSONファイルに出力します。
```bash:charitesで出力
charites build esri/style.yml style1.json
```


</div></details>


## Step 4. HTMLファイルの準備
ここまででマップライブラリやプラグインの準備ができたので、htmlファイルを作ってみます。map.htmlとしてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/410dab44-341c-487d-0659-f6c2d57bfc07.png)

map.htmlとstyle1.json というスタイルファイルがdocsフォルダにあるとして以下のようにします。プラグインも入れています。


```html:map.html
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Test- UN Clear Map Vector - test </title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='maplibre-gl@2.4.0/maplibre-gl.js'></script>
<link href='maplibre-gl@2.4.0/maplibre-gl.css' rel='stylesheet' />
<link href='./watergis/maplibre-gl-export@1.3.8/maplibre-gl-export.css' rel='stylesheet' />
<script src='./watergis/maplibre-gl-export@1.3.8/maplibre-gl-export.js'></script>
<link href='./watergis/maplibre-gl-legend@1.2.8/maplibre-gl-legend.css' rel='stylesheet' />
<script src='./watergis/maplibre-gl-legend@1.2.8/maplibre-gl-legend.js'></script>
<style>
body { margin:0; padding:0; }
#map {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 100%;
}
</style>
</head>
<body onload="popupFunction()">
 
<div id='map'></div>

<script>
function popupFunction() {
   alert("You can show a pop-up message")
}

var map = new maplibregl.Map({
  container: 'map', 
  attributionControl: true,
  hash: true, 
  //renderWorldCopies: false,
  style: './style1.json', // style file path
  center: [0,0], // center [lng, lat]
  zoom: 1, // zoom level at loading
  maxPitch: 85,
  maxZoom: 0, // min zoom
  maxZoom: 6 // max zoom
});

//UI
map.addControl(new maplibregl.AttributionControl({customAttribution: "This is just for practice. The United Nations Clear Map (hereinafter &quot;Clear Map&quot;) is a background reference web mapping service produced to facilitate &quot;the issuance of any map at any duty station, including dissemination via public electronic networks such as Internet&quot; and &quot;to ensure that maps meet publication standards and that they are not in contravention of existing United Nations policies&quot; in accordance with the in the Administrative Instruction on &quot;Regulations for the Control and Limitation of Documentation - Guidelines for the Publication of Maps&quot; of 20 January 1997 (http://undocs.org/ST/AI/189/Add.25/Rev.1). Clear Map is created for the use of the United Nations Secretariat and community. All departments, offices and regional commissions of the United Nations Secretariat including offices away from Headquarters using Clear Map remain bound to the instructions as contained in the Administrative Instruction and should therefore seek clearance from the UN Geospatial Information Section (formerly Cartographic Section) prior to the issuance of their thematic maps using Clear Map as background reference." , compact: true}));
map.addControl(new maplibregl.NavigationControl(), 'bottom-right');
map.addControl(new maplibregl.ScaleControl() );

//Export
map.addControl(new MaplibreExportControl({
  PageSize: Size.A4,
  PageOrientation: PageOrientation.Portrait,
  Format: Format.PNG,
  DPI: DPI[96],
  //Crosshair: true,
  PrintableArea: true,
  Local: 'en'
}), 'top-right');

//legend
map.addControl(new MaplibreLegendControl({}, {
   showDefault: false,
   onlyRendered: true,
   reverseOrder: false
}), 'bottom-left');

//debug
map.showTileBoundaries = false;
map.showCollisionBoxes = false;

</script>
 
</body>
</html>

```

:::note info
もしも、スタイルを複数もっていて、スタイルのスイッチ機能を付けたい場合は、私の別の記事などにも方法を書いてありますのでご覧ください。→ https://qiita.com/T-ubu/items/b6755b062effbcb896c5
:::


## Step 5. GitHubページの設定

できたらGitHubレポジトリにプッシュして、GitHubページの設定もしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/125fdefb-c36c-1220-fe53-a368c6e51166.png)


## Step 6. 作業結果の確認
こうすることで、ウェブ地図が見られるようになりました。Export機能（右上）とレイヤ一覧（左下）もあり、プラグインがきちんと動いていることもわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/1d242305-277c-93cb-3580-cfe4ec5a6ce3.png)

ベクトルタイルなので、傾けたり回転させたりしてもラベルがまっすぐになっています。（もともとのラベルは北を上にしたときの地図用に最適化していたので、ぐるぐる回すと少し離れすぎる場所もでてくるなぁということを発見しました。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/e7962622-5449-ef2b-238a-433e73f8ee4e.png)

レジェンドでチェックするとスタイルレイヤが200くらいあります。これだけ細かな描画にはかなり苦労したのではないかと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679143/6431e574-2160-8f63-50e3-61529cfc6491.png)



# まとめ
この記事では、MapLibreを使ってウェブ地図を作るところまで作業の流れを紹介しました。Web地図作成にはいろいろな手順、方法があると思いますので、これは単なる一例だと思いますが、経験を共有させていただければ幸いです。

なお、この作業はこれで完結ではありません。今回確認したベクトルタイル地図はスタイルのレイヤ数と、そもそもベクトルタイルのレイヤ数が多いので、ベクトルタイルのデータの構造の見直しと、スタイルレイヤの構造の見直しをしたいと思っています。今回の作業はそのための前段階と考えています。

# 謝辞
* unvt/charitesの開発者の皆様に敬意を表します。特にレイヤID中の/（スラッシュ）について作業してくださったkeichan34さんのおかげで、Esriスタイルのインポートが格段にやりやすくなりました。
* MapLibreのプラグインを開発されたwataregis、Jin Igarashiさんに感謝します。とても便利に使わせていただいています。
* Clear Map VectorのスタイルをデザインしたUNGSCの同僚に敬意を表します。
* S[Smart Maps Advent Calendar 2022](https://qiita.com/advent-calendar/2022/smart-maps) の機会を得て、経験の一つを共有させていただく機会を得られました。ありがとうございます。

# 参考ページ
* UN Geospatial Web Services: https://www.un.org/geospatial/mapsgeo/webservices
* UN ClearMap: https://geoservices.un.org/Html5Viewer/index.html?viewer=clearmap
* MapLibre GL JS: https://github.com/maplibre/maplibre-gl-js
* UNPKG.com: https://unpkg.com/
* watergis/maplibre-gl-export : https://github.com/watergis/maplibre-gl-export
* watergis/maplibre-gl-legend : https://github.com/watergis/maplibre-gl-legend
* unvt/charites: https://github.com/unvt/charites
* keichan34/charites: https://github.com/keichan34/charites
* MapLibre style specification: https://maplibre.org/maplibre-gl-js-docs/style-spec/