11
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MapLibreAdvent Calendar 2023

Day 10

MapLibre GL JS の expression 事例

Last updated at Posted at 2023-12-09

背景

MapLibre GL JS を使って地図描画をしていると、複雑な条件を使って何かを描画したいことにちょくちょく直面。悲しいことに、MapLibre GL JSのドキュメントを見てもイマイチ使い方が分からないことも多々💦
ということで、それぞれ使い方を試しつつ、まとめてみました。

MapLibre GL JS の細かい書き方については記載していないので、ご了承ください。

事例

その前に、単純なケース

通常、GeoJSONのプロパティ値が以下のようなデータであった場合、

データ
{ "properties": { "data": 20 } }

その値に応じて円の色を変えるといったときは、このように記述する。

円を着色
"circle-color": [
  "step",
  ["get", "data"], // properties の data の値を取得
  "#ffffff", 5, // 5 まで白
  "#ffe100", 10, // 10 まで黄色
  "#76ff00", 15, // 15 まで緑
  "#00fffa", 20, // 20 まで水色
  "#7700ff" // 20 以上 紫
],

オブジェクトの値を取得

データが上記のように単純だと話は早いが、そうもいかないケースもある。
以下のように階層構造を持つオブジェクトになっている場合。

データ
{ "properties": { "data": { "2020": 20, "2021": 15, "2022": 10, "2023": 5 } } }

このようなケースでは、以下のように get を繰り返すことで値を取得することができる。

取得方法
"circle-color": [
  "step",
  ["get", "2020", ["get", "data"]], // get を何度も繰り返す
  "#ffffff", 5,
   <>
],

["get", "data"] で まずは { "2020": 20, .... } をオブジェクトとして取得して、そのオブジェクトから、再度 ["get", "2020"] で値を取り出している。
分かれば単純だけど、いきなりこのケースに直面すると、結構思考が停止する(した)。

See the Pen maplibre template by humo (@humo-tech) on CodePen.

配列の値を取得(at)

オブジェクトとくれば、配列。

データ
{ "properties": { "data": [ 20, 15, 10, 5 ] } }

配列の取得に使うのは at
["get", "data"] で まずは [ 20, 15, 10, 5 ] を配列として取得して、その配列からのN番目の値を ["at", 2] といった形で取得する。

取得方法
"circle-color": [
  "step",
  ["at", 2, ["get", "data"]], // at で 配列の値を取得
  "#ffffff", 5,
   <>
],

See the Pen maplibre expressions (object) by humo (@humo-tech) on CodePen.

配列がproperties に直接書かれている場合(properties)

そんなケースないかもしれないけれど「propertiesに配列が直接定義されている場合」って、値取れるんだろうか?と思って実験してみた。

データ
{ "properties": [ 20, 15, 10, 5 ] }

結論としては、以下の方法で取れました(知らなかった)。

取得方法
["get", "2", ["properties"]

properties は「プロパティをオブジェクトで返してくれる」。配列で返ってきてくれれば at で取得するのだけれど、オブジェクトで返ってくるので、get で取得。
何かほかにも方法知ってる方いれば、教えてくださいm__m

文字列の整形 (format)

地図上に文字を表示したい場合に、ちょっと工夫を加えるケース。

通常は、こんな感じで記述

データ
{ "properties": { "name": "東京", "density": 6409.29 } }
通常の方法
{
  "text-field": ["get", "name"]
}
テキストつなげる
{
  "text-field": ["concat", 
    ["get", "name"], 
    "\n", 
    ["get", "density"]
  ]
}

See the Pen maplibre expressions (concat) by humo (@humo-tech) on CodePen.

それでも十分色々表現できるけれど、2つの項目(この場合 name と density)でフォントサイズ変えたり、色変えたりといったこともしたいケースが出てくる。
そんなときに役立つのが format

記述方法
"text-field":[
    "format",
    ["get", "name"], {"text-color": "#000"}, // name は黒で表記
    "\n", // 改行を繋げて
    ["get", "density"], {"text-color": "#f00", "font-scale": 0.8}, // density は 赤文字で少し小さめに

]

See the Pen maplibre expressions (concat) by humo (@humo-tech) on CodePen.

数値の整形 (number-format)

上の例で、3桁カンマが欲しいな、小数第1位まででいいな、と思うケースでは、number-format で整形ができる。

記述方法
[ "number-format", // number-format を使うと、3桁カンマが入る
  [ "get", "density" ],
  { "min-fraction-digits": 1, "max-fraction-digits": 1 } // min/max-fraction-digits で桁数調整
],

なお、{ "min-fraction-digits": 3 } のように指定すると、データが 29.12 だった場合に 29.120 とゼロ埋め表示してくれるみたいです。

See the Pen maplibre expressions (format) by humo (@humo-tech) on CodePen.

代替データ(coalesce)

symbol 機能で、マップ上に画像を表示する際に、あらかじめ用意した画像以外を呼び出してしまったときの fallback として、coalesce の機能が使えます。
なお、発音難しいこの coalesceは(こあれす?)、 与えられた値を順番に評価していって、最初にnullじゃない値が返ってきたものを使用してくれる ものになります。

データ
properties: { "image": 'kitten1' }

coalesce を使わない普通の書き方↓。この場合、存在しない画像は何も表示されない。

記述方法
"icon-image": ["get", "image"]

coalesce を使ってフォールバック画像を表示する書き方↓。こう書くと、存在しない画像を指定してしまった場合にダミーの画像を替わりに表示してくれます。

記述方法
"icon-image": [
  "coalesce", 
  ["image", ["get", "image"]], 
  "dummy"
]

なお、ここで突如出てくる image は、画像があるかどうかを判定して、あれば画像パスを、なければ null を返してくれるものとなっています。このことにより「存在しない画像(null)の替わりにdummy画像を表示する」という動きをしてくれます。

以下の例で石川県あたりにある「ダミー」画像は、そのようにして表示されたものになっています。

See the Pen maplibre expressions (coalesce) by humo (@humo-tech) on CodePen.

ポリゴン内判定(within)

「特定のポリゴン内にあるかどうか」の判定をしてくれるもの。
これを使うと「ポリゴン内にあるもの」と「そうでないもの」でスタイルを分けることができるようになる。

'circle-color': [
  'case',
  ['within', areaPolygon],
  '#f0f',
  '#0ff'
],
areaPolygon の値
{
"type": "FeatureCollection",
"features": [
  {
    "type": "Feature",
    "geometry": {
      "coordinates": [
        [
          [
            139.44336833463933,
            35.989952696688704
          ],
          [
            139.5366426753509,
            35.57883425456254
          ],
          [
            140.02011467470146,
            35.588948481200916
          ],
          [
            140.29838312448936,
            35.90185508166421
          ],
          [
            139.6268078713709,
            36.12442624848467
          ],
          [
            139.44336833463933,
            35.989952696688704
          ]
        ]
      ],
      "type": "Polygon"
    }
  }
]
}

See the Pen maplibre expressions (at (Array)) by humo (@humo-tech) on CodePen.

例はあげないですけれど、地名表記の言語設定に within を用いれば、例えば「北方領土の地名は必ず日本語で表示する」ということも、できたりします(日本語表記の定義があることは前提)。

変数(let/var)

properties から計算で求めた値を、何度も使いまわしたいときに、let/var を使うことができます。
MapLibre公式サイトからの抜粋になりますが、こんな感じです。

let/var 実装例
'fill-color': [
  // 人口(population)と面積(sq-km)から 人口密度(density)を計算
  'let',
  'density',
  ['/', ['get', 'population'], ['get', 'sq-km']],
  [
    'interpolate', ['linear'], ['zoom'], 
    // zoomレベル8まで
    8,
    [
      'interpolate',
      ['linear'],
      ['var', 'density'], // 人口密度の値で色を変える
      274,
      ['to-color', '#edf8e9'],
      1551,
      ['to-color', '#006d2c']
    ],
    // zoomレベル10まで
    10,
    [
      'interpolate',
      ['linear'],
      ['var', 'density'], // 人口密度の値で色を変える
      274,
      ['to-color', '#eff3ff'],
      1551,
      ['to-color', '#08519c']
    ]
  ]
],

距離(distance)

MapLibre GL JS では、未サポートだった、、、。
(MapBox GL JS ver3 ではサポートされた)
距離に応じた着色とかができるんだと期待しています。

まとめ

MapLibre GL JS の Expressionを色々試してみました。
知らなかった機能もチラホラ。
何かのお役に立てば幸いです。

11
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?