Protomaps Basemap タイル
ズーム 0 から 15 までの Protomaps Basemap 全体を、1 つの PMTiles アーカイブとして通常の HTTP で配信しています。任意の PMTiles クライアントに URL を指定すれば動作します。
https://tiles.jasontally.com/basemap.pmtiles
- API キー不要登録は不要で、コードにトークンを入れる必要もありません。
- レート制限なし帯域制限、クォータ、バースト上限はありません。
- 毎月更新最新の Protomaps リリースから月 1 回再ビルドされます。
OpenStreetMap タイルのブロックや、tile.openstreetmap.org からの 403 エラーでお困りですか? こちらから始めてください。
試してみる
同じアーカイブを 3 つのスタイルで表示できます。スタイルを切り替えても読み込み済みのタイルはそのまま使われるため、即座に反映されます。
自分のスタイルを作る
色を変更したりレイヤーを非表示にしたりすると、地図がその場で更新されます。その後、スタイル JSON をコピーしてください。これは自前でホストできる通常の MapLibre スタイルです。このタイルを参照しているため、どこに配置してもそのまま動作します。
使い方
このアーカイブは HTTP 範囲リクエストに対応しているため、すべての PMTiles クライアントから読み取れます。
1. ページに地図を表示する
このファイルをコピーしてください。タグ 3 つ、スタイル URL 1 つ、地図 1 つだけの完全なサンプルです。両方のライブラリはバージョン固定で BSD-3-Clause ライセンスであり、CDN を使いたくない場合はこのホストからも同じファイルを取得できます。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Basemap</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css">
<script src="https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js"></script>
<script src="https://cdn.jsdelivr.net/npm/pmtiles@3.2.1/dist/pmtiles.js"></script>
<style>html, body, #map { margin: 0; height: 100%; }</style>
</head>
<body>
<div id="map"></div>
<script>
// Teach MapLibre how to read a PMTiles archive.
const protocol = new pmtiles.Protocol();
maplibregl.addProtocol("pmtiles", protocol.tile);
new maplibregl.Map({
container: "map",
style: "https://tiles.jasontally.com/styles/light.json",
center: [2.35, 48.85], // Paris
zoom: 4
});
</script>
</body>
</html>
2. 既存のスタイルのタイルを差し替える
すべての MapLibre スタイルはソースからタイルを読み取ります。このタイルを使うには、その 1 つの url を変更してください。スタイルの他の部分は変更不要です。
"sources": {
"protomaps": {
"type": "vector",
- "url": "pmtiles://https://demo-bucket.protomaps.com/v4.pmtiles",
+ "url": "pmtiles://https://tiles.jasontally.com/basemap.pmtiles",
"attribution": "<a href=\"https://www.openstreetmap.org/copyright\">© OpenStreetMap contributors</a>"
}
}
またはコード内で変更します。管理外のサーバーからスタイルを取得する場合に便利です:
// Rewrite every PMTiles source in a style to point at these tiles.
const style = await (await fetch("https://your-server/style.json")).json();
for (const source of Object.values(style.sources)) {
if (source.url?.startsWith("pmtiles://")) {
source.url = "pmtiles://https://tiles.jasontally.com/basemap.pmtiles";
}
}
new maplibregl.Map({ container: "map", style, center: [2.35, 48.85], zoom: 4 });
3. 作成したスタイルの配置場所
上のビルダーにある スタイル JSON ボタンを押すと、編集内容を含め、この地図が現在描画しているスタイルが開きます。ダイアログにはコピーボタンがあります。出力されたスタイルの使い方は 2 通りあり、どちらも通常のウェブページとして機能します。
このホストの 3 つのスタイルと同様に、ページの隣にファイルとして保存し、style からそれを指定します。スタイルを自分で管理し、サイトに合わせて変更する場合はこちらを選んでください:
// Save the copied JSON as my-basemap.json beside this file.
new maplibregl.Map({
container: "map",
style: "my-basemap.json", // relative, same origin
center: [2.35, 48.85],
zoom: 4
});
または HTML 内に直接記述します。移動するファイルが 1 つで済み、別途ホストするものもありません。配置場所が違うだけで同じスタイルです:
// The copied JSON, pasted straight into the page.
const myStyle = {
"version": 8,
"name": "Protomaps Basemap — custom",
"glyphs": "https://tiles.jasontally.com/font/{fontstack}/{range}.pbf",
"sprite": "https://tiles.jasontally.com/sprites/v4/light",
"sources": { /* ... as copied ... */ },
"layers": [ /* ... as copied ... */ ]
};
new maplibregl.Map({ container: "map", style: myStyle, zoom: 4 });
glyphs と sprite の行はそのままにしておいてください。これらはラベルとアイコンを描画するためのもので、このホストを参照しているため、自分のページに貼り付けたスタイルはどこから配信しても動作し続けます。このホストに一切依存したくない場合は、代わりに protomaps.github.io/basemaps-assets を指定してください。
差し替える前に確認すべきこと。このアーカイブは Protomaps スキーマ v3 です。別のスキーマ向けに書かれたスタイルは、読み込まれてもソースレイヤー名が一致しないため何も描画されません。9 つのレイヤー名と、コピーして使える完成済みの v3 スタイルを以下に掲載しています。
4. フォント、スプライト、ライブラリ
地図はタイル、テキスト用の glyphs URL、必要に応じたアイコン用の sprite URL、そしてそれらを描画するライブラリで構成されます。これらはすべて、それぞれが期待するパスでこのホスト上に用意されているため、必要であれば単一のホスト名から地図全体を配信できます。
| 項目 | このホストの URL | ライセンス |
|---|
MapLibre と PMTiles のファイルは jsDelivr とこのホストの両方にあります。どちらの URL でも動作します。
最後の行はこのページでは使用されていません。これは Leaflet 用のベクターレンダラーで、スクリプトタグ 1 つで差し替えられるようここで公開しています。Leaflet 本体は公式の CDN のままにします。
Noto Sans Regular と Noto Sans Medium の 2 つのフォントスタックを、MapLibre が要求する 256 コードポイントずつの 256 範囲に分割しています。ラテン文字だけでなく 256 範囲すべてを公開しています。Protomaps は地物に現地の名称でラベルを付けるため、東京やカイロでは数万番台の範囲が必要になります。英語の地図が使う範囲だけを公開すると、エラーも出ないままそれらのラベルが消えてしまいます。
アップストリームのフレーバーごとに 1 つずつ、1x と 2x の 5 つのスプライトシートがあります。以下のスタイルはこれらからアイコンを描画するため、これらが取得されます。自作のスタイルで市街地マーク、高速道路の路線番号標識、POI アイコンを表示したい場合は、使用するフレーバーに対応するものを sprite に指定してください。シート内のアイコン名は light.json に記載されています。
地図が描画するものと、そのデータ元
すべてのレイヤーはこのアーカイブ内の 9 つのソースレイヤーのいずれかを使うため、別のアーカイブは不要です。そのうち 2 つは、不具合で欠けているように見えやすいため特記しておきます:
| レイヤー | データ元 | このビルドに含まれるか |
|---|---|---|
| 地形、道路、建物、水域、陸地 | earth, landcover, landuse, water, roads, buildings | あり |
| 地名、道路名、水域名 | 同じレイヤーの name フィールド | あり |
| POI アイコン、市街地マーク | pois.kind、places、およびスプライト | あり |
| 路線番号標識 | roads.shield_text | なし。現在配信中のビルドにはこのフィールドがありません |
| 住居番号 | buildings.kind=address および buildings.addr_housenumber | なし(理由は同じ) |
別の住所アーカイブはなく、取得すべき住所レイヤーもありません。アップストリームのプロジェクトでは、住所ポイントを buildings レイヤーの内部に、kind=address と addr_housenumber を持つ地物として格納しています。このフィールドはこの地図が配信しているものより後の Protomaps ビルドで追加されたため、住居番号レイヤーはすでに組み込まれており、次回の月次更新でデータが入ると自動的に有効になります。路線番号のテキストも同様です。現在のビルドには路線番号の文字数だけを表す shield_text_length しかなく文字列そのものがないため、これを描画するとすべての高速道路に空白の標識が表示されてしまいます。
自分でレイヤーを記述して色を着ける
レイヤーはフィルター、ズーム、形状、色で構成されます。フィルターには誰もが引っかかる 2 つの落とし穴があり、どちらもエラーを出さずに失敗します:
kindは大分類の名称です。道路の場合はhighway、major_road、minor_road、path、railのいずれかになります。アップストリームのドキュメントにあるmotorway、primary、residentialといった名称はkind_detailの値であり、kindをこれらでフィルターしても何も描画されず、エラーも表示されません。- レイヤー自身の
minzoom未満では描画されません。アーカイブは z15 までしかないため、minzoom が 16 のレイヤーは一切描画されません。また、ほとんどの地物には表示に適したズームを示す独自のmin_zoomがあり、提供されているスタイルではそれも判定しています。
道路、その名称、および線を非表示にする切り替えの例です:
{
"id": "road-major",
"type": "line",
"source": "protomaps",
"source-layer": "roads", // one of the nine
"minzoom": 4, // not drawn below this
"filter": ["==", ["get", "kind"], "major_road"],
"layout": {
"line-cap": "round",
"visibility": "visible" // "none" to hide it
},
"paint": {
"line-color": "#f7c44d", // any CSS colour
"line-width": ["interpolate", ["linear"], ["zoom"], 4, 1.7, 12, 6]
}
}
名称は name フィールドを読み取るシンボルレイヤーで、背景に縁取り(ハロー)を付けるか何も付けないかを指定します。グリフが見つからない場合もエラーは出ないため、指定するフォントスタックはこのホストにある 2 つのうちのいずれかでなければなりません:
{
"id": "road-label-major",
"type": "symbol",
"source": "protomaps",
"source-layer": "roads",
"minzoom": 11,
"layout": {
"text-field": ["coalesce", ["get", "name:en"], ["get", "name"]],
"text-font": ["Noto Sans Medium"], // or Noto Sans Regular
"text-size": 10,
"symbol-placement": "line" // follow the road
},
"paint": {
"text-color": "#2b2a26",
"text-halo-color": "#ffffff",
"text-halo-width": 1.1
}
}
レイヤーに何も描画されなくてもエラーにはならず、MapLibre はどの条件で失敗したかを通知しません。何も表示されない場合は、フィールド名、レイヤーの minzoom、フォントスタック、そしてスプライトシートの light.json に対するアイコン名の順に確認してください。
グリフの参照先が demotiles.maplibre.org になっているスタイルをお持ちなら、そのホスト名を置き換えるだけで完了します。パスは意図的に同じにしてあります。
"glyphs": "https://tiles.jasontally.com/font/{fontstack}/{range}.pbf",
"sprite": "https://tiles.jasontally.com/sprites/v4/light"
// before, in a style you already have
- "glyphs": "https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf",
+ "glyphs": "https://tiles.jasontally.com/font/{fontstack}/{range}.pbf",
上記の 2 つ以外のフォントスタックをスタイルが要求した場合、テキストは何も描画されません。Protomaps 公式のスタイルは太字に Noto Sans Medium を使用しています。このセットには Noto Sans Bold がないため、それを要求するスタイルではエラーも出ないまますべてのラベルが消えてしまいます。
どちらのホストが速いか、そしてどう判断したか。推測ではなく実測しました。1 つの接続で 1 件ずつ交互にリクエストを送り、最初の 3 サンプルを除外し、最初のバイトが届くまでの時間(TTFB)を転送時間と分離して計測しました。このテストでは、すべてのファイルで CDN の方が 10〜45 ミリ秒速く応答し、15% ルールを明確に上回る差がつきました。そのため、ライブラリは jsDelivr から読み込んでいます。
同じ計測で CDN の方が約 10 ミリ秒速く応答するという結果が出ていますが、フォントとスプライトはこのホストから配信しています。これは計測結果ではなく設計上の判断です。単一のホスト名から配信される地図は他所の障害で壊れることがなく、グリフサーバーが落ちると地図からラベルが消えてしまうためです。10 ミリ秒の短縮を優先する場合は、glyphs に protomaps.github.io/basemaps-assets/fonts を指定すれば同じパスで動作します。これはある 1 都市からの計測値であり、比較の再実行は 1 つのコマンドで行えます。
5. Leaflet について
Leaflet の L.tileLayer は画像を描画しますが、このタイルはベクタータイルです。それでも Leaflet の地図から利用することは可能で、どちらの方法でもマーカー、ポップアップ、コントロールはそのまま維持されます。両方の差し替え手順はOpenStreetMap タイルからの移行にあります。
6. PMTiles ライブラリでアーカイブを読み取る
地図は描画せず、データだけを取得します。スクリプトやテストなど、画像表示以外の用途に役立ちます。
import { PMTiles, FetchSource } from "pmtiles";
const archive = new PMTiles(new FetchSource("https://tiles.jasontally.com/basemap.pmtiles"));
const header = await archive.getHeader();
console.log(header.minZoom, header.maxZoom); // 0 15
console.log(header.numAddressedTiles); // 1431655765
const metadata = await archive.getMetadata();
console.log(metadata.name); // "Protomaps Basemap"
const tile = await archive.getZxy(3, 4, 2); // z, x, y
console.log(tile.data.byteLength);
2 つのクラスに注意してください。FetchSource は HTTP 経由でバイト列を読み取るリーダーであり、PMTiles はその上でフォーマットを解釈するリーダーです。FileSource はローカルファイル用の FetchSource 相当クラスであり、URL 文字列を渡しても動作しません。
このアーカイブはスナップショットであり、ライブフィードではありません。更新時期についてはデータの鮮度についてを参照してください。
OpenStreetMap タイルからの移行
タイル利用ポリシーによるブロックや 403 エラーへの対処法です。キーもクォータもありません。
地図にブロック画像が表示される理由
App is not following the tile usage policy of OpenStreetMap’s volunteer-run servers
General block: app is not following the tile usage policy of openstreetmap volunteer-run servers
403 Forbidden — tile.openstreetmap.org
タイルサーバーがタイル利用ポリシーに基づき、地図の代わりに意図的にその画像を送信しています。障害ではないため、URL を修正しても解消されません。
よくある最初の試行錯誤:
a/b/cを正規ホストに変更してもブロックは解除されません。正規 URL の使用は必須ですが、ブロックはホスト名ではなく、アクセス元のアプリや Referer に対して行われています。- リトライするとさらに悪化します。リクエスト量そのものがブロックの要因の一部だからです。
- ディスクから開いたページはヘッダーを工夫しても救済できません。
file://はRefererを送信しません。代わりにX-Requested-Withを送信しようとするとタイルごとに CORS プリフライトが必要になりますが、タイルサーバーはそのプリフライトも拒否します。Firefox と Chromium では送信するヘッダーが異なりますが、ルールはどちらに対しても同じです。 localhostでは動くのに本番サイトではブロックされる場合。原因はオリジンにあります。ブロックされているのはあなたのドメインの Referer であり、localhostは別の Referer を送信しているためです。
オフィス、学校、VPN、モバイルネットワークでは IP アドレスが共有されており、ブロックはアプリだけでなくアドレスに対しても適用されます。利用者側からはどちらが原因かを判別できません。
そのため、それらのサーバーへのリクエストを止める必要があります。移行方法は 3 つあります。
Leaflet をそのまま使う
protomaps-leaflet は Leaflet 上でベクタータイルを描画します。アーカイブを直接読み取って通常のレイヤーとして追加されるため、マーカー、ポップアップ、GeoJSON、コントロールはそのまま維持されます。このアーカイブで動作確認済みのコピーがこちらにあります: https://tiles.jasontally.com/vendor/protomaps-leaflet.js。
変更前:
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
const map = L.map("map").setView([48.85, 2.35], 12);
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
maxZoom: 19,
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
</script>
変更後:
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
+ <script src="https://tiles.jasontally.com/vendor/protomaps-leaflet.js"></script>
<script>
const map = L.map("map").setView([48.85, 2.35], 12);
- L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { ... }).addTo(map);
+ protomapsL.leafletLayer({
+ url: "https://tiles.jasontally.com/basemap.pmtiles",
+ flavor: "light",
+ lang: "en"
+ }).addTo(map);
</script>
flavorにはlight、dark、white、grayscale、blackのいずれかを指定します。これらはライブラリ内蔵のテーマであり、上記のスタイルとは異なるため、色は完全には一致しません。- ラベルはシステムフォントを使った Canvas テキストです。グリフ URL は不要で、フォントが 404 になることもありません。
- 最も詳細なタイルは z15 です。それ以降はレイヤーが拡大表示(オーバーズーム)を行い、
maxDataZoom: 15が最初からデフォルトになっています。
protomaps-leaflet はアップストリームでメンテナンスモードに入っており、バグ修正のみで新機能の追加はありません。既存の Leaflet アプリには十分ですが、新規開発は MapLibre GL で始めるべきです。
Leaflet を維持し、MapLibre で描画する
@maplibre/maplibre-gl-leaflet は、Leaflet のタイルペイン内に MapLibre の地図を配置します。アプリを Leaflet のままにしつつ、上記のスタイルや地形・陰影起伏を使いたい場合に使用してください。
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<link rel="stylesheet" href="https://tiles.jasontally.com/vendor/maplibre-gl.css">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://tiles.jasontally.com/vendor/maplibre-gl.js"></script>
<script src="https://tiles.jasontally.com/vendor/pmtiles.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@maplibre/maplibre-gl-leaflet@0.1.4/dist/leaflet-maplibre-gl.js"></script>
<script>
// The style's source is pmtiles://, so MapLibre needs the protocol first.
maplibregl.addProtocol("pmtiles", new pmtiles.Protocol().tile);
const map = L.map("map", { minZoom: 1 }).setView([48.85, 2.35], 12);
const gl = L.maplibreGL({
style: "https://tiles.jasontally.com/styles/light.json"
}).addTo(map);
// gl.getMaplibreMap() is the MapLibre map, for sources and layers.
</script>
- 最初にプロトコルを登録してください。
addProtocolの行がないと、コンソールにURL scheme "pmtiles" is not supportedと出力され、地図は空白のままになります。 - MapLibre は Leaflet より 1 段低いズームで描画します。Leaflet の 256 px に対して MapLibre のタイルは 512 px のため、
minzoomが 12 のレイヤーは Leaflet が z13 に達したときに表示されます。 - Leaflet は引き続きその上に描画します。Canvas はタイルペイン内に配置されるため、マーカーやオーバーレイはラスターレイヤー上と同じように動作します。
MapLibre GL に移行する
ライブラリは 1 つ、アダプターは不要で、上記のスタイルがそのまま動作します。ページ全体のコードは1. ページに地図を表示するにあります。書き換えの大部分は座標の順序の入れ替えです:
| Leaflet | MapLibre GL |
|---|---|
L.map("map").setView([lat, lng], z) | new maplibregl.Map({ container: "map", center: [lng, lat], zoom: z }) |
L.tileLayer(url).addTo(map) | ベクターソースを持つスタイル(上記参照) |
L.marker([lat, lng]).bindPopup(html) | new maplibregl.Marker().setLngLat([lng, lat]).setPopup(new maplibregl.Popup().setHTML(html)) |
L.geoJSON(data) | addSource(id, { type: "geojson", data }) の後に addLayer |
L.control.scale() | new maplibregl.ScaleControl() |
map.invalidateSize() | map.resize() |
setView([lat, lng]) は緯度が先ですが、MapLibre のすべての呼び出しは [lng, lat](経度、緯度)の順です。地図が別の半球に表示される場合、原因はタイルではなくこれです。また、スタイルの読み込み完了前に追加されたソースやレイヤーはエラーなしで破棄されるため、map.on("load", …) の中で追加してください。
“archive contains MVT vector tiles, but leafletRasterLayer is for displaying raster tiles”
これはラスター PMTiles を画像として描画する pmtiles.leafletRasterLayer のエラーです。このアーカイブはベクタータイルであるため拒否されます。オプションで変更することはできず、ラスター版のコピーもここにはありません。上記のいずれかの方法を使用してください。
“download.openstreetmap.fr returned 403”
これは抽出データとレプリケーションのミラーであり、タイルサーバーではありません。2026 年 10 月に過剰なダウンロードへの対策としてアドレスのブロックが行われ、運営者は個別にアクセスを復旧させると発表しました。ここにあるものはどれもそれを必要としません。このアーカイブは Protomaps のリリースから作られているため、自分で .osm.pbf を組み立てる必要はありません。もし OSM データを取得する場合は、分単位のストリームではなく定期的な抽出データを使用してください。
スタイル
同じタイルで、配色が異なります。1 つ選んでコピーし、変更して自分のものとして使えます。
| スタイル | 外観 | ファイル |
|---|
タイルに含まれるデータ
ページ読み込み時にアーカイブ自身のメタデータから読み取った内容です。スタイルではこれらのレイヤー名を使用する必要があります。
| ソースレイヤー | ズーム | フィールド |
|---|---|---|
| 読み込み中… | ||
このサービスが提供しないもの
後から想定外の事態にならないよう、あらかじめ明記しておきます。
- サービス品質保証(SLA)はありません。これは契約のない無料サービスです。遅くなることも、停止することもあり、URL やデータが予告なく変更・消滅する可能性もあります。プロダクトがこれに依存する場合は、タイルのコピーをセルフホストするか、サポートのある有料プランを他社から契約してください。
- 1 時間単位の最新データは反映されません。このコピーは最新の Protomaps リリースのスナップショットであり、ライブフィードではありません。今日行われた編集は含まれていません。更新時期についてはデータの鮮度についてを参照してください。
- ファイル全体のダウンロードはできません。リクエストには最大 512 KiB の範囲で応答します。PMTiles クライアントが要求するのは常にその範囲内です。1 回のリクエストでアーカイブ全体を要求すると
416が返されますが、これは正常な動作です。 - 帰属表示が必要です。このタイルは OpenStreetMap から派生しています。スタイル内または地図上に帰属表示を残してください。
データの鮮度について
このタイルは Protomaps Basemap のコピーであり、月 1 回再ビルドされます。
- 月 1 回再ビルドされます。ビルドは毎月第 1 日曜日の 00:30 から 07:00 UTC の間に実行されます。これは過去 3 年間のリリース履歴において、アップストリームが一度も公開を行っていない唯一の時間帯です。毎週ではなく毎月としているのは、新しいアーカイブによって CDN や各ブラウザに保存されているキャッシュが無効化されるためであり、また再ビルドのたびにビルド時間を消費するためです。
- ほとんどの月はそのまま動作します。再ビルドは、アップストリームが新しいリリースを公開したときにのみ実行されます。
- 現在どのアップストリームビルドが使われているか。このページの上部にあるアーカイブのメタデータボタンを使ってください。アーカイブ自身のヘッダーを読み取るため、ページの記載が古くなる心配がなく、常に現在の情報が表示されます。
- 再ビルドは一瞬では終わりません。約 6 時間半かけて 13 回の個別の再ビルドが 1 つずつ順番に実行されます。その時間帯の大部分では、コピーは 2 つのアップストリームビルドが混在した状態になります。その間にリクエストしたタイルは、リーダーが認識しているビルドではなく新しい方のビルドから返されることがあり、地図が壊れるのではなく別の場所のタイルとして表示されることがあります。
- 再ビルドが見送られることもあります。これはベストエフォートの処理であり、保証されたものではありません。1 つのステップが失敗した場合、残りのコピーは前回のアップストリームビルドのまま維持されます。それでも地図としては正常に動作します。