Cómo utilizar marcadores SVG en Google Maps API v3

91

¿Puedo usar mi image.svg convertido como icono de mapa de Google? Estaba convirtiendo mi imagen png a svg y quiero usar esto como el símbolo del mapa de Google que se puede rotar. Ya intenté usar el símbolo del mapa de Google pero quiero tener un ícono como auto, hombre, etc ... Es por eso que convertí algunos archivos png a svg, al igual que este sitio de ejemplo, ¿qué usa para estos http: / /www.goprotravelling.com/

jemz
fuente
1
Para aquellos que buscan esta pregunta similar: una solución parcial compatible con IE 9+ aquí: stackoverflow.com/a/30890373/634386 colocará un SVG en el DOM en lugar de en el lienzo para que pueda manipular con CSS3 después (si ' busca rotar ese elemento específicamente sobre la marcha).
Mike Kormendy

Respuestas:

144

Puede renderizar su icono usando la notación de ruta SVG .

Consulte la documentación de Google para obtener más información.

Aquí tienes un ejemplo básico:

var icon = {

    path: "M-20,0a20,20 0 1,0 40,0a20,20 0 1,0 -40,0",
    fillColor: '#FF0000',
    fillOpacity: .6,
    anchor: new google.maps.Point(0,0),
    strokeWeight: 0,
    scale: 1
}

var marker = new google.maps.Marker({
    position: event.latLng,
    map: map,
    draggable: false,
    icon: icon
});

Aquí hay un ejemplo práctico sobre cómo mostrar y escalar un icono SVG de marcador:

Demostración de JSFiddle

Editar:

Otro ejemplo aquí con un ícono complejo:

Demostración de JSFiddle

Edición 2:

Y así es como puede tener un archivo SVG como icono:

Demostración de JSFiddle

MrUpsidown
fuente
, es muy difícil hacer coche svg.
jemz
¿Tiene una imagen SVG del auto que desea? Luego, simplemente ábralo con un editor de texto y encuentre la ruta en él.
MrUpsidown
3
SVG se usa generalmente para iconos y otros símbolos. Un color. Es solo un camino, como una letra en una fuente. Las ventajas es que puedes escalarlo, rotarlo y cambiar su color fácilmente, sin perder calidad. Es una forma vectorial.
MrUpsidown
1
¡Esa fue una de las cosas más útiles que leí este año! Muchas gracias
Jonathan La'Fey
2
@MrUpsidown Pero, por supuesto, SVG funciona bien fuera de los mapas, nunca tuve un problema allí. Tiene que ver con una falla en el soporte de Google para SVG en su renderizado de lienzo, que se puede desactivar para que los SVG se inserten en el DOM. Luego está el problema con los tamaños calculados de los marcadores, que hay un par de propiedades en la implementación de los marcadores de mapa que pueden forzar el tamaño. Encontré una solución parcial aquí: stackoverflow.com/a/30890373/634386
Mike Kormendy
68

Si necesita un svg completo, no solo una ruta y desea que sea modificable en el lado del cliente (por ejemplo, cambiar texto, ocultar detalles, ...) puede usar una 'URL' de datos alternativa con svg incluido:

var svg = '<svg width="400" height="110"><rect width="300" height="100" /></svg>';
icon.url = 'data:image/svg+xml;charset=UTF-8;base64,' + btoa(svg);

JavaScript (Firefox) btoa () se utiliza para obtener la codificación base64 del texto SVG. También puede utilizar http://dopiaza.org/tools/datauri/index.php para generar URL de datos base.

Aquí hay un ejemplo completo de jsfiddle :

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
        <script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script>
    </head>
    <body>
        <div id="map" style="width: 500px; height: 400px;"></div>
        <script type="text/javascript">
            var map = new google.maps.Map(document.getElementById('map'), {
                zoom: 10,
                center: new google.maps.LatLng(-33.92, 151.25),
                mapTypeId: google.maps.MapTypeId.ROADMAP
            });

            var template = [
                '<?xml version="1.0"?>',
                    '<svg width="26px" height="26px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg">',
                        '<circle stroke="#222" fill="{{ color }}" cx="50" cy="50" r="35"/>',
                    '</svg>'
                ].join('\n');
            var svg = template.replace('{{ color }}', '#800');

            var docMarker = new google.maps.Marker({
                position: new google.maps.LatLng(-33.92, 151.25),
                map: map,
                title: 'Dynamic SVG Marker',
                icon: { url: 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg), scaledSize: new google.maps.Size(20, 20) },
optimized: false
            });

            var docMarker = new google.maps.Marker({
                position: new google.maps.LatLng(-33.95, 151.25),
                map: map,
                title: 'Dynamic SVG Marker',
                icon: { url: 'data:image/svg+xml;charset=UTF-8;base64,' + btoa(svg), scaledSize: new google.maps.Size(20, 20) },
optimized: false
            });
        </script>
    </body>
</html>

Puede encontrar información adicional aquí .

Evite la codificación base64:

Para evitar la codificación base64, puede reemplazar 'data:image/svg+xml;charset=UTF-8;base64,' + btoa(svg)con'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg)

Esto debería funcionar con navegadores modernos hasta IE9. La ventaja es que encodeURIComponentes una función js predeterminada y está disponible en todos los navegadores modernos. También puede obtener enlaces más pequeños, pero debe probar esto y considerar usarlos en 'lugar de "en su svg.

Consulte también Optimización de SVG en URI de datos para obtener información adicional.

Compatibilidad con IE: para admitir marcadores SVG en IE, se necesitan dos pequeñas adaptaciones como se describe aquí: Marcadores SVG en IE . Actualicé el código de ejemplo para admitir IE.

Tom
fuente
3
Buena información, pero tenga en cuenta que la codificación base64 no es realmente necesaria ni deseable. En su lugar, puede usar la codificación svg directamente en el URI de datos, como data:image/svg+xml;utf8,. Más información: css-tricks.com/probably-dont-base64-svg
Josh de Qaribou
Tienes razón, sería genial si pudiéramos evitar la codificación base64. ¿Cómo lo hizo? ¿Podría dar un ejemplo? Si solo agrego el código svg al URI, no funciona con algunos caracteres, como #¿usó codificación de URL?
Tom
Debo mencionar que está codificado en utf8; svg es el formato. El enlace proporcionado tiene varios ejemplos.
Josh de Qaribou
1
¿Cómo usar rotar imagen svg usando esto?
Somnath Kharat
¿No nos encanta IE? Como de costumbre, requiere un poco de trabajo adicional para que funcione en todos los navegadores. Adapté el ejemplo para admitir IE, espero que esto ayude.
Tom
30

Sé que esta publicación es un poco vieja, pero he visto tanta mala información sobre esto en SO que podría gritar. Así que solo tengo que poner mis dos centavos en un enfoque completamente diferente que sé que funciona, ya que lo uso de manera confiable en muchos mapas. Además de eso, creo que el OP quería la capacidad de rotar el marcador de flecha alrededor del punto del mapa también, que es diferente a rotar el icono alrededor de su propio eje x, y que cambiará hacia donde apunta el marcador de flecha en el mapa.

Primero, recuerde que estamos jugando con mapas de Google y SVG, por lo que debemos adaptarnos a la forma en que Google implementa su implementación de SVG para marcadores (o en realidad símbolos). Google establece su ancla para la imagen del marcador SVG en 0,0, que NO ES la esquina superior izquierda del viewBox SVG. Para evitar esto, debe dibujar su imagen SVG de manera un poco diferente para darle a Google lo que quiere ... sí, la respuesta está en la forma en que realmente crea la ruta SVG en su editor SVG (Illustrator, Inkscape, etc. .).

El primer paso es deshacerse de viewBox. Esto se puede hacer configurando viewBox en su XML en 0 ... así es, solo un cero en lugar de los cuatro argumentos habituales para viewBox. Esto apaga el cuadro de vista (y sí, esto es semánticamente correcto). Probablemente notará que el tamaño de su imagen salta inmediatamente cuando hace esto, y eso se debe a que el svg ya no tiene una base (el viewBox) para escalar la imagen. Así que creamos esa referencia directamente, estableciendo el ancho y el alto en el número real de píxeles que desea que tenga su imagen en el editor XML de su editor SVG.

Al establecer el ancho y el alto de la imagen svg en el editor XML, crea una línea de base para escalar la imagen, y este tamaño se convierte en un valor de 1 para las propiedades de escala del marcador de forma predeterminada. Puede ver la ventaja que esto tiene para la escala dinámica del marcador.

Ahora que tiene el tamaño de su imagen, mueva la imagen hasta que la parte de la imagen que desea tener como ancla esté sobre las coordenadas 0,0 del editor svg. Una vez que haya hecho esto, copie el valor del atributo d de la ruta svg. Notará que aproximadamente la mitad de los números son negativos, que es el resultado de alinear su punto de anclaje para el 0,0 de la imagen en lugar del viewBox.

El uso de esta técnica le permitirá rotar el marcador correctamente, alrededor de los puntos lat y lng en el mapa. Esta es la única forma confiable de vincular el punto del marcador svg que desea a la latitud y longitud de la ubicación del marcador.

Intenté hacer un JSFiddle para esto , pero hay algún error en la implementación, una de las razones por las que no me gusta tanto el código reinterpretado. Entonces, en cambio, he incluido un ejemplo completamente autónomo a continuación que puede probar, adaptar y usar como referencia. Este es el mismo código que probé en JSFiddle que falló, pero navega a través de Firebug sin un gemido.

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8" />
    <meta name="viewport"  content="width=device-width, initial-scale=1" />
    <meta name="author"    content="Drew G. Stimson, Sr. ( Epiphany )" />
    <title>Create Draggable and Rotatable SVG Marker</title>
    <script src="http://maps.googleapis.com/maps/api/js?sensor=false"> </script>
    <style type="text/css">
      #document_body {
        margin:0;
        border: 0;
        padding: 10px;
        font-family: Arial,sans-serif;
        font-size: 14px;
        font-weight: bold;
        color: #f0f9f9;
        text-align: center;
        text-shadow: 1px 1px 1px #000;
        background:#1f1f1f;
      }
      #map_canvas, #rotation_control {
        margin: 1px;
        border:1px solid #000;
        background:#444;
        -webkit-border-radius: 4px;
           -moz-border-radius: 4px;
                border-radius: 4px;
      }
      #map_canvas { 
        width: 100%;
        height: 360px;
      }
      #rotation_control { 
        width: auto;
        padding:5px;
      }
      #rotation_value { 
        margin: 1px;
        border:1px solid #999;
        width: 60px;
        padding:2px;
        font-weight: bold;
        color: #00cc00;
        text-align: center;
        background:#111;
        border-radius: 4px;
      }
</style>

<script type="text/javascript">
  var map, arrow_marker, arrow_options;
  var map_center = {lat:41.0, lng:-103.0};
  var arrow_icon = {
    path: 'M -1.1500216e-4,0 C 0.281648,0 0.547084,-0.13447 0.718801,-0.36481 l 17.093151,-22.89064 c 0.125766,-0.16746 0.188044,-0.36854 0.188044,-0.56899 0,-0.19797 -0.06107,-0.39532 -0.182601,-0.56215 -0.245484,-0.33555 -0.678404,-0.46068 -1.057513,-0.30629 l -11.318243,4.60303 0,-26.97635 C 5.441639,-47.58228 5.035926,-48 4.534681,-48 l -9.06959,0 c -0.501246,0 -0.906959,0.41772 -0.906959,0.9338 l 0,26.97635 -11.317637,-4.60303 c -0.379109,-0.15439 -0.812031,-0.0286 -1.057515,0.30629 -0.245483,0.33492 -0.244275,0.79809 0.0055,1.13114 L -0.718973,-0.36481 C -0.547255,-0.13509 -0.281818,0 -5.7002158e-5,0 Z',
    strokeColor: 'black',
    strokeOpacity: 1,
    strokeWeight: 1,
    fillColor: '#fefe99',
    fillOpacity: 1,
    rotation: 0,
    scale: 1.0
  };

function init(){
  map = new google.maps.Map(document.getElementById('map_canvas'), {
    center: map_center,
    zoom: 4,
    mapTypeId: google.maps.MapTypeId.HYBRID
  });
  arrow_options = {
    position: map_center,
    icon: arrow_icon,
    clickable: false,
    draggable: true,
    crossOnDrag: true,
    visible: true,
    animation: 0,
    title: 'I am a Draggable-Rotatable Marker!' 
  };
  arrow_marker = new google.maps.Marker(arrow_options);
  arrow_marker.setMap(map);
}
function setRotation(){
  var heading = parseInt(document.getElementById('rotation_value').value);
  if (isNaN(heading)) heading = 0;
  if (heading < 0) heading = 359;
  if (heading > 359) heading = 0;
  arrow_icon.rotation = heading;
  arrow_marker.setOptions({icon:arrow_icon});
  document.getElementById('rotation_value').value = heading;
}
</script>
</head>
<body id="document_body" onload="init();">
  <div id="rotation_control">
    <small>Enter heading to rotate marker&nbsp;&nbsp;&nbsp;&nbsp;</small>
    Heading&deg;<input id="rotation_value" type="number" size="3" value="0" onchange="setRotation();" />
    <small>&nbsp;&nbsp;&nbsp;&nbsp;Drag marker to place marker</small>
    </div>
    <div id="map_canvas"></div>
</body>
</html>

Esto es exactamente lo que hace Google para sus propios símbolos disponibles en la clase SYMBOL de la API de Maps, así que si eso no te convence ... De todos modos, espero que esto te ayude a crear y configurar correctamente un marcador SVG para tus mapas de Google endevours.

Epifanía
fuente
2
Buena respuesta, hice un violín funcional de su código para que la gente vea jsfiddle.net/v2pjfp7r
Rob Schmuecker
"<3" Esta es información esencial que debería formar parte de la documentación de Google.
user7653815
12

Sí, puede usar un archivo .svg para el ícono al igual que puede usar .png u otro formato de archivo de imagen. Simplemente establezca la URL del icono en el directorio donde se encuentra el archivo .svg. Por ejemplo:

var icon = {
        url: 'path/to/images/car.svg',
        size: new google.maps.Size(sizeX, sizeY),
        origin: new google.maps.Point(0, 0),
        anchor: new google.maps.Point(sizeX/2, sizeY/2)
};

var marker = new google.maps.Marker({
        position: event.latLng,
        map: map,
        draggable: false,
        icon: icon
});
ThePersonWithoutC
fuente
1
El objeto de icono que está mostrando es para un marcador estándar, no un marcador SVG. Un marcador svg usa propiedades de la clase de símbolo, que luego se asignan como propiedad de icono de la clase de marcador. (ver objeto de icono MrUpsidown para una referencia correcta ...)
Epifanía
2
Esto también se romperá si el SVG tiene más de una ruta 'd' (generalmente asociada con más de una capa en la imagen svg).
Epifanía
2
Tenga en cuenta que su archivo svg debe establecer explícitamente las dimensiones del icono de esta manera: width="33px" height="50px"- consulte stackoverflow.com/a/21783089/165673
Yarin
10

Las cosas van mejor, ahora mismo puedes usar archivos SVG.

        marker = new google.maps.Marker({
            position: {lat: 36.720426, lng: -4.412573},
            map: map,
            draggable: true,
            icon: "img/tree.svg"
        });
gengns
fuente
1
no permite la rotación
Matt Westlake
5

Como lo mencionaron otros en este hilo, no olvide establecer explícitamente los atributos de ancho y alto en el svg así:

<svg id="some_id" data-name="some_name" xmlns="http://www.w3.org/2000/svg"
     viewBox="0 0 26 42"
     width="26px" height="42px">

si no lo hace, ninguna manipulación de js puede ayudarlo, ya que gmaps no tendrá un marco de referencia y siempre usará un tamaño estándar.

(Sé que se ha mencionado en algunos comentarios, pero es fácil pasarlos por alto. Esta información me ayudó en varios casos)

Larzan
fuente
1

¡OKAY! Hice esto pronto en mi web, pruebo dos formas de crear el marcador de mapa de Google personalizado, este código de ejecución, canvg.js, es la mejor compatibilidad para el navegador. El código comentado no es compatible con IE11 actualmente.

var marker;
var CustomShapeCoords = [16, 1.14, 21, 2.1, 25, 4.2, 28, 7.4, 30, 11.3, 30.6, 15.74, 25.85, 26.49, 21.02, 31.89, 15.92, 43.86, 10.92, 31.89, 5.9, 26.26, 1.4, 15.74, 2.1, 11.3, 4, 7.4, 7.1, 4.2, 11, 2.1, 16, 1.14];

function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    zoom: 13,
    center: {
      lat: 59.325,
      lng: 18.070
    }
  });
  var markerOption = {
    latitude: 59.327,
    longitude: 18.067,
    color: "#" + "000",
    text: "ha"
  };
  marker = createMarker(markerOption);
  marker.setMap(map);
  marker.addListener('click', changeColorAndText);
};

function changeColorAndText() {
  var iconTmpObj = createSvgIcon( "#c00", "ok" );
  marker.setOptions( {
                icon: iconTmpObj
            } );
};

function createMarker(options) {
  //IE MarkerShape has problem
  var markerObj = new google.maps.Marker({
    icon: createSvgIcon(options.color, options.text),
    position: {
      lat: parseFloat(options.latitude),
      lng: parseFloat(options.longitude)
    },
    draggable: false,
    visible: true,
    zIndex: 10,
    shape: {
      coords: CustomShapeCoords,
      type: 'poly'
    }
  });

  return markerObj;
};

function createSvgIcon(color, text) {
  var div = $("<div></div>");

  var svg = $(
    '<svg width="32px" height="43px"  viewBox="0 0 32 43" xmlns="http://www.w3.org/2000/svg">' +
    '<path style="fill:#FFFFFF;stroke:#020202;stroke-width:1;stroke-miterlimit:10;" d="M30.6,15.737c0-8.075-6.55-14.6-14.6-14.6c-8.075,0-14.601,6.55-14.601,14.6c0,4.149,1.726,7.875,4.5,10.524c1.8,1.801,4.175,4.301,5.025,5.625c1.75,2.726,5,11.976,5,11.976s3.325-9.25,5.1-11.976c0.825-1.274,3.05-3.6,4.825-5.399C28.774,23.813,30.6,20.012,30.6,15.737z"/>' +
    '<circle style="fill:' + color + ';" cx="16" cy="16" r="11"/>' +
    '<text x="16" y="20" text-anchor="middle" style="font-size:10px;fill:#FFFFFF;">' + text + '</text>' +
    '</svg>'
  );
  div.append(svg);

  var dd = $("<canvas height='50px' width='50px'></cancas>");

  var svgHtml = div[0].innerHTML;

  canvg(dd[0], svgHtml);

  var imgSrc = dd[0].toDataURL("image/png");
  //"scaledSize" and "optimized: false" together seems did the tricky ---IE11  &&  viewBox influent IE scaledSize
  //var svg = '<svg width="32px" height="43px"  viewBox="0 0 32 43" xmlns="http://www.w3.org/2000/svg">'
  //    + '<path style="fill:#FFFFFF;stroke:#020202;stroke-width:1;stroke-miterlimit:10;" d="M30.6,15.737c0-8.075-6.55-14.6-14.6-14.6c-8.075,0-14.601,6.55-14.601,14.6c0,4.149,1.726,7.875,4.5,10.524c1.8,1.801,4.175,4.301,5.025,5.625c1.75,2.726,5,11.976,5,11.976s3.325-9.25,5.1-11.976c0.825-1.274,3.05-3.6,4.825-5.399C28.774,23.813,30.6,20.012,30.6,15.737z"/>'
  //    + '<circle style="fill:' + color + ';" cx="16" cy="16" r="11"/>'
  //    + '<text x="16" y="20" text-anchor="middle" style="font-size:10px;fill:#FFFFFF;">' + text + '</text>'
  //    + '</svg>';
  //var imgSrc = 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg);

  var iconObj = {
    size: new google.maps.Size(32, 43),
    url: imgSrc,
    scaledSize: new google.maps.Size(32, 43)
  };

  return iconObj;
};
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Your Custom Marker </title>
  <style>
    /* Always set the map height explicitly to define the size of the div
       * element that contains the map. */
    #map {
      height: 100%;
    }
    /* Optional: Makes the sample page fill the window. */
    html,
    body {
      height: 100%;
      margin: 0;
      padding: 0;
    }
  </style>
</head>

<body>
  <div id="map"></div>
    <script src="https://canvg.github.io/canvg/canvg.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script async defer src="https://maps.googleapis.com/maps/api/js?callback=initMap"></script>
</body>

</html>

Dom
fuente
-4

Tienes que pasar optimized: false.

P.ej

var img = { url: 'img/puff.svg', scaledSide: new google.maps.Size(5, 5) };
new google.maps.Marker({position: this.mapOptions.center, map: this.map, icon: img, optimized: false,});

Sin pasar optimized: false, mi svg apareció como una imagen estática.

Jeremy Eaton
fuente