MapLibre GL JSのスタイルファイルを読む

# はじめに
私はこれまで様々な形式によるスタイルファイルの記述をして来ましたが、MapLibre GL JSのスタイルファイルが自分にはわかりやすくて、気に入っています。その最大の理由は、地図描画の流れをまるで物語のように読める（味わえる）からです。

## MapLibre GL JSのスタイルファイルの特徴
MapLibre GL JSのスタイルファイルは、1つのファイルでスタイルを記述します。これに対して、かつてMapZenが開発してオープンソースライセンスで提供されている[Tangram](https://github.com/tangrams/tangram)（開発は終了）のスタイルファイルのように、POIを定義するファイル、ラベルを定義するファイルなどのように、複数のファイルを作成し、それぞれが関連付けられているものがあります。

それぞれの方式には良いところがあり、前者は「簡単に持ち運べる」ことです。つまり、メールに添付するなりして、他人と簡単に共有できます。一方、複数のファイルに分かれるスタイルファイルの記述では、全てのファイルを共有して、共有される側はそれらの関係などを正しく理解する必要があります。

あ、そう言えば、似たような（似てないかな？）記事があったっけ。。。

https://qiita.com/Yfuruchin/items/9995b178ba2bb97a4c39

もちろん、後者の方式には良いところもあります。それは、ノーマルモードのスタイルに加えて、ダークモード、あるいは衛星画像や地形モードなど複数のスタイルを作成して管理していく場合に、共通基盤部分を記述するスタイルファイルと、個別のスタイルを記述するものに分けることで、コードの冗長性を減らして、効率的なスタイルファイルとなります。また、国や州ごとに道路番号のシールドが様々ですが、そのシールドのspriteだけを定義するファイルを作成して、常時そこを参照するようにすれば、spriteが追加されたり変更されたりする度にあちこちに手を入れずに済みます。そして、複数のCartographerが共同作業を行うような場合は、この方式は生産性やメンテナンス性を高めるでしょう。

今回のテーマは、スタイルファイルの記述の生産性ではなくて、スタイルファイルという物語を読むことにありますので、その話はまたいずれの機会に。

## スタイルファイルを読む前に
さて、MapLibre GL JSのスタイルファイルを読む前の準備をします。
### スタイルファイルのダウンロード

https://maputnik.github.io/editor/#13/35.6768/139.76957

このリンクから、下の画面が開きます。

![スクリーンショット 2023-12-11 10.21.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/5dd1f2be-d6cc-4991-cfbc-f1dbdcdfd58a.png)

これ自体は、Maputnikという、MapLibre GL JSのスタイルファイルを記述するオープンソースライのエディタです。この左上の**Export**（赤囲み）をクリックすると、osm_liberty.jsonという名のスタイルファイルをダウンロードすることができます。

![スクリーンショット 2023-12-11 10.21.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/5a19e026-c831-de21-5e41-aab9d38526f8.png)

## スタイルファイルを読む　
では、このosm_liberty.jsonをコードエディタで開いてみましょう。
MapLibre GL JSでは、スタイルファイルの最初の行から最後の行の方向に読み込みながら、レイヤーを順番にレンダリングしていきます。従って、後半に配置されているレイヤーによって、前半のレイヤーはマスクされる（塗り覆われる）ことになります。

このスタイルファイルは、OpenMapTilesに対して、スタイルを記述していて、行数は1743あります。OpenMapTilesにデータがあってもスタイルを指定していないこともあり、かなり簡潔なスタイルです。従って、スタイルファイルを読むには、ちょうど適しているでしょう。

### 冒頭部分
ファイルの冒頭部分にメタデータが記載してあり、その次のsourcesが、スタイルファイルが使用しているベクトルタイルの参照先が書かれています。ここでは、[OpenMapTiles](https://openmaptiles.org)と[Natural Earth](https://www.naturalearthdata.com)が指定されています。

![スクリーンショット 2023-12-11 16.57.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/4deb3fd5-2bd0-02fa-9965-028c93b6c135.png)

OpenMapTilesは、OpenStreetMapのデータから作成されているベクトルタイルデータセットです。Natural Earthは、[NACIS](https://nacis.org)のメンバーらによって作成、メンテナンスされている地球規模のオープンデータです。どちらかというと、低めのズームレベルで採用するのに適しています。データソースは追加することも減らすこともできます。衛星画像などのラスター形式のデータソースも指定できます。

### spriteとglyphs
続いて、spriteとglyphsのURLが記述されています。
![スクリーンショット 2023-12-11 17.17.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/3695e3dc-ef86-68e5-08b0-62873e669b90.png)
spriteはいわゆるアイコンイメージのことです。glyphsはフォントセットのことです。地図表現にとっては、アイコンとフォントはとても重要なものです。MapLibre GL JSでは、これらにURLを記述することで、アイコンイメージのセットやフォントスタックを参照しに行きます。

### いよいよ描画レイヤーへ
MapLibre GL JSの描画は、スタイルファイルの最初の行から最後の行の順に行います。従って、最初の方の行の指定に従って描かれたレイヤーは、後に記述される指定に従って描かれたレイヤーによって上書きされます。

![スクリーンショット 2023-12-11 23.22.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/421fa5d4-d448-e031-ff7c-d95e05c0cd5a.png)
このスタイルファイルに限らず、陸地の背景色としてbackgroundという名称のレイヤーが定義されることが多いです。その上で、広域の土地利用（landuse）や土地被覆（landcover）を情報を描画するレイヤーとしてスタイルファイルに記述するのが一般的です。

![スクリーンショット 2023-12-11 23.32.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/39e21646-c063-84a0-ce6c-a5bbda9ec427.png)
この例では、住居地域としての土地利用エリアを、OpenMapTilesからclassがresidentialというプロパティを持ったデータを対象に、ズームレベルの変化に対応した色指定を行っています。その後で、classがwoodである森林植生のポリゴンを描画し、さらにclassがgrassである緑地のポリゴンを描画します。

### 水系レイヤーの描画位置に注意
MapLibre GL JSに限らないのですが、レイヤーの描画順位で、水系（water）レイヤーは、背景色（background）レイヤーの上に描画します。そして、少し気をつけなければならない点としては、広域の土地利用・土地被覆の上位に描かないと、特に河川や湖沼が表示できない場合があります。例えば、広域で森林植生のポリゴンがあれば、そのエリアを流れる河川などは森林植生のポリゴンにマスクされてしまわないように描画位置はポリゴンの上になります。

![スクリーンショット 2023-12-12 9.26.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/e6c628f6-5e6a-df12-8306-e7285c8a4125.png)

![スクリーンショット 2023-12-12 9.40.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/ddb01065-fe6a-4c19-08bf-7f10ff3303d2.png)

このように、道路や施設などの人工物を描く前に、自然界に存在する様々な土地利用・土地被覆、水系などを描きます。

### 道路の描画は複雑多岐にわたる

スタイルファイル全体の中で、道路の描画に関するスタイルは多くの部分を占めます。その理由は、高速道路、国道、県道、一般道など複数の道路クラスがあり、これらは本線と接続道路（link）に分かれ、それぞれに色や太さを設定する必要が出てくるからです。それに加えて、橋（bridge）、トンネル（tunnel）などの属性も加わってきますので、それが乗数となってレイヤー数が増えます。

ちなみに、このスタイルファイルでは、主要な道路網にはそれぞれに黄色系の色を与え、一般道には白色を与えています。
![スクリーンショット 2023-12-12 10.09.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/73012b31-c513-44af-a929-2e1721164b83.png)

道路の描画は、アウトライン（casing）を道路本体よりもやや太く設定して、その上に道路本体を描画する方法が一般的に使われます。

スタイルファイルで見てみましょう。
この例は、長野自動車道（motorway）のインターチェンジ部分に実際に採用されているbridge属性を持ったレイヤーになります。

まず、アウトライン（casing）を最初に描画します。
![スクリーンショット 2023-12-12 10.20.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/a7dd80d9-f83d-8c45-6f5f-613dd56e9f31.png)

その後で、道路本体を描画します。
![スクリーンショット 2023-12-12 10.20.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/37550a39-e896-9459-f23e-70c3ff3b6c59.png)

それぞれ、ズームレベルの変化に応じて線の太さが変化するように指定しています。

一般的にはbridge属性の道路は、そうで無い道路の上にあるため、上位に描画されるように配置します。また、tunnel属性の道路は、それ以外の道路属性の下位に描画されるように配置します。もちろん、これには例外もありますし、ベクトルタイルデータの属性が不完全な場合もあるため、全てを実態通りの見せ方にできる保証はありません。

### 建物や境界線の描画
道路を描いた後は、建物を描きます。MapLibre GL JSには2次元の建物表現（typeがfill）と3次元の建物表現（typeがfill-extrusion）があり、建物データに高さの値がある場合は、後者のレイヤーを設置することで、見栄えが良くなります。なお、OpenMapTilesでは、日本地域の高さデータが無いため、ニューヨークの例をご覧ください。
![スクリーンショット 2023-12-12 10.43.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/42049787-8c6b-d5c7-5d5c-9be049e4b1de.png)

![スクリーンショット 2023-12-12 10.32.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/594e195f-6b57-d9b3-335f-2fc7f52b04e4.png)

### 行政境界線の描画
行政境界線は、土地利用・土地被覆、道路、建物などによってマスクされては困るため、描画順位はこれらの後になります。都道府県界はboundary_3というレイヤーで描画されています。破線表示の指定もここで行います。
![スクリーンショット 2023-12-12 10.48.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/a57ef2fe-ae00-403e-ea31-02eb66359c5d.png)
![スクリーンショット 2023-12-12 10.50.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/115baa10-bbdf-c340-4d6c-70ac64b12051.png)

### ラベルの表示
今までは、文字以外、つまりポリゴンやライン形式のデータを描画してきました。いよいよこの後は様々なラベル表示を行います。ラベル表示も上位レイヤー（スタイルファイルの後半に配置）が優先され、もし文字同士が衝突する場合には、上位レイヤーが優先され、そうで無いラベルは機械的に非表示となります。ズームレベルの変化に応じて描画可能なスペースがあれば、下位のレイヤー（スタイルファイルの前半に配置）のラベルも表示されるようになります。従って、日本の場合では、鉄道駅や主要な施設のレイヤを上位に配置し、そうで無い一般施設を下位に配置します。

この例は、ズームレベル16で表示するPOIラベルのレイヤーです。
![スクリーンショット 2023-12-12 11.19.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/b5953ecf-f5b4-17c9-f933-9616da2c68fa.png)

ラベルのスタイリングは、タイルデータのどのラベルを描画するのか、どのアイコンを指定するのか、レイアウトはどうするのか、フォントの色や大きさなどのスタイルはどうするのかなど様々な指定項目があります。

このスタイルファイルは、日本語での表記がされていないラベルが多数あります。その理由はレイヤーにおいて、参照するテキストデータをnameに指定しているからです。
![スクリーンショット 2023-12-12 11.21.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/be14a315-fc47-6765-87e4-1173464ae295.png)
実際のデータ属性を確認すると、name以外にも日本語などの多言語データが存在することがわかります。
![スクリーンショット 2023-12-12 11.22.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/fbcb3e76-c660-5fd6-833a-d5ec2a0712c2.png)
そこで、text-fieldの指定をnameからname:jaに変えてみます。
![スクリーンショット 2023-12-12 11.23.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/14f11c76-d97f-0318-68c2-2ee1caa86c8f.png)
そうすると、日本語が表示されるようになりました。

ただし、このままですと、日本以外でラベルデータにname:jaが無ければ、文字自体が表示されなくなってしまいます。それを避けるために、nameを第1行目に、2行目にname:jaを表記するなどの工夫が必要となります。（この記事は、スタイルファイルの記述方法を解説する目的ではありませんので、詳細は割愛します）

ということで、POIなどのラベルレイヤーをずっと読み進んでいくと、最後は大陸名を記述するレイヤーで締めくくられています。つまり、このスタイルファイルでは、大陸名の描画が最優先されているのです。
、![スクリーンショット 2023-12-12 11.30.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531580/b13805dc-aa83-9091-d214-e78998c5987a.png)

# おわりに
スタイルファイルを読むことは、スタイルファイルの作者の意図を知ることになります。誰に対して、どのような価値を与えようとしているのかによって、スタイルファイルは大きく変わってきます。

今回使用したベクトルタイルデータのOpenMapTilesは、様々な用途で使用されています。[MapTiler](https://maptiler.jp)のサービスでMIERUNEが提供しているスタイルも、このOpenMapTilesを使用しています。こちらの記事もご覧ください。

https://qiita.com/moritoru/items/193f6d9bfd4b6bccea46