Multiple Markers as Layer
Render hundreds or thousands of markers efficiently using a symbol layer with dynamic icons based on categories — far more performant than individual Marker objects.
Quick Usage
map.addLayer({
id: "poi-layer",
type: "symbol",
source: "pois",
layout: {
"icon-image": ["get", "icon"], // Dynamic icon from properties
"icon-size": 0.09,
},
});
Full Working Example
<!DOCTYPE html>
<html lang="en">
<head>
<title>Multiple Markers as Layer - Barikoi GL</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Barikoi GL -->
<link
rel="stylesheet"
href="https://unpkg.com/bkoi-gl@latest/dist/style/bkoi-gl.css"
/>
<script src="https://unpkg.com/bkoi-gl@latest/dist/iife/bkoi-gl.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
html,
body,
#map {
height: 100%;
}
.legend {
position: absolute;
bottom: 30px;
left: 20px;
background: white;
padding: 16px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
font-family: system-ui, sans-serif;
z-index: 10;
max-width: 180px;
}
.legend h4 {
margin: 0 0 12px 0;
font-size: 14px;
color: #333;
}
.legend-item {
display: flex;
align-items: center;
margin: 8px 0;
font-size: 13px;
}
.legend-item img {
width: 28px;
height: 28px;
margin-right: 10px;
}
</style>
</head>
<body>
<div id="map"></div>
<div class="legend">
<h4>Location Types</h4>
<div class="legend-item">
<img src="https://cdn-icons-png.flaticon.com/512/3448/3448609.png" />
Restaurant
</div>
<div class="legend-item">
<img src="https://cdn-icons-png.flaticon.com/512/2933/2933921.png" />
Hotel
</div>
<div class="legend-item">
<img src="https://cdn-icons-png.flaticon.com/512/3514/3514491.png" />
Shop
</div>
<div class="legend-item">
<img src="https://cdn-icons-png.flaticon.com/512/4298/4298087.png" />
Office
</div>
<div class="legend-item">
<img src="https://cdn-icons-png.flaticon.com/512/2785/2785482.png" />
Hospital
</div>
</div>
<script>
const map = new bkoigl.Map({
container: "map",
style: "https://map.barikoi.com/styles/barikoi-light/style.json",
center: [90.4125, 23.8103], // Dhaka
zoom: 12,
accessToken: "YOUR_BARIKOI_API_KEY",
});
const icons = {
restaurant: "https://cdn-icons-png.flaticon.com/512/3448/3448609.png",
hotel: "https://cdn-icons-png.flaticon.com/512/2933/2933921.png",
shop: "https://cdn-icons-png.flaticon.com/512/3514/3514491.png",
office: "https://cdn-icons-png.flaticon.com/512/4298/4298087.png",
hospital: "https://cdn-icons-png.flaticon.com/512/2785/2785482.png",
};
function generateRandomPoints(count = 200) {
const categories = Object.keys(icons);
const features = [];
const center = [90.4125, 23.8103];
const radius = 0.18;
for (let i = 0; i < count; i++) {
const angle = Math.random() * Math.PI * 2;
const distance = Math.random() * radius;
const category =
categories[Math.floor(Math.random() * categories.length)];
features.push({
type: "Feature",
properties: {
id: i,
name: `${category.charAt(0).toUpperCase() + category.slice(1)} #${
i + 1
}`,
category: category,
icon: `${category}-icon`,
},
geometry: {
type: "Point",
coordinates: [
center[0] + Math.cos(angle) * distance,
center[1] + Math.sin(angle) * distance,
],
},
});
}
return features;
}
map.on("load", async () => {
// Load all icons
await Promise.all(
Object.entries(icons).map(async ([name, url]) => {
const res = await map.loadImage(url);
map.addImage(`${name}-icon`, res.data);
})
);
// Add GeoJSON source
map.addSource("pois", {
type: "geojson",
data: {
type: "FeatureCollection",
features: generateRandomPoints(250),
},
});
// Add symbol layer
map.addLayer({
id: "pois-layer",
type: "symbol",
source: "pois",
layout: {
"icon-image": ["get", "icon"],
"icon-size": 0.09,
"icon-allow-overlap": false,
"icon-ignore-placement": false,
"icon-anchor": "bottom",
},
});
// Click: Show popup
map.on("click", "pois-layer", (e) => {
const props = e.features[0].properties;
const coords = e.features[0].geometry.coordinates;
new bkoigl.Popup({ offset: 30 })
.setLngLat(coords)
.setHTML(
`
<strong>${props.name}</strong><br>
<small style="color:#666">Category: ${props.category}</small>
`
)
.addTo(map);
});
// Hover cursor
map.on(
"mouseenter",
"pois-layer",
() => (map.getCanvas().style.cursor = "pointer")
);
map.on(
"mouseleave",
"pois-layer",
() => (map.getCanvas().style.cursor = "")
);
});
</script>
</body>
</html>
Why Use Symbol Layers?
| Method | Performance | Best For |
|---|---|---|
new bkoigl.Marker() | Slow (1–100) | Few markers with rich popups |
| Symbol Layer (this) | Fast (100–10,000+) | Large datasets, clustering, filtering |
Symbol layers are 10–50x faster than individual markers.
Key Layout Properties
| Property | Value Example | Purpose |
|---|---|---|
"icon-image" | ["get", "icon"] | Dynamic icon from feature properties |
"icon-size" | 0.09 | Scale (0.05 = small, 0.15 = large) |
"icon-allow-overlap" | false | Prevent stacking |
"icon-anchor" | "bottom" | Align pin tip to coordinate |
Advanced: Filter by Category
// Show only restaurants
map.setFilter("pois-layer", ["==", ["get", "category"], "restaurant"]);
// Clear filter
map.setFilter("pois-layer", null);
Advanced: Dynamic Icon Size by Zoom
"icon-size": [
"interpolate",
["linear"],
["zoom"],
10, 0.06,
15, 0.12
]
Scale to thousands — effortlessly!