はじめに
TerraMap APIでも利用している町丁目ポリゴンを、tippecanoeでPMTiles形式に変換し、Web地図上に表示してみることにしました。作成したPMTilesファイルについては、MapLibreでOpenStreetMap上に表示しています。
PMTilesとは
単一ファイル形式のタイルデータです。種類には背景地図、ベクトルタイル、画像等があり、S3のようなストレージサービスにホストすることができます。サーバープログラムは不要で、低コストでタイルデータを提供することができるとされています。
PMTilesファイル作成
以下の手順で、町丁目ポリゴンのGeoJSONファイルからベクトルタイルのPMTilesファイルを作成しました。作業OSはWindowsで、GitやDockerが使えることが前提の手順になります。
-
tippecanoeのソースを取得
最新tippecanoeのDockerイメージが共有されているかは分からなかったので、まず現在も更新され続けている以下のソースを取得しました。
https://github.com/felt/tippecanoe -
Dockerイメージのビルド
READMEに従って、Dockerイメージのビルドから実行していきます。docker build -t tippecanoe:latest .
-
コンテナの起動
docker run --name pmtiles-conv -it ^ -v {ホスト側の作業ディレクトリパス}:/data ^ tippecanoe:latest
-
GeoJSONをPMTilesへ変換
tippecanoe -zg -pk -pn -l chomoku-layer -o /data/chomoku.pmtiles /data/chomoku.geojson
tippecanoeのオプションについてはケースバイケースと思いますが、今回は以下を使用しています。
オプション 概要 -zg ズームレベル0から妥当な最大ズームレベルまでのタイルを作成 -pk タイルを500Kバイトに制限しない -pn フィーチャ間で共有しているノードは簡素化しない -l -l {レイヤー名}
でレイヤー名を指定
※ 省略した場合はGeoJSONのファイル名になる-o -o {ファイル名}
で出力ファイル名を指定処理の結果、980MBあったGeoJSONが、77MBのPMTilesに変換されました。
MapLibreでのPMTiles表示処理
この記事ではMapLibreでPMTilesを表示させる方法を知りたいので、ローカルにPMTilesファイルを置いて確認しました。ソースは以下のようになりました。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>MapLibreで町丁目ポリゴンのPMTilesを表示</title>
<script src="https://unpkg.com/maplibre-gl@4.7.0/dist/maplibre-gl.js"></script>
<link
href="https://unpkg.com/maplibre-gl@4.7.0/dist/maplibre-gl.css"
rel="stylesheet"
/>
<script src="https://unpkg.com/pmtiles@3.2.0/dist/pmtiles.js"></script>
</head>
<body>
<div id="map" style="width: 100%; height: 95vh"></div>
<script>
const protocol = new pmtiles.Protocol();
maplibregl.addProtocol("pmtiles", protocol.tile);
const map = new maplibregl.Map({
container: "map",
center: [139.66204291, 35.67260863],
zoom: 10,
maxZoom: 17,
style: {
version: 8,
sources: {
osm: {
type: "raster",
tiles: ["https://tile.openstreetmap.org/{z}/{x}/{y}.png"],
tileSize: 256,
attribution:
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
},
},
layers: [
{
id: "osm-layer",
source: "osm",
type: "raster",
},
],
},
});
map.addControl(new maplibregl.NavigationControl());
map.on("load", function () {
map.addSource("mvt_data", {
type: "vector",
tiles: [
`pmtiles://chomoku.pmtiles/{z}/{x}/{y}`,
],
minzoom: 0,
maxzoom: 11, // タイルがある最大ズームレベルを設定
});
map.addLayer({
id: "mvt_polygon",
type: "fill",
source: "mvt_data",
"source-layer": "chomoku-layer", // PMTilesのレイヤー名
layout: {},
paint: {
"fill-color": "#7fffd4",
"fill-opacity": 0.2,
},
});
map.addLayer({
id: "mvt_line",
type: "line",
source: "mvt_data",
"source-layer": "chomoku-layer", // PMTilesのレイヤー名
layout: {},
paint: {
"line-color": "#2e8b57",
"line-width": 0.5,
},
});
});
</script>
</body>
</html>
詳細① PMTilesを扱うにはpmtiles.jsを利用します。
<script src="https://unpkg.com/pmtiles@3.2.0/dist/pmtiles.js"></script>
PMTilesをロードするためのプロトコルを追加します。
const protocol = new pmtiles.Protocol();
maplibregl.addProtocol("pmtiles", protocol.tile);
詳細② tilesのURLは、プロトコル名pmtilesを先頭に付与し、pmtiles:PMTilesファイルのURL/{z}/{x}/{y}
という形式で指定します。
tiles: [
`pmtiles://chomoku.pmtiles/{z}/{x}/{y}`,
],
町丁目ポリゴンの表示結果
実際に町丁目ポリゴンを表示した例が以下になります。
ローカル環境ではありますが、静的なファイルなのでパフォーマンスに優れているのが分かると思います。
町丁目ポリゴンをFlatGeobuf形式に変換したこともありますが、それと比べても高速でメモリ使用量の心配もほとんどありません。これはtippecanoeによるズームレベルに応じたポリゴンの簡素化に起因していると思われます。
その他の参考情報