بلاطات خريطة الأساس Protomaps Basemap

خريطة الأساس Protomaps Basemap كاملةً، من مستوى التكبير 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 بلاطاته من مصدر بيانات. لاستخدام هذه البلاطات، غيِّر قيمة 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 schema v3. أي نمط كُتب لمخطط مختلف سيُحمَّل ثم لا يرسم شيئًا، لأن أسماء طبقات المصدر غير متطابقة. أسماء الطبقات التسع مدرجة أدناه، وكذلك نمط v3 جاهز يمكنك النسخ منه.

رابعًا. الخطوط والصور المجمعة (sprites) والمكتبات

تتكون الخريطة من البلاطات، ورابط glyphs للنصوص، واختياريًا رابط sprite للأيقونات، والمكتبات اللازمة لرسم ذلك كله. كل هذه العناصر متوفرة هنا في المسارات التي يتوقعها كل منها، بحيث يمكن تقديم الخريطة بالكامل من اسم مضيف واحد إذا أردت.

العنصرالرابط هناالرخصة

ملفات MapLibre وPMTiles متوفرة على jsDelivr وهنا أيضًا. كلا الرابطين يعمل.

الصف الأخير لا تستخدمه هذه الصفحة. إنه مصيِّر البلاطات المتجهة لـ Leaflet، وهو منشور هنا ليكون الاستبدال مجرد وسم script واحد. أما مكتبة Leaflet نفسها فتبقى على شبكة CDN الخاصة بها.

حزمتا خطوط، Noto Sans Regular وNoto Sans Medium، مقسمتان إلى 256 نطاقًا يطلبها MapLibre، بواقع 256 نقطة ترميز في كل نطاق. النطاقات الـ 256 منشورة كلها، وليس اللاتينية فحسب: إذ تسمّي Protomaps المعالم بأسمائها المحلية، لذا تحتاج طوكيو أو القاهرة إلى نطاقات في عشرات الآلاف. إذا نشرت فقط ما تستخدمه خريطة باللغة الإنجليزية فستختفي تلك التسميات دون أي رسالة خطأ توضح السبب.

خمس لوحات صور مجمعة (sprites)، واحدة لكل مظهر من المصدر الأصلي، بدقتي 1x و2x. ترسم الأنماط أدناه الأيقونات منها، لذلك يتم جلبها. إذا أردت نقاط المدن أو دروع الطرق السريعة أو نقاط الاهتمام في نمطك الخاص، فوجِّه sprite إلى اللوحة الموافقة لمظهرك. أسماء الأيقونات في اللوحة مدرجة في ملف light.json الخاص بها.

ما ترسمه الخريطة، ومن أين يأتي كل عنصر

كل طبقة هي واحدة من طبقات المصدر التسع في هذا الأرشيف، لذا لا يحتاج أي شيء هنا إلى أرشيف ثانٍ. وهناك طبقتان تجدر الإشارة إليهما، لأن كلتيهما تبدوان كأن شيئًا مفقود بينما الأمر ليس كذلك:

الطبقةالمصدرفي هذه النسخة؟
الأشكال، الطرق، المباني، المياه، اليابسةearth, landcover, landuse, water, roads, buildingsنعم
أسماء الأماكن والطرق والمسطحات المائيةحقل name في تلك الطبقات نفسهانعم
أيقونات نقاط الاهتمام ونقاط المدنpois.kind وplaces، إضافة إلى اللوحة الرسومية (sprite)نعم
دروع أرقام الطرق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 الخاص باللوحة.

إذا كان لديك نمط يوجِّه محارفه (glyphs) إلى 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 نفسها الخط Noto Sans Medium للنص العريض؛ ولا يوجد Noto Sans Bold في هذه المجموعة، ولهذا فإن النمط الذي يطلبه يفقد كل التسميات بصمت.

أي مضيف أسرع، وما القرار الذي اتُّخذ بهذا الشأن. بالقياس لا بالافتراض: طلبات متداخلة، واحدًا تلو الآخر، عبر اتصال واحد، مع استبعاد أول ثلاث عينات، وفصل زمن وصول أول بايت عن زمن النقل. في هذا الاختبار استجابت شبكة CDN أسرع بـ 10 إلى 45 مللي ثانية لكل ملف من هذه الملفات، وهو ما يتجاوز قاعدة الـ 15% بهامش واضح في العينات. لهذا السبب تُحمَّل المكتبات من jsDelivr.

تُقدَّم الخطوط والصور المجمعة (sprites) من هنا على الرغم من أن القياس نفسه يُظهر أن شبكة CDN تستجيب أسرع بنحو 10 مللي ثانية. هذا قرار تصميمي وليس قياسًا. فالخريطة التي تأتي من اسم مضيف واحد لا تتعطل عندما يتعطل خادم طرف آخر، وتوقف خادم المحارف يعني خريطة بلا تسميات. إذا كنت تفضل كسب الـ 10 مللي ثانية، فوجِّه 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. استخدمها عندما تريد أحد الأنماط أعلاه، أو التضاريس وتظليل التلال، في تطبيق يظل مبنيًا على 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”

هذه مرآة للاستخراج والمزامنة وليست خادم بلاطات. في أكتوبر 2026 حظرت المرآة عناوين بعد عمليات تنزيل جائرة، وذكر المشغلون أنهم سيعيدون الوصول لكل حالة على حدة. لا شيء هنا يعتمد عليها: فهذا الأرشيف مبني من إصدارات Protomaps، لذا لا تحتاج إلى تجميع ملفات .osm.pbf بنفسك. وإذا كنت تسحب بيانات OSM مباشرةً، فاستخدم مستخرجًا دوريًا بدلًا من التدفق الدقيقي.

الأنماط

البلاطات نفسها بألوان مختلفة. اختر نمطًا وانسخه وعدِّله كما تشاء.

النمطالمظهرالملف

محتويات البلاطات

تُقرأ من البيانات الوصفية للأرشيف نفسه عند تحميل الصفحة. هذه هي أسماء الطبقات التي يجب أن يستخدمها النمط.

طبقة المصدرمستوى التكبيرالحقول
جارٍ التحميل…

ما لا تقدمه هذه الخدمة

إجابات واضحة ومباشرة حتى لا يفاجئك شيء لاحقًا.

ما مدى حداثة البيانات؟

البلاطات هي نسخة من خريطة الأساس Protomaps Basemap، ويُعاد بناؤها مرة واحدة في الشهر.

JSON النمط

open raw