Marcatori SVG Leaflet: allinea la punta con iconAnchor
Posiziona marcatori SVG in Leaflet mappando la punta visibile su iconAnchor. Considera margini del viewBox, adattamento delle proporzioni e dimensioni dell’icona.
In questa pagina
Per un marcatore SVG personalizzato di Leaflet, imposta iconSize alle dimensioni dell’immagine visualizzata e iconAnchor alla posizione in pixel della punta visibile dentro quell’immagine. Misura dall’angolo superiore sinistro dell’immagine. I margini SVG trasparenti contano nelle dimensioni, quindi metà larghezza e altezza intera possono mancare il punto in un segnaposto asimmetrico o con margini.
Questa guida usa un nostro segnaposto con la punta a destra del centro del corpo. Confrontiamo due cornici sorgente a due dimensioni dell’icona in una mappa browser senza tasselli. Se hai già grafica SVG pulita, usala direttamente: il posizionamento dell’ancora non richiede vettorializzazione.
Separa grafica, riquadro dell’immagine e punto sulla mappa
Qui si incontrano tre sistemi di coordinate. Il tracciato SVG descrive la grafica in unità sorgente. iconSize stabilisce il riquadro dell’immagine visualizzata in pixel. Leaflet colloca il punto interno al riquadro specificato da iconAnchor nella posizione geografica del marcatore. Il riferimento Icon definisce queste opzioni e l’origine dell’ancora nell’angolo superiore sinistro.
Un segnaposto convenzionale può arrivare al centro del bordo inferiore, ma la tua grafica può terminare altrove. L’impostazione predefinita documentata di Leaflet è un’ancora centrata quando è specificata una dimensione. I valori al centro in basso del nostro esperimento sono un’ipotesi esplicita che testiamo, non quell’impostazione predefinita.
Il tutorial ufficiale sulle icone personalizzate mostra come passare un’istanza L.icon a un marcatore. Il lavoro aggiuntivo per la grafica importata è trovare il punto dell’immagine che deve toccare la coordinata sulla mappa.

Mappa la punta sorgente nei pixel dell’icona
La punta del nostro segnaposto è alla coordinata sorgente (40, 80). La cornice stretta è viewBox="0 0 60 80". La versione con margini cambia solo la cornice in viewBox="-20 -10 100 120"; entrambi i file conservano lo stesso tracciato e l’apertura trasparente.
Il riferimento viewBox descrive questo rettangolo sorgente. Lo spazio vuoto al suo interno viaggia con la grafica. La nostra guida al riquadro di delimitazione SVG spiega perché una tela e una forma dipinta possono avere limiti diversi.
Entrambe le versioni usano esplicitamente preserveAspectRatio="xMidYMid meet". Questo adatta uniformemente la cornice dentro il viewport e centra lo spazio residuo. Vedi il riferimento MDN su preserveAspectRatio per queste impostazioni.
Per questa modalità, siano W e H le dimensioni dell’icona, (minX, minY, vbW, vbH) il viewBox e (tipX, tipY) la punta sorgente:
s = min(W / vbW, H / vbH)
offsetX = (W - vbW * s) / 2
offsetY = (H - vbH * s) / 2
anchorX = (tipX - minX) * s + offsetX
anchorY = (tipY - minY) * s + offsetYPer l’icona con margini 60×80, s è 0.6. La cornice sorgente 100×120 occupa 60×72 pixel, lasciandone quattro sopra e sotto. La punta diventa [36, 58], incluso quello scostamento verticale. Prendere semplicemente una frazione di ogni dimensione separatamente ignorerebbe l’adattamento uniforme.
| Cornice sorgente | Dimensioni dell’icona | Ancora della punta calcolata |
|---|---|---|
| Stretta, 0 0 60 80 | 60×80 | [40, 80] |
| Stretta, 0 0 60 80 | 90×120 | [60, 120] |
| Con margini, -20 -10 100 120 | 60×80 | [36, 58] |
| Con margini, -20 -10 100 120 | 90×120 | [54, 87] |
Questi calcoli valgono per la nostra immagine non trasformata e l’impostazione esplicita meet centrata. Un allineamento diverso, slice o none richiede la relativa mappatura delle coordinate. La nostra guida alle proporzioni tratta queste scelte. Anche trasformazioni CSS aggiuntive sull’immagine del marcatore richiedono un controllo separato.
Controlla il posizionamento in una mappa senza tasselli
Abbiamo usato Leaflet 1.9.4, la versione stabile indicata nella pagina ufficiale di download. L’esperimento carica localmente JavaScript e CSS fissati e non aggiunge livelli di tasselli. Ogni mappa proietta la coordinata [0, 0] su un mirino blu e mostra un’immagine SVG accanto a quel riferimento.
L’ispezione sorgente ha trovato un tracciato composto identico e nessuna immagine raster incorporata in ogni SVG. Le dimensioni delle immagini nel browser erano 60×80 e 90×120 pixel, come richiesto. Usando il rettangolo osservato di ogni immagine, abbiamo confrontato la posizione mappata della punta con il punto geografico proiettato:
| Sorgente | Dimensioni dell’icona | Errore con ancora ipotizzata al centro in basso | Errore con ancora della punta calcolata |
|---|---|---|---|
| Stretta | 60×80 | 10 px a destra | 0 px |
| Stretta | 90×120 | 15 px a destra | 0 px |
| Con margini | 60×80 | 6 px a destra, 22 px in alto | 0 px |
| Con margini | 90×120 | 9 px a destra, 33 px in alto | 0 px |
Il mirino coincideva visivamente con le punte corrette. Sono osservazioni di questo file creato in un browser Chrome, non una garanzia per ogni SVG, browser o integrazione cartografica. Il segnaposto con margini resta più piccolo dentro lo stesso riquadro dopo la correzione dell’ancora. Posizionamento e dimensione della grafica sono decisioni separate.
Copia un esempio iniziale funzionante
Salva questo HTML completo come marker.html e servilo da un server web locale. Carica i file Leaflet fissati dalla CDN collegata nella pagina di download. L’SVG è codificato in un URL immagine, quindi l’esempio non richiede un file icona o un servizio di tasselli. Per i due file della libreria serve accesso a Internet.
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Leaflet SVG tip anchor</title>
<link rel="stylesheet"
href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<style>
#map { width: 360px; height: 300px; background: #edf3ef; }
.cross { position: absolute; z-index: 1000; pointer-events: none;
width: 24px; height: 24px; transform: translate(-50%, -50%); }
.cross::before, .cross::after { content: ""; position: absolute;
background: #006cbe; }
.cross::before { top: 12px; width: 24px; height: 1px; }
.cross::after { left: 12px; width: 1px; height: 24px; }
</style>
<div id="map"></div>
<pre id="status">Loading marker…</pre>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="60" height="80" viewBox="-20 -10 100 120" preserveAspectRatio="xMidYMid meet"><path fill="#bf593c" fill-rule="evenodd" d="M30 0 C46 0 60 12 60 28 C60 46 46 54 40 80 C30 62 0 46 0 28 C0 12 14 0 30 0 Z M40 26 A10 10 0 1 0 20 26 A10 10 0 1 0 40 26 Z"/></svg>`;
const size = [60, 80];
const viewBox = [-20, -10, 100, 120];
const sourceTip = [40, 80];
const scale = Math.min(size[0] / viewBox[2], size[1] / viewBox[3]);
const anchor = [
(sourceTip[0] - viewBox[0]) * scale
+ (size[0] - viewBox[2] * scale) / 2,
(sourceTip[1] - viewBox[1]) * scale
+ (size[1] - viewBox[3] * scale) / 2
];
const map = L.map("map", {
zoomControl: false, attributionControl: false,
dragging: false, scrollWheelZoom: false, doubleClickZoom: false,
boxZoom: false, keyboard: false, touchZoom: false
}).setView([0, 0], 2);
const position = [0, 0];
const icon = L.icon({
iconUrl: "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg),
iconSize: size,
iconAnchor: anchor
});
const marker = L.marker(position, { icon }).addTo(map);
const container = document.querySelector("#map");
const cross = document.createElement("span");
cross.className = "cross";
container.append(cross);
const point = map.latLngToContainerPoint(position);
cross.style.left = point.x + "px";
cross.style.top = point.y + "px";
(async () => {
const image = marker.getElement();
await image.decode();
const ir = image.getBoundingClientRect();
const mr = container.getBoundingClientRect();
const displacement = [
ir.left + anchor[0] - mr.left - point.x,
ir.top + anchor[1] - mr.top - point.y
];
document.querySelector("#status").textContent = JSON.stringify({
version: L.version, size, anchor,
observedImageSize: [ir.width, ir.height], displacement
}, null, 2);
})();
</script>
</html>Lo stato mostrato dovrebbe riportare anchor: [36, 58], dimensioni immagine [60, 80] e scostamento zero. Cambia size in [90, 120]: lo stesso calcolo dà [54, 87]. Per usare la cornice stretta, cambia sia il viewBox dell’SVG sia l’array JavaScript viewBox in 0 0 60 80. Mantienili coerenti.
Il tracciato include un contorno interno circolare con fill-rule="evenodd", creando un’apertura reale. Il riferimento MDN su fill-rule spiega la regola. L’esempio non aggiunge bordi o trasformazioni al contenitore della mappa; richiederebbero attenzione nel confronto fra rettangoli del viewport e coordinate del contenitore.
Controlla la destinazione prima di cambiare la grafica
Se il marcatore continua a mancare la coordinata, controlla in quest’ordine:
- Conferma che l’immagine SVG sia caricata e che l’elemento del marcatore abbia larghezza e altezza in pixel desiderate.
- Individua la punta sorgente effettiva, comprese eventuali trasformazioni interne all’SVG.
- Controlla tutto il viewBox, le coordinate minime e la modalità di adattamento.
- Ricalcola l’ancora ogni volta che cambi cornice o dimensione dell’icona.
- Rimuovi sostituzioni CSS estranee durante il test, poi ricontrolla lo stile finale e le dimensioni di visualizzazione previste.
Mantieni fissa la coordinata geografica durante il controllo. Spostarla per compensare i margini dell’immagine nasconderebbe l’errore di posizionamento della grafica.
Questo è un procedimento per marcatori nel browser. Un marcatore SVG QGIS usa propri controlli di destinazione e sintassi di pittura parametrica; conserva un master SVG ordinario per la consegna generica nei browser.
Se l’icona esiste solo come PNG o JPG e ti servono contorni modificabili, converti la grafica con PerfectVector, esamina apertura e punta e salva un master SVG pulito. Poi calcola l’ancora Leaflet dalla cornice sorgente finale. La vettorializzazione prepara la grafica; il codice della mappa fornisce posizioni e collocazione. Se l’unico problema è uno scostamento in un SVG esistente, correggi direttamente cornice o ancora. La guida alla vettorializzazione delle immagini spiega quando è utile convertire una sorgente raster.
Domande frequenti
iconAnchor è una latitudine e longitudine? No. È una posizione in pixel dentro l’immagine dell’icona visualizzata, misurata dall’angolo superiore sinistro. La posizione geografica del marcatore è fornita separatamente.
Perché l’SVG con margini resta piccolo dopo aver corretto l’ancora? La cornice vuota della sorgente occupa ancora parte del riquadro dell’immagine. Correggere l’ancora posiziona la punta; non rimuove lo spazio vuoto e non ingrandisce la forma dipinta.
Devo sempre usare metà larghezza e altezza intera? Usa quei valori solo quando il punto di contatto visibile si trova davvero lì. Il nostro segnaposto asimmetrico e la sorgente con margini richiedono valori diversi, pur essendo entrambi grafica per marcatori di posizione.
Convertire un PNG in SVG corregge la posizione del marcatore? La conversione può fornire grafica modificabile. Cornice sorgente finale, dimensioni visualizzate, ancora e coordinate della mappa determinano comunque il posizionamento.
Fonti
- Leaflet — Riferimento Icon — Dimensioni in pixel, origine dell’ancora, centratura predefinita e allineamento geografico.
- Leaflet — Marcatori con icone personalizzate — Creare un’icona e assegnarla a un marcatore.
- Leaflet — Download — Versione stabile e relativi collegamenti fissati a JavaScript e CSS.
- MDN — viewBox — Rettangolo di coordinate sorgente usato nelle due cornici.
- MDN — preserveAspectRatio — Adattamento uniforme e spazio residuo centrato per xMidYMid meet.
- MDN — fill-rule — Regola pari-dispari usata per l’apertura trasparente.
Altro dal blog

Immagini SVG nei nodi Cytoscape.js: adatta i margini
Adatta grafica SVG personalizzata ai nodi Cytoscape.js con un esempio funzionante. Confronta spazio vuoto, contain e cover, poi controlla caricamento ed etichette.

Misura i tracciati SVG senza confondere la scala
Misura tracciati SVG in unità utente, considera trasformazioni e dimensioni nominali e distingui i totali dei contorni dalla calibrazione pathLength o dal percorso macchina.
