
📍 Como adicionar vários marcadores personalizados com uma legenda ao Google Maps
Os marcadores são a principal forma de indicar um ponto num mapa. O «pin» vermelho padrão do Google Maps é reconhecível, mas quando tem cinco pontos de tipos diferentes, os utilizadores perdem-se: qual é uma morada exata e qual é um ponto de referência aproximado?
Isto acontece constantemente: um catálogo de lojas dividido em localizações «próprias» e «parceiras», um mapa de entregas com zonas, um painel logístico com tipos de carga. Sempre que tem mais de três pontos com significados diferentes, instala-se o caos sem ícones personalizados e uma legenda.
A solução são ícones de marcadores personalizados e uma legenda que explique cada símbolo. Em 20 minutos vai construir um mapa com vários tipos de marcadores, e os utilizadores vão perceber o que é o quê à primeira vista. Na prática, já vimos inúmeras vezes que dois ou três pins diferentes e um bloco de legenda no canto poupam minutos de decifração aos utilizadores e poupam às equipas de suporte dezenas de perguntas do género «o que significa o círculo azul?».
Abaixo está uma explicação passo a passo usando apenas JavaScript, sem frameworks. Vamos usar coordenadas perto da Torre Eiffel, um ponto de referência conveniente com geometria reconhecível. O código é intencionalmente minimalista, com cada método explicado linha a linha para que o possa adaptar à sua tarefa sem ler toda a documentação.
O material progride do simples para o complexo: primeiro vai obter uma chave de API e renderizar um mapa básico, depois adicionar marcadores com ícones diferentes, construir uma legenda e, por fim, ter um ficheiro HTML funcional que corre sem configuração adicional. Todos os exemplos foram testados no Google Chrome 120+, Firefox 121+ e Safari 17+.
💡 Visão geral rápida:
- Obtenha uma chave da API do Google Maps e inclua a biblioteca
- Prepare um array de pontos em JSON: nome, coordenadas, tipo (exato/aproximado)
- Atribua a cada tipo o seu próprio ícone através da propriedade
icon - Construa a legenda como um elemento HTML e anexe-a ao mapa através de
map.controls - Resultado: um mapa, dois tipos visuais de marcadores, legenda no canto superior direito
Obter uma chave da API do Google Maps
Trabalhar com a API JavaScript do Maps requer uma chave. Se não tiver uma, crie-a na Google Cloud Console.
Passos rápidos: vá à Console, crie um projeto (ou selecione um existente), ative a API Maps JavaScript, gere uma chave. Para desenvolvimento local, restringir a chave por referenciador HTTP é suficiente; para produção, adicione o seu domínio à lista de permissões. Em projetos reais, criamos sempre uma chave separada para cada aplicação: é mais fácil monitorizar quotas na Cloud Console e revogar o acesso de forma precisa se uma chave for comprometida.
Incluir a biblioteca ocupa uma linha em <script>:
1 <script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=drawMap"></script>
O atributo callback especifica qual a função a executar após o carregamento da API. Vamos escrevê-la de seguida.

Importante: desde fevereiro de 2024, a classe
google.maps.Markerestá oficialmente marcada como obsoleta. Foi substituída porgoogle.maps.marker.AdvancedMarkerElement, que oferece melhor desempenho e suporta HTML personalizado. O código abaixo usa oMarkerclássico (ainda funciona e é mais fácil para começar), e a secção de FAQ inclui um link para o guia de migração para mudar para o AdvancedMarkerElement.
Inicializar o mapa e preparar os dados
Primeiro, um contentor para o mapa e um array de coordenadas. Use as coordenadas que quiser; aqui temos cinco pontos perto da Torre Eiffel em Paris, três exatos e dois aproximados.
1 <div id="mapCanvas"></div>
Empacotamos os dados num array de objetos. O campo is_exact vai separar os marcadores em dois tipos: os que indicam uma morada exata e os que indicam uma área geral.
1 const locations = [ 2 { name: "Eiffel Tower", lat: 48.85837, lng: 2.294481, is_exact: true }, 3 { name: "UNESCO HQ", lat: 48.84956, lng: 2.306326, is_exact: false }, 4 { name: "Trocadéro", lat: 48.86278, lng: 2.28766, is_exact: false }, 5 { name: "Champ de Mars",lat: 48.85609, lng: 2.29820, is_exact: true }, 6 { name: "Bir-Hakeim", lat: 48.85032, lng: 2.28926, is_exact: false } 7 ];
JSON aqui é simplesmente um formato conveniente: legível, extensível e qualquer backend pode devolver um array destes numa linha.
O mapa em si é inicializado com um objeto google.maps.Map. Centre-o no primeiro ponto do array, defina o zoom para que os cinco caibam:
1 const centerMap = new google.maps.LatLng(48.856, 2.294); 2 3 const map = new google.maps.Map(document.getElementById("mapCanvas"), { 4 zoom: 14, 5 center: centerMap, 6 mapTypeId: google.maps.MapTypeId.ROADMAP, 7 mapTypeControl: true, 8 fullscreenControl: false 9 });
Adicionar marcadores personalizados com ícones diferentes
Sem um ícone personalizado, cada chamada a google.maps.Marker desenha o pin vermelho padrão. Para distinguir tipos de pontos, forneça a sua própria imagem na propriedade icon.
Prepare dois ícones, por exemplo marker_exact.png (um pin verde para moradas exatas) e marker_approx.png (um círculo azul para localizações aproximadas). Coloque os ficheiros numa pasta images/ junto ao HTML.
A lógica de seleção do ícone usa um operador ternário baseado no indicador is_exact:
1 function plotMarker(location) { 2 const iconFile = location.is_exact ? "marker_exact.png" : "marker_approx.png"; 3 4 new google.maps.Marker({ 5 position: new google.maps.LatLng(location.lat, location.lng), 6 icon: "images/" + iconFile, 7 title: location.name, 8 map: map 9 }); 10 }
Para colocar todos os marcadores de uma vez, itere pelo array:
1 function setMarkers(locations) { 2 for (let i = 0; i < locations.length; i++) { 3 plotMarker(locations[i]); 4 } 5 }
Pronto. Cinco pontos, dois ícones, o mapa conhece cada marcador. Na nossa experiência, nesta fase deve abrir a consola e verificar se os cinco marcadores foram renderizados: se um ícone não for encontrado no caminho especificado, o marcador reverte silenciosamente para o pin padrão, e pode nem reparar visualmente. Mas o utilizador ainda não percebe. Precisa de uma legenda.
Algumas palavras sobre ícones. O tamanho ideal é 40×40 px para ecrãs normais e 80×80 px para Retina (especifique o ficheiro maior no atributo icon e defina scaledSize como new google.maps.Size(40,40); a imagem ficará nítida em ecrãs retina). O formato pode ser PNG/SVG/WebP, mas para pins personalizados preferimos SVG: pesa menos, redimensiona sem pixelização e é fácil de editar. Se ainda usar PNG, escolha 24 bits com canal alfa; caso contrário, aparece um fundo branco à volta do pin, o que fica deslocado em mapas escuros.
Um erro comum: especificar o caminho do ficheiro do ícone relativo ao HTML em vez de relativo à raiz do servidor. Coloque marker_exact.png na mesma pasta que index.html e especifique "marker_exact.png" sem barra inicial. Se os ícones estiverem numa subpasta como images/, o caminho será "images/marker_exact.png", exatamente como mostrado no código acima.
Criar uma legenda para os marcadores
Uma legenda é apenas um elemento DOM normal que colocamos na zona de controlos do mapa. O Google Maps permite posicionar um controlo numa das posições padrão: por exemplo TOP_LEFT, TOP_CENTER ou RIGHT_TOP. Para uma legenda, o canto superior direito faz sentido.
Primeiro, descreva o contentor em HTML e estilize-o:
1 <div id="mapLegend"> 2 <h2>Обозначения</h2> 3 </div> 4 5 css 6 #mapLegend { 7 background: #fdfdfd; 8 color: #3c4750; 9 padding: 0 10px; 10 margin: 10px; 11 font-weight: bold; 12 opacity: 0.85; 13 border: 2px solid #000; 14 } 15 #mapLegend div { 16 height: 40px; 17 line-height: 25px; 18 font-size: 1.2em; 19 } 20 #mapLegend div img { 21 float: left; 22 margin-right: 10px; 23 } 24 #mapLegend h2 { 25 text-align: center; 26 }
Agora preencha a legenda com linhas de "ícone + etiqueta" e entregue-a ao mapa:
1 const legend = document.getElementById("mapLegend"); 2 3 const exactDiv = document.createElement("div"); 4 exactDiv.innerHTML = '<img src="images/marker_exact.png"> Точное местоположение'; 5 legend.appendChild(exactDiv); 6 7 const approxDiv = document.createElement("div"); 8 approxDiv.innerHTML = '<img src="images/marker_approx.png"> Примерное местоположение'; 9 legend.appendChild(approxDiv); 10 11 map.controls[google.maps.ControlPosition.RIGHT_TOP].push(legend);
A ordem importa: primeiro monte legend como um nó DOM, depois insira-o. Se inverter esta ordem, o controlo permanece vazio.
Código completo: juntar tudo
Combine HTML, CSS e JavaScript num único ficheiro. Guarde-o como index.html, substitua YOUR_API_KEY por uma chave válida e abra-o num navegador.

Aqui está a listagem final:
1 <!doctype html> 2 <html lang="ru"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Custom Markers with Legend — Google Maps</title> 6 <style> 7 #mapCanvas { 8 width: 775px; 9 height: 500px; 10 margin: 0 auto; 11 } 12 #mapLegend { 13 background: #fdfdfd; 14 color: #3c4750; 15 padding: 0 10px; 16 margin: 10px; 17 font-weight: bold; 18 opacity: 0.85; 19 border: 2px solid #000; 20 } 21 #mapLegend div { 22 height: 40px; 23 line-height: 25px; 24 font-size: 1.2em; 25 } 26 #mapLegend div img { 27 float: left; 28 margin-right: 10px; 29 } 30 #mapLegend h2 { 31 text-align: center; 32 } 33 </style> 34 </head> 35 <body> 36 37 <div id="mapCanvas"></div> 38 39 <div id="mapLegend"> 40 <h2>Обозначения</h2> 41 </div> 42 43 <script> 44 const locations = [ 45 { name: "Eiffel Tower", lat: 48.85837, lng: 2.294481, is_exact: true }, 46 { name: "UNESCO HQ", lat: 48.84956, lng: 2.306326, is_exact: false }, 47 { name: "Trocadéro", lat: 48.86278, lng: 2.28766, is_exact: false }, 48 { name: "Champ de Mars",lat: 48.85609, lng: 2.29820, is_exact: true }, 49 { name: "Bir-Hakeim", lat: 48.85032, lng: 2.28926, is_exact: false } 50 ]; 51 52 let map; 53 54 function drawMap() { 55 const centerMap = new google.maps.LatLng(48.856, 2.294); 56 57 map = new google.maps.Map(document.getElementById("mapCanvas"), { 58 zoom: 14, 59 center: centerMap, 60 mapTypeId: google.maps.MapTypeId.ROADMAP, 61 mapTypeControl: true, 62 fullscreenControl: false 63 }); 64 65 setMarkers(locations); 66 buildLegend(); 67 } 68 69 function setMarkers(locations) { 70 for (let i = 0; i < locations.length; i++) { 71 plotMarker(locations[i]); 72 } 73 } 74 75 function plotMarker(location) { 76 const iconFile = location.is_exact ? "marker_exact.png" : "marker_approx.png"; 77 78 new google.maps.Marker({ 79 position: new google.maps.LatLng(location.lat, location.lng), 80 icon: "images/" + iconFile, 81 title: location.name, 82 map: map 83 }); 84 } 85 86 function buildLegend() { 87 const legend = document.getElementById("mapLegend"); 88 89 const exactDiv = document.createElement("div"); 90 exactDiv.innerHTML = '<img src="images/marker_exact.png"> Точное местоположение'; 91 legend.appendChild(exactDiv); 92 93 const approxDiv = document.createElement("div"); 94 approxDiv.innerHTML = '<img src="images/marker_approx.png"> Примерное местоположение'; 95 legend.appendChild(approxDiv); 96 97 map.controls[google.maps.ControlPosition.RIGHT_TOP].push(legend); 98 } 99 </script> 100 101 <script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=drawMap"></script> 102 </body> 103 </html>
Substitua YOUR_API_KEY e o mapa ganha vida. Adicionar um terceiro tipo de marcador significa outro ícone, outro div na legenda e outra condição em plotMarker. Escala linearmente.
Ao transferir este código para o seu projeto, preste atenção a dois aspetos. Primeiro: numa aplicação real, os dados de locations provavelmente virão de um backend via fetch ou XMLHttpRequest, em vez de estarem codificados no <script>. Segundo: se tiver mais de dez pontos, mova o array para um ficheiro .json separado e carregue-o de forma assíncrona; isto mantém o HTML legível e evita que o mapa bloqueie a renderização da página.
Para produção com dezenas de marcadores, adicione agrupamento (clustering) através da biblioteca @googlemaps/markerclusterer: ela agrupa pontos próximos em círculos com números e expande-os para pins individuais ao fazer zoom. Instale via npm (npm i @googlemaps/markerclusterer) e ligue com três linhas: new MarkerClusterer({ map, markers }). Sem agrupamento, cinquenta marcadores ao zoom da cidade transformam o mapa numa confusão ilegível de ícones sobrepostos.
⁉️🤔 Perguntas frequentes
Posso usar SVG em vez de PNG para os ícones dos marcadores?
Sim, o Google Maps aceita qualquer formato que o browser consiga renderizar: PNG, SVG, WebP. O SVG é especialmente conveniente, pois não perde qualidade ao fazer zoom e pesa menos. Passe o caminho para o ficheiro
.svgna propriedadeiconexatamente como faria para PNG. Defina o tamanho do ícone através dos atributoswidth/heightdo próprio ficheiro SVG: o Google Maps irá usá-los como base e escalá-los para as densidades de visualização.
Como migro de google.maps.Marker para AdvancedMarkerElement?
Desde fevereiro de 2024,
google.maps.Markerestá marcado como obsoleto. A nova classegoogle.maps.marker.AdvancedMarkerElementusa conteúdo HTML em vez da propriedadeicon, pelo que pode inserir um botão, distintivo ou SVG animado diretamente no marcador. Inclua a biblioteca de marcadores (&libraries=markerno URL da API) e, no construtor, substituaiconporcontentcontendo um elemento DOM:
1 > const marker = new google.maps.marker.AdvancedMarkerElement({ 2 > map, 3 > position: new google.maps.LatLng(location.lat, location.lng), 4 > title: location.name, 5 > content: document.createElement("img") 6 > }); 7 >
O conteúdo pode ser qualquer elemento DOM: <img>, um <div> com um fundo ou SVG. O guia de migração oficial cobre cada cenário de substituição passo a passo com exemplos.
Como adiciono uma legenda na versão móvel do mapa?
O controlo
RIGHT_TOPsobrepõe-se ao mapa em ecrãs estreitos (menos de 480 px). A solução é uma media query CSS: paramax-width: 480px, mova a legenda paraBOTTOM_CENTERou reduza o tamanho da fonte e o padding para metade. Uma alternativa é colocar a legenda fora do mapa como um bloco estático e sincronizar o estado via JavaScript. ParaBOTTOM_CENTER, basta alterar a posição dentro debuildLegend().
Quantos marcadores o mapa suporta sem problemas de desempenho?
Até 100 marcadores no ecrã não causam atrasos percetíveis, mesmo em smartphones de gama média. De 100 a 500, começam a notar-se atrasos ao fazer zoom e arrastar. Para mais de 500, ative o agrupamento; a Google fornece a biblioteca pronta a usar
@googlemaps/markerclusterer, que agrupa marcadores próximos em círculos numerados. Na prática: 50 marcadores com ícones PNG de 2 KB significam 100 KB de downloads e renderização instantânea. 500 marcadores sem agrupamento significam que o mapa faz cerca de 500 chamadasdrawImage()por frame de zoom, o que se torna notório.
O que fazer se os marcadores não aparecerem
Primeiro, verifique a consola do browser. Erros típicos: MissingKeyMapError (chave de API não fornecida), RefererNotAllowedMapError (domínio não está na lista de permissões da chave), Cannot read property 'maps' of undefined (o script da API não carregou; verifique o URL e o callback). Abra as DevTools no separador Consola e atualize a página; a linha de erro a vermelho mostrará imediatamente a causa raiz.
Segundo, certifique-se de que o contentor #mapCanvas existe no DOM quando drawMap() é chamada. Se o seu script estiver no <head> sem defer, o elemento ainda não foi renderizado e o mapa falha silenciosamente. Os atributos [async defer](/orig_post/luchshij-sposob-zagruzit-vneshnij-javascript) na tag <script> resolvem isto, mas a melhor prática é colocar a tag <script> no final do <body>, depois de todos os elementos DOM.
Terceiro, verifique os caminhos dos ícones. Se marker_exact.png não estiver em images/ relativamente ao ficheiro HTML, o browser devolve um 404 para a imagem e o marcador é renderizado como o pin vermelho padrão (ou não é renderizado de todo, dependendo da versão da API). Dica: verifique sempre os caminhos através do separador Rede nas DevTools, filtrando por tipo Img; os ícones quebrados serão imediatamente destacados a vermelho.
Mais importante: teste sempre com uma chave real. Sem uma chave ou com uma chave inválida, o mapa escurece com uma marca de água "Apenas para fins de desenvolvimento" e alguns métodos da API devolvem respostas vazias. Tivemos um caso em que o mapa funcionava perfeitamente em staging, mas a legenda não aparecia em produção: descobriu-se que a chave de produção estava restrita por IP e a API recusava silenciosamente os controlos. Verifique as restrições da chave na Cloud Console antes de publicar.
Cobrimos o ciclo completo: desde obter uma chave até um mapa ao vivo com uma legenda. Testámos o código acima na Google Maps API v3.56 (atual em junho de 2026); todos os cinco marcadores com ícones personalizados foram renderizados corretamente e a legenda em RIGHT_TOP não se sobrepôs aos controlos de zoom.
Se for mais longe, tenha três aspetos em mente. Primeiro: carregue sempre os ícones no tamanho em que aparecerão no mapa, pois o redimensionamento feito pelo browser em tempo real consome frames durante o zoom. Segundo: para mapas com dezenas de marcadores, o agrupamento é obrigatório, caso contrário os clientes móveis agradecer-lhe-ão com lentidão. Terceiro: mantenha a chave de API sob restrição de referenciador HTTP desde o primeiro dia, mesmo em ambientes de desenvolvimento; uma chave exposta sem restrições é um caminho direto para o tráfego de outra pessoa na sua conta de faturação.
Se a tarefa se tornar mais complexa (agrupamento, janelas de informação ao clicar, filtragem de marcadores por categorias), diga-nos nos comentários qual o cenário a abordar a seguir.



