Mapas interativos open-source com Leaflet

O Leaflet é uma biblioteca JavaScript open-source voltada para a criação de mapas interativos. Ela é responsável apenas pela renderização e pela interação do mapa.
Na prática, o Leaflet consome imagens de mapa, conhecidas como tiles, fornecidas por serviços externos. Essas imagens são organizadas em uma grade, formando o mapa completo na tela.
A partir disso, a biblioteca permite adicionar elementos interativos, como: markers (marcadores), linhas e polígonos, popups, eventos de clique e interação.
Um ponto importante: o Leaflet não possui dados geográficos próprios. Para funcionar, ele depende de uma fonte externa de tiles, como serviços de mapas online.
OpenStreetMap: de onde vêm os dados dos mapas
Como o Leaflet não possui dados geográficos próprios, é necessário utilizar uma fonte externa. Neste exemplo, vamos usar o OpenStreetMap, um banco de dados geográfico mantido por uma comunidade global de voluntários.
O OpenStreetMap é baseado em dados abertos, ou seja, qualquer pessoa pode utilizar as informações livremente, desde que dê os devidos créditos ao projeto e aos seus contribuidores. Na prática, vamos configurar o OpenStreetMap como provedor de tiles do nosso mapa, veremos isso mais adiante no código.
⚠️ Importante: O OpenStreetMap possui limitações quanto ao volume de requisições. Por isso, ele é mais indicado para: projetos de pequeno porte ou aplicações em desenvolvimento ou testes. Para aplicações com alto volume de acessos, o ideal é utilizar um provedor de tiles dedicado (geralmente pago), que ofereça maior desempenho e escalabilidade.
Configurando o mapa com Leaflet
Para exibir um mapa com o Leaflet, o primeiro passo é incluir a biblioteca no projeto. A forma mais simples de fazer isso é utilizando um CDN.
Na prática, basta adicionar os arquivos de CSS e JavaScript do Leaflet diretamente no HTML.
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
crossorigin=""></script>
Outra opção é instalar o Leaflet utilizando o NPM, o que é mais comum em projetos que utilizam ferramentas de build como Webpack ou Vite. Para instalar o ele via NPM, execute o seguinte comando no terminal:
npm install leaflet
Após instalar o Leaflet, você pode importar a biblioteca e o CSS diretamente no seu arquivo JavaScript:
// main.js
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
⚠️ Atenção: É fundamental garantir que o arquivo leaflet.css seja carregado corretamente. Sem ele, elementos como os controles de zoom e os ícones dos marcadores não serão exibidos, um problema bastante comum.
Com a biblioteca configurada, o próximo passo é criar o elemento HTML que irá conter o mapa. Para isso, utilizamos uma div simples.
<div id="map" style="width: 600px;height: 400px;"></div>
Agora vamos criar o mapa, definindo um ponto inicial e o nível de zoom. Neste exemplo, utilizamos as coordenadas -30.036808837888316, -51.21598009185646, que correspondem ao Parque da Redenção, em Porto Alegre.
Também definimos o nível de zoom como 15, o que proporciona uma visualização mais detalhada da região.
var map = L.map('map').setView([-30.036808837888316, -51.21598009185646], 15);
Observe que passamos o id do elemento HTML para o método L.map(), indicando onde o mapa será renderizado. Em seguida, utilizamos o método setView() para definir a posição inicial do mapa. Esse método recebe dois parâmetros: um array com latitude e longitude (lat, lng), o nível de zoom
Se for necessário alterar a posição ou o zoom dinamicamente, basta chamar novamente o método setView() no objeto map, informando os novos valores.
O próximo passo é adicionar uma tile layer, que é a camada responsável pelas imagens do mapa.
É aqui que configuramos o endpoint que o Leaflet utilizará para buscar os tiles e montar o mapa na tela. Como vimos anteriormente, vamos usar o OpenStreetMap como provedor dessas imagens.
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);
Chamamos o método L.tileLayer() informando o template de URL do serviço de tiles do OpenStreetMap e um objeto de configuração com as opções da camada. Nesse objeto, definimos: maxZoom como 19, limitando o nível máximo de zoom, attribution, responsável por exibir os créditos exigidos pelo OpenStreetMap. Por fim, utilizamos o método addTo(map) para adicionar a camada ao mapa criado anteriormente.
Você conferir ver o exemplo de um mapa simples aqui.
Adicionando marcadores, círculos e polígonos
Podemos adicionar diferentes elementos ao mapa, sendo o mais comum o marcador, utilizado para indicar um ponto específico.
Para adicioná-lo, basta utilizar o método L.marker(), informando um array com as coordenadas de latitude e longitude. Em seguida, chamamos addTo(map) para inserir o marcador no mapa desejado.
var marker = L.marker([-30.036808837888316, -51.21598009185646]).addTo(map);
Outro elemento bastante comum é o círculo, geralmente utilizado para representar uma área a partir de um ponto central.
Para criá-lo, utilizamos o método L.circle(), informando as coordenadas de latitude e longitude. O raio deve ser passado como um segundo parâmetro inteiro (em metros) ou por meio de um objeto de configuração, que também permite personalizar a aparência do círculo.
Nesse objeto, podemos ajustar propriedades como a cor da borda(color), a cor de preenchimento(fillColor), o nível de transparência (fillOpacity) e o raio(radius), que sempre é definido em metros a partir do ponto central.
Após criar o círculo, podemos encadear a chamada do método addTo(map) para adicioná-lo ao mapa desejado.
var circle = L.circle([-30.036808837888316, -51.21598009185646], 400).addTo(map);
// ou
var circle = L.circle([-30.036808837888316, -51.21598009185646], {
color: '#FF0000',
fillColor: '#FF0033',
fillOpacity: 0.5,
radius: 400
}).addTo(map);
Quando precisamos representar uma área mais personalizada no mapa, podemos utilizar um polígono.
Para isso, usamos o método L.polygon(), informando um array com as coordenadas que definem os pontos da área. Assim como no círculo, também é possível passar um objeto de configuração para personalizar a aparência do polígono.
var polygon = L.polygon([
[-30.033902795609123, -51.221540632725805],
[-30.032405440492138, -51.22047848211876],
[-30.033640294462014, -51.21568410639647],
[-30.0362672518493, -51.2103433760591],
[-30.03989256949555, -51.21698907291952],
]).addTo(map);
Você pode conferirum exemplo de mapas com marcadores, círculo e poligono.
Adicionando popups ao mapa
Podemos adicionar um popup a um elemento do mapa utilizando o método bindPopup("<mensagem>"). Por padrão, o popup só é exibido quando o usuário clica no objeto ao qual ele foi associado.
Se quisermos que o popup já apareça aberto ao carregar o mapa, basta encadear a chamada do método openPopup().
marker.bindPopup("Parque da Redenção").openPopup();
O conteúdo de um popup aceita HTML, o que permite enriquecer a mensagem com diferentes elementos, como <strong> para destaque de texto ou até imagens com <img>.
circle.bindPopup("<div><strong style='color:#FF0000'>Parque da Redenção</strong></div> <p>Visite!</p>").openPopup();
polygon.bindPopup("<div><strong>Parque da Redenção</strong></div> <div><img src='foto.jpg' style='width:100%'></div>").openPopup();
Confira um exemplo de mapas com elementos que utilizam popups.
Também podemos adicionar um popup diretamente no mapa utilizando o método L.popup().
Nesse caso, encadeamos alguns métodos para configurar o comportamento: setLatLng() define as coordenadas onde o popup será exibido, setContent() define o conteúdo e openOn(map) exibe o popup no mapa desejado.
var popup = L.popup()
.setLatLng([-30.036808837888316, -51.21598009185646])
.setContent("Um popup direto no mapa")
.openOn(map);
Trabalhando com eventos no Leaflet
O usuário pode interagir com o mapa, gerando eventos que podem ser capturados pela aplicação.
No Leaflet, utilizamos o método on() do objeto do mapa para ouvir esses eventos. Esse método recebe o nome do evento e uma função que será executada quando ele ocorrer. Essa função, por sua vez, recebe um objeto com as informações do evento.
Um dos eventos mais comuns de trabalhar é o de click no mapa.
var marker = null;
map.on('click', function(e){
if(marker !== null && map.hasLayer(marker)){
map.removeLayer(marker)
}
document.querySelector('#lat').innerText = e.latlng.lat;
document.querySelector('#lng').innerText = e.latlng.lng;
marker = L.marker([e.latlng.lat, e.latlng.lng]).addTo(map);
});
No exemplo acima, utilizamos o método on() para tratar o evento de click. A cada clique no mapa, removemos o marcador anterior, adicionamos um marcador na posição selecionada e atualizamos dois elementos do DOM com as coordenadas do ponto.
Primeiro, verificamos se já existe um marcador e se ele está presente no mapa. Para isso, utilizamos o método hasLayer(). Caso exista, removemos o marcador com removeLayer().
Em seguida, acessamos as coordenadas do ponto clicado através do objeto de evento (e.latlng) e utilizamos esses valores para atualizar os elementos com os ids #lat e #lng.
Por fim, criamos um novo marcador com as coordenadas capturadas e o adicionamos ao mapa.
Confira um exemplo de mapa com o tratamento de eventos.
Como obter a localização do usuário
O Leaflet possui suporte nativo à API de geolocalização do navegador (Geolocation API). Com isso, é possível obter a localização do usuário e centralizar o mapa de forma simples.
Para isso, utilizamos o método locate(), que permite solicitar a posição do usuário, passando algumas configurações.
map.locate({
setView: true, // centraliza o mapa automaticamente
maxZoom: 15, // zoom máximo ao centralizar
watch: false // true = atualiza continuamente (GPS ao vivo)
});
O método locate() tenta obter a posição do usuário e dispara eventos conforme o resultado da operação.
Se a localização for obtida com sucesso, o evento locationfound é acionado. Caso ocorra algum erro, como falta de permissão, o evento locationerror é disparado.
No caso de sucesso, a função de tratamento recebe um objeto com os dados da localização, incluindo latlng (latitude e longitude) e accuracy, que representa a precisão da posição em metros.
map.on('locationfound', function(e){
const raio = e.accuracy; // precisão em metros
L.marker(e.latlng)
.addTo(map)
.bindPopup(`Você está aqui! (±${Math.round(raio)}m)`)
.openPopup();
L.circle(e.latlng, { radius: raio }).addTo(map);
});
map.on('locationerror', function(e){
alert('Localização indisponível: ' + e.message);
});
No exemplo acima, tratamos o evento locationfound adicionando um marcador na posição obtida a partir do objeto de evento. Também desenhamos um círculo com o mesmo centro, utilizando o valor de precisão (accuracy) como raio, representando a área aproximada onde o usuário pode estar.
Já no evento locationerror, exibimos apenas uma mensagem de alert informando que não foi possível obter a localização.
Essa foi uma breve introdução ao Leaflet, uma opção bastante interessante para quem precisa de uma biblioteca open-source para exibição de mapas interativos no navegador.
Além disso, o Leaflet conta com um ecossistema rico de plugins, que permite expandir suas funcionalidades para diversos cenários.
E por hoje é isso. T++
