TLDR: Implement a solution to efficiently render over 10,000 map entities on a Mapbox map while maintaining with caching and clustering.
// Example of viewport-based rendering
const visibleFeatures = features.filter(feature => {
const [x, y] = map.project(feature.geometry.coordinates);
return (
x >= 0 && x <= map.getCanvas().width &&
y >= 0 && y <= map.getCanvas().height
);
});
// Example of marker clustering
const clusterOptions = {
radius: 50,
maxZoom: 15,
minPoints: 2,
style: {
color: '#00ff00',
size: 20
}
};
// Example of data batching
const batchSize = 1000;
for (let i = 0; i < features.length; i += batchSize) {
const batch = features.slice(i, i + batchSize);
map.addSource(`batch-${i}`, {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: batch
}
});
}
Dynamic clustering based on the current viewport to optimize rendering performance and improve user experience.
// Viewport-based clustering implementation
class ViewportCluster {
constructor(map, options = {}) {
this.map = map;
this.options = {
clusterRadius: options.clusterRadius || 50,
maxZoom: options.maxZoom || 15,
minPoints: options.minPoints || 2,
...options
};
this.clusters = new Map();
this.updateViewport = this.updateViewport.bind(this);
this.map.on('moveend', this.updateViewport);
}
updateViewport() {
const bounds = this.map.getBounds();
const zoom = this.map.getZoom();
const visibleFeatures = this.getVisibleFeatures(bounds);
// Clear previous clusters
this.clearClusters();
// Create new clusters based on viewport
this.createClusters(visibleFeatures, zoom);
}
getVisibleFeatures(bounds) {
return this.features.filter(feature => {
const [lng, lat] = feature.geometry.coordinates;
return bounds.contains([lng, lat]);
});
}
createClusters(features, zoom) {
const clusters = new Map();
features.forEach(feature => {
const clusterKey = this.getClusterKey(feature, zoom);
if (!clusters.has(clusterKey)) {
clusters.set(clusterKey, {
features: [],
center: feature.geometry.coordinates,
count: 0
});
}
const cluster = clusters.get(clusterKey);
cluster.features.push(feature);
cluster.count++;
// Update cluster center
cluster.center = this.calculateClusterCenter(cluster.features);
});
// Filter and process clusters
clusters.forEach((cluster, key) => {
if (cluster.count >= this.options.minPoints) {
this.createClusterMarker(cluster);
} else {
this.createIndividualMarkers(cluster.features);
}
});
this.clusters = clusters;
}
getClusterKey(feature, zoom) {
const precision = Math.pow(10, Math.floor(zoom / 2));
const [lng, lat] = feature.geometry.coordinates;
return `${Math.floor(lng * precision)},${Math.floor(lat * precision)}`;
}
calculateClusterCenter(features) {
const sum = features.reduce((acc, feature) => {
const [lng, lat] = feature.geometry.coordinates;
return {
lng: acc.lng + lng,
lat: acc.lat + lat
};
}, { lng: 0, lat: 0 });
return [
sum.lng / features.length,
sum.lat / features.length
];
}
createClusterMarker(cluster) {
// Implementation for creating a cluster marker
const marker = new mapboxgl.Marker({
element: this.createClusterElement(cluster.count)
})
.setLngLat(cluster.center)
.addTo(this.map);
marker.getElement().addEventListener('click', () => {
this.handleClusterClick(cluster);
});
}
createIndividualMarkers(features) {
features.forEach(feature => {
const marker = new mapboxgl.Marker()
.setLngLat(feature.geometry.coordinates)
.addTo(this.map);
});
}
createClusterElement(count) {
const el = document.createElement('div');
el.className = 'cluster-marker';
el.innerHTML = `<div class="cluster-content">${count}</div>`;
return el;
}
handleClusterClick(cluster) {
const zoom = this.map.getZoom();
if (zoom < this.options.maxZoom) {
this.map.flyTo({
center: cluster.center,
zoom: zoom + 2
});
} else {
this.showClusterPopup(cluster);
}
}
clearClusters() {
// Implementation for clearing existing clusters
this.clusters.forEach(cluster => {
if (cluster.marker) {
cluster.marker.remove();
}
});
}
}
// Usage example
const viewportCluster = new ViewportCluster(map, {
clusterRadius: 60,
maxZoom: 16,
minPoints: 3
});
Let's start with a quick question:
What is the capital of Israel?