MapLibreのExpressionsにおけるテキストシンボルの表記に慣れる

# はじめに
MapLibreではStyle Specificationsの中のExpressions（[リンク先参照](https://maplibre.org/maplibre-style-spec/expressions/)）で、様々な表現が可能になっています。
しかし、記述方法は直感的とは言い難いので、本記事では特に**テキストシンボル**（文字ラベル）の記法について説明します。

なお、Expressionsでの様々な表現については別の方(@humohumoさん)が2023年MapLibreアドベントカレンダー記事（[MapLibre GL JS の expression 事例](https://qiita.com/humohumo/items/6460188ab7e8214a89bd)）で解説していますので、あわせてご参照ください。

# どんなテキストシンボル表現ができる？

本記事では、下記のようなテキストシンボル表示を属性値の組み合わせによって行う記法について説明します。
**各項目の属性値を繋げたり、計算したり、色やサイズを細かく変えたり**、といったことが可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122865/66d402d9-a95d-202b-62a8-7eda561d4594.png)

# 使用するサンプルデータの属性項目

この記事では、千葉県内の各市区町村における選挙区や投票者数に関するデータを例に扱います。
データは市区町村界のポリゴン図形で、属性項目には下記のデータが格納されています。
このデータのうち、例えば「投票者数」を「有権者数」で割れば投票率が算出できるわけですが、あえて属性項目には計算結果を含めていません。こうした計算もExpressionsの記述の中で対応していきます。

| 項目名 | 属性値の例 | 内容 |
|:-|:-|:-|
| ID  | 1  | 連番のID  |
| 名称 | 柏市  | 市区町村名  |
| 面積  | 114743533  | GIS上で算出したポリゴン面積(数値)|
| 選挙区  | 8区  | 選挙区名  |
| 有権者数  | 360412  | 有権者数(数値)  |
| 投票者数  | 188725 | 有権者数(数値)  |

# テキストシンボルの記述実例

ここからは、実際にサンプルデータを使ってテキストシンボルを記述して表示を確かめていきます。

## 単一の属性情報をそのまま表示する場合

では早速、テキストシンボルを表示させてみましょう。
まず最も基本的な表示方法として、単一の属性情報をそのまま表示してみます。
下記はMapLibre GL JSで上記のサンプルデータを読み込んで、基本的なシンボルレイヤ設定を行った例となります。

`'text-field': ['get', '名称']` の部分で各フィーチャの「名称」の属性値を読み込んで表示します。なお、単一の属性項目であれば`'text-field': '{名称}'`という記述でもOKです。

```javascript:index.js
map.addLayer({
    'id': 'voter-turnout-symbol',
    'type': 'symbol', //ソースデータは市区町村界のポリゴン図形だがsymbolとして読み込む
    'source': 'voterturnout',
    'layout': {
        'icon-image': '', //アイコン画像は使わない
        'text-font': ["NotoSans-SemiBold"], //文字フォントの指定
        'text-anchor': 'top', //テキスト表示位置の指定
        'text-allow-overlap': true, //テキストが重なっても表示する
        'text-size': ['interpolate',['linear'],['zoom'],5,8,8,11,15,18], //テキストサイズはズームレベルに応じて指定
        'text-offset': [0,0], //テキストのオフセットは無し
        'text-field': ['get', '名称'], //テキスト表示する属性項目を指定（'{名称}'と記述してもOK）
    },
    'paint':{
        'text-color': '#555', //テキストの色指定
        'text-halo-color': '#fff', //テキストの外縁の色指定
        'text-halo-width': 1 //テキストの外縁の幅指定
    }
});
```

表示結果は下図のようになりました。
自治体名のみが設定に基づいて表示されている状態です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122865/a007bc49-10b3-8a10-7617-22bfe8b5a2db.png)

## 複数の属性情報を連結して表示する場合

次に、`'text-field'`の記述内容を下記のように変えて、「名称」と「選挙区」の属性値情報を連結してみます。

`'text-field': ['concat', ['get', '名称'], "\n(選挙区:", ['get', '選挙区'], ")"]`

'concat'を使うとカンマ区切りで列記した各テキスト情報を連結することができます。
これにより`['get', '名称']`と`['get', '選挙区']`でフィーチャの各属性値を繋げるわけですが、その合間に改行区切り文字 "\n" や鉤括弧 "( )" も文字列で追記しました。

また'concat'と'get'の関係性を見ると分かる通り、**入れ子構造にして各記法を重ねていく**のがExpressionsの基本となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122865/3778e92d-bbe4-b1d6-d31c-2b66cc5c358b.png)

## 属性項目ごとに文字の大きさ等のスタイルを調整する

文字のスタイルを個別に定義するには、['format', "対象とする文字列値", {'font-scale': 1, ...}]という形で列記します。

`'text-field': ['format', ['get', '名称'], {'font-scale': 1}, ['concat', "\n(選挙区:", ['get', '選挙区'], ")"], {'font-scale': 0.8, 'text-color':"#d7191c"}]`

これにより、テキストシンボル2行目の「選挙区」についてフォントサイズがやや小さめになり、さらに文字色を変更できました。メリハリが付きますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122865/dcaa007d-b015-9109-0039-b07f52992ec6.png)

## 面積などの数値表記を調整する

続いて、数値の表記についても追加します。
まずは単純に、上述の内容に「面積」の値を追記してみます。なお、2行目の「選挙区」と「面積」を'concat'で連結させて一括りにすることで、'font-scale'をまとめて適用しました。

`'text-field': ['format', ['get', '名称'], {'font-scale': 1}, ['concat', "\n(選挙区:", ['get', '選挙区'], ")\n", ['get', '面積'], "m2"], {'font-scale': 0.8, 'text-color':"#d7191c"}]`

もし面積などの属性項目が数値型ではなく文字列型になっていたら、`['to-number', "値"]`という文字列=>数値変換用のExpressionもありますので、うまく適用してみてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122865/8da1b810-17c4-3b3c-163e-f7587509f2d0.png)

追記できましたが、数値が大きいのでわかりづらいですね。そこで下記の対応を行います。
・数値を割り算してm2からkm2へ変更
・km2に変更した際の小数点以下は四捨五入で丸め込む

`'text-field': ['format', ['get', '名称'], {'font-scale': 1}, ['concat', "\n(選挙区:", ['get', '選挙区'], ")\n", ["round", ["/", ['get', '面積'], 1000000]], "km2"], {'font-scale': 0.8, 'text-color':"#d7191c"}]`

このように、割り算とroundでの丸め込みは、["round", 数値]、["/", 数値1, 数値2] でそれぞれ記述します。**割り算以外の四則演算も基本的には同じ記述方法**となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122865/5348aca4-fa82-cd29-2e58-6fc80d1d0ef8.png)

上記で数値の大きさはすっきりしましたが、小数点以下を第二桁くらいまでは記載したいですね。
こういう場合は、"round"ではなく"number-format"を使います。"number-format"の方が、より柔軟な設定が可能となります。

`'text-field': ['format', ['get', '名称'], {'font-scale': 1}, ['concat', "\n(選挙区:", ['get', '選挙区'], ")\n", ['number-format', ["/", ['get', '面積'], 1000000], {'locale': "ja-JP", 'min-fraction-digits': 0, 'max-fraction-digits': 2}], "km2"], {'font-scale': 0.8}]`

これは['number-format', "数値", {オプション設定}]の形で定義します。この'number-format'により、桁数が大きい場合に桁区切りカンマが挿入されたり、'max-fraction-digits'で小数点第何桁まで表示するかを定義できます。
また、{'locale': "ja-JP"}は必須でないものの、あったほうが日本向け表記で明示的に統一できるので設定しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122865/5ffde40c-3104-5e9d-cf84-c399af3df30c.png)

## ここまでの記述を整理してみる

段々と'text-field'の記述が長くなってきたので、インデントをつけながら再整理してみます。
ついでに、投票率の計算結果（投票者数 / 有権者数 * 100）も小数点以下第1桁まで表示する形で追記してみました。

```javascript:index.js
'text-field': [
                'format', 
                ['get', '名称'], {'font-scale': 1}, 
                ['concat', 
                    "\n(選挙区:", ['get', '選挙区'], ")", 
                    "\n面積:", 
                    ['number-format', 
                        ["/", ['get', '面積'], 1000000], {'locale': "ja-JP", 'min-fraction-digits': 0, 'max-fraction-digits': 2}
                    ], "km2",
                    "\n投票率:",
                    ['number-format', 
                        ["*", ["/", ['get', '投票者数'], ['get', '有権者数']], 100], {'locale': "ja-JP", 'min-fraction-digits': 1, 'max-fraction-digits': 1}
                    ], "%",
                ], {'font-scale': 0.8}
            ]
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122865/194be41c-9d8d-5dc1-3dd9-f981556b286e.png)

## 数値の大きさによってテキストを色分けしてみる

最後に、ラベルの一部を数値の大きさに応じて色を変えて表示します。

```javascript:index.js
'text-field': [
                'format', 
                ['get', '名称'], {'font-scale': 1}, 
                ['concat', 
                    "\n(選挙区:", ['get', '選挙区'], ")", 
                    "\n面積:", 
                    ['number-format', 
                        ["/", ['get', '面積'], 1000000], {'locale': "ja-JP", 'min-fraction-digits': 0, 'max-fraction-digits': 2}
                    ], "km2",
                ], {'font-scale': 0.8},
                ['concat',
                    "\n投票率:",
                    ['number-format', 
                        ["*", ["/", ['get', '投票者数'], ['get', '有権者数']], 100], {'locale': "ja-JP", 'min-fraction-digits': 1, 'max-fraction-digits': 1}
                    ], "%",
                ], {'font-scale': 0.8, 'text-color':['step', ["*", ["/", ['get', '投票者数'], ['get', '有権者数']], 100], "#2b83ba", 52, '#008000', 54, '#ffa500', 56, "#d7191c"]}
            ]
```
「投票率」の'text-color'設定部分に、'step'にて指定の数値を閾値として色を変える記述を行なっています。
`'text-color':['step', ["*", ["/", ['get', '投票者数'], ['get', '有権者数']], 100], "#2b83ba", 52, '#008000', 54, '#ffa500', 56, "#d7191c"]`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122865/c8ae6999-b7b9-3d4f-b9ed-b830804fbdf1.png)

# おわりに

以上、MapLibreのExpressionsのテキストシンボル表記について色々試した結果の紹介でした。
実際にはここまでラベルの内容だけを情報豊富にするケースは少ないと思いますが、必要に応じて柔軟な記述ができれば表現の幅が広がって便利ですね。