小樽 赤岩海岸遊歩道


Date/Time: 2013:09:23 12:33:06
Camera: Panasonic
Model: DMC-LX2
Exporsure Time: 1/500
FNumber: 6.3
Aperture Value: 5.3
Focal Length: 10.0

Close

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

4

09

2024

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

Mapbox GLを使いこなすために


このサイトではGoogle Mapsのフレームワークの利用を止め、ベクター系のWEBマップフレームワークである “Mapbox GL” へ切り替えている.これに伴い、ベースとなるマップデータもこれまでの国土地理院のラスターイメージタイルからベクタータイル形式へと変更している.


“Mapbox GL”はベクタータイル形式の地図データを扱うために最適化されており、Web GLに対応した最近のWEBブラウザであればかなり高速にベクター形式のデータをレンダリングして表示することが可能だ.


国土地理院が実験的に提供している『地理院地図ベクター(試験公開)』や現在のYahoo! Japanマップもこの “Mapbox GL” を使って構築されており、ベクター形式のマップデータを扱う事が可能なWEBマップ界ではメジャーな存在だ.現在ではGoogle Mapsもベースとなるシステムはベクター形式に対応したフレームワークを使っているようだが、ベクター形式のデータを扱うようなAPIは公開されていない模様.残念ながらGoogle Mapsでは”Mapbox GL” のように外部のベクタータイル形式のデータを表示することはできないようだ.


“Mapbox GL”はとても機能が豊富でユーザが自由に地図の表示をカスタマイズすることが可能なフレームワークとなっており、APIに関する様々なドキュメントが用意されている.Mapboxが用意している “streets-v12” などの標準的なマップスタイルを使っている分には、とても簡潔なJavascriptコードを記載するだけで済むが、少し凝った事をしようとするとAPIドキュメントをかなり読み込まないとならないだろう.


Mapboxのフレームワークを用いて地図を作成&公開するためには、Mapboxのサイトでユーザアカウントを作成し、取得したユーザのアクセストークンをJavascriptコードに埋め込まなければならないが、この辺の手続きは別途簡単に紹介する予定ではあるが、この記事を参考にするような人たちは既にGoogle MapsなどのWEBマップサービスに慣れ親しんでいる人たちだと思うので、アクセストークンのセキュリティー対策などについてはここでは言及しない.


この記事では、最終的に “Mapbox GL” を使って、現在試験公開中の国土地理院の地形図のベクタータイルセットをベースマップとして表示させ、”Mapbox GL”の高度な機能を駆使して、魅力的なWEBマップを作成する方法について説明する予定だ.



“Mapbox GL”の要である スタイル “style”について


“Mapbox GL”では “style” というJSONで記載された非常に重要なデータを内部に持っており、地図の描画は全てこのスタイルで定義された情報によって制御されている.地物などの情報はスタイルの中でソースデータとして定義されており、ソース中の様々な地物データからどの地物データを描画するのか、どの様な順番やレイヤーで描画するのかというような地図描画の基本となる情報がこの中で定義されている.スタイルを一言で表すと『地図の設計図』と言ったところだろうか.


このスタイルをユーザが作成するのは大変骨の折れる作業が必要なのだが、Mapboxが用意しているスタイルのテンプレートを用いる場合はマップオプションに ” style: ‘mapbox://styles/mapbox/xxxxxxxxxx’ ” という行を指定するだけで、簡単に地図のスタイルを指定することが可能だ.


Mapboxが予め用意しているスタイルとしてどのような物があるのかは、”Mapbox styles” を参照すると良いだろう.下記の例ではスタイルの指定方法として、Mapbox独自のURL記法を用いているのでMapboxに慣れ親しんでいないユーザにとっては謎のURL記法かも知れない.このちょっと不思議なURL記法はMapboxのクラウドサーバ上に置かれているスタイルのテンプレートファイルへのシンボリックリンク(エイリアス)と解釈しておけば良いだろう.


実際には、”How to use a style” の説明にあるように、Styles APIの呼び出しに置き換えているだけのようだ.


“mapbox://styles/mapbox/streets-v12” の中身を直接覗いて見たい場合には、Styles API を使って取り出す事が可能だ.


curl -o "streets-v12.json" "https://api.mapbox.com/styles/v1/mapbox/streets-v12?access_token=------------"


スタイルの指定方法として、マップオブジェクトの生成時のオプションパラメータ中に直接JSONテキストを埋め込むこともできるが、可読性に欠けるので大抵は、 style: “https://xxxxxx/mystyle.json” や style: “./xyz/mystyle.json” のようなURI形式で参照させるのが良いだろう.


Mapboxのアカウントを作成すると、Mapboxのユーザ領域上にユーザが自分で作成したスタイルファイルを置くことも可能なので、この場合は style: “mapbox://styles/{username}/{style-id}” という記述が可能だ.


スタイルファイル内ではベースとなるマップについての情報だけを記載し、それ以外にユーザが独自にマップ上へ追加する地物などの情報やスタイル情報の変更は、MapboxのAPIを使ってダイナミックにスタイルデータに反映させ方法が一般的なやり方だろう.


Mapboxの標準的なスタイルファイルを使った場合のコード例

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Display a default styled map on a browser</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v3.2.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v3.2.0/mapbox-gl.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 800px;  height: 600px; }
</style>
</head>
<body>
<div id="map"></div>
<script>
    mapboxgl.accessToken = 'pk.ey------------------------------------------------------';
    const map = new mapboxgl.Map({
        container: 'map', // container ID
        center: [ 139.313561, 35.311448 ],  // starting position [lng, lat]
        zoom: 15, // starting zoom level
        //style: 'mapbox://styles/mapbox/standard', // style URL
        //style: 'mapbox://styles/mapbox/streets-v12', // style URL
        //style: 'mapbox://styles/mapbox/outdoors-v12', // style URL
        style: 'mapbox://styles/mapbox/satellite-streets-v12', // style URL
        language: 'ja'
    });
</script>
</body>
</html>

Standard Street Map
デフォルトの “standard”
Outdoor Map
“outdoors-v12”



“Mapbox GL”のJavascripyコードが簡潔に記載可能なのは、このスタイルというJSON形式のデータセットが、地図の描画に関する殆どの情報を保持しているからで、”Mapbox GL”を使いこなすためにはこのスタイルを理解することが非常に重要な鍵となっている.


このスタイルについての詳細は、”Mapbox Style Specification” というMapboxのドキュメントに記載されているが、とても複雑なので先ずは一通り目を通しておいて、必要に応じて各項目の内容を追って行くのが良いだろう.とりあえず、”Spec Reference” に記載されている項目の中から、”Sources” と”Layers”の内容を理解できれば、”Mapbox GL” を用いてカスタマイズした地図を表示させることができるようになるだろう.


このスタイル定義ファイルの中身を一から自分で作成するのは大変面倒な作業が必要になるのであまり現実的ではない.幸いなことに、Mapboxではユーザが自分で地図をカスタマイズするためのWebGUIベースのツール “Mapbox Studio” が提供されている.


この”Mapbox Studio”を使えば、スタイルファイルを自分で一から記述することなく、WEBブラウザの画面で実際の見栄えを確認しながら地図スタイルの変更が可能だ.例えば高速道路部分の色をグレーから淡い黄色に変えるというようなカスタマイズが簡単にできる.カスタマイズしたスタイルは自分専用のスタイルとして利用可能となるだけではなく、一般に公開(public share)することも可能なので他のユーザにカスタマイズしたスタイルの地図を提供することも可能だ.


“Mapbox Studio”では、Mapboxが用意しているスタイルのテンプレートをユーザの領域にコピーすることが可能なので、コピーしたテンプレートをベースにカスタマイズを加えるというのが、最も簡単なカスタムスタイルの作成方法だろう.”Mapbox Studio”は殆ど触ったことがないので、操作方法やどのようなことができるのかいまいち理解できていないが、国道の色を変更してみた様子を下記に示す、



MB Studio 1
Mapbox Studio で”streets-v12″をコピーした “Streets” に手を加えてみる

MB Studio 2
道路に関するレイヤーを探し出し色を変えてみる

MB Studio 3
国道1号と国道134号の塗りつぶし色が赤系統に変更された

MB Studio 4
最後に変更確定作業(Publish)を行う



作成したスタイルはstyleオプションでMapbox形式のURLで呼び出し可能

<script>
    mapboxgl.accessToken = 'pk.ey------------------------------------------------------';
    const map = new mapboxgl.Map({
        container: 'map', // container ID
        center: [ 139.313561, 35.311448 ],  // starting position [lng, lat]
        zoom: 15, // starting zoom level
        style: "mapbox://styles/xxxxxxxxxx/cluq-----------------", // my custom style "Streets"
        language: 'ja'
    });
</script>

Map with Streets Style
カスタマイズした ”Streets” を用いて地図を表示してみる

Downloading the style file bundle
カスタマイズしたスタイルファイル ”Streets” のバンドルデータをダウンロードする


ユーザが作成したカスタムスタイル “Streets”は手元のPCにZIP圧縮された形でダウンロードすることができ、このZIPファイルを解凍するとスタイルを定義したJSONデータと”Sprite“形式のイメージデータを作成するためのSVG形式の個別イメージデータが含まれている.


“style.json” ファイルが今回作成した “Streets” のスタイルデータで、その中身は7,000行弱もある複雑なJSONデータとなっていた.中身は膨大なので一部を抜粋して、その中身を覗いてみることにする.


{
    "version": 8,
    "name": "Streets",
    "metadata": {
        "mapbox:type": "default",
        "mapbox:origin": "streets-v12",
        "mapbox:sdk-support": {
            "js": "3.3.0",
            "android": "11.2.0",
            "ios": "11.2.0"
        },
        "mapbox:autocomposite": true,
        "mapbox:groups": {
            "Land & water, land": {
                "name": "Land & water, land",
                "collapsed": true
            },
            "Land & water, water": {
                "name": "Land & water, water",
                "collapsed": true
            },
            "Land & water, built": {
                "name": "Land & water, built",
                "collapsed": true
            },
            "propConfig": {
                "road-network": {
                    "colorBase": "hsl(20, 20%, 95%)",
                    "colorRoad": "hsl(0, 0%, 100%)",
                    "colorRoadOutline": "hsl(220, 20%, 85%)",
                    "colorMotorwayTrunk": "hsl(340, 50%, 70%)",
                    "colorRoadLabel": "hsl(0,0%, 0%)",
                    "turningCircles": true,
                    "roadMarkings": true
                },

         ...  (省略)

            "Road network, surface": {
                "name": "Road network, surface",
                "collapsed": false
            },

        ...  (省略)

    },
    "center": [-92.25, 37.75],
    "zoom": 2,
    "sources": {
        "composite": {
            "url": "mapbox://mapbox.mapbox-streets-v8,mapbox.mapbox-terrain-v2,mapbox.mapbox-bathymetry-v2",
            "type": "vector"
        }
    },
    "sprite": "mapbox://sprites/xxxxxxx/cluq--------------------------9i2",
    "glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf",
    "projection": {"name": "globe"},
    "layers": [
        {
            "id": "land",
            "type": "background",
            "metadata": {
                "mapbox:featureComponent": "land-and-water",
                "mapbox:group": "Land & water, land"
            },
            "layout": {},
            "paint": {
                "background-color": [
                    "interpolate",
                    ["linear"],
                    ["zoom"],
                    9,
                    "hsl(20, 20%, 95%)",
                    11,
                    "hsl(20, 18%, 91%)"
                ]
            }
        },
        {
            "id": "landcover",
            "type": "fill",
            "metadata": {
                "mapbox:featureComponent": "land-and-water",
                "mapbox:group": "Land & water, land"
            },
            "source": "composite",
            "source-layer": "landcover",
            "maxzoom": 12,
            "filter": [
                "match",
                ["get", "class"],
                ["scrub", "grass"],
                ["step", ["zoom"], true, 8, false],
                true
            ],

          ...  (省略)

        },
        {
            "id": "road-motorway-trunk",
            "type": "line",
            "metadata": {
                "mapbox:featureComponent": "road-network",
                "mapbox:group": "Road network, surface"
            },
            "source": "composite",
            "source-layer": "road",
            "minzoom": 3,
            "filter": [
                "all",
                [
                    "step",
                    ["zoom"],
                    [
                        "match",
                        ["get", "class"],
                        ["motorway", "trunk"],
                        true,
                        false
                    ],
                    5,
                    [
                        "all",
                        [
                            "match",
                            ["get", "class"],
                            ["motorway", "trunk"],
                            true,
                            false
                        ],
                        [
                            "match",
                            ["get", "structure"],
                            ["none", "ford"],
                            true,
                            false
                        ]
                    ]
                ],
                ["==", ["geometry-type"], "LineString"]
            ],
            "layout": {
                "line-cap": ["step", ["zoom"], "butt", 13, "round"],
                "line-join": ["step", ["zoom"], "miter", 13, "round"]
            },
            "paint": {
                "line-width": [
                    "interpolate",
                    ["exponential", 1.5],
                    ["zoom"],
                    3,
                    0.8,
                    18,
                    30,
                    22,
                    300
                ],
                "line-color": [
                    "step",
                    ["zoom"],
                    [
                        "match",
                        ["get", "class"],
                        "motorway",
                        "hsl(330, 73%, 64%)",
                        "trunk",
                        "hsl(350, 66%, 54%)",
                        "hsl(20, 18%, 100%)"
                    ],
                    9,
                    [
                        "match",
                        ["get", "class"],
                        "motorway",
                        "hsl(330, 63%, 70%)",
                        "hsl(350, 67%, 70%)"
                    ]
                ],
                "line-opacity": [
                    "interpolate",
                    ["linear"],
                    ["zoom"],
                    3,
                    0,
                    3.5,
                    1
                ]
            }
        },

         ...  (省略)

    ],
    "created": "2024-04-08T08:46:08.457Z",
    "modified": "2024-04-08T08:46:13.325Z",
    "id": "cluq------------------"
    "owner": "xxxxxxxxx",
    "visibility": "private",
    "protected": false,
    "draft": false
}


この膨大なスタイルデータの中身を理解して、テキストベースで変更を加えるのは現実的な作業ではないが、このスタイルデータの構造を読み解いていくと、”Mapbox GL” の各種APIのメソッドやオプションパラメータによってユーザが設定した項目は、スタイルデータに対する修正や追加、条件の変更や数値パラメターの変更であることが理解できるだろう.


“streets-v12″スタイルを使って地図を表示すると、ズームレベルに応じて地物が表示されたりされなかったりするが、これはマップデザイナーがどのズームレベルでも見やすく適切に表示されるように、このスタイルデータを細かくコントロールしているからだ.


上記の”streets-v12″スタイルデータ中に、”layers” というプロパティがあり、その中に”filter”というオプション項目がある.この”filter”は、ソースデータに含まれる地物データの中から目的の地物をフィルタリングして抽出するためのもので、”paint”オプションで抽出された地物データをズームレベルに応じて色や線の描画方法などを使い分けていることが判るだろう.


“Mapbox GL”を使いこなすためには、このスタイルという仕組みを理解することが鍵となっているが、”streets-v12” はMapboxの顔とも呼ぶべき一般向けの汎用的な地図なので、内容てんこ盛りの巨大なデータセットとなっているので、このスタイルの内容を完全に理解することは無理だろう.


スタイルの仕組みを理解するには簡単なスタイルの事例を使った方が分かり易いので、次の記事ではもう少しスタイルの仕組みについて掘り下げて説明しようと思う.