地図タイルサーバを立てたい話 (その1):TileServer GL でタイルをサービングする

2025-11-27 追記: この記事は「地図タイルサーバを立てて地図を表示する手順の一つ」になっていますが、Qiita の方に地図を表示するのに何が必要か順を追って整理する記事 (以下) を書きました。

まとめ: 地図タイルサーバを立てたいといっても「データソース」「タイルデータ形式」「配信方法」「利用方法」が色々あります。この記事では日本の OpenStreetMap 生データを Planetiler (ビルドが高速です) でベクタタイル MBTiles にビルドして TileServer GL でサービングし、TileServer GL がレンダリングしてくれたラスタタイルを Leaflet から利用します (TileServer GL はベクタタイルのままも配信するのでそちらも利用できます)。

ただ、まともな地名 (細かい地名がピンインにならない) になるスタイル定義までは特定していません (スタイル定義以前にベクタタイル時点でまともな地名が入っていないのかからまだわかっていません)。→ 2025-11-27 追記:解決しました。地図タイルサーバを立てたい話 (その1.5) : まともな地名が表示されるようにする - クッキーの日記

なお、Leaflet から手軽に利用できるのはラスタタイルなのでこの手順を取りましたが、ベクタタイルだけなら PMTiles 形式ファイルを Web サーバや S3 に置くだけで配信できるそうなので、Leaflet でちゃんとベクタタイルのスタイルを記述するようにするか MapLibre GL JS に移行するかして TileServer GL を脱却する道もあるかもしれません。

  1. OpenStreetMap
  2. 地理院地図|地理院タイル一覧
  3. Download OpenStreetMap for Japan | Geofabrik Download Server: ここに日本の生 OSM データの PBF 形式ファイル japan-latest.osm.pbf がある。地方単位のファイルもある。が、Planetiler [4] で日本の地図タイルをビルドする場合は勝手にダウンロードしてくれるのでダウンロードしておく必要はない。
  4. GitHub - onthegomap/planetiler: Flexible tool to build planet-scale vector tilesets from OpenStreetMap data fast : MBTiles や PMTiles といった地図タイルデータ形式ファイルを高速にビルドするエンジン。
  5. OpenMapTiles Planet | Schema | MapTiler : Planetiler [4] で地図タイルを生成するときに使用される標準プロファイル (設定) である。低ズームでは Natural Earth Data の水域が、高ズームでは OpenStreetMapData の水域ポリゴンが表示されるとある (この水域ポリゴンのダウンロードがおそらく配布元の関係で遅い)。
  6. GitHub - maptiler/tileserver-gl: Vector and raster maps with GL styles. Server side rendering by MapLibre GL Native. Map tile server for MapLibre GL JS, Android, iOS, Leaflet, OpenLayers, GIS via WMTS, etc. : ベクタ MBTiles / PMTiles をベクタタイルのままサービングも、オンデマンドにラスタタイルにレンダリングしてサービングもしてくれるタイルサーバソフト。
  7. GitHub - klokantech/tileserver-gl-styles: Prebuilt map styles in GL JSON format for use in the TileServerGL and OpenMapTiles : TileServer GL [6] で設定ファイルを指定しないときに適用されるスタイルがこれと思われる。
背景と目標
Leaflet を利用して地図を表示する Web アプリケーションを作っているとします。地図を表示するには、OpenStreetMap [1] や国土地理院 [2] などの配信サーバを参照することができます。
参照先を切り替えて表示する HTML ファイルの例は以下です。
https://gist.github.com/CookieBox26/dd24f3cbad9fa91b9884cc5bae06424c
ただ、大量にアクセスするときや、外部ネットワークを参照できない環境など、上記のような一般公開サーバを参照できない場合もあります。なので、自前で以下のように地図タイル (ラスタ) をサービングしたいです。

http://my-server:XXXX/{z}/{x}/{y}.png

解決方法
Planetiler [4] で MBTiles 形式ベクタタイル群データファイルをビルドし、これを TileServer GL [5] にラスタタイルにレンダリングしながらサービングしてもらいます。

以下では Amazon EC2 インスタンス (Ubuntu, m8a.4xlarge) で作業します。Planetiler も TileServer GL も Docker コンテナで動かすので、Docker がなければ入れておきます (手順)。

作業手順
MBTiles 形式ベクタタイル群データファイルのビルド
Planetiler [4] で日本の MBTiles 形式ベクタタイル群データファイルをビルドします。Planetiler の Docker イメージは GitHub コンテナレジストリ (GHCR) に公開されているので、[4] の README のように以下のコマンドでコンテナを起動して地図タイルのビルドを実行できます。

mkdir data  # 念のため data/ を作っておく
docker run -e JAVA_TOOL_OPTIONS="-Xmx16g" \
  -v "$(pwd)/data":/data ghcr.io/onthegomap/planetiler:latest \
  --download --area=japan

--area=japan で日本の OpenStreetMap データを取得してくれます。またデフォルトのプロファイル [5] では水域データなども取得します。この OpenStreetMap の水域ポリゴン (water_polygons) のダウンロードはおそらく配布元の関係で時間帯によってかなり時間がかかるようです (11/24 の 14時頃には数分でダウンロードされたのが、19 時頃には 1 時間半経っても 25% しかダウンロードされませんでした)。ダウンロードにかかる時間を除く、MBtiles のビルド所要時間自体は手元では 3 分半でした。

data/
 ├ output.mbtiles  # これができていればよい
 └ sources/  # ここ以下はダウンロードされた元データ
  ├ japan.osm.pbf
  ├ lake_centerline.shp.zip
  ├ natural_earth_vector.sqlite.zip
  └ water-polygons-split-3857.zip  # 取得が遅い場合がある

TileServer GL によるサービング
[4] の README にあるように先ほど MBTiles をビルドした場所で以下を実行すると http://{EC2 インスタンスの IP アドレス}:5000/ で TileServer GL 画面がみられます。

docker run -it -v "$(pwd)/data":/data -p 5000:8080 maptiler/tileserver-gl

コンテナ名を付けてバックグラウンド実行し、中断と再開をしやすくするには以下のようにします。

docker run -d --name tileserver -v "$(pwd)/data":/data -p 5000:8080 maptiler/tileserver-gl
docker ps -a  # tileserver が起動していることの確認
docker stop tileserver  # 中断
docker start tileserver  # 再開

TileServer GL 画面の STYLES の XYZ をクリックすると以下のようにタイル URL が表示されます。

http://{EC2 インスタンスの IP アドレス}:5000/styles/basic-preview/512/{z}/{x}/{y}.png

Leaflet からの利用
この記事の「背景と目標」にある HTML ファイル内の以下の箇所に先ほど確認したタイル URL を追加すると、自前の地図タイルサーバにも切り替えることができます。

const servers = {
  'OpenStreetMap': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
  '国土地理院 (標準地図)': 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
  '国土地理院 (淡色地図)': 'https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png',
  '自前サーバ': 'http://{EC2 インスタンスの IP アドレス}:5000/styles/basic-preview/512/{z}/{x}/{y}.png',
};

上図はクレジット表記ができていませんが、自前でサービングしていてもデータは OpenStreetMap なので適切なクレジット表記をしてください。

備考
  • Planetiler [4] は Java でも動かせます。Java を入れるのも含めたコマンドは以下です。

    sudo apt update
    sudo apt install openjdk-21-jdk
    java -version  # インストールされたバージョンを確認
    wget https://github.com/onthegomap/planetiler/releases/latest/download/planetiler.jar
    java -Xmx16g -jar planetiler.jar --download --area=japan
    

  • 上記のキャプチャ画像は地名が表示されていません。これは、basic-preview [7] というスタイルを適用しているのにフォントを渡していないからなのではないかと思います。地名が必要な場合、[7] にフォントがあるのでこれを取得して config.json にパスを指定するのでもよい気がしますが、[6] の README にテスト用の config.json, styles/, fonts/ の使用例があるのでそれを拝借する方法を以下に記します。 ただ先に書いておくと、以下の対応で市区町村名はローマ字ながらも表示されると思いますが、より細かい地名はしばしばピンイン表記になるなどめちゃくちゃなので表示しない方がよいまであります。ちゃんと地名が必要なら更なる対応が必要です。→ 2025-11-27 追記:解決しました。地図タイルサーバを立てたい話 (その1.5) : まともな地名が表示されるようにする - クッキーの日記


    ▼クリックして展開

    [6] の README にある test_data.zip を取得して解凍します。

    wget https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/test_data.zip
    unzip test_data.zip
    

    解凍された config.json, styles/, fonts/ を data/ 以下に配置します。チューリッヒのタイルは要りません。

    data/
     ├ output.mbtiles
     ├ config.json  # これ
     ├ styles/  # これ
     └ fonts/  # これ
    

    config.json 内を手元の日本のタイルに書き換え、日本のバウンディングボックスに書き換えます (以下のバウンディングボックスは離島などが入っていないかもしれないので目的に応じて適切な値にします)。

    {
      "options": {
        "paths": {
          "fonts": "fonts",
          "styles": "styles"
        }
      },
      "styles": {
        "test-style": {
          "style": "osm-bright/style.json",
          "tilejson": {
            "type": "overlay",
            "bounds": [128.0, 30.0, 147.0, 45.5]  # これ
          }
        },
        "maptiler-basic": {
          "style": "maptiler-basic/style.json",
          "tilejson": {
            "type": "overlay",
            "bounds": [128.0, 30.0, 147.0, 45.5]  # これ
          }
        }
      },
      "data": {
        "openmaptiles": {
          "mbtiles": "output.mbtiles"  # これ
        }
      }
    }
    

    先ほどと同じコマンドで TileServer GL を起動し、タイル URL を確認し、タイルを参照する HTML ファイル側を書き換えます。これで地名が表示されます (が、しばしばピンインになっています)。

    const servers = {
      'OpenStreetMap': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
      '国土地理院 (標準地図)': 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
      '国土地理院 (淡色地図)': 'https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png',
      '自前サーバ (Basic)': 'http://{IP アドレス}:5000/styles/maptiler-basic/512/{z}/{x}/{y}.png',
      '自前サーバ (Bright)': 'http://{IP アドレス}:5000/styles/test-style/512/{z}/{x}/{y}.png',
    };