PerfectVector
Von Irene Kim

Leaflet-SVG-Marker: Spitze mit iconAnchor ausrichten

Positioniere eigene SVG-Marker in Leaflet über die sichtbare Spitze und iconAnchor. Berücksichtige viewBox-Ränder, Seitenverhältnisanpassung und geänderte Icongrößen.

9 Min. Lesezeit
Auf dieser Seite

Setze bei einem eigenen SVG-Marker in Leaflet iconSize auf die Abmessungen des angezeigten Bilds und iconAnchor auf die Pixelposition der sichtbaren Spitze innerhalb dieses Bilds. Miss von der oberen linken Bildecke aus. Transparente SVG-Ränder zählen zu diesen Abmessungen. Die halbe Breite und volle Höhe können deshalb bei einem asymmetrischen Pin oder einer Datei mit Rand danebenliegen.

Dieser Leitfaden verwendet einen selbst erstellten Pin, dessen Spitze rechts von der Mitte seines Körpers liegt. Wir vergleichen zwei Quellrahmen in zwei Icongrößen auf einer Browserkarte ohne Kacheln. Hast du bereits saubere SVG-Grafik, verwende sie direkt. Die Ankerplatzierung benötigt keine Vektorisierung.

Trenne Grafik, Bildrahmen und Kartenpunkt

Hier treffen drei Koordinatensysteme aufeinander. Der SVG-Pfad beschreibt die Grafik in Quelleinheiten. iconSize legt ihren angezeigten Bildrahmen in Pixeln fest. Leaflet platziert den durch iconAnchor angegebenen Punkt innerhalb dieses Rahmens an der geografischen Markerposition. Die Icon-Referenz definiert diese Optionen und den Ankerursprung links oben.

Ein üblicher Pin kann bis zur Mitte der Unterkante reichen. Deine Grafik kann aber an einer anderen Stelle enden. Leaflets dokumentierter Standard ist ein zentrierter Anker, wenn eine Größe angegeben wird. Die Werte für unten mittig in unserem Experiment sind eine ausdrücklich getestete Annahme, nicht dieser Standard.

Das offizielle Tutorial zu eigenen Icons zeigt, wie du eine L.icon-Instanz an einen Marker übergibst. Bei importierter Grafik musst du zusätzlich den Punkt im Bild finden, der die Kartenkoordinate berühren soll.

Sichtbare Spitze und Anker
Schematischer Vergleich eines asymmetrischen Standortpins in einer Zeichenfläche mit Rand, dessen Spitze erst neben einem Fadenkreuz liegt und es dann trifft
Illustration: Berücksichtige den vollständigen Bildrahmen beim Positionieren der sichtbaren Spitze. Das unten gemessene Browserexperiment verwendet separat erstellte SVG-Dateien.

Rechne die Quellspitze in Iconpixel um

Die Spitze unseres Pins liegt an der Quellkoordinate (40, 80). Sein enger Rahmen lautet viewBox="0 0 60 80". Die Variante mit Rand ändert nur diesen Rahmen zu viewBox="-20 -10 100 120". Beide Dateien behalten denselben Pfad und dieselbe transparente Öffnung.

Die viewBox-Referenz beschreibt dieses Quellrechteck. Darin enthaltener Leerraum wandert mit der Grafik mit. Unser Leitfaden zum SVG-Begrenzungsrahmen erklärt, warum Zeichenfläche und bemalte Form unterschiedliche Grenzen haben können.

Beide Versionen verwenden ausdrücklich preserveAspectRatio="xMidYMid meet". Damit wird der Rahmen gleichmäßig in den Viewport eingepasst und verbleibender Platz zentriert. Siehe MDNs preserveAspectRatio-Referenz für diese Einstellungen.

Für diesen Einpassungsmodus seien W und H die Iconabmessungen, (minX, minY, vbW, vbH) die viewBox und (tipX, tipY) die Quellspitze:

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 + offsetY

Beim 60×80-Icon mit Rand beträgt s genau 0.6. Der Quellrahmen von 100×120 belegt 60×72 Pixel. Oben und unten bleiben je vier Pixel. Die Spitze wird einschließlich dieses vertikalen Versatzes zu [36, 58]. Wer einfach jede Abmessung getrennt anteilig umrechnet, übersieht den Schritt der gleichmäßigen Einpassung.

QuellrahmenIcongrößeBerechneter Spitzenanker
Eng, 0 0 60 8060×80[40, 80]
Eng, 0 0 60 8090×120[60, 120]
Mit Rand, -20 -10 100 12060×80[36, 58]
Mit Rand, -20 -10 100 12090×120[54, 87]

Diese Rechnungen gelten für unser nicht transformiertes Bild und die ausdrücklich zentrierte meet-Einstellung. Eine andere Ausrichtung, slice oder none benötigt die passende Koordinatenabbildung. Unser Seitenverhältnis-Leitfaden behandelt diese Auswahl. Zusätzliche CSS-Transformationen am Markerbild musst du ebenfalls separat prüfen.

Prüfe die Platzierung auf einer Karte ohne Kacheln

Wir verwendeten Leaflet 1.9.4, die auf der offiziellen Downloadseite genannte stabile Version. Das Experiment lädt JavaScript und CSS in festgelegter Version lokal und fügt keine Kachelebene hinzu. Jede Karte projiziert die Koordinate [0, 0] auf ein blaues Fadenkreuz und zeigt ein SVG-Bild an diesem Referenzpunkt.

Die Quellprüfung fand in jedem SVG einen identischen zusammengesetzten Pfad und kein eingebettetes Rasterbild. Die Browserbilder waren wie gewünscht 60×80 und 90×120 Pixel groß. Anhand des beobachteten Bildrechtecks verglichen wir die umgerechnete Spitzenposition mit dem projizierten geografischen Punkt:

QuelleIcongrößeFehler mit angenommenem Anker unten mittigFehler mit berechnetem Spitzenanker
Eng60×8010 px rechts0 px
Eng90×12015 px rechts0 px
Mit Rand60×806 px rechts, 22 px oben0 px
Mit Rand90×1209 px rechts, 33 px oben0 px

Das Fadenkreuz traf auch sichtbar die korrigierten Pinspitzen. Das sind Beobachtungen dieser selbst erstellten Testdatei in einem Chrome-Browser, keine Garantie für jedes SVG, jeden Browser oder jede Kartenintegration. Der Pin mit Rand bleibt nach der Ankerkorrektur innerhalb desselben Bildrahmens kleiner. Platzierung und Grafikgröße sind getrennte Entscheidungen.

Kopiere eine funktionierende Markervorlage

Speichere dieses vollständige HTML als marker.html und stelle es über einen lokalen Webserver bereit. Es lädt die festgelegten Leaflet-Dateien vom CDN, auf das Leaflets Downloadseite verweist. Das SVG ist in einer Bild-URL kodiert, daher benötigt das Beispiel keine Icondatei und keinen Kacheldienst. Für diese beiden Bibliotheksdateien ist Internetzugriff erforderlich.

<!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>

Der angezeigte Status sollte anchor: [36, 58], die Bildgröße [60, 80] und einen Versatz von null melden. Ändere size zu [90, 120]. Dieselbe Rechnung ergibt [54, 87]. Um stattdessen den engen Rahmen zu verwenden, ändere sowohl die SVG-viewBox als auch das JavaScript-Array viewBox zu 0 0 60 80. Halte beide konsistent.

Der Pfad enthält eine kreisförmige Innenkontur mit fill-rule="evenodd", die eine echte Öffnung erzeugt. MDNs fill-rule-Referenz erklärt diese Regel. Das Beispiel fügt dem Kartencontainer weder Rahmen noch Transformation hinzu. Beides würde beim Vergleich von Viewport-Rechtecken mit Containerkoordinaten besondere Sorgfalt erfordern.

Prüfe das Ziel, bevor du die Grafik änderst

Wenn dein Marker weiterhin seine Koordinate verfehlt, prüfe Folgendes der Reihe nach:

  1. Bestätige, dass das SVG-Bild geladen wurde und das Markerelement die gewünschte Pixelbreite und -höhe hat.
  2. Finde die tatsächliche Quellspitze einschließlich möglicher Transformationen innerhalb des SVGs.
  3. Prüfe die vollständige viewBox, ihre Mindestkoordinaten und ihren Einpassungsmodus.
  4. Berechne den Anker bei jeder Änderung von Rahmen oder Icongröße neu.
  5. Entferne während des Tests unbeteiligte CSS-Überschreibungen und prüfe anschließend die endgültigen Stile und vorgesehenen Darstellungsgrößen erneut.

Halte die geografische Koordinate dabei fest. Diese Koordinate zum Ausgleich von Bildrändern zu verschieben würde den Fehler der Grafikplatzierung verdecken.

Das ist ein Browsermarker-Ablauf. Ein QGIS-SVG-Marker verwendet eigene Zielsteuerelemente und eine parametrisierte Farbsyntax. Behalte eine gewöhnliche SVG-Masterdatei für die allgemeine Browserausgabe.

Liegt dein Icon nur als PNG oder JPG vor und brauchst du bearbeitbare Konturen, konvertiere die Grafik mit PerfectVector, prüfe Öffnung und Spitze und speichere eine saubere SVG-Masterdatei. Berechne danach den Leaflet-Anker aus dem endgültigen Quellrahmen. Vektorisierung bereitet die Grafik vor; dein Kartencode liefert Standorte und Platzierung. Ist das einzige Problem ein Versatz in einem vorhandenen SVG, korrigiere direkt Rahmen oder Anker. Der Leitfaden zur Bildvektorisierung erklärt, wann das Konvertieren einer Rastervorlage sinnvoll ist.

Häufige Fragen

Ist iconAnchor ein Breiten- und Längengrad? Nein. Es ist eine Pixelposition im angezeigten Iconbild, gemessen von seiner oberen linken Ecke. Die geografische Markerposition wird separat angegeben.

Warum bleibt mein SVG mit Rand nach der Ankerkorrektur klein? Der leere Quellrahmen belegt weiterhin einen Teil des Bildrahmens. Die Ankerkorrektur positioniert die Spitze. Sie entfernt weder Leerraum noch vergrößert sie die bemalte Form.

Sollte ich immer die halbe Breite und volle Höhe verwenden? Verwende dieses Wertepaar nur, wenn der sichtbare Ansatzpunkt tatsächlich dort liegt. Unser asymmetrischer Pin und unsere Quelle mit Rand benötigen andere Werte, obwohl beide Standortmarker darstellen.

Behebt die Umwandlung von PNG in SVG die Markerposition? Die Konvertierung kann bearbeitbare Grafik liefern. Endgültiger Quellrahmen, angezeigte Abmessungen, Anker und Kartenkoordinaten bestimmen weiterhin die Platzierung des Markers.

Quellen

  1. Leaflet – Icon-Referenz – Pixelabmessungen, Ankerursprung, standardmäßige Zentrierung und geografische Ausrichtung.
  2. Leaflet – Marker mit eigenen Icons – Ein Icon erstellen und einem Marker zuweisen.
  3. Leaflet – Download – Stabile Version und ihre festgelegten JavaScript- und CSS-Links.
  4. MDN – viewBox – Das Quellkoordinatenrechteck der beiden Rahmen.
  5. MDN – preserveAspectRatio – Gleichmäßige Einpassung und zentrierter Restplatz bei xMidYMid meet.
  6. MDN – fill-rule – Gerade-Ungerade-Regel für die transparente Öffnung.

Mehr aus dem Blog

PerfectVector

Starte mit einer sauberen SVG,
die leichter zu bearbeiten ist

Keine Kreditkarte nötig