Como usar marcadores SVG na API do Google Maps v3

91

Posso usar meu image.svg convertido como ícone do mapa do Google. Eu estava convertendo minha imagem png para svg e quero usar isso como o símbolo do mapa do Google que pode ser girado. Já tentei usar o símbolo do mapa do google mas quero ter um ícone como carro, homem, etc ... É por isso que converti alguns arquivos png para svg, assim como neste site de exemplo o que ele usa para esses http: / /www.goprotravelling.com/

Jemz
fonte
1
Para aqueles que procuram por esta questão semelhante: uma solução parcial compatível com IE 9+ aqui: stackoverflow.com/a/30890373/634386 colocará um SVG no DOM em vez de na tela para que você possa manipular com CSS3 depois (se você ' estamos tentando girar esse elemento especificamente em tempo real).
Mike Kormendy,

Respostas:

144

Você pode renderizar seu ícone usando a notação de caminho SVG .

Consulte a documentação do Google para obter mais informações.

Aqui está um exemplo 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
});

Aqui está um exemplo prático de como exibir e dimensionar um ícone de marcador SVG:

JSFiddle demo

Editar:

Outro exemplo aqui com um ícone complexo:

JSFiddle demo

Editar 2:

E aqui está como você pode ter um arquivo SVG como um ícone:

JSFiddle demo

MrUpsidown
fonte
, é muito difícil fazer o carro SVG.
jemz
Você tem uma imagem SVG daquele carro que deseja? Em seguida, basta abri-lo com um editor de texto e encontrar o caminho nele!
MrUpsidown
3
SVG geralmente é usado para ícones e outros símbolos. Uma cor. É apenas um caminho, como uma letra em uma fonte. A vantagem é que você pode facilmente dimensioná-lo, girá-lo e mudar sua cor, sem perder qualidade. É uma forma vetorial.
MrUpsidown
1
Essa foi uma das coisas mais úteis que li este ano! Muito obrigado
Jonathan La'Fey
2
@MrUpsidown Mas é claro que o SVG está funcionando bem fora dos mapas - nunca teve um problema lá. Isso tem a ver com uma falha no suporte do Google para SVG em sua renderização de tela - que pode ser desativada para que os SVGs sejam inseridos no DOM. Depois, há o problema com os tamanhos calculados dos marcadores, pois há algumas propriedades na implementação para marcadores de mapa que podem forçar o tamanho. Encontrei uma solução parcial aqui: stackoverflow.com/a/30890373/634386
Mike Kormendy
68

Se você precisa de um svg completo não apenas de um caminho e deseja que ele seja modificável no lado do cliente (por exemplo, alterar o texto, ocultar detalhes, ...), você pode usar um 'URL' de dados alternativo com o svg incluído:

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 () é usado para obter a codificação base64 do texto SVG. Você também pode usar http://dopiaza.org/tools/datauri/index.php para gerar URLs de dados de base.

Aqui está um exemplo 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>

Informações adicionais podem ser encontradas aqui .

Evite a codificação base64:

Para evitar a codificação base64, você pode substituir 'data:image/svg+xml;charset=UTF-8;base64,' + btoa(svg)por'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg)

Isso deve funcionar com navegadores modernos até o IE9. A vantagem é que encodeURIComponenté uma função js padrão e está disponível em todos os navegadores modernos. Você também pode obter links menores, mas você precisa testar isso e considerar o uso em 'vez de "em seu SVG.

Consulte também Otimizando SVGs em URIs de dados para obter informações adicionais.

Suporte ao IE: para oferecer suporte aos marcadores SVG no IE, são necessárias duas pequenas adaptações, conforme descrito aqui: Marcadores SVG no IE . Atualizei o código de exemplo para oferecer suporte ao IE.

Tom
fonte
3
Boas informações, mas observe rapidamente que a codificação base64 não é realmente necessária ou desejável. Você pode usar a codificação svg diretamente no URI de dados, como data:image/svg+xml;utf8,. Mais informações: css-tricks.com/probably-dont-base64-svg
Josh de Qaribou
Você está certo, seria ótimo se pudéssemos evitar a codificação base64. Como você fez isso poderia dar um exemplo? Se eu apenas adicionar o código SVG ao URI, ele não funciona com alguns caracteres, como #você usou a codificação de URL?
Tom
Devo mencionar que é codificado em utf8; svg é o formato. O link fornecido contém vários exemplos.
Josh de Qaribou
1
Como usar girar imagem SVG usando isso?
Somnath Kharat
Todos nós não amamos o IE;) Como de costume, requer algum trabalho adicional para que funcione em todos os navegadores. Adaptei o exemplo para suportar o IE, espero que ajude.
Tom
30

Eu sei que este post é um pouco antigo, mas eu vi tanta informação ruim sobre isso no SO que eu poderia gritar. Então, eu só preciso jogar meus dois centavos em uma abordagem totalmente diferente que eu sei que funciona, já que a uso de maneira confiável em muitos mapas. Além disso, acredito que o OP queria a capacidade de girar o marcador de seta ao redor do ponto do mapa também, o que é diferente de girar o ícone em torno de seu próprio eixo x, y, que mudará para onde o marcador de seta aponta no mapa.

Primeiro, lembre-se de que estamos brincando com o Google maps e SVG, então devemos acomodar a maneira como o Google implementa sua implementação de SVG para marcadores (ou, na verdade, símbolos). O Google define sua âncora para a imagem do marcador SVG em 0,0, que NÃO É o canto superior esquerdo do viewBox SVG. Para contornar isso, você deve desenhar sua imagem SVG de forma um pouco diferente para dar ao Google o que ele deseja ... sim, a resposta está na maneira como você realmente cria o caminho SVG em seu editor SVG (Illustrator, Inkscape, etc. .).

A primeira etapa é livrar-se do viewBox. Isso pode ser feito definindo viewBox em seu XML como 0 ... isso mesmo, apenas um zero em vez dos quatro argumentos usuais para viewBox. Isso desativa a caixa de visualização (e sim, isso é semanticamente correto). Você provavelmente notará que o tamanho da sua imagem pula imediatamente ao fazer isso, e isso porque o svg não tem mais uma base (o viewBox) para dimensionar a imagem. Portanto, criamos essa referência diretamente, definindo a largura e a altura para o número real de pixels que você deseja que a sua imagem esteja no editor XML do seu editor SVG.

Ao definir a largura e a altura da imagem SVG no editor XML, você cria uma linha de base para o dimensionamento da imagem e esse tamanho se torna um valor de 1 para as propriedades de escala do marcador por padrão. Você pode ver a vantagem que isso tem para o dimensionamento dinâmico do marcador.

Agora que você tem sua imagem dimensionada, mova a imagem até que a parte da imagem que você deseja ter como âncora esteja sobre as coordenadas 0,0 do editor SVG. Depois de fazer isso, copie o valor do atributo d do caminho svg. Você notará que cerca de metade dos números são negativos, que é o resultado do alinhamento de seu ponto de ancoragem para 0,0 da imagem em vez de viewBox.

Usar essa técnica permitirá que você gire o marcador corretamente, ao redor do ponto lat e lng no mapa. Esta é a única maneira confiável de vincular o ponto no marcador SVG que você deseja à latitude e longitude da localização do marcador.

Tentei fazer um JSFiddle para isso , mas há algum bug na implementação, um dos motivos pelos quais não gosto tanto de código reinterpretado. Então, em vez disso, incluí um exemplo totalmente independente abaixo que você pode experimentar, adaptar e usar como referência. Este é o mesmo código que tentei no JSFiddle que falhou, mas ele navega pelo Firebug sem um 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>

Isso é exatamente o que o Google faz pelos seus próprios poucos símbolos disponíveis na classe SYMBOL da API do Google Maps, então se isso não convence você ... De qualquer forma, espero que isso ajude você a fazer e configurar corretamente um marcador SVG para seus esforços do Google Maps.

Epifania
fonte
2
Boa resposta, fiz um violino funcional do seu código para que as pessoas vissem jsfiddle.net/v2pjfp7r
Rob Schmuecker
"<3" Esta é uma informação essencial que deve fazer parte da documentação do Google.
user7653815
12

Sim, você pode usar um arquivo .svg para o ícone da mesma forma que pode usar .png ou outro formato de arquivo de imagem. Basta definir a url do ícone para o diretório onde o arquivo .svg está localizado. Por exemplo:

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
fonte
1
O objeto de ícone que você está mostrando é para um marcador padrão, não um marcador SVG. Um marcador svg usa propriedades da classe de símbolo, que são então atribuídas como a propriedade do ícone da classe do marcador. (veja o objeto de ícone MrUpsidown para uma referência correta ...)
Epiphany
2
Isso também será interrompido se o SVG tiver mais de um caminho 'd' (geralmente associado a mais de uma camada na imagem SVG).
Epifania
2
Observe que seu arquivo SVG deve definir explicitamente as dimensões do ícone da seguinte forma: width="33px" height="50px"- consulte stackoverflow.com/a/21783089/165673
Yarin
10

As coisas estão melhores, agora você pode usar arquivos SVG.

        marker = new google.maps.Marker({
            position: {lat: 36.720426, lng: -4.412573},
            map: map,
            draggable: true,
            icon: "img/tree.svg"
        });
gengns
fonte
1
não permite rotação
Matt Westlake
5

Conforme mencionado por outros neste tópico, não se esqueça de definir explicitamente os atributos de largura e altura no svg assim:

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

se você não fizer isso, nenhuma manipulação de js poderá ajudá-lo, pois os gmaps não terão um quadro de referência e sempre usarão um tamanho padrão.

(sei que foi mencionado em alguns comentários, mas são fáceis de passar despercebidos. Esta informação ajudou-me em vários casos)

Larzan
fonte
1

ESTÁ BEM! Fiz isso em breve na minha web, tento duas maneiras de criar o marcador de mapa personalizado do google, este código de execução usando canvg.js é a melhor compatibilidade para navegador. O código comentado não é compatível com o IE11 atualmente.

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>

Sol
fonte
-4

Você precisa passar optimized: false.

Por exemplo

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,});

Sem passar optimized: false, meu svg apareceu como uma imagem estática.

Jeremy Eaton
fonte