Icônes SVG MapLibre : pixels et taille du symbole
Décode un dessin SVG pour une couche de symboles MapLibre, règle la densité et la taille de l’icône, puis garde un maître modifiable. Vérifie la forme affichée et son ouverture.
Sur cette page
Pour utiliser une image d’icône SVG dans MapLibre GL JS, décode le SVG avec une Image du navigateur, fournis cette image à addImage et référence son ID dans icon-image d’une couche de symboles. Garde le SVG source séparément : l’image de style enregistrée fournit des pixels pour le rendu, pas des objets de chemin SVG modifiables.
L’exemple officiel actuel de symbole SVG montre cette méthode de décodage. Affirmer globalement que MapLibre ne peut pas afficher de dessin SVG passerait à côté. Notre exemple fixe la version 6.12.0 et teste un emblème creux original dans une carte vide, sans jeton.
Garde la source et l’image enregistrée séparées
La source conservée contient un chemin composé en losange avec une ouverture intérieure. Son viewBox reste 0 0 64 64. Nous produisons deux copies avec une largeur et une hauteur explicites de 64 ou 128 ; les données de chemin restent identiques.
Le décodage du navigateur transforme chaque copie en image aux dimensions déclarées. L’API addImage de MapLibre accepte des objets image comme HTMLImageElement et ImageData. Elle n’accepte pas un chemin SVG comme instruction de géométrie de symbole.
Cette distinction compte lorsque tu changes une couleur ou un contour. Modifie le maître SVG, décode l’image révisée et actualise l’image de ta carte via l’API appropriée. Examiner les pixels enregistrés ne retrouvera pas la construction originale du chemin. Notre guide des SVG avec image matricielle intégrée explique la différence générale entre un conteneur SVG et une géométrie modifiable.

Choisis ensemble la densité d’image et la taille d’icône
Deux réglages influent sur le cadre nominal de l’image de notre symbole. Le pixelRatio de l’image décrit sa densité ; l’icon-size de la couche met l’icône obtenue à l’échelle. Consulte les métadonnées actuelles d’image de style et la spécification de disposition des symboles.
Pour notre symbole centré, sans rotation ni ajustement au texte, le cadre nominal est :
logical width = decoded width / image pixelRatio * icon-size
logical height = decoded height / image pixelRatio * icon-sizeUne image de 128×128 enregistrée au rapport 2 donne le même cadre nominal de 64×64 qu’une image de 64×64 au rapport 1 lorsque icon-size vaut 1. Changer le premier rapport à 1 double son cadre. Augmenter icon-size à 1.5 fait passer un cadre logique de 64×64 à 96×96.
Ce cadre inclut les espaces vides de la source. Si l’emblème paraît trop petit à l’intérieur, examine les limites SVG avant d’augmenter la taille de tous les symboles. Une image décodée plus grande avec le même rapport agrandit aussi le symbole ; les seules dimensions d’entrée ne constituent pas une stratégie de densité.
Le canevas de carte possède son propre rapport de pixels. Il est distinct du rapport fourni à addImage. Notre fichier de mesure fixe explicitement le rapport du canevas à 1 avec les options de Map documentées ; le rapport de pixels de l’appareil du navigateur était de 2. Cela isole les comparaisons suivantes au lieu de traiter les pixels d’appareil d’une capture comme des dimensions CSS.
Compare l’empreinte réellement peinte
Nous avons rendu cinq cas de symboles dans Chrome avec WebGL 2. Chaque carte avait un point, le même fond clair, aucune tuile et aucun jeton. Les deux sources SVG avaient un chemin identique et aucune image intégrée.
Pour mesurer le dessin terre cuite, notre lecture balayait les pixels WebGL réels pour repérer une couleur à dominante rouge : rouge supérieur à 1.35 fois le vert et 1.6 fois le bleu. Nous avons divisé les dimensions de la boîte englobante détectée par le rapport entre la taille du tampon du canevas et sa taille CSS. Cela mesure une région peinte détectée par seuil, pas le cadre entier de l’image.
| Image décodée | Rapport d’image | icon-size | Cadre nominal | Limites peintes observées |
|---|---|---|---|---|
| 64×64 | 1 | 1 | 64×64 | 62×62 px CSS |
| 128×128 | 1 | 1 | 128×128 | 126×126 px CSS |
| 128×128 | 2 | 1 | 64×64 | 62×62 px CSS |
| 64×64 | 1 | 1.5 | 96×96 | 94×94 px CSS |
| 128×128 | 2 | 1.5 | 96×96 | 94×94 px CSS |
Les pixels de bord ne passent pas tous ce seuil de couleur : cela explique pourquoi les limites peintes mesurées sont plus petites que les cadres nominaux. Les paires montrent la relation de taille voulue dans ce fichier de test ; elles ne prouvent pas une qualité de pixels identique à toutes les densités et à tous les zooms.
L’alpha central de l’image enregistrée était nul dans chaque cas. Dans la carte rendue, cette position affichait la couleur de fond (244, 242, 232) avec un alpha de 255, car la couche de fond elle-même était opaque. Voir des pixels de carte opaques à travers l’ouverture ne signifie pas que l’icône a perdu sa transparence.
Ces observations se limitent à notre source, notre navigateur, nos réglages de canevas et notre seuil. Teste le dessin livré aux tailles d’affichage prévues, surtout s’il contient des traits fins ou de petites ouvertures.
Copie une base de symbole sans jeton
Enregistre ce HTML complet et sers-le depuis un serveur web local. Une connexion Internet est nécessaire pour la bibliothèque de version fixe et le CSS ; la carte n’utilise ni service de tuiles ni clé d’API. Le SVG est intégré sous forme de texte et encodé en URL de données.
L’exemple attend image.decode() : l’enregistrement suit donc un décodage réussi. Il enregistre l’image connue après le chargement du style ; il n’a pas besoin du mécanisme de résolution des images manquantes de l’exemple officiel pour cet emblème unique et fixe.
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>MapLibre decoded SVG symbol</title>
<link rel="stylesheet"
href="https://unpkg.com/maplibre-gl@6.12.0/dist/maplibre-gl.css">
<style>#map { width: 360px; height: 280px; }</style>
<div id="map"></div>
<pre id="status">Loading…</pre>
<script type="module">
import * as ml from
'https://unpkg.com/maplibre-gl@6.12.0/dist/maplibre-gl.mjs';
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 64 64"><path fill="#c45b39" fill-rule="evenodd" d="M32 0 L64 32 L32 64 L0 32 Z M32 16 L16 32 L32 48 L48 32 Z"/></svg>`;
const image = new Image();
image.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);
try {
await image.decode();
const ratio = 2;
const iconSize = 1;
const map = new ml.Map({
container: 'map', center: [0, 0], zoom: 2, interactive: false,
attributionControl: false, fadeDuration: 0,
style: { version: 8, sources: {}, layers: [{
id: 'background', type: 'background',
paint: { 'background-color': '#f4f2e8' }
}] }
});
map.on('error', e => {
document.querySelector('#status').textContent = e.error.message;
});
await map.once('load');
map.addImage('emblem', image, { pixelRatio: ratio });
map.addSource('point', { type: 'geojson', data: {
type: 'Feature', geometry: { type: 'Point', coordinates: [0, 0] },
properties: {}
} });
map.addLayer({ id: 'emblem', type: 'symbol', source: 'point', layout: {
'icon-image': 'emblem', 'icon-size': iconSize, 'icon-overlap': 'always'
} });
await map.once('idle');
const registered = map.getImage('emblem');
document.querySelector('#status').textContent = JSON.stringify({
version: ml.getVersion(),
decoded: [image.naturalWidth, image.naturalHeight],
registered: [registered.data.width, registered.data.height],
imagePixelRatio: registered.pixelRatio,
logicalFrame: [image.naturalWidth / ratio * iconSize,
image.naturalHeight / ratio * iconSize]
}, null, 2);
} catch (error) {
document.querySelector('#status').textContent = String(error);
}
</script>
</html>L’état devrait montrer des dimensions décodées et enregistrées de [128, 128], un rapport d’image de 2 et un cadre logique de [64, 64]. Cet état indique le cadre nominal, pas une boîte englobante peinte mesurée. Change iconSize à 1.5 pour un cadre de 96×96. Pour comparer l’entrée de faible densité, change la largeur et la hauteur du SVG à 64 et ratio à 1, tout en gardant le même chemin et viewBox.
Garde icon-image égal à l’ID enregistré. Si une icône n’apparaît pas, confirme le décodage et l’enregistrement réussis, puis examine la source, la couche, la visibilité et les réglages de positionnement. Notre exemple à un point utilise icon-overlap: 'always' pour rendre cette comparaison indépendante des collisions avec d’autres symboles. Ton style de production peut nécessiter un autre comportement de collision.
Utilise un marqueur DOM si c’est le modèle de livraison
MapLibre documente aussi les icônes personnalisées avec les marqueurs DOM. Cette voie enveloppe un élément HTML au lieu d’enregistrer une image de style. Choisis-la lorsque le processus de marqueurs à base d’éléments convient à l’application, puis examine séparément son style et sa position dans le navigateur.
Ne transfère pas les affirmations des fonctions d’icônes vectorielles de Mapbox Studio ou d’un exemple du SDK MapTiler à cette chaîne d’images de style MapLibre. Une image SVG ordinaire en couleur ne devient pas non plus un champ de distance signé simplement en changeant l’indicateur sdf. Les réglages de densité contrôlent l’interprétation et l’affichage de cette image ; ils ne transforment pas ses pixels en contours modifiables.
Si une destination exige un fichier PNG, notre guide de rendu SVG avec Sharp explique comment préparer une copie matricielle à livrer. Garde le maître SVG ordinaire pour les modifications ; SVG en ligne et livraison comme image ont aussi des modèles d’interaction différents.
Si ton icône n’existe qu’en PNG ou JPG et que tu as besoin d’un dessin modifiable, récupère des contours SVG avec PerfectVector, examine la forme extérieure et l’ouverture, puis garde ce maître avant de décoder ton image de carte. Le code de carte contrôle toujours la densité, la taille du symbole et sa position géographique. Un SVG propre existant peut passer directement par le décodage ; le guide de vectorisation explique quand une conversion matricielle est utile.
Questions fréquentes
MapLibre GL JS peut-il afficher un dessin SVG comme symbole ? Oui. Son exemple officiel actuel décode le texte SVG avec une Image du navigateur et enregistre cette image pour une couche de symboles. Garde le SVG original séparé de l’image enregistrée.
Pourquoi une entrée plus grande a-t-elle agrandi mon icône ? Augmenter les dimensions décodées en conservant le même rapport de pixels d’image et le même icon-size agrandit le cadre nominal. Associe volontairement la densité d’image et l’échelle d’affichage.
pixelRatio conserve-t-il des chemins SVG modifiables dans le symbole ? Non. Il décrit la densité d’image. Conserve et modifie le SVG source, puis décode le dessin révisé pour la carte.
Pourquoi le pixel de carte dans le trou de mon icône est-il opaque ? Une couche de fond opaque peut apparaître à travers une ouverture transparente de l’icône. Examine l’alpha de l’image enregistrée séparément du canevas de carte entièrement composé.
Sources
- MapLibre — Afficher un symbole SVG distant — Méthode actuelle par texte SVG, image du navigateur et addImage.
- MapLibre — API Map — Objets image acceptés et enregistrement d’images à l’exécution.
- MapLibre — StyleImageMetadata — Densité d’image et métadonnées d’interprétation SDF.
- MapLibre — Spécification de disposition des symboles — Échelle des icônes et comportement des collisions.
- MapLibre — MapOptions — Options de canevas utilisées pour contrôler le fichier de mesure.
- MDN — HTMLImageElement.decode — Attente du décodage d’image dans le navigateur et gestion des échecs.
- MapLibre — Ajouter des icônes personnalisées avec Markers — Processus distinct de marqueurs par éléments HTML.
Plus depuis le blog

Logos SVG Satori : garder le dessin dans une carte de marque
Intègre un logo SVG dans une carte Satori avec des dimensions explicites et des données d’image autonomes. Examine le dessin imbriqué, puis rends un PNG de livraison séparé.

SVG vers Canvas : charger le dessin avant l’export PNG
Charge un SVG entier via Image.decode, dessine-le à la taille bitmap choisie, teste CORS et la lecture PNG, et sépare la source modifiable des pixels exportés.
