Protomaps Basemap টাইল
জুম ০ থেকে ১৫ পর্যন্ত সম্পূর্ণ Protomaps Basemap সাধারণ HTTP-এর মাধ্যমে একটিমাত্র PMTiles আর্কাইভ হিসেবে পরিবেশন করা হয়। যেকোনো PMTiles ক্লায়েন্টকে এই URL-এ নির্দেশ করলেই তা কাজ করবে।
https://tiles.jasontally.com/basemap.pmtiles
- কোনো API কী লাগবে নাসাইন আপ করার কিছু নেই, আপনার কোডে কোনো টোকেনও লাগবে না।
- কোনো রেট লিমিট নেইকোনো থ্রটলিং নেই, কোনো কোটা নেই, কোনো বার্স্ট ক্যাপ নেই।
- মাসিক হালনাগাদমাসে একবার সর্বশেষ Protomaps রিলিজ থেকে নতুন করে তৈরি করা হয়।
OpenStreetMap টাইল কি ব্লক হয়েছে, অথবা tile.openstreetmap.org থেকে 403 আসছে? এখান থেকে শুরু করুন।
পরীক্ষা করে দেখুন
একই আর্কাইভ তিনটি স্টাইলে। স্টাইল পরিবর্তন করলে আগে থেকে লোড হওয়া টাইলগুলো স্বস্থানেই থাকে, তাই পরিবর্তনটি তাৎক্ষণিকভাবে ঘটে।
নিজের মতো তৈরি করুন
কোনো রঙ পরিবর্তন করুন বা একটি লেয়ার বন্ধ করুন, সাথে সাথে মানচিত্রটি আপডেট হবে। এরপর স্টাইল JSON কপি করে নিন; এটি একটি সাধারণ MapLibre স্টাইল যা আপনি নিজেই হোস্ট করতে পারবেন। এটি এই টাইলগুলোকেই নির্দেশ করে, তাই যেখানেই রাখুন না কেন এটি কাজ করতে থাকবে।
ব্যবহার করুন
আর্কাইভটি HTTP রেঞ্জ রিকোয়েস্ট সমর্থন করে, তাই যেকোনো PMTiles ক্লায়েন্ট এটি পড়তে পারে।
এক. পেজে একটি মানচিত্র বসান
এই ফাইলটি কপি করুন। এটিই সম্পূর্ণ উদাহরণ: তিনটি ট্যাগ, একটি স্টাইল URL, একটি মানচিত্র। দুটি লাইব্রেরির সংস্করণই নির্দিষ্ট করা এবং 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\">© 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 URL, ঐচ্ছিকভাবে আইকনের জন্য একটি sprite URL এবং এগুলো আঁকার লাইব্রেরি। এর সবকিছুই এখানে প্রত্যাশিত পাথে (path) রয়েছে, তাই আপনি চাইলে পুরো মানচিত্রটি একটিমাত্র হোস্টনেম থেকেই লোড করতে পারেন।
| কী | এখানকার URL | লাইসেন্স |
|---|
MapLibre এবং PMTiles ফাইলগুলো jsDelivr এবং এখানে—উভয় স্থানেই আছে। যেকোনো একটি URL ব্যবহার করলেই কাজ করবে।
শেষ সারিটি এই পেজে ব্যবহৃত হয়নি। এটি Leaflet ভেক্টর রেন্ডারার; এখানে প্রকাশ করা হয়েছে যাতে একটিমাত্র স্ক্রিপ্ট ট্যাগ দিয়েই পরিবর্তন করা যায়। মূল Leaflet তার নিজস্ব CDN-এই থাকে।
দুটি ফন্ট স্ট্যাক, Noto Sans Regular এবং Noto Sans Medium, প্রতিবারে ২৫৬টি কোডপয়েন্ট করে MapLibre-এর চাহিদামতো ২৫৬টি রেঞ্জে ভাগ করা হয়েছে। শুধু লাতিনগুলো নয়, সবকটি ২৫৬টি রেঞ্জই প্রকাশ করা হয়েছে: Protomaps স্থানগুলোর নাম স্থানীয় ভাষায় দেখায়, তাই টোকিও বা কায়রোর জন্য কয়েক হাজারের ঘরের রেঞ্জ প্রয়োজন হয়। শুধু ইংরেজি মানচিত্রের ফন্ট প্রকাশ করলে কোনো ত্রুটি না দেখিয়েই সেই লেবেলগুলো অদৃশ্য হয়ে যাবে।
পাঁচটি স্প্রাইট শিট, প্রতিটি আপস্ট্রিম ফ্লেভারের জন্য একটি করে, 1x এবং 2x রেজোলিউশনে। নিচের স্টাইলগুলো এগুলো থেকেই আইকন আঁকে, তাই এগুলো ফেচ করা হয়। আপনি যদি নিজের স্টাইলে শহরের বিন্দু (townspots), হাইওয়ে শিল্ড বা স্থানের চিহ্ন রাখতে চান, তবে sprite-কে আপনার ফ্লেভারের শিটটিতে নির্দেশ করুন। একটি শিটের আইকনগুলোর নাম তার light.json ফাইলে তালিকাভুক্ত আছে।
মানচিত্র কী আঁকে এবং প্রতিটি জিনিস কোথা থেকে আসে
প্রতিটি লেয়ার এই আর্কাইভের নয়টি সোর্স লেয়ারের একটি, তাই এখানে কোনো কিছুর জন্যই দ্বিতীয় কোনো আর্কাইভের প্রয়োজন নেই। এর মধ্যে দুটি আলাদা করে বলা দরকার, কারণ দুটোই না থাকার মতো মনে হলেও আসলে তা নয়:
| লেয়ার | উৎস | এই বিল্ডে আছে? |
|---|---|---|
| আকৃতি, রাস্তা, ভবন, পানি, ভূমি | earth, landcover, landuse, water, roads, buildings | হ্যাঁ |
| স্থান, রাস্তা এবং জলাশয়ের নাম | ওই একই লেয়ারগুলোর name ফিল্ড | হ্যাঁ |
| স্থানের আইকন, শহরের বিন্দু (townspots) | pois.kind, places, এবং স্প্রাইট | হ্যাঁ |
| রুট শিল্ড | roads.shield_text | না। বর্তমানে পরিবেশন করা বিল্ডে এই ফিল্ডটি নেই |
| বাড়ির নম্বর | buildings.kind=address এবং buildings.addr_housenumber | না, একই কারণে |
আলাদা কোনো ঠিকানার আর্কাইভ নেই এবং ফেচ করার মতো আলাদা কোনো ঠিকানার লেয়ারও নেই। আপস্ট্রিম প্রজেক্ট ঠিকানার বিন্দুগুলোকে ভেতরেই রাখে, buildings লেয়ারে থাকা এমন ফিচার হিসেবে যেগুলোতে kind=address এবং addr_housenumber আছে। এই মানচিত্রটি যে বিল্ড পরিবেশন করছে, সেই ফিল্ডটি তার পরবর্তী একটি Protomaps বিল্ডে যুক্ত হয়েছে; তাই বাড়ির নম্বরের লেয়ারটি তৈরি করাই আছে এবং পরবর্তী মাসিক রিফ্রেশে ডেটা এলে এটি স্বয়ংক্রিয়ভাবে চালু হয়ে যাবে। শিল্ড টেক্সটের ক্ষেত্রেও একই কথা প্রযোজ্য: বর্তমান বিল্ডে shield_text_length আছে, যা রুট নম্বরটি কী তা না বলে শুধু তাতে কয়টি অক্ষর আছে তা জানায়; ফলে সেটি দিয়ে শিল্ড আঁকলে প্রতিটি মহাসড়কে একটি ফাঁকা আকৃতি দেখা যাবে।
নিজে একটি লেয়ার লেখা এবং তাতে রঙ দেওয়া
একটি লেয়ার হলো একটি ফিল্টার, একটি জুম, একটি আকৃতি এবং একটি রঙ। ফিল্টারের দুটি জায়গায় সবাই ভুল করে এবং দুটোই কোনো ত্রুটি না দেখিয়ে ব্যর্থ হয়:
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 ফিল্ড পড়ে, যার পেছনে হ্যালো (halo) থাকতে পারে বা নাও থাকতে পারে। কোনো গ্লিফ না পাওয়া গেলে তা নীরবে বাদ পড়ে, তাই আপনি যে ফন্ট স্ট্যাক চাইবেন তা অবশ্যই এই হোস্টে থাকা দুটির একটি হতে হবে:
{
"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-এর নিজস্ব স্টাইলগুলো বোল্ড লেখার জন্য Noto Sans Medium ব্যবহার করে; এই সেটে কোনো Noto Sans Bold নেই, তাই কোনো স্টাইল সেটি চাইলে নীরবেই সব লেবেল হারিয়ে ফেলে।
কোন হোস্ট দ্রুততর এবং সে বিষয়ে কী সিদ্ধান্ত নেওয়া হয়েছে। অনুমানের বদলে পরিমাপ করা হয়েছে: একটি সংযোগে একবারে একটি করে পর্যায়ক্রমিক রিকোয়েস্ট পাঠিয়ে, প্রথম তিনটি নমুনা বাদ দিয়ে, ট্রান্সফার থেকে টাইম টু ফার্স্ট বাইট (TTFB) আলাদা করা হয়েছে। সেই পরীক্ষায় এই ফাইলগুলোর প্রতিটির জন্য CDN ১০ থেকে ৪৫ মিলিসেকেন্ড আগে সাড়া দিয়েছে, যা ১৫% নিয়মকে এবং নমুনার ব্যবধানকে স্পষ্টভাবে অতিক্রম করে। এই কারণেই লাইব্রেরিগুলো jsDelivr থেকে লোড করা হয়।
একই পরিমাপে CDN প্রায় ১০ মিলিসেকেন্ড আগে সাড়া দেবে জানা সত্ত্বেও ফন্ট এবং স্প্রাইটগুলো এখান থেকেই পরিবেশন করা হয়। এটি একটি সিদ্ধান্ত, পরিমাপ নয়। একটিমাত্র হোস্টনেম থেকে আসা মানচিত্র অন্য কারো সার্ভার ডাউন হলে ভেঙে পড়ে না, আর গ্লিফ সার্ভার ডাউন থাকা মানে লেবেলহীন মানচিত্র। আপনি যদি সেই ১০ মিলিসেকেন্ড বাঁচাতে চান, তবে glyphs-কে protomaps.github.io/basemaps-assets/fonts-এ নির্দেশ করুন; একই পাথে এটি কাজ করবে। এগুলো একটি শহরের একজন পাঠকের পরিসংখ্যান, এবং একটিমাত্র কমান্ড চালিয়ে তুলনাটি পুনরায় করা যায়।
পাঁচ. Leaflet প্রসঙ্গে
Leaflet-এর L.tileLayer ছবি আঁকে; কিন্তু এগুলো ভেক্টর টাইল। তবুও একটি 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 স্ট্রিং দিলে কাজ করবে না।
আর্কাইভটি একটি স্থিরচিত্র (snapshot), সরাসরি ফিড নয়। সময়ের হিসাব জানতে এটি কতটা নতুন? দেখুন।
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পরিবর্তন করে মূল (canonical) হোস্টে নিলে আনব্লক হয় না। মূল URL ব্যবহার করা বাধ্যতামূলক, কিন্তু ব্লকটি যুক্ত থাকে অ্যাপ বা দেখা যাওয়া Referer-এর সাথে, হোস্টনেমের সাথে নয়।- বারবার চেষ্টা করলে পরিস্থিতি আরও খারাপ হয়। অতিরিক্ত রিকোয়েস্টের চাপ ঠেকানোই এই ব্লকের অন্যতম উদ্দেশ্য।
- ডিস্ক থেকে খোলা পেজকে কোনো হেডার কৌশলই বাঁচাতে পারে না।
file://কোনোRefererপাঠায় না। এর বদলেX-Requested-Withপাঠাতে গেলে প্রতিটি টাইলের জন্য CORS প্রিফ্লাইট প্রয়োজন হবে, এবং টাইল সার্ভারগুলো সেই প্রিফ্লাইটও প্রত্যাখ্যান করে। Firefox এবং Chromium কোন হেডার পাঠাবে তাতে পার্থক্য থাকলেও নিয়ম দুটির জন্যই এক। localhost-এ কাজ করে, কিন্তু লাইভ সাইটে ব্লকড? কারণটি হলো অরিজিন (origin)। আপনার ডোমেইনের Referer-টিই ব্লক করা হয়েছে, আরlocalhostভিন্ন একটি Referer পাঠায়।
অফিস, স্কুল, 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: '© <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। এগুলো ওই লাইব্রেরির নিজস্ব থিম, উপরের স্টাইলগুলো নয়, তাই রঙগুলো হুবহু মিলবে না।- লেবেলগুলো সিস্টেম ফন্টে ক্যানভাস টেক্সট হিসেবে আঁকা হয়। কোনো গ্লিফ URL নেই, 404 হতে পারে এমন কোনো ফন্টও নেই।
- z15 হলো সবচেয়ে গভীর টাইল। লেয়ারটি এর পরে ওভারজুম (overzoom) করে, এবং
maxDataZoom: 15আগে থেকেই ডিফল্ট হিসেবে দেওয়া থাকে।
আপস্ট্রিমে protomaps-leaflet এখন রক্ষণাবেক্ষণ মোডে রয়েছে: শুধু বাগ ফিক্স হয়, নতুন ফিচার যোগ হয় না। বিদ্যমান Leaflet অ্যাপের জন্য এটি ঠিক আছে। নতুন কাজ MapLibre GL দিয়ে শুরু করা উচিত।
Leaflet রেখে MapLibre দিয়ে রেন্ডার করুন
@maplibre/maplibre-gl-leaflet Leaflet-এর টাইল পেইনে (pane) একটি 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-এর চেয়ে এক জুম নিচে আঁকে। Leaflet-এর ২৫৬ পিক্সেলের বিপরীতে এর টাইলগুলো ৫১২ পিক্সেলের, তাই
minzoom12 দেওয়া একটি লেয়ার তখনই দেখা যাবে যখন Leaflet z13-এ পৌঁছাবে। - Leaflet তখনো ওপরেই আঁকে। ক্যানভাসটি টাইল পেইনে থাকে, তাই মার্কার এবং ওভারলেগুলো রাস্টার লেয়ারের ওপর যেভাবে কাজ করত ঠিক সেভাবেই কাজ করে।
MapLibre GL-এ চলে যান
একটিমাত্র লাইব্রেরি, কোনো অ্যাডাপ্টার নেই, এবং উপরের স্টাইলগুলো যেমন আছে তেমনই কাজ করে। সম্পূর্ণ পেজের উদাহরণ এক. পেজে একটি মানচিত্র বসান অংশে রয়েছে। পরিবর্তনটি মূলত স্থানাঙ্কের ক্রম উল্টে দেওয়া:
| 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])-এ আগে অক্ষাংশ (latitude) থাকে; কিন্তু 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”
ওটি একটি এক্সট্র্যাক্ট এবং রেপ্লিকেশন মিরর, কোনো টাইল সার্ভার নয়। ২০২৬ সালের অক্টোবরে অতিরিক্ত ডাউনলোডের পর এটি কিছু ঠিকানা ব্লক করে দেয় এবং পরিচালকরা জানান যে তাঁরা আলাদাভাবে বিবেচনা করে অ্যাক্সেস ফিরিয়ে দেবেন। এখানকার কোনো কিছুর জন্যই সেটির প্রয়োজন নেই: এই আর্কাইভটি Protomaps রিলিজ থেকে আসে, তাই আপনাকে নিজে .osm.pbf তৈরি করতে হবে না। আপনি যদি OSM ডেটা নামাতেই চান, তবে প্রতি মিনিটের স্ট্রিমের বদলে নির্দিষ্ট সময় পরপর প্রকাশিত এক্সট্র্যাক্ট নিন।
স্টাইলসমূহ
একই টাইল, ভিন্ন রঙ। যেকোনো একটি বেছে নিন, কপি করুন, পরিবর্তন করুন—এটি আপনারই।
| স্টাইল | দেখতে যেমন | ফাইল |
|---|
টাইলের ভেতরে কী আছে
পেজ লোড হওয়ার সময় আর্কাইভের নিজস্ব মেটাডেটা থেকে পড়া হয়েছে। একটি স্টাইলে অবশ্যই এই লেয়ার নামগুলো ব্যবহার করতে হবে।
| সোর্স লেয়ার | জুম | ফিল্ডসমূহ |
|---|---|---|
| লোড হচ্ছে… | ||
এটি যা নয়
সোজা উত্তর, যাতে পরে কোনো কিছু আপনাকে অবাক না করে।
- নিরবচ্ছিন্ন সেবার কোনো নিশ্চয়তা নেই। এটি কোনো চুক্তি ছাড়া একটি বিনামূল্যের সেবা। এটি ধীরগতির হতে পারে, বন্ধ হয়ে যেতে পারে, এবং URL ও ডেটা কোনো পূর্বঘোষণা ছাড়াই পরিবর্তিত বা বিলুপ্ত হতে পারে। আপনার পণ্য যদি এর ওপর নির্ভরশীল হয়, তবে টাইলের নিজস্ব কপি নিজে হোস্ট করুন অথবা কারো কাছ থেকে সমর্থিত প্ল্যান কিনুন।
- এটি কখনোই প্রতি ঘণ্টার তাজা ডেটা নয়। এই কপিটি সর্বশেষ Protomaps রিলিজের একটি স্থিরচিত্র, কোনো সরাসরি ফিড নয়। আজ করা সম্পাদনাগুলো এতে নেই। সময়ের হিসাব জানতে এটি কতটা নতুন? দেখুন।
- পুরো ফাইল একসাথে ডাউনলোড করা যাবে না। রিকোয়েস্টগুলোর উত্তর সর্বোচ্চ 512 KiB রেঞ্জে দেওয়া হয়, এবং একটি PMTiles ক্লায়েন্ট কখনো এর বেশি চায় না। এক রিকোয়েস্টে পুরো আর্কাইভটি চাইলে
416রিটার্ন করে, যা সঠিক আচরণ। - অ্যাট্রিবিউশন (স্বীকৃতি) আবশ্যক। টাইলগুলো OpenStreetMap থেকে তৈরি। আপনার স্টাইলে বা মানচিত্রে অ্যাট্রিবিউশন লাইনটি বজায় রাখুন।
এটি কতটা নতুন?
টাইলগুলো Protomaps Basemap-এর একটি কপি, যা মাসে একবার নতুন করে তৈরি করা হয়।
- মাসে একবার নতুন করে তৈরি। মাসের প্রথম রবিবার 00:30 থেকে 07:00 UTC-এর মধ্যে বিল্ডটি চলে; তিন বছরের রিলিজ ইতিহাসে এটিই একমাত্র সময়সীমা যখন আপস্ট্রিম কখনো নতুন রিলিজ প্রকাশ করেনি। এটি সাপ্তাহিক না করে মাসিক করা হয়েছে, কারণ নতুন আর্কাইভ CDN এবং ব্যবহারকারীদের ব্রাউজারে আগে থেকে থাকা ক্যাশ বাতিল করে দেয় এবং প্রতিটি রিবিল্ডে বিল্ড টাইম খরচ হয়।
- বেশিরভাগ মাসে কিছুই করার থাকে না। শুধুমাত্র আপস্ট্রিম নতুন কোনো রিলিজ প্রকাশ করলেই রিবিল্ড করা হয়।
- এই মুহূর্তে কোন আপস্ট্রিম বিল্ডটি ব্যবহৃত হচ্ছে? এই পেজের ওপরে থাকা আর্কাইভ মেটাডেটা বোতামটি ব্যবহার করুন। এটি সরাসরি আর্কাইভের নিজস্ব হেডার পড়ে, তাই উত্তরটি পেজের কোনো পুরনো তথ্যের ওপর নির্ভর না করে সবসময় বর্তমান অবস্থা জানায়।
- রিবিল্ড তাৎক্ষণিকভাবে সম্পন্ন হয় না। এটি প্রায় সাড়ে ছয় ঘণ্টা ধরে একে একে চলা তেরোটি আলাদা রিবিল্ডের সমষ্টি। ওই সময়ের বেশিরভাগ অংশে কপিটি দুটি আপস্ট্রিম বিল্ডের মিশ্রণ হিসেবে থাকে। এই সময়ে আপনার রিকোয়েস্ট করা কোনো টাইল হেডারে ঘোষিত বিল্ডের বদলে নতুন বিল্ড থেকে আসতে পারে, যা ভাঙা মানচিত্র হিসেবে নয় বরং ভুল স্থানের টাইল হিসেবে দেখা দেয়।
- রিবিল্ড বাদও পড়তে পারে। এটি সর্বোচ্চ চেষ্টার (best-effort) একটি কাজ, কোনো নিশ্চয়তা নয়। কোনো একটি ধাপ ব্যর্থ হলে কপির বাকি অংশ আগের আপস্ট্রিম বিল্ডেই থেকে যায়। মানচিত্রটি তখনো সচল থাকে।