I'm trying to create custom SVG markers using L.svgOverlay in Leaflet. The markers are implemented as SVG <g> elements. They work correctly when I zoom in and out: they stay at the correct geographic positions. However, if I pan (move) the map and then zoom, the markers go insane. I can't figure out what I do wrong.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
<style>
html, body {
height: 100%;
margin: 0;
font-family: Arial;
}
#map {
height: 100vh;
width: 100vw;
}
.villageMarker {
pointer-events: auto !important;
cursor: pointer;
}
</style>
</head>
<body>
<div id="map"></div>
</div>
<script>
function updateSvgPositionsZoom() {
const mapBounds = map.getBounds();
svgOverlay.setBounds(mapBounds);
const svgOverlayElement = svgOverlay.getElement();
Array.from(svgOverlayElement.children).forEach(icon => {
const i = icon.id.split("_")[1];
const coords = coordinates[i];
const point = map.latLngToLayerPoint(coords);
icon.setAttribute('transform', `translate(${point.x},${point.y})`);
});
};
async function show() {
for (const i in iconWrappers) {
const coords = coordinates[i];
const iconWrapper = iconWrappers[i];
const circle = document.createElementNS(ns, "circle");
circle.setAttribute('r', 10);
circle.setAttribute('fill', '#fc43ab');
circle.setAttribute('stroke', '#000000');
circle.setAttribute('stroke-width', '1');
iconWrapper.appendChild(circle)
const point = map.latLngToLayerPoint(coords);
iconWrapper.setAttribute('transform', `translate(${point.x},${point.y})`);
};
};
async function main() {
map = L.map('map', {center: [52.38, 9.77], zoom: 6});
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
svg = document.createElementNS(ns, "svg");
svg.style.pointerEvents = 'none';
for (const i in coordinates) {
const iconWrapper = document.createElementNS(ns, "g");
iconWrapper.setAttribute('id', `marker_${i}`);
iconWrapper.classList.add('villageMarker');
svg.appendChild(iconWrapper);
iconWrappers.push(iconWrapper);
};
svgOverlay = L.svgOverlay(svg, map.getBounds(), {
interactive: true,
opacity: 1
}).addTo(map);
svgOverlay.getElement().setAttribute('xmlns', ns);
map.on('zoomend', updateSvgPositionsZoom);
show();
};
let pathname;
let map;
let ns = "http://www.w3.org/2000/svg";
let svgOverlay;
let svg;
let coordinates = [
[52.38, 9.77],
[51.52, 7.47],
[53.56, 9.97]
]
let iconWrappers = [];
main();
</script>
</body>
</html>
What am I missing to make markers stable during both panning and zooming? Should I be handling different events or is there a fundamental mistake in my coordinate conversion?
L.Markerinstead of aL.SvgOverlay.