বেশিরভাগ লোকাল ডিসকভারি প্রোডাক্ট একটি স্ক্রিনের উপর বাঁচে বা মরে: ম্যাপ। একজন ব্যবহারকারী একটি সহজ প্রশ্নের উত্তর পেতে আপনার অ্যাপ খোলেন, এখন আমার কাছাকাছি কী চলছে, আর ম্যাপই হলো সেই উত্তর পাওয়ার উপায়। সেই ম্যাপটি সঠিকভাবে তৈরি করা, সঠিক পিন, দ্রুত ফিল্টারিং এবং search engine সত্যিই পড়তে পারে এমন লিস্টিংসহ, যেকোনো ইভেন্ট গাইডের পেছনের মূল ইঞ্জিনিয়ারিং কাজ।
এই গাইড শূন্য থেকে একটি শহরের ইভেন্ট ম্যাপ তৈরি করার প্রতিটি ধাপ দেখায়: ইভেন্ট ডেটা মডেল করা, ভেন্যু geocode করে coordinate-এ আনা, marker প্লট ও cluster করা, ক্যাটেগরি ও এলাকা অনুযায়ী ফিল্টার করা এবং লিস্টিংকে সহজে খুঁজে পাওয়ার উপযোগী করা। এটি ধরে নেয় যে আপনি ইতিমধ্যে একটি base map রেন্ডার করতে জানেন। যদি না জানেন, তাহলে আমাদের টিউটোরিয়াল আপনার ওয়েবসাইটে ইন্টারঅ্যাক্টিভ ম্যাপ কীভাবে যুক্ত করবেন দিয়ে শুরু করুন এবং ইভেন্ট-নির্দিষ্ট অংশগুলোর জন্য এখানে ফিরে আসুন।
ইভেন্টকে তার ভেন্যুকে কেন্দ্র করে মডেল করুন
একটি ইভেন্ট হলো সময়-নির্দিষ্ট একটি ঘটনা যা কোনো একটি স্থানে ঘটে। স্থানটিই ম্যাপে যায়, তাই ভেন্যু হলো আপনার ডেটা মডেলের অ্যাঙ্কর। একটি ন্যূনতম ইভেন্ট রেকর্ড দেখতে এমন:
const event = {
id: 'evt_8471',
title: 'Late Night Jazz at The Vortex',
category: 'live-music',
venue: 'The Vortex Jazz Club, 11 Gillett Square, London N16 8AZ',
coords: null, // geocoding দ্বারা একবার পূরণ করা হয়
neighbourhood: null, // reverse geocoding দ্বারা একবার পূরণ করা হয়
startsAt: '2026-07-09T20:30:00Z',
url: 'https://example.com/tickets/8471',
};
দুটি null ফিল্ড গুরুত্বপূর্ণ। ম্যাপ যতবার লোড হয় ততবার আপনি একটি ভেন্যু geocode করতে চান না। একবার geocode করুন, যখন ইভেন্ট তৈরি বা import হয়, এরপর coordinate সংরক্ষণ করুন এবং সেগুলোকে স্থায়ী হিসেবে বিবেচনা করুন।
ধাপ ১: প্রতিটি ভেন্যুকে coordinate-এ geocode করুন
Geocoding একটি মানুষের বোধগম্য ঠিকানাকে এমন latitude ও longitude-এ পরিণত করে যা আপনি প্লট করতে পারেন। ভেন্যু string-টি Geocoding API-তে পাঠান এবং শীর্ষ রেজাল্ট থেকে coordinate পড়ে নিন:
async function geocodeVenue(address) {
const url = new URL('https://api.mapatlas.eu/geocoding/v1/search');
url.searchParams.set('text', address);
url.searchParams.set('size', '1');
const res = await fetch(url, { headers: { Authorization: `Bearer ${API_KEY}` } });
const data = await res.json();
const top = data.features?.[0];
if (!top) return null;
const [lon, lat] = top.geometry.coordinates;
return { lat, lon, label: top.properties.label };
}
এটি write time-এ চালান, যখন একটি ইভেন্ট আপনার সিস্টেমে প্রবেশ করে, এবং ফিরে আসা coordinate রেকর্ডে persist করুন। সঠিক পিন অবস্থান কেন গুরুত্বপূর্ণ এবং rooftop coordinate কীভাবে মানুষ আসলে যে দরজায় হেঁটে যায় তার থেকে আলাদা, তার পূর্ণ ব্যাখ্যার জন্য দেখুন ভবনের প্রবেশপথের geocoding।
ধাপ ২: ইভেন্টগুলোকে marker হিসেবে প্লট করুন
প্রতিটি ইভেন্টে coordinate থাকলে, প্লটিং একটি loop। প্রতিটি ক্যাটেগরিকে তার নিজস্ব রঙ দিন যাতে ম্যাপ এক নজরেই পড়া যায়:
const CATEGORY_COLORS = {
'live-music': '#E75480',
'theatre': '#006BA6',
'comedy': '#EE7C0E',
'food': '#16A34A',
'nightlife': '#7C3AED',
};
function plotEvents(map, events) {
events.forEach((evt) => {
if (!evt.coords) return;
const popup = new mapmetricsgl.Popup().setHTML(
`<strong>${evt.title}</strong><br>${new Date(evt.startsAt).toLocaleString()}` +
`<br><a href="${evt.url}">টিকিট</a>`,
);
new mapmetricsgl.Marker({ color: CATEGORY_COLORS[evt.category] || '#0c3456' })
.setLngLat([evt.coords.lon, evt.coords.lat])
.setPopup(popup)
.addTo(map);
});
}
এটিই বাস্তব প্রোডাক্টের পেছনের প্যাটার্ন। একটি লাইভ উদাহরণ হলো OnlyHere, লন্ডনে করার মতো কাজের একটি দৈনিক গাইড যা প্রতিটি ভেন্যু geocode করে এবং দিনের ইভেন্টগুলোকে একটি ইন্টারঅ্যাক্টিভ MapAtlas ম্যাপে প্লট করে, যাতে একজন ব্যবহারকারী একটি সমতল তালিকা স্ক্রল না করে ভেন্যু ও এলাকা অনুযায়ী কী চলছে তা ব্রাউজ করতে পারেন।
ধাপ ৩: শহর ভরে গেলে cluster করুন
কয়েকটি ইভেন্টের জন্য আলাদা marker ঠিক আছে। একটি শহরব্যাপী ফিড কয়েকটি নয়। একবার আপনি প্রায় ১০০ থেকে ২০০ পিন পার হয়ে গেলে, ইভেন্টগুলোকে একটি GeoJSON source-এ নিয়ে যান এবং ম্যাপকে সেগুলো cluster করতে দিন:
map.addSource('events', {
type: 'geojson',
cluster: true,
clusterRadius: 50,
data: {
type: 'FeatureCollection',
features: events.filter((e) => e.coords).map((e) => ({
type: 'Feature',
geometry: { type: 'Point', coordinates: [e.coords.lon, e.coords.lat] },
properties: { title: e.title, category: e.category },
})),
},
});
এখন শহরের কেন্দ্রে একটি ব্যস্ত রাত একশোটি ওভারল্যাপিং পিনের বদলে একটি সংখ্যাসহ একটি বুদবুদ দেখায়, এবং ব্যবহারকারী zoom in করলে cluster-টি আলাদা হয়ে যায়।
ধাপ ৪: ক্যাটেগরি ও এলাকা অনুযায়ী ফিল্টার করুন
ফিল্টারিং সেই জায়গা যেখানে একটি ইভেন্ট ম্যাপ একটি ইভেন্ট প্রোডাক্টে পরিণত হয়। পূর্ণ তালিকাটি মেমরিতে রাখুন এবং array ফিল্টার করুন, এরপর হাতে হাতে marker পুনর্নির্মাণ না করে ফিল্টার করা সেটটি ম্যাপ source-এ হস্তান্তর করুন:
function applyFilters(map, allEvents, { category, neighbourhood }) {
const filtered = allEvents.filter((e) =>
(!category || e.category === category) &&
(!neighbourhood || e.neighbourhood === neighbourhood),
);
map.getSource('events').setData({
type: 'FeatureCollection',
features: filtered.filter((e) => e.coords).map((e) => ({
type: 'Feature',
geometry: { type: 'Point', coordinates: [e.coords.lon, e.coords.lat] },
properties: { title: e.title },
})),
});
return filtered;
}
neighbourhood ফিল্ড পূরণ করতে, প্রতিটি ভেন্যুর coordinate একবার reverse geocode করুন এবং admin এলাকার নাম সংরক্ষণ করুন। সেই একটি মান একটি এলাকা dropdown, একটি "এই এলাকায় কী চলছে" ভিউ এবং একটি পরিচ্ছন্ন লিস্টিং পেজ চালায়, সবই run time-এ আরেকটি lookup ছাড়াই।
ধাপ ৫: লিস্টিংগুলোকে সহজে খুঁজে পাওয়ার উপযোগী করুন
একটি ম্যাপ ইতিমধ্যে আপনার অ্যাপে থাকা ব্যক্তির জন্য প্রশ্নের উত্তর দেয়। যে আরও অনেক বড় শ্রোতা একটি search engine বা একটি AI assistant-কে জিজ্ঞাসা করছে, "আজ রাতে Shoreditch-এ কী চলছে", তাদের জন্য এটি কিছুই করে না। সেই সিস্টেমগুলো পিক্সেল নয়, structured data পড়ে।
প্রতিটি ইভেন্টকে schema.org Event ফিল্ড দিয়ে মার্কআপ করুন এবং আপনার কাছে ইতিমধ্যে থাকা coordinate পুনরায় ব্যবহার করুন:
{
"@context": "https://schema.org",
"@type": "Event",
"name": "Late Night Jazz at The Vortex",
"startDate": "2026-07-09T20:30:00Z",
"location": {
"@type": "Place",
"name": "The Vortex Jazz Club",
"geo": { "@type": "GeoCoordinates", "latitude": 51.5462, "longitude": -0.0753 }
}
}
যে coordinate পিন আঁকে সেটিই structured data-কেও খাওয়ায়, তাই ডিসপ্লে ও ডিসকভারি একটি single source of truth থেকে আসে। এটিই একটি দেখতে সুন্দর ম্যাপ এবং একটি খুঁজে পাওয়া যায় এমন প্রোডাক্টের মধ্যে পার্থক্য। machine-readable লোকেশন ডেটা কেন AI search দৃশ্যমানতা চালায় সে সম্পর্কে আরও জানতে, দেখুন আমাদের গাইড AI search-এর জন্য লোকেশন-নির্দিষ্ট FAQ।
সবকিছু একসাথে আনা
একটি শহরের ইভেন্ট ম্যাপ হলো পাঁচটি চলমান অংশ: একটি ভেন্যু-অ্যাঙ্করড ডেটা মডেল, write time-এ geocoding, রঙিন marker, স্কেলে clustering এবং array-ভিত্তিক ফিল্টারিং, সবই এমন structured data-র উপর বসে যা লিস্টিংগুলোকে সহজে খুঁজে পাওয়ার উপযোগী রাখে। সেই অংশগুলো একটি geospatial প্ল্যাটফর্মে তৈরি করুন এবং coordinate পরিষ্কারভাবে geocoder থেকে ম্যাপে ম্যাপ থেকে schema-তে প্রবাহিত হয়, কোনো ডুপ্লিকেট lookup ছাড়াই এবং কোনো drift-করা ডেটা ছাড়াই।
MapAtlas আপনাকে geocoding, reverse geocoding, place search এবং map rendering দেয় যাতে এই সবকিছু EU-hosted, GDPR-compliant অবকাঠামোতে তৈরি করা যায়। আপনার নিজের শহর প্লট করা শুরু করতে Geocoding API এবং Search API এক্সপ্লোর করুন।
সাধারণ জিজ্ঞাসা
শহরের ইভেন্ট ম্যাপ তৈরি করতে আমার কী ডেটা দরকার?
সর্বনিম্ন হিসেবে, প্রতিটি ইভেন্টের একটি ভেন্যু ঠিকানা, একটি ক্যাটেগরি এবং একটি শুরুর সময় দরকার। ঠিকানাই একটি ইভেন্টকে ম্যাপের পিন-এ রূপান্তরিত করে: আপনি একবার এটি geocode করে latitude ও longitude পান, সেই coordinate সংরক্ষণ করেন এবং ইভেন্টটি প্রতিবার দেখানোর সময় সেগুলো পুনরায় ব্যবহার করেন। বাকি সবকিছু, যেমন টিকিট লিঙ্ক, ইমেজ এবং বর্ণনা, হলো metadata যা আপনি marker-এর popup-এ যুক্ত করেন।
ভেন্যুর ঠিকানাকে কীভাবে ম্যাপ coordinate-এ রূপান্তরিত করব?
ভেন্যুর ঠিকানা string দিয়ে একটি geocoding API কল করুন এবং response থেকে latitude ও longitude পড়ে নিন। MapAtlas Geocoding API-তে আপনি ঠিকানাটি একটি text query হিসেবে https://api.mapatlas.eu/geocoding/v1/search-এ পাঠান এবং শীর্ষ রেজাল্টের coordinate ব্যবহার করেন। প্রতিটি ভেন্যু একবার geocode করুন, ইভেন্ট তৈরির সময়, প্রতিটি পেজ লোডের সময় নয়, যাতে ম্যাপ দ্রুত থাকে এবং আপনার API ব্যবহার কম থাকে।
একটি ম্যাপ ধীর হওয়ার আগে কতগুলো ইভেন্ট marker দেখাতে পারে?
আলাদা আলাদা marker রেন্ডার করা zoom-out ভিউতে প্রায় ১০০ থেকে ২০০ পিন পর্যন্ত মসৃণ থাকে। এর বেশি হলে GeoJSON source clustering-এ চলে যান, যা low zoom-এ কাছাকাছি ইভেন্টগুলোকে একটি সংখ্যাযুক্ত বুদবুদে গোষ্ঠীবদ্ধ করে এবং ব্যবহারকারী zoom in করলে সেগুলো আলাদা হয়ে যায়। শত শত লিস্টিংসহ একটি শহরব্যাপী ইভেন্ট ফিডে শুরু থেকেই clustering ব্যবহার করা উচিত।
ব্যবহারকারীদের ক্যাটেগরি বা এলাকা অনুযায়ী ইভেন্ট ফিল্টার করতে কীভাবে দেব?
পূর্ণ ইভেন্ট তালিকাটি একটি array হিসেবে মেমরিতে রাখুন, প্রতিটি আইটেমে একটি ক্যাটেগরি ও একটি এলাকা যুক্ত করুন এবং ব্যবহারকারীর নির্বাচনের প্রতিক্রিয়ায় সেই array ফিল্টার করুন। এরপর marker ধ্বংস করে পুনরায় তৈরি না করে ফিল্টার করা সেট দিয়ে ম্যাপ source আপডেট করুন। এলাকা অনুযায়ী ব্রাউজিং একইভাবে কাজ করে: যেসব ইভেন্টের coordinate নির্বাচিত এলাকার ভেতরে পড়ে, বা reverse geocoding থেকে পাওয়া একই admin নাম ভাগ করে, সেগুলোতে ফিল্টার করুন।
একটি ইভেন্ট ম্যাপের কেন structured data দরকার?
একটি ভিজ্যুয়াল ম্যাপ মানুষকে সাহায্য করে, কিন্তু search engine ও AI assistant structured data পড়ে। প্রতিটি ইভেন্টকে schema.org Event ফিল্ড দিয়ে মার্কআপ করা, যার মধ্যে ভেন্যুর নাম, coordinate এবং শুরুর সময় থাকে, এটিই একটি লিস্টিংকে 'আজ রাতে Shoreditch-এ কী চলছে' জাতীয় প্রশ্নের উত্তরে হাজির হতে দেয়। আপনি ম্যাপে যে coordinate প্লট করেন সেটিই structured data-তে যায়, তাই ডিসকভারি ও ডিসপ্লে একটি single source of truth থেকে আসে।

