کاشی‌های نقشه پایه Protomaps

تمام نقشه پایه Protomaps، از زوم 0 تا 15، در قالب یک آرشیو PMTiles روی HTTP ساده ارائه می‌شود. هر کلاینت PMTiles را به این نشانی وصل کنید تا کار کند.

https://tiles.jasontally.com/basemap.pmtiles

کاشی مسدودشده در OpenStreetMap، یا خطای 403 از tile.openstreetmap.org؟ از اینجا شروع کنید.

امتحان کنید

همان آرشیو در سه استایل. با تغییر استایل، کاشی‌های بارگذاری‌شده سر جای خود می‌مانند و تغییر در لحظه انجام می‌شود.

z0

استایل خودتان را بسازید

رنگی را تغییر دهید یا لایه‌ای را خاموش کنید تا نقشه هم‌زمان به‌روز شود. سپس JSON استایل را کپی کنید؛ این یک استایل معمولی MapLibre است که می‌توانید خودتان میزبانی کنید. این استایل به همین کاشی‌ها اشاره می‌کند، بنابراین هر جا قرارش دهید کار می‌کند.

نحوه استفاده

این آرشیو از درخواست‌های بازه‌ای HTTP پشتیبانی می‌کند، بنابراین هر کلاینت PMTiles می‌تواند آن را بخواند.

یک. قرار دادن نقشه در صفحه

این فایل را کپی کنید. کل نمونه همین است: سه تگ، یک نشانی استایل، یک نقشه. هر دو کتابخانه روی نسخه مشخصی قفل شده‌اند و مجوز 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>

دو. جایگزینی کاشی‌ها در استایلی که از قبل دارید

هر استایل MapLibre کاشی‌های خود را از یک منبع (source) می‌خواند. برای استفاده از این کاشی‌ها، فقط همان یک 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\">&copy; 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 });

سه. استایلی را که ساخته‌اید کجا قرار دهید

دکمه JSON استایل در سازنده بالا، استایلی را که نقشه در همین لحظه رسم می‌کند به همراه ویرایش‌ها باز می‌کند و پنجره آن دکمه کپی دارد. دو راه برای استفاده از خروجی آن وجود دارد و هر دو صفحات وب معمولی هستند.

آن را به صورت یک فایل در کنار صفحه خود ذخیره کنید و 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 نگه دارید تا فقط یک فایل برای جابه‌جایی داشته باشید و نیازی به میزبانی فایل جداگانه نباشد. استایل همان است و فقط محل قرارگیری آن فرق می‌کند:

// 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 استفاده می‌کند. استایلی که برای طرح‌واره دیگری نوشته شده باشد بارگذاری می‌شود اما چیزی رسم نمی‌کند، چون نام لایه‌های منبع هم‌خوانی ندارند. نام نُه لایه در پایین فهرست شده است و یک استایل آماده v3 هم برای کپی کردن قرار دارد.

چهار. فونت‌ها، اسپرایت‌ها و کتابخانه‌ها

یک نقشه از کاشی‌ها، نشانی glyphs برای متن، در صورت نیاز نشانی sprite برای آیکون‌ها، و کتابخانه‌هایی برای رسم آن‌ها تشکیل می‌شود. همه این موارد در مسیرهایی که هر بخش انتظار دارد در اینجا موجود است، تا اگر بخواهید کل نقشه از یک نام میزبان بارگذاری شود.

موردنشانی در این میزبانمجوز

فایل‌های MapLibre و PMTiles هم روی jsDelivr و هم در اینجا قرار دارند. هر دو نشانی کار می‌کنند.

ردیف آخر در این صفحه استفاده نشده است. این فایل رندرکننده برداری Leaflet است که در اینجا منتشر شده تا جایگزینی فقط با یک تگ اسکریپت انجام شود. خود Leaflet روی CDN خودش باقی می‌ماند.

دو پشته فونت Noto Sans Regular و Noto Sans Medium به ۲۵۶ بازه‌ای که MapLibre درخواست می‌کند تقسیم شده‌اند، هر کدام شامل ۲۵۶ نقطه کد. هر ۲۵۶ بازه منتشر شده‌اند، نه فقط بازه‌های لاتین: Protomaps عوارض نقشه را با نام محلی آن‌ها برچسب‌گذاری می‌کند، بنابراین توکیو یا قاهره به بازه‌هایی در حدود ده‌ها هزار نیاز دارند. اگر فقط بازه‌های مورد نیاز نقشه انگلیسی منتشر شوند، آن برچسب‌ها بدون هیچ پیغام خطایی ناپدید می‌شوند.

پنج برگه اسپرایت، یکی برای هر تم بالادستی، در اندازه‌های 1x و 2x. استایل‌های زیر آیکون‌ها را از آن‌ها رسم می‌کنند، بنابراین این فایل‌ها واکشی می‌شوند. اگر در استایل خودتان نقاط شهرها، تابلوهای بزرگراه یا نقاط مکان‌ها را می‌خواهید، sprite را به فایل متناظر با تم خود ارجاع دهید. نام آیکون‌های هر برگه در فایل light.json آن فهرست شده است.

نقشه چه چیزهایی را رسم می‌کند و هر کدام از کجا می‌آید

هر لایه یکی از نُه لایه منبع در این آرشیو است، بنابراین هیچ بخشی در اینجا به آرشیو دومی نیاز ندارد. دو مورد از آن‌ها نیاز به توضیح دارند، چون ممکن است به نظر برسد داده‌ای کم است در حالی که این‌طور نیست:

لایهمنبعدر این نسخه هست؟
اشکال، راه‌ها، ساختمان‌ها، آب، زمینearth, landcover, landuse, water, roads, buildingsبله
نام مکان‌ها، راه‌ها و پهنه‌های آبیفیلد name در همان لایه‌هابله
آیکون نقاط مکان‌ها، نقاط شهرهاpois.kind، places، به همراه اسپرایتبله
تابلوهای مسیرroads.shield_textنه. نسخه‌ای که اکنون ارائه می‌شود این فیلد را ندارد
شماره پلاک‌هاbuildings.kind=address و buildings.addr_housenumberنه، به همان دلیل

هیچ آرشیو جدایی برای نشانی‌ها و هیچ لایه نشانی جداگانه‌ای برای واکشی وجود ندارد. پروژه بالادستی نقاط نشانی را درون لایه buildings به صورت عوارضی با kind=address و فیلد addr_housenumber قرار می‌دهد. این فیلد در نسخه‌ای از Protomaps اضافه شده که جدیدتر از نسخه فعلی این نقشه است؛ بنابراین لایه شماره پلاک‌ها ساخته شده و با به‌روزرسانی ماهانه بعدی خودبه‌خود فعال می‌شود. همین موضوع برای متن تابلوهای مسیر هم صادق است: نسخه فعلی دارای shield_text_length است که تعداد نویسه‌های شماره مسیر را مشخص می‌کند نه خود متن آن را، بنابراین تابلویی که از روی آن رسم شود یک شکل خالی روی هر آزادراه خواهد بود.

نوشتن یک لایه توسط خودتان و رنگ‌آمیزی آن

یک لایه شامل فیلتر، زوم، شکل و رنگ است. دو نکته در فیلتر اکثر افراد را به اشتباه می‌اندازد و هر دو بدون هیچ خطایی باعث رسم نشدن لایه می‌شوند:

بنابراین یک راه، نام آن، و کلیدی برای خاموش کردن خط به این صورت است:

{
  "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]
  }
}

نام، یک لایه نماد (symbol) است که فیلد name را با هاله یا بدون هاله در پس‌زمینه می‌خواند. نبودن گلیف هیچ خطایی ایجاد نمی‌کند، بنابراین پشته فونتی که درخواست می‌کنید باید یکی از دو مورد موجود روی این میزبان باشد:

{
  "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",

اگر استایلی پشته فونتی غیر از دو مورد بالا را درخواست کند، هیچ متنی برای آن رسم نخواهد شد. استایل‌های خود Protomaps برای متن توپر (bold) از Noto Sans Medium استفاده می‌کنند؛ در این مجموعه Noto Sans Bold وجود ندارد و به همین دلیل استایلی که آن را درخواست کند، تمام برچسب‌ها را بدون اعلام خطا از دست می‌دهد.

کدام میزبان سریع‌تر است و چه تصمیمی درباره آن گرفته شد. بر اساس اندازه‌گیری نه حدس و گمان: درخواست‌های یکی‌درمیان، تک‌به‌تک، روی یک اتصال، با کنار گذاشتن سه نمونه اول و تفکیک زمان دریافت اولین بایت از زمان انتقال. در این آزمایش، CDN برای تک‌تک این فایل‌ها ۱۰ تا ۴۵ میلی‌ثانیه زودتر پاسخ داد که به روشنی از قاعده ۱۵٪ عبور می‌کند. به همین دلیل کتابخانه‌ها از jsDelivr بارگذاری می‌شوند.

فونت‌ها و اسپرایت‌ها از همین میزبان ارائه می‌شوند، هرچند همان اندازه‌گیری نشان می‌دهد که CDN حدود ۱۰ میلی‌ثانیه زودتر به آن‌ها پاسخ می‌دهد. این یک تصمیم است، نه یک اندازه‌گیری. نقشه‌ای که از یک نام میزبان واحد بارگذاری شود، با از کار افتادن سرور دیگران خراب نمی‌شود، و از کار افتادن سرور گلیف یعنی نقشه‌ای بدون هیچ برچسب. اگر آن ۱۰ میلی‌ثانیه برایتان مهم‌تر است، glyphs را به protomaps.github.io/basemaps-assets/fonts ارجاع دهید تا با همان مسیر کار کند. این ارقام مربوط به یک کاربر در یک شهر است و اجرای دوباره این مقایسه تنها با یک دستور انجام می‌شود.

پنج. درباره Leaflet

تابع L.tileLayer در Leaflet تصویر رسم می‌کند، در حالی که این‌ها کاشی‌های برداری هستند. با این حال نقشه Leaflet همچنان می‌تواند از آن‌ها استفاده کند و در هر دو روش، نشانگرها، پاپ‌آپ‌ها و کنترل‌های شما حفظ می‌شوند. هر دو روش جایگزینی در بخش مهاجرت از کاشی‌های OpenStreetMap آمده‌اند.

شش. خواندن آرشیو با کتابخانه 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);

به دو کلاس دقت کنید. 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

سرورهای کاشی بر اساس سیاست استفاده از کاشی‌ها، از قصد آن تصویر را به جای نقشه فرستاده‌اند. این یک خرابی فنی نیست و اصلاح نشانی هم آن را برطرف نمی‌کند.

کارهایی که معمولاً اول امتحان می‌کنند:

شرکت‌ها، مدارس، VPNها و شبکه‌های موبایل از نشانی‌های مشترک استفاده می‌کنند و مسدودیت‌ها علاوه بر برنامه‌ها روی نشانی‌ها هم اعمال می‌شوند. از سمت خودتان نمی‌توانید تشخیص دهید کدام مورد شامل حال شما شده است.

بنابراین ارسال درخواست به آن سرورها را متوقف کنید. سه راه برای بی‌نیاز شدن از آن‌ها وجود دارد.

نگه داشتن 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: '&copy; <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>

پروژه protomaps-leaflet در منبع اصلی در حالت نگهداری است: فقط رفع اشکال، بدون قابلیت جدید. برای یک برنامه موجود با Leaflet مناسب است. کارهای جدید بهتر است با MapLibre GL شروع شوند.

نگه داشتن Leaflet، رندر با MapLibre

کتابخانه @maplibre/maplibre-gl-leaflet یک نقشه MapLibre را در قاب کاشی‌های Leaflet قرار می‌دهد. زمانی از آن استفاده کنید که یکی از استایل‌های بالا، یا عوارض زمین و سایه‌روشن پستی‌وبلندی‌ها (hillshade) را در برنامه‌ای می‌خواهید که همچنان بر پایه 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>

مهاجرت به MapLibre GL

یک کتابخانه، بدون مبدل، و استایل‌های بالا همان‌طور که هستند کار می‌کنند. کد کامل صفحه در بخش یک. قرار دادن نقشه در صفحه آمده است. تغییر اصلی، جابه‌جایی ترتیب مختصات است:

LeafletMapLibre 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.leafletRasterLayer است که فایل‌های PMTiles رستر را به صورت تصویر رسم می‌کند. این کاشی‌ها برداری هستند، بنابراین آن‌ها را نمی‌پذیرد. هیچ تنظیمی این رفتار را تغییر نمی‌دهد و هیچ نسخه رستری هم در اینجا وجود ندارد. از یکی از روش‌های بالا استفاده کنید.

“download.openstreetmap.fr returned 403”

این نشانی یک آینه (mirror) برای دریافت برش‌ها و همگام‌سازی داده است، نه سرور کاشی. در اکتبر ۲۰۲۶ پس از دانلودهای بی‌رویه، نشانی‌ها را مسدود کرد و گردانندگان آن اعلام کردند که دسترسی را به صورت موردی بازمی‌گردانند. هیچ بخشی در اینجا به آن نیاز ندارد: این آرشیو از انتشارهای Protomaps می‌آید، بنابراین نیازی نیست خودتان فایل‌های .osm.pbf را سرهم کنید. اگر داده‌های OSM را دریافت می‌کنید، به جای جریان لحظه‌ایِ دقیقه‌ای، از برش‌های دوره‌ای استفاده کنید.

استایل‌ها

همان کاشی‌ها، با رنگ‌های متفاوت. یکی را انتخاب کنید، کپی کنید و تغییر دهید؛ مال خودتان است.

استایلنمای ظاهریفایل

درون کاشی‌ها چه چیزهایی هست

هنگام بارگذاری صفحه از فراداده خود آرشیو خوانده می‌شود. این‌ها نام لایه‌هایی هستند که یک استایل باید از آن‌ها استفاده کند.

لایه منبعزومفیلدها
در حال بارگذاری…

این سرویس چه چیزی نیست

پاسخ‌های صریح، تا بعداً چیزی غافلگیرتان نکند.

این داده‌ها چقدر جدید هستند؟

این کاشی‌ها نسخه‌ای از نقشه پایه Protomaps هستند که ماهی یک بار بازسازی می‌شوند.

JSON استایل

open raw