続・O-map風ウェブ地図作成 20250104

↓の続きです。

ある程度形になってきたので、今朝GitHub Pagesで地図を公開しました。

※ 画面に何も表示されない場合は、F5キーでページを再読み込みしてみてください。

やったこと

箇条書きで、これまでの進捗です。

プラグイン化

MapLibre GL JS の機能拡張部分を独立した機能ごとに分割し、プラグインとしてGitHubで公開しました。これらを組み合わせて地図上で活用しています。

スタイル記法の開発

OpenOrienteering Mapperを参考に、色の優先度に基づいてレイヤを重ねるためのスタイル記法を開発しました。
また、記法の変換ツールやリアルタイムエディタも作成しました(後述)。

開発環境の整備

  • Gitの導入

  • VS Codeプラグインの導入

    • ローカルWebサーバ、コードの構文チェック、整形、自動補完等

  • Jira での課題管理

  • テスティングフレームワーク(Jest)の導入

  • 開発規約の調査と導入

GitHub公開

  • リポジトリごとのREADME作成

    • ※ 一部はまだ作成途中。

  • プラグインの動作デモをGitHub Pagesで作成

  • ライセンスの決定

  • データソースごとに地図を複数パターン用意

    • 日本国内用は、国土地理院ベクトルタイル・標高タイルと農林水産省筆ポリゴンを使って地図の正確さと印刷・再配布可能であることを両立させた。

    • 海外用は、印刷・再配布可能な精度の高い標高タイルが見つからなかったので、表示だけの等高線ありバージョン(MapTilerのTerrain RGBを利用)と印刷・再配布可能な等高線なしバージョンにわけた。

検証: 縮尺の正確さについて

縮尺が表示されているけど本当に合っているの? は気になるところだと思うので、検証してみます。

1 地図上で、任意の陸上競技場に移動し、縮尺・ページサイズ・DPIを指定して画像エクスポートします。

2 PurplePenで同じ縮尺・ページサイズ・DPIを指定して画像を開きます。

3 メニューバーからコース > コースを追加します。

4 陸上競技場のスタートっぽいところにスタートコントロール、ゴールっぽいところにフィニッシュコントロールを起きます。

5 スタートからフィニッシュまでの距離が100メートルと表示されました。これで、地図と実際の距離の縮尺が正しく設定されていると判断できます。

ISOMizer の紹介

これまで、MapLibre GL JS のスタイル設定を行う際、スタイルJSONを直接編集していました。しかし今回、入力を簡便化するためにyamlフォーマットを用いた記法と、yaml からスタイルjsonの変換ツールを自作しました。このツールを使ってスタイル設定を行う例を紹介します。

以下は、前回の「 続・O-map風ウェブ地図作成 20240705」 で使用したスタイルjsonです。これを、今回のyaml記法を用いて生成していきます。

  • スタイルJSON

        {
          "id": "509-線路-黒部分-osm",
          "type": "line",
          "source": "openmaptiles",
          "source-layer": "transportation",
          "filter": [
            "all",
            ["==", "class", "rail"],
            ["!=", "brunnel", "tunnel"]
          ],
          "layout": {
            "line-join": "miter",
            "line-cap": "butt"
          },
          "paint": {
            "line-color": "#000000",
            "line-width": 1.4
          }
        },
        {
          "id": "509-線路-白部分-osm",
          "type": "line",
          "source": "openmaptiles",
          "source-layer": "transportation",
          "filter": [
            "all",
            ["==", "class", "rail"],
            ["!=", "brunnel", "tunnel"]
          ],
          "layout": {
            "line-join": "miter",
            "line-cap": "butt"
          },
          "paint": {
            "line-color": "#ffffff",
            "line-width": 1.0,
            "line-dasharray": [4, 6]
          }
        },

ISOMizer で使用するファイルごとに最小の設定を入れていきます。

design-plan.yml

どの記号でどの地物を表示するかを定義します。

sources:
  openmaptiles:
    type: vector
    url: https://tiles.openfreemap.org/planet

rules:
  - symbol_id:
      - 509-1-railway-centerline
      - 509-2-railway-casing
    links: 
      - source: openmaptiles
        source-layer: transportation
        filter:
          - all
          - - "=="
            - class
            - rail
          - - "!="
            - brunnel
            - tunnel
  • sources:
    表示に必要なデータソースを定義します。これはJSON形式に変換するとそのままスタイルJSONのsource部分に対応します。YAMLの文法(例えばハイフンの有無による配列とオブジェクトの区別など)を正確に守る必要があります。

  • rules:
    記号パレットで使用する記号IDを定義します。以下のような特徴があります:

    • symbol_id: 記号パレットでの記号IDを指定。複数の記号IDを配列として記述可能です。

    • links: ソース、ソースレイヤ、フィルタ条件を指定。複数の地物を同じ記号に割り当てる場合は配列で記述します。

symbol-palette.yml

記号IDごとのレイヤのデザインを記述します。

symbol-palette:
  - symbol_id: 509-1-railway-centerline
    type: line
    layout:
      line-join: miter
      line-cap: butt
    color-key: upper.white
    line-width(mm): 0.3
    line-dasharray(mm): 
      - 1.0
      - 1.5   
  - symbol_id: 509-2-railway-casing
    type: line
    layout:
      line-join: miter
      line-cap: butt
    color-key: upper.black
    line-width(mm): 0.7
  • symbol_id: design-plan.yml で地物に対応する記号IDとして使用される識別子です。

  • type: スタイルJSONのレイヤのtype要素に対応します(例: line, fill, symbolなど)。

  • layout: スタイルJSONのレイヤのlayout要素に対応し、描画設定を定義します。

  • color-key: カラーパレット内の色をキーで指定します。

  • line-width(mm): 線の幅を地図上のミリメートル単位で指定できるようにしています。スタイルJSONのpaint要素を直接使い、ピクセル単位で入力することも可能です。

color-palette.yml

カラーキーごとの色を記述します。これについては、全体を見せたほうがわかりやすいと思うので全体を載せます。

color-palette:
  - lower:
    - yellow_green:
        hex: "#9BBB1D"
        description: "Yellow green for out-of-bounds area"
    - white:
        hex: "#FFFFFF"
        description: "White for forest"
    - green_30%:
        hex: "#C5FFC4"
        description: "Green 30% for vegetation, slow running"
    - green_60%:
        hex: "#8AFF74"
        description: "Green 60% for vegetation, walk"
    - green_100%:
        hex: "#3FFF17"
        description: "Green for vegetation, fight"
    - yellow_50%:
        hex: "#FFDD9A"
        description: "Yellow 50% for rough open land"
    - yellow:
        hex: "#FFBA35"
        description: "Yellow for open land"
  - middle:
    - brown:
        hex: "#D25C00"
        description: "Brown for contour lines and land features"
  - upper:
    - green:
        hex: "#3FFF17"
        description: "Green for point vegetation features"
    - blue_50%:
        hex: "#4DFFFF"
        description: "Blue for shallow water"
    - blue:
        hex: "#00FFFF"
        description: "Blue for water features"
    - brown_50%:
        hex: "#E8AD80"
        description: "Brown 50% for road and paved area"
    - black_25%:
        hex: "#BFBFBF"
        description: "Black 25% for bare rock"
    - black_20%:
        hex: "#CCCCCC"
        description: "Black 20% for canopy"
    - black_65%:
        hex: "#595959"
        description: "Black 65% for building and stairway"
    - black:
        hex: "#000000"
        description: "Black for black map symbols"
    - white:
        hex: "#FFFFFF"
        description: "White for railway"
  - overlay:
    - black:
        hex: "#000000"
        description: "Black for technical symbols and optional additional information"
    - purple:
        hex: "#A824FF"
        description: "Purple for course planning symbols" 

上に書いた色からレイヤが順に重ねて表示されます。このファイルで色の順番を並び替えればレイヤの重なる順番も変更されます。
レイヤIDにここでの順番を接頭辞でいれることで並び替えに使っています。
今回の例では、upperが配列の[2] (ゼロからはじまるので)、black と white がそれぞれ 7 と 8 で、 2-7、2-8 が接頭辞になります。同じカラーキーの中での順番は見た目に影響しないので並べ替えません。同じ色だけど別の場所に並べたいときは、同じカラーコードを持つカラーキーを複数用意して別の場所に配置します。(例だと、lower.whiteとupper.whiteなど)

プロジェクト構成ファイル: project-config.yml

project-config.ymlは、プロジェクト全体の設定を管理するファイルです。このファイルで、スタイル設定に必要な各種リソースファイルのパスを指定します。

resources:
  design-plan:
    file: "./design-plan.yml"
  symbol-palette:
    file: "./palettes/symbol-palette.yml"
  color-palette:
    file: "./palettes/color-palette.yml"
  svg-palette:
    file: "./palettes/svg-palette.yml"

表示

あとは、project-config.ymlのファイルパスを引数に入れてCDNからインポートしたISOMizerをjavascriptで実行すれば、ISOMizerがyamlをスタイルJSONに変換して、地図が表示されます。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ISOMizer beta demo</title>
    <link
      href="https://unpkg.com/maplibre-gl/dist/maplibre-gl.css"
      rel="stylesheet"
    />
    <script src="https://unpkg.com/maplibre-gl/dist/maplibre-gl.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/js-yaml@4.1.0/dist/js-yaml.min.js"></script>
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    <div id="map"></div>
    <script type="module">
      import { isomizer } from "https://cdn.jsdelivr.net/gh/tjmsy/maplibre-gl-isomizer@v0.1.1/src/isomizer.js";

      const mapConfig = {
        container: "map",
        center: [139.7, 35.7],
        zoom: 12,
      };

      const map = isomizer(
        mapConfig,
        "./project-config.yml"
      );
    </script>
  </body>
</html>


生成されたスタイルJSON

今回生成されたスタイルJSONです。

{
  "version": 8,
  "sources": {
    "openmaptiles": {
      "type": "vector",
      "url": "https://tiles.openfreemap.org/planet"
    }
  },
  "layers": [
    {
      "id": "2-7_509-2-railway-casing_openmaptiles_transportation_0",
      "type": "line",
      "source": "openmaptiles",
      "source-layer": "transportation",
      "filter": [
        "all",
        [
          "==",
          "class",
          "rail"
        ],
        [
          "!=",
          "brunnel",
          "tunnel"
        ]
      ],
      "layout": {
        "line-join": "miter",
        "line-cap": "butt"
      },
      "paint": {
        "line-color": "#000000",
        "line-width": 2.6599999999999997
      }
    },
    {
      "id": "2-8_509-1-railway-centerline_openmaptiles_transportation_0",
      "type": "line",
      "source": "openmaptiles",
      "source-layer": "transportation",
      "filter": [
        "all",
        [
          "==",
          "class",
          "rail"
        ],
        [
          "!=",
          "brunnel",
          "tunnel"
        ]
      ],
      "layout": {
        "line-join": "miter",
        "line-cap": "butt"
      },
      "paint": {
        "line-color": "#FFFFFF",
        "line-width": 1.14,
        "line-dasharray": [
          3.8,
          5.699999999999999
        ]
      }
    }
  ]
}


ISOMizer Studio

リアルタイムで視覚効果を確かめながらISOMizerのスタイル記法で地図をデザインできるエディタを作成・公開しています。

例: カラーパレットで水を赤くする

特に意味はありません。単にシンプルな試し方。

例: Symbol Palette で、背景のlayoutに visibility: none を追加して立禁色を非表示にする

例: 等高線間隔を0.5m(計曲線間隔2.5m)にする。

dem-contour://{z}/{x}/{y}?buffer=1&contourLayer=contours&elevationKey=ele&extent=4096&levelKey=level&thresholds=14*0.5*2.5~13*5*25~12*10*50~11*50*250~10*100*500~2*200*1000~1*400*2000

の、thresholds の値がズームレベルと主曲線間隔と計曲線間隔の組み合わせなので、書き換えることで等高線間隔を設定できます。
等高線は自分の端末でリアルタイムで計算しながら書いているものなので、低いズームレベルで広範囲を表示しているときの等高線間隔を狭くしすぎるのは反応が悪くなりおすすめしません。

例: 夜間のトレーニング時の街灯での読図走に最適化された地図を模索する

見本はてきとーに線を太くしたり色を濃くしてみたり背景色を抜いてみたりしただけで、これが効果があるかはわかりませんがアイデアのひとつとして。

例: バージョン管理

この記事を書いている途中で、色の並び順で道路用の茶色と黒が水用の青よりも下層にあることによる表示の不具合を発見したので、color-paletteでの順番の入れ替えと、今回、階層も変更したのでsymbol-paletteの修正も行いました。(同じ階層内の移動ならsymbol-paletteでの修正は不要。ということで階層の種類は少ないほうがいいなと判断してmiddle階層も消しました)

修正内容についてgithubで差分表示できて、color-paletteについてもsymbol-paletteについても、変更した行と変更してない行が明確です。

スタイルJSONの場合、レイヤの場所が入れ替わると、レイヤ全体が差分ということになって、レイヤ内のどこが変更されたのかの特定が難しいです。(比較に用いているツールが違うのはアンフェアですが、GitHubでスタイルJSONをバージョン管理していたとしてもおそらく同様のはずです)



あとは、テキストのレイヤが追加できれば普段使いのための地名や駅名だったり山頂だったりを追加しておもしろい地図が作れそうですが、私が実装を後回しにしてしまっているのでしばしお待ちを。

他にも、Symbol Palette だけじゃなくDesign PlanのrulesでもLayoutやPaintを設定できるようにして、設定項目が衝突する場合はDesign Planを優先するようにしたほうがいいな(現状、道の太さごとにSymbol Palette に記号を用意しているのがいらなくなる)、とかいろいろ機能を追加予定です。


他の地物を追加する

他に、私のサンプルに含まれていない地物を追加したいときは↓らへんが役に立ちます。

OpenMapTilesの場合

地理院ベクトルタイルの場合

https://maps.gsi.go.jp/help/pdf/vector/dataspec.pdf

https://maps.gsi.go.jp/help/pdf/vector/attribute.pdf

他にも任意のベクトルタイルをソースに追加してデザインできます。

作成したスタイルを利用する

ISOMizer Studio の初期表示に含まれるサンプルプロジェクトについては、誰でも自由に使ってもらえるように「クリエイティブ・コモンズ・ゼロ(CC0)」ライセンスにしました。自由に利用、改変、再配布が可能です。コピーレフト系のライセンスにもいろいろありますが、クリエイティブに使ってもらいたいというニュアンスでこのライセンスを選択しました。みなさんのサービスやプロジェクトで使ってもらえたら嬉しいです。

利用方法について、YAMLをISOMizerで地図にロードする場合は上のほうのISOMizerの紹介の項で書いた方法で利用できます。生成されたスタイルJSONを利用する場合はMapLibreの通常の操作方法で利用できます。


地図を公開したついでに作業日誌的に進捗を書いてみようと思ったのが話広がって長文になってしまいましたが、今日はこんなところで。

いいなと思ったら応援しよう!