Iconos SVG de MapLibre: revisa píxeles y tamaño del símbolo
Decodifica diseños SVG para una capa de símbolos de MapLibre, controla la densidad de imagen y el tamaño del icono, conserva un maestro editable y verifica la forma y su abertura.
En esta página
Para usar una imagen de icono SVG en MapLibre GL JS, decodifica el SVG con un Image del navegador, pasa esa imagen a addImage y referencia su ID en icon-image de una capa de símbolos. Conserva el SVG de origen por separado: la imagen de estilo registrada proporciona píxeles para renderizar, no objetos de trazado SVG editables.
El ejemplo oficial actual de símbolos SVG demuestra esta vía de decodificación. Una afirmación general de que MapLibre no puede mostrar diseños SVG la pasaría por alto. Nuestro ejemplo fija la versión 6.12.0 y prueba un emblema hueco original en un mapa vacío que no necesita token.
Separa el origen de la imagen registrada
El origen conservado contiene un trazado de rombo compuesto con una abertura interior. Su viewBox sigue siendo 0 0 64 64. Creamos dos copias con ancho y alto explícitos de 64 o 128; los datos del trazado permanecen idénticos.
La decodificación del navegador convierte cada copia en una imagen con sus dimensiones declaradas. La API addImage de MapLibre acepta objetos de imagen como HTMLImageElement e ImageData. No acepta un trazado SVG como instrucción geométrica para un símbolo.
Esa diferencia importa cuando cambias un color o un contorno. Edita el maestro SVG, decodifica la imagen revisada y actualiza la imagen del mapa mediante la API adecuada. Inspeccionar los píxeles registrados no recuperará la construcción original del trazado. Nuestra guía de SVG con ráster incrustado explica la diferencia general entre un contenedor SVG y geometría editable.

Elige juntos la densidad de imagen y el tamaño del icono
Dos ajustes afectan al marco nominal de imagen de nuestro símbolo. El pixelRatio de la imagen describe su densidad; icon-size de la capa escala el icono resultante. Consulta los metadatos actuales de imagen de estilo y la especificación de distribución de símbolos.
Para nuestro símbolo centrado, sin rotación ni ajuste al texto, el marco nominal es:
logical width = decoded width / image pixelRatio * icon-size
logical height = decoded height / image pixelRatio * icon-sizeUna imagen de 128×128 registrada con relación 2 da el mismo marco nominal de 64×64 que una imagen de 64×64 con relación 1 cuando icon-size es 1. Cambiar la primera relación a 1 duplica su marco. Aumentar icon-size a 1.5 convierte un marco de imagen lógico de 64×64 en uno de 96×96.
Ese marco incluye el espacio vacío de origen. Si el emblema parece demasiado pequeño dentro de él, revisa los límites del SVG antes de aumentar el tamaño de todos los símbolos. Una imagen decodificada mayor con la misma relación también hace mayor el símbolo; las dimensiones de entrada por sí solas no son una estrategia de densidad.
El canvas del mapa tiene su propia relación de píxeles. Es independiente de la relación que se pasa a addImage. Nuestro archivo de medición establece explícitamente la relación del canvas en 1 mediante las opciones de Map documentadas; la relación de píxeles del dispositivo del navegador era 2. Esto aísla las comparaciones siguientes en lugar de tratar los píxeles de dispositivo de una captura como dimensiones CSS.
Compara la huella pintada real
Renderizamos cinco casos de símbolos en Chrome con WebGL 2. Cada mapa tenía un punto, el mismo fondo claro, ninguna tesela y ningún token. Ambos orígenes SVG tenían un trazado idéntico y ninguna imagen incrustada.
Para medir el diseño terracota, nuestra lectura buscó en los píxeles WebGL reales un color con predominio de rojo: rojo mayor que 1.35 veces el verde y 1.6 veces el azul. Dividimos las dimensiones del cuadro delimitador detectado por la relación entre el tamaño interno del canvas y su tamaño CSS. Esto mide una región pintada seleccionada por umbral, no el marco completo de la imagen.
| Imagen decodificada | Relación de imagen | icon-size | Marco nominal | Límites pintados observados |
|---|---|---|---|---|
| 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 |
No todos los píxeles del borde superan ese umbral de color, lo que explica que los límites pintados medidos sean menores que los marcos nominales. Las parejas muestran la relación de tamaños prevista en este archivo de prueba; no demuestran una calidad de píxel idéntica en todas las densidades o niveles de zoom.
El alfa central de la imagen registrada era cero en todos los casos. En el mapa renderizado, esa posición mostraba el color de fondo (244, 242, 232) con alfa 255, porque la propia capa de fondo era opaca. Ver píxeles opacos del mapa a través de la abertura no significa que el icono haya perdido la transparencia.
Estas observaciones están limitadas a nuestro origen, navegador, ajustes de canvas y umbral. Prueba el diseño que vas a entregar a sus tamaños de visualización previstos, sobre todo si contiene trazos finos o aberturas pequeñas.
Copia una plantilla de símbolos sin token
Guarda este HTML completo y sírvelo desde un servidor web local. Se necesita acceso a Internet para la biblioteca de versión fija y el CSS; el mapa no usa servicio de teselas ni clave API. El SVG se incrusta como texto y se codifica en una URL de datos.
El ejemplo espera a image.decode(), así que el registro ocurre después de una decodificación correcta. Registra la imagen conocida después de cargar el estilo; no necesita el mecanismo de resolución de imágenes ausentes del ejemplo oficial para este único emblema fijo.
<!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>El estado debería mostrar dimensiones decodificadas y registradas de [128, 128], relación de imagen 2 y marco lógico [64, 64]. Este estado informa del marco nominal, no de un cuadro delimitador pintado medido. Cambia iconSize a 1.5 para obtener un marco de 96×96. Para comparar la entrada de baja densidad, cambia el ancho y alto del SVG a 64 y ratio a 1, conservando el mismo trazado y viewBox.
Mantén icon-image igual al ID registrado. Si no aparece un icono, confirma que la decodificación y el registro sean correctos; después revisa el origen, la capa, la visibilidad y los ajustes de colocación. Nuestro ejemplo de un solo punto usa icon-overlap: 'always' para que esta comparación sea independiente de las colisiones con otros símbolos. Tu estilo de producción puede necesitar otro comportamiento de colisión.
Usa un marcador DOM cuando ese sea el modelo de entrega
MapLibre también documenta iconos personalizados con marcadores DOM. Esa vía envuelve un elemento HTML en lugar de registrar una imagen de estilo. Elígela cuando el flujo de marcadores basado en elementos encaje con la aplicación y revisa después su estilo y colocación en el navegador por separado.
No traslades afirmaciones sobre las funciones de iconos vectoriales de Mapbox Studio o un ejemplo del SDK de MapTiler a este proceso de imágenes de estilo de MapLibre. Una imagen SVG normal a todo color tampoco se convierte en un campo de distancia con signo solo por cambiar el indicador sdf. Los ajustes de densidad controlan cómo se interpreta y se muestra esta imagen; no convierten sus píxeles en contornos editables.
Si un destino necesita un archivo PNG, nuestra guía de renderizado SVG con Sharp cubre la preparación de una copia ráster de entrega. Conserva el maestro SVG normal para editar; SVG inline e imágenes también tienen modelos de interacción diferentes.
Si tu icono solo existe como PNG o JPG y necesitas un diseño editable, recupera contornos SVG con PerfectVector, inspecciona la forma exterior y la abertura, y conserva ese maestro antes de decodificar la imagen del mapa. El código del mapa sigue controlando la densidad, el tamaño del símbolo y la posición geográfica. Un SVG limpio existente puede pasar directamente por el flujo de decodificación; la guía de vectorización explica cuándo resulta útil convertir un ráster.
Preguntas frecuentes
¿Puede MapLibre GL JS mostrar diseños SVG como símbolos? Sí. Su ejemplo oficial actual decodifica texto SVG con un Image del navegador y registra esa imagen para una capa de símbolos. Conserva el SVG original separado de la imagen registrada.
¿Por qué una entrada más grande hizo mayor mi icono? Aumentar las dimensiones decodificadas y conservar la misma relación de píxeles de imagen e icon-size aumenta el marco nominal. Combina la densidad de imagen y la escala de visualización de forma deliberada.
¿pixelRatio conserva trazados SVG editables en el símbolo? No. Describe la densidad de imagen. Conserva y edita el SVG de origen y decodifica después el diseño revisado para el mapa.
¿Por qué el píxel del mapa dentro del hueco del icono es opaco? Una capa de fondo opaca puede verse a través de una abertura transparente del icono. Revisa el alfa de la imagen registrada por separado del canvas del mapa completamente compuesto.
Fuentes
- MapLibre — Mostrar un símbolo SVG remoto — Vía actual de texto SVG, imagen de navegador y addImage.
- MapLibre — API de Map — Objetos de imagen aceptados y registro de imágenes en ejecución.
- MapLibre — StyleImageMetadata — Metadatos de densidad de imagen e interpretación SDF.
- MapLibre — Especificación de distribución de símbolos — Escala de iconos y comportamiento de colisión.
- MapLibre — MapOptions — Opciones de canvas usadas para controlar el archivo de medición.
- MDN — HTMLImageElement.decode — Espera a la decodificación de imagen del navegador y gestión de fallos.
- MapLibre — Añadir iconos personalizados con Markers — Flujo separado de marcadores con elementos HTML.
Más del blog

Logotipos SVG en Satori: conserva el diseño de tu tarjeta
Incrusta un logotipo SVG en una tarjeta Satori con dimensiones explícitas y datos de imagen autónomos. Inspecciona el diseño anidado y renderiza por separado el PNG de entrega.

SVG a Canvas: carga el diseño antes de exportar PNG
Carga un SVG completo con Image.decode, dibújalo al tamaño de mapa de bits elegido, prueba CORS y la lectura de PNG, y separa el origen editable de los píxeles exportados.
