Marcadores SVG Leaflet: alinhe a ponta com iconAnchor
Posicione marcadores SVG no Leaflet mapeando a ponta visível para iconAnchor. Considere margens do viewBox, ajuste de proporção e mudanças no tamanho do ícone.
Nesta página
Para um marcador SVG personalizado do Leaflet, defina iconSize como as dimensões da imagem exibida e iconAnchor como a posição em pixels da ponta visível dentro dela. Meça a partir do canto superior esquerdo da imagem. Margens SVG transparentes contam nessas dimensões, portanto metade da largura e a altura inteira podem errar em um pino assimétrico ou com margens.
Este guia usa um pino original cuja ponta fica à direita do centro do corpo. Comparamos dois enquadramentos de origem em dois tamanhos de ícone em um mapa de navegador sem mosaicos. Se você já tem arte SVG limpa, use diretamente; posicionar a âncora não exige vetorização.
Separe a arte, a caixa da imagem e o ponto no mapa
Três sistemas de coordenadas se encontram aqui. O caminho SVG descreve a arte em unidades de origem. iconSize estabelece a caixa de imagem exibida em pixels. O Leaflet posiciona o ponto dentro dessa caixa especificado por iconAnchor na localização geográfica do marcador. A referência de Icon define essas opções e a origem da âncora no alto à esquerda.
Um pino convencional pode terminar no meio da borda inferior, mas sua arte pode terminar em outro lugar. O padrão documentado do Leaflet é uma âncora centralizada quando um tamanho é especificado. Os valores de centro inferior do nosso experimento são uma suposição explícita que testamos, não esse padrão.
O tutorial oficial de ícones personalizados mostra como passar uma instância de L.icon para um marcador. O trabalho extra para arte importada é encontrar o ponto dentro da imagem que deve tocar a coordenada do mapa.

Mapeie a ponta da origem para pixels do ícone
A ponta do nosso pino fica na coordenada de origem (40, 80). Seu enquadramento justo é viewBox="0 0 60 80". A versão com margens muda só esse quadro para viewBox="-20 -10 100 120"; os dois arquivos mantêm o mesmo caminho e abertura transparente.
A referência de viewBox descreve esse retângulo de origem. O espaço vazio dentro dele acompanha a arte. Nosso guia de caixa delimitadora SVG explica por que uma tela e uma forma pintada podem ter limites diferentes.
As duas versões usam explicitamente preserveAspectRatio="xMidYMid meet". Isso ajusta o quadro uniformemente dentro da viewport e centraliza o espaço restante. Veja a referência de preserveAspectRatio da MDN sobre essas configurações.
Para esse modo de ajuste, considere W e H as dimensões do ícone, (minX, minY, vbW, vbH) o viewBox e (tipX, tipY) a ponta de origem:
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 + offsetYPara o ícone com margens de 60×80, s é 0.6. O quadro de origem 100×120 ocupa 60×72 pixels, deixando quatro pixels acima e abaixo. A ponta se torna [36, 58], incluindo esse deslocamento vertical. Simplesmente usar uma fração de cada dimensão separadamente ignoraria a etapa de ajuste uniforme.
| Quadro de origem | Tamanho do ícone | Âncora da ponta calculada |
|---|---|---|
| Justo, 0 0 60 80 | 60×80 | [40, 80] |
| Justo, 0 0 60 80 | 90×120 | [60, 120] |
| Com margens, -20 -10 100 120 | 60×80 | [36, 58] |
| Com margens, -20 -10 100 120 | 90×120 | [54, 87] |
Esses cálculos se aplicam à nossa imagem sem transformação e à configuração explícita meet centralizada. Outro alinhamento, slice ou none exige o mapeamento de coordenadas correspondente. Nosso guia de proporção cobre essas escolhas. Transformações CSS adicionais na imagem do marcador também precisam de inspeção separada.
Confira a posição em um mapa sem mosaicos
Usamos Leaflet 1.9.4, a versão estável identificada na página oficial de download. O experimento carrega localmente seu JavaScript e CSS fixados e não adiciona camada de mosaicos. Cada mapa projeta a coordenada [0, 0] em uma mira azul; exibe uma imagem SVG junto desse ponto de referência.
A inspeção da origem encontrou um caminho composto idêntico e nenhuma imagem raster incorporada em cada SVG. As dimensões das imagens no navegador foram 60×80 e 90×120 pixels, conforme solicitado. Usando o retângulo observado de cada imagem, comparamos a posição mapeada da ponta com o ponto geográfico projetado:
| Origem | Tamanho do ícone | Erro com âncora presumida no centro inferior | Erro com âncora da ponta calculada |
|---|---|---|---|
| Justa | 60×80 | 10 px à direita | 0 px |
| Justa | 90×120 | 15 px à direita | 0 px |
| Com margens | 60×80 | 6 px à direita, 22 px acima | 0 px |
| Com margens | 90×120 | 9 px à direita, 33 px acima | 0 px |
A mira também encontrou visualmente as pontas corrigidas dos pinos. São observações deste arquivo criado em um navegador Chrome, não uma garantia para todo SVG, navegador ou integração de mapas. O pino com margens continua menor dentro da mesma caixa de imagem após corrigir sua âncora. Posição e tamanho da arte são decisões separadas.
Copie um exemplo funcional de marcador
Salve este HTML completo como marker.html e sirva em um servidor web local. Ele carrega os arquivos Leaflet fixados da CDN indicada pela página de download do Leaflet. O SVG é codificado em uma URL de imagem, portanto o exemplo não precisa de arquivo de ícone nem serviço de mosaicos. O acesso à internet é necessário para os dois arquivos da biblioteca.
<!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>O estado exibido deve informar anchor: [36, 58], tamanho da imagem [60, 80] e deslocamento zero. Mude size para [90, 120]; o mesmo cálculo fornece [54, 87]. Para usar o quadro justo, mude tanto o viewBox do SVG quanto o array JavaScript viewBox para 0 0 60 80. Mantenha-os consistentes.
O caminho inclui um contorno interno circular com fill-rule="evenodd", criando uma abertura real. A referência de fill-rule da MDN explica a regra. O exemplo não adiciona borda ou transformação ao contêiner do mapa; elas exigiriam cuidado ao comparar retângulos de viewport com coordenadas do contêiner.
Examine o destino antes de mudar a arte
Se seu marcador ainda erra a coordenada, examine nesta ordem:
- Confirme que a imagem SVG carregou e que o elemento do marcador tem a largura e a altura em pixels pretendidas.
- Localize a ponta real da origem, incluindo transformações aplicadas dentro do SVG.
- Confira o viewBox completo, suas coordenadas mínimas e seu modo de ajuste.
- Recalcule a âncora sempre que mudar o quadro ou o tamanho do ícone.
- Remova sobrescritas CSS sem relação durante o teste e confira novamente o estilo final e os tamanhos de exibição pretendidos.
Mantenha a coordenada geográfica fixa durante essa verificação. Movê-la para compensar margens da imagem ocultaria o erro de posicionamento da arte.
Este é um fluxo de marcador de navegador. Um marcador SVG no QGIS usa controles próprios de destino e sintaxe de pintura parametrizada; mantenha um original SVG comum para entrega geral no navegador.
Se seu ícone existe só como PNG ou JPG e você precisa de contornos editáveis, converta a arte com o PerfectVector, examine a abertura e a ponta e salve um original SVG limpo. Depois calcule a âncora Leaflet a partir do quadro de origem final. A vetorização prepara a arte; seu código de mapa fornece locais e posicionamento. Se o único problema é um deslocamento em um SVG existente, corrija diretamente o quadro ou a âncora. O guia de vetorização de imagens explica quando converter uma origem raster é útil.
Perguntas frequentes
iconAnchor é latitude e longitude? Não. É uma posição em pixels dentro da imagem de ícone exibida, medida a partir do canto superior esquerdo. A posição geográfica do marcador é fornecida separadamente.
Por que meu SVG com margens continua pequeno após corrigir a âncora? O quadro vazio da origem ainda ocupa parte da caixa da imagem. Corrigir a âncora posiciona a ponta; não remove o espaço vazio nem amplia a forma pintada.
Devo sempre usar metade da largura e a altura inteira? Use esse par só quando o ponto visível de contato realmente cair ali. Nosso pino assimétrico e nossa origem com margens precisam de valores diferentes, embora ambos sejam arte de marcador de localização.
Converter um PNG em SVG corrige a localização do marcador? A conversão pode fornecer arte editável. O quadro final da origem, as dimensões exibidas, a âncora e as coordenadas do mapa ainda determinam a posição do marcador.
Fontes
- Leaflet — Referência de Icon — Dimensões em pixels, origem da âncora, centralização padrão e alinhamento geográfico.
- Leaflet — Marcadores com ícones personalizados — Criar um ícone e atribuí-lo a um marcador.
- Leaflet — Download — A versão estável e seus links fixados de JavaScript e CSS.
- MDN — viewBox — O retângulo de coordenadas de origem usado nos dois quadros.
- MDN — preserveAspectRatio — Ajuste uniforme e espaço restante centralizado em xMidYMid meet.
- MDN — fill-rule — A regra par-ímpar usada para a abertura transparente.
Mais do blog

Imagens SVG em nós Cytoscape.js: ajuste e margens
Ajuste arte SVG personalizada dentro de nós Cytoscape.js com um exemplo funcional. Compare espaço vazio, contain e cover; confira o carregamento e a posição dos rótulos.

Meça o comprimento de caminhos SVG sem confundir a escala
Meça caminhos SVG em unidades de usuário, considere transformações e tamanho nominal e diferencie totais de contornos, calibração pathLength e percurso de máquina.
