利尻山(オタドマリ沼)


Date/Time: 2018:05:27 16:17:12
Camera: RICOH
Model: GR DIGITAL 4
Exporsure Time: 1/1000
FNumber: 6.3
Aperture Value: 5.3
Focal Length: 6.0

Close

y2blog » MapboxでカスタムWebmap作りに挑戦 #2

4

12

2024

MapboxでカスタムWebmap作りに挑戦 #2

スタイルの中身を覗いてみる


前の記事では、Mapboxで地図をカスタマイズする際にスタイルの内容を理解することが重要だという事を述べたが、今回はこのスタイルの中身についてもう少し深入りしてみようと思う.


スタイルの中身については、Mapboxのドキュメント “Mapbox Style Specification” に詳細が記載されているので、実際にスタイルを弄る場合はこのドキュメントを一通り目を通す必要がある.


上記のドキュメントはスタイルの仕組みを学ぶための物ではなく、仕様書的な位置付けの物なので、初心者向けのドキュメント “Getting Started” から順番に辿っていくと良いだろう.スタイルに関しては “Dive Deeper” の “Map Designe” に、”What is a style?” という項目があるので、それを参照すると良いだろう.


同じドキュメントの中に、”Example of a style and map” という項目があり、簡略化されたスタイルのサンプルを使った実例が記載されているので、それを使って説明して行くことにする.但し、簡略化されたスタイルと言っても、2,072行もあるスタイル定義ファイルなので、この内容を更にシンプルにしたサンプルスタイルを作ることにする.


オリジナルのサンプルスタイルではコロラド州のデンバー近辺の緯度・経度が設定されているが、マップインスタンスを作成する際のオプションパラメータで、緯度・経度、ズームレベルをオーバーライドしている.地図の表示についても日本語ベースに変更し、現在のズームレベルが簡単に把握できるように、地図の左下にズームレベルを表示している.





上記のサンプルのJavascriptコード(抜粋)



    const map = new mapboxgl.Map({
        container: "map", // container ID
        center: [ 139.313561, 35.308448 ],  // starting position [lng, lat]
        zoom: 14.5, // starting zoom level
        language: "ja",  // <=== 日本語表示へ変更
        style: "/MapTest/MapboxTest/Examples/very-simplified-style1.json" // <=== スタイルファイルを指定
    });

    // draw map controls 
    map.addControl( new mapboxgl.NavigationControl(), "bottom-right" );
    const scale = new mapboxgl.ScaleControl({ 
        maxWidth: 120,
        unit: "metric"
    });
    map.addControl(scale);



上記のサンプル地図のスタイルファイルの中身(654行程のJSONデータ)



スタイルデータの構成については、Mapboxのドキュメント "Spec Reference > Root"に詳しい情報が記載されているので、こちらのドキュメントを参照して欲しい.


今回、地図のソースとして "mapbox://mapbox.mapbox-streets-v8"を用いており、Mapboxの地図データの中でも最も汎用的なベクタータイル形式の地図ソースデータセットだ.Mapboxではこれ以外にも多くのベクタータイルセット、ラスタータイルセットが用意されている.タイルセットとしてどのような物があるのかは、"Data coverage and quality > Map data" に詳しい記載がある.


"mapbox-streets-v8"データについては、"Mapbox Streets v8" にどのようなレイヤー構成になっているのか説明されている."admin", "bilding", "landuse", "poi_label", "road", ... などの17レイヤーから構成されている.世界全体では"OpenStreetMap"、”Microsoft Open Maps"などをベースにしたデータを用いているようだが、日本に関してはゼンリンの地図データも利用しているようだ.


Mapboxのベクタータイルデータの仕様については、"Mapbox Vector Tile Specification" (GitHub) で公開されているので、自作のベクター地図データのソースを作成する場合の参考になるだろう.


"mapbox-streets-v8"データの情報を見る限り、フィールド名やアイコンなどの名前などの付け方が、日本の文化にはあまりなじまない物も多く見受けられるが、Mapboxが提供しているデータを利用する際にはこの仕様に応じたスタイルの記述の仕方が必要だ.


データセットのレイヤー構成の話とスタイルのレイヤーとを混同してしまいそうだが、地図の表示で重要な役目を果たすスタイルのレイヤーについてその中身を見ていくことにしよう.


上記のサンプル地図ではスタイルデータ中のレイヤーの項目数を絞っており、

  "background", "landuse", "water", "building", "road", "road-label", "poi-label","place-city-label"

だけを表示する様に構成している.


レイヤーの一般的な構成は、次のようになっている.


"layers": [
    {
      "id": "building",
      "type": "fill",
      "source": "composite",
      "source-layer": "building",
      "minzoom": 15,
      "filter": [
        "all",
        [
        ...
       ],
      "paint": {
        "fill-color": "hsl(34, 35%, 70%)",
        "fill-opacity": [
          "interpolate",
          [
            "linear"
          ],
          [
            "zoom"
          ],
          15.0,
          0.0,
          16.0,
          1.0
        ]
      }
    },
    {
      "id": "road",
      "type": "line",
      "source": "composite",
      "source-layer": "road",
      "minzoom": 5,
         ...

    }
]

"id" はレイヤーの名前で、他のレイヤーの名前と重複しない一意な名前である必要がある."type" はレンダリングのタイプで、"fill"(塗りつぶし)、"line"、"circle", "symbol"(シンボルアイコン)、"raster"(ラスターイメージデータ)、"background"(背景色)、"hillshade"(陰影処理)などがある.


"source"はスタイルで定義されたソースデータセットの名前で、"source-layer"はソースデータセットの中からどのレーヤーデータを拾ってくるのかを指定している.(レイヤータイプが"backgroud"と"slot"の場合は、ソースの指定はない)


この他に、"filter", "layout", "minzoom", "maxzoom", "paint", "slot"などの項目がレイヤーに含まれる.詳細については、"Style Spec > Spec Reference >Layers"を参照して欲しい.


上記のサンプルスタイルのレイヤーの中に "filter" という項目が含まれているが、この"filter"項目が、地図を表示(レンダリング)する際の肝となる項目で、ズームレベルなどの条件によってシンボルのアイコンや地名などを表示したり文字のサイズを変えたりする役目を担っている.地図を分かり易くかつ綺麗に見せるためには、この"filter"項目を適切に設定することが必須となっている.


レイヤーの"filter"項目以外にも、"layout property"や"paint property" の中でも条件に応じて線の太さを変えたり、色を変えたりすることによって、地図の見栄えを最適化する仕組みが備わっている.


この "filter"、"layout property"、 "paint property" の項目野中で、 "[ ]" かぎ括弧(brace) を用いたLISP言語の式の様なものが記述されているが、これは "Expressions" (式)と呼ばれているもので、プロパティーの値などによってどのような処理(オペレーション)を行うのかを指定している.一般的な四則演算の数式やプログラミングの論理演算式とはかなり様相が異なるので、このドキュメントの仕様の説明を読んでもピンとこないかもしれない.


難解な仕様書よりも、"Get started with Mapbox GL JS expressions" というチュートリアルドキュメントを読む方が分かり易いので、最初はこちらのドキュメントを読むと良いだろう.


このチュートリアルに、"π*3^2" という計算式を Mapbox流の式で表した例が載っている.



   ["*", ["pi"], ["^", 3, 2]] 


年配の輩であれば、この式を見ると逆ポーランド記法で計算していた昔のHPの電卓を思い出すのではないだろうか.(歳がバレてしまうね)


サンプルスタイルのレイヤーの中から "building"(143〜184行目)を例に具体的に式の中身を見ていくことにする.


先ずは、"filter" の式、


      "minzoom": 15,
      "filter": [
        "all",
        [
          "!=",
          [
            "get",
            "type"
          ],
          "building:part"
        ],
        [
          "==",
          [
            "get",
            "underground"
          ],
          "false"
        ]
      ],


であるが、このままでは分かり難いので、式の部分を1行で表すことにする.



 [ "all", [  "!=", [  "get",  "type" ],  "building:part" ],  [  "==", [  "get", "underground" ], "false" ] ]

最初の演算子(オペレータ)は "all" であり、その項(オペランド)として、[ "!=", [ "get", "type" ], "building:part" ] と [ "==", [ "get", "underground" ], "false" ] の2つが与えられていることが分かるだろう.


1つ目のオペランド自体も1つの式となっていて、そのオペレータがブーリアン演算子の "!=" で、最初のオペランドは [ "get", "type" ] という式になっている."get" オペレータはその名前から容易に推測できると思うが "type" という項目の値を取り出している.2つ目のオペランドは文字列のリテラルで、"building:part" となっている.つまり、データの"type" 項目の値を読み取って、その値が "building:part" でなければ "true" を返すことになる.


同様に "all" 演算子の2つ目のオペランドの [ "==", [ "get", "underground" ], "false" ] は、データ中の"underground" の値が "false" でなければ "true" を返すということが分かるだろう.


最終的に "all"演算子が評価されて、その結果をブーリアン値("true"/"false")で返されることになる.この値が"true" であれば描画の対象となり、"false"の場合は描画されないということになる.


データソースの "mapbox-streets-v8" の "building" レイヤーの"building" データ構成に説明があるように、建物の一部分のデータの場合は、"building:part" というデータの項目が "true" で、地下構造物の場合は "underground" が"true" になるようだ.


実際のソースデータの内容が分かっていなければ正確な"filter"の式は書けないが、スタイルのレイヤーを弄る際には、"filter"の式を調整することで、地物の描画のON/OFFのコントロールが可能であることが理解できただろうか.


このスタイル定義データの"building" レイヤーには、"paint"項目でも "fill-opacity"(塗りつぶし色の透明度)の指定に式が記載されている.



      "paint": {
        "fill-color": "hsl(34, 35%, 70%)",  // 塗りつぶし色
        "fill-opacity": [
          "interpolate",
          [
            "linear"
          ],
          [
            "zoom"
          ],
          15.0,
          0.0,
          16.0,
          1.0
        ]
      }


先ほどと同様に、1行で "fill-opacity" の式を記載すると、


    "fill-opacity": [ "interpolate", [ "linear" ], [ "zoom" ], 15.0, 0.0, 16.0, 1.0 ]

となる."interpolate" は補間のオペレータで、最初のオペランドが補間の方法で、この場合は "linear"(線形補間)を選んでいる.他に"exponential"(指数補間)、"cubic-bezier"(3次ベジエ補間)が使える.


2つ目のオペランド [ "zoom" ] は入力値で、この場合は現在のズームレベル値が与えられる.3つ目以降の数値は入力レベルに対する出力値の対の組み合わせを並べている.この場合は、単純にズームレベル値が15.0未満の場合は 0.0, ズーム値が16.0以上は 1.0、 ズーム値が 15.0 〜 16.0 の間は、線形補間で0〜1.0の値が設定されることになる.従ってズーム値が 15.7 の場合は "fill-opacity" の値が 0.7 に設定されることになる.


上記の例では入出力値の対は2組だけだが、複数の組を指定できるので、折れ線グラフのような出力設定も可能だ.


上記の地図サンプルで実際にズームレベルを変化させて、建物の輪郭がどのように描写されるのか試して見て欲しい.ズームレベルの増減は、地図画面右下のコントロールボタンをの"+"/"-"をクリックするたびに 1.0 単位で増減する.マウスのズームコントロールでは、連続したズーミングを行うことが可能だ.


初期のズームレベルは14.5 に設定してあるので、建物の輪郭は表示されていないが、ズームレベルが15.0を超えた時点で、建物の輪郭が現れ、ズームレベルに応じた透明度で建物の枠内が塗りつぶされていることだろう.


恐らく、多くの人はこのサンプル地図を見たときに何か足りないような気がしているのではないだろうか.このサンプル地図の場所は神奈川県の大磯近辺なので、普通ならJR東海道線の線路が記載されていなければならない筈だが、この地図では完全に抜け落ちてしまっている.


勿論、マップデータソースの"streets-v8"中には鉄道の線路データが含まれているので、これを地図に反映せせるには単純にスタイルのレイヤー中に線路データに関するレイヤーを追加すれば良い.


線路データに関するレイヤーの情報は、前回Mapbox Studioで作成したカスタムスタイル "Streets" のJSONファイルに含まれているので、その情報を今回のスタイルサンプルに付け加えることにする.


線路データに関するレイヤー情報は、次のようになっている.



        {
            "id": "road-rail",
            "type": "line",
            "metadata": {
                "mapbox:featureComponent": "transit",
                "mapbox:group": "Transit, surface"
            },
            "source": "composite",
            "source-layer": "road",
            "minzoom": 13,
            "filter": [
                "all",
                [
                    "match",
                    ["get", "class"],
                    ["major_rail", "minor_rail"],
                    true,
                    false
                ],
                ["match", ["get", "structure"], ["none", "ford"], true, false]
            ],
            "paint": {
                "line-color": [
                    "interpolate",
                    ["linear"],
                    ["zoom"],
                    13,
                    "hsl(35, 25%, 82%)",
                    16,
                    "hsl(220, 4%, 71%)"
                ],
                "line-width": [
                    "interpolate",
                    ["exponential", 1.5],
                    ["zoom"],
                    14,
                    0.5,
                    20,
                    1
                ]
            }
        },
        {
            "id": "road-rail-tracks",
            "type": "line",
            "metadata": {
                "mapbox:featureComponent": "transit",
                "mapbox:group": "Transit, surface"
            },
            "source": "composite",
            "source-layer": "road",
            "minzoom": 13,
            "filter": [
                "all",
                [
                    "match",
                    ["get", "class"],
                    ["major_rail", "minor_rail"],
                    true,
                    false
                ],
                ["match", ["get", "structure"], ["none", "ford"], true, false]
            ],
            "paint": {
                "line-color": [
                    "interpolate",
                    ["linear"],
                    ["zoom"],
                    13,
                    "hsl(35, 25%, 82%)",
                    16,
                    "hsl(220, 4%, 71%)"
                ],
                "line-width": [
                    "interpolate",
                    ["exponential", 1.5],
                    ["zoom"],
                    14,
                    4,
                    20,
                    8
                ],
                "line-dasharray": [0.1, 15],
                "line-opacity": [
                    "interpolate",
                    ["linear"],
                    ["zoom"],
                    13.75,
                    0,
                    14,
                    1
                ]
            }
        },




JR東海道線のラインが描かれるようになったが、このスタイルのサンプルのアイコンのシンボルはいまいちなので、Mapbox の"Streets-V12"スタイルで使われている標準的なシンボルアイコンセットに変更してみることにする.


シンボルアイコンの画像データセットをスタイルの中で扱う際は "Sprite と呼ばれる仕組みでアイコン画像を管理している.このスタイルサンプルで使われているSpriteデータの中身は、"Streets-V12"スタイルで使われている標準的なものとは違うようなので、前回の記事で"Streets-V12"スタイルからコピーしたユーザ定義のカスタムスタイル "Street" から、"Styles API"を用いて抽出したものと置き換えることにする.


"Sprite"の取り扱い方法については別途説明する予定だが、今回用意した"Sprite"データは、

[ json定義ファイル ]
  "https://y2works.net/MapTest/MapboxTest/Examples/sprites/sprite.json"
  "https://y2works.net/MapTest/MapboxTest/Examples/sprites/sprite@2x.json"

[ png画像ファイル ]
  "https://y2works.net/MapTest/MapboxTest/Examples/sprites/sprite.png"
  "https://y2works.net/MapTest/MapboxTest/Examples/sprites/sprite@2x.png"
に置いてある.


今回の"Sprite"データの変更により、アイコンの名前の付け方が一部異なっているようなので、スタイルファイル中でシンボルアイコンのレイヤー "poi-label" を次のように修正している.



【元のスタイル 】
  "sprite": "mapbox://sprites/mapbox/cjf4m44iw0uza2spb3q0a7s41/4ju5zyizue7d2qr71g29q47y7",

   {
      "id": "poi-label",
      "type": "symbol",
      "source": "composite",
      "source-layer": "poi_label",
      "minzoom": 6,
      "filter": [
        "<=",
          ... 省略
        ],
        "icon-image": [
          "concat",
          [
            "get",
            "maki"
          ],
          "-11"
        ],
        "text-max-angle": 38,
        "text-font": [
          ... 省略
    },

【修正したスタイル 】
  "sprite": "https://y2works.net/MapTest/MapboxTest/Examples/sprites/sprite",

   {
      "id": "poi-label",
      "type": "symbol",
      "source": "composite",
      "source-layer": "poi_label",
      "minzoom": 6,
      "filter": [
        "<=",
          ... 省略
        ],
        "icon-image": [ "get", "maki" ],   <===  "maki" に変更
        "text-max-angle": 38,
        "text-font": [


 "maki" は、アイコンセットの名前で、地図上に表示されるPOIアイコンの汎用的なイメージライブラリの名前ということのようだ.大磯駅で使われているのアイコンの名前は "rail" で、カメラのアイコンの名前は "attraction" なので、大部分のアイコンは日本の文化や慣習とはちょっとずれている.日本固有のシンボルである郵便局 "〒" や学校の "文" アイコンなども含まれてはいるが、このアイコンセットだけでは殆ど実用にならないのではないかと思う.


https://labs.mapbox.com/maki-icons/



Mapbox の地図の要である、スタイルの働きや仕組みがある程度理解できたであろうか.実際にユーザがスタイルをカスタマイズする場合は、既存のスタイルに"Mapbox Studio" などを使ってカスタマイズをすることが多いが、このスタイルの取り扱いの基本を理解していていて、ある程度プログラミングの素養があればカスタマイズ地図を作成することは技術的にはそれほど難しくはないだろう.


勿論、素晴らしい地図を作るには技術だけでは駄目で地図作りのセンスが問われることは言うまでもない.Google やAppleの技術力やリソースを駆使しても、最初はなかなか思うような地図作りができていなかったのは、この地図作りの勘所(センス)が足りなかったと言うことだろう.


今回はスタイルの中身を把握しやすいように、敢えて一つのスタイルファイルを事前に作成してスタイルの振る舞いを追っていったが、実際に "Mapbox GL" を使ってWEB地図を作成する場合には、ある程度出来上がったベースマップのスタイルを作成しておき、そのベースマップに追加したい地物やインタラクティブに表示を変えたい場合には、"Mapbox GL"で用意されているメソッドを使って、スタイルの中身をダイナミックに変更して、地図の表示を変えることが一般的なやり方だ.


今回の記事の本題である国土地理院のベクタータイルデータを用いた地図をMapboxで取り扱うための方法について説明する前に、もう少し"Mapbox GL"の取り扱いについて説明が必要かと思われるので、次回は"Mapbox GL"でどのように地図のスタイルを操作するのかについて説明することにする予定だ.


Calendar

June 2024
S M T W T F S
 1
2345678
9101112131415
16171819202122
23242526272829
30  
  • Blogroll

  • Meta