Biblioteca JavaScript para buscar e exibir polígonos de cidades no Leaflet usando a API do OpenStreetMap (Nominatim)
npm install city-polygonBiblioteca JavaScript para buscar e exibir polígonos de cidades no Leaflet usando a API do OpenStreetMap (Nominatim).
Simplesmente inclua o script na sua página HTML após o Leaflet:
``html
`
`javascript
// 1. Crie um mapa Leaflet
const map = L.map('map').setView([-15.78, -47.93], 4);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
// 2. Inicialize a biblioteca
const cityLib = new CityPolygonLib(map);
// 3. Carregue uma cidade
await cityLib.loadCity('São Paulo');
`
`javascript`
const cityLib = new CityPolygonLib(map, options);
Parâmetros:
- map (L.Map): Instância do mapa Leaflet (obrigatório)options
- (Object): Opções de configuração
Opções disponíveis:
`javascript`
{
userAgent: 'CityPolygonLib/1.0', // User agent para API
language: 'pt-BR', // Idioma dos resultados
style: { // Estilo do polígono
color: '#667eea',
weight: 3,
opacity: 0.8,
fillColor: '#764ba2',
fillOpacity: 0.3
},
autoZoom: true, // Auto zoom no polígono
zoomPadding: [50, 50], // Padding do zoom
showPopup: true, // Mostrar popup automático
usePrimaryPolygonOnly: true // Usar apenas polígono principal
}
Explicação do Construtor:
O construtor valida se um mapa Leaflet foi fornecido e inicializa a biblioteca com as opções padrão ou personalizadas. Ele armazena a referência do mapa, inicializa variáveis para o polígono atual e dados da cidade, e mescla as opções fornecidas com os padrões.
#### loadCity(cityName, options)searchCity
Busca e adiciona a cidade ao mapa (método mais conveniente). Este método combina e addPolygonToMap em uma única chamada.
`javascript`
const result = await cityLib.loadCity('Rio de Janeiro');
// Retorna: { cityData, polygon }
Com opções:
`javascript`
await cityLib.loadCity('Paris', {
searchOptions: {
limit: 1,
countrycodes: 'fr'
},
styleOptions: {
color: '#ff0000',
fillColor: '#ff6666'
},
useAllCoordinates: true // Usa todas as coordenadas do MultiPolygon
});
Explicação: Este método é um wrapper que executa a busca da cidade e imediatamente adiciona o polígono ao mapa. É útil quando você quer fazer tudo de uma vez. As opções permitem personalizar a busca, o estilo e se deve usar todas as coordenadas ou apenas a primeira.
#### searchCity(cityName, searchOptions)
Busca a cidade na API Nominatim e retorna os dados brutos (sem adicionar ao mapa).
`javascript`
const cityData = await cityLib.searchCity('Curitiba');
console.log(cityData);
Explicação: Este método faz uma requisição HTTP para a API Nominatim do OpenStreetMap usando fetch. Ele constrói a URL com os parâmetros de busca, incluindo o nome da cidade, formato JSON, solicitação de GeoJSON do polígono, e detalhes do endereço. Retorna o primeiro resultado da busca ou lança um erro se a cidade não for encontrada.
#### addPolygonToMap(cityData, styleOptions, useAllCoordinates)
Adiciona um polígono ao mapa a partir dos dados da cidade já buscados.
`javascript`
const cityData = await cityLib.searchCity('Salvador');
const polygon = cityLib.addPolygonToMap(cityData, {
color: '#00ff00',
fillColor: '#00cc00'
}, false); // false = usa apenas primeira coordenada
Parâmetros:
- cityData (Object): Dados da cidade retornados por searchCitystyleOptions
- (Object): Opções de estilo personalizadas (opcional)useAllCoordinates
- (boolean): Se true, usa todas as coordenadas; se false, usa apenas a primeira (padrão: false)
Explicação: Este método processa o GeoJSON da cidade usando processPrimaryPolygon, remove qualquer polígono anterior do mapa, cria uma camada GeoJSON do Leaflet com o estilo especificado, adiciona ao mapa, e opcionalmente ajusta o zoom e adiciona um popup. O parâmetro useAllCoordinates controla se múltiplos polígonos (MultiPolygon) devem ser mantidos ou apenas o primeiro.
#### getCoordinates(cityNameOrData, searchOptionsOrUseAll, useAllCoordinates)
Retorna apenas as coordenadas do polígono, sem adicionar ao mapa. Útil quando você precisa apenas das coordenadas para processamento ou armazenamento.
`javascript
// Opção 1: Passando nome da cidade
const coordinates = await cityLib.getCoordinates('São Paulo');
// Retorna: [[[lng, lat], [lng, lat], ...]]
// Opção 2: Passando nome com opções de busca
const coordinates2 = await cityLib.getCoordinates('São Paulo', { limit: 1 });
// Opção 3: Passando nome e useAllCoordinates como boolean
const allCoords = await cityLib.getCoordinates('São Paulo', true);
// Opção 4: Passando dados já buscados
const cityData = await cityLib.searchCity('Rio de Janeiro');
const coordinates3 = await cityLib.getCoordinates(cityData, true);
`
Parâmetros:
- cityNameOrData (string|Object): Nome da cidade ou dados da cidade já buscadossearchOptionsOrUseAll
- (Object|boolean): Se cityNameOrData for string: objeto com opções de busca. Se for objeto: boolean indicando se deve usar todas as coordenadasuseAllCoordinates
- (boolean): Se true, usa todas as coordenadas; se false, usa apenas a primeira (padrão: false). Apenas usado quando cityNameOrData for string
Retorna: Promise - Array de coordenadas no formato [[[lng, lat], [lng, lat], ...]] para Polygon, ou array de arrays para MultiPolygon
Explicação: Este método é flexível e aceita tanto o nome da cidade quanto os dados já buscados. Se receber uma string, faz a busca automaticamente. O parâmetro useAllCoordinates permite escolher entre usar todas as coordenadas de um MultiPolygon ou apenas a primeira. As coordenadas retornadas seguem o padrão GeoJSON: [longitude, latitude].
#### processPrimaryPolygon(geojson, useAllCoordinates)
Processa o GeoJSON para usar apenas o polígono principal ou todos os polígonos.
`javascript
// Processa e retorna apenas o primeiro polígono
const processed = cityLib.processPrimaryPolygon(multiPolygonData, false);
// Processa e mantém todos os polígonos
const allPolygons = cityLib.processPrimaryPolygon(multiPolygonData, true);
`
Parâmetros:
- geojson (Object): Objeto GeoJSONuseAllCoordinates
- (boolean): Se true, mantém todas as coordenadas; se false, usa apenas a primeira (padrão: false)
Explicação: Este método processa diferentes tipos de geometrias GeoJSON:
- Polygon: Retorna como está
- MultiPolygon: Se useAllCoordinates for false, extrai apenas o primeiro polígono e converte para Polygon. Se true, mantém como MultiPolygon
- GeometryCollection: Extrai a primeira geometria e processa recursivamente
É usado internamente por addPolygonToMap e getCoordinates para normalizar os dados antes de usar.
#### getCityInfo()
Retorna informações estruturadas e processadas da cidade atual.
`javascript`
const info = cityLib.getCityInfo();
console.log(info);
/*
{
name: "São Paulo",
displayName: "São Paulo, Brasil",
type: "city",
country: "Brasil",
state: "São Paulo",
bounds: LatLngBounds,
area: 1521.11, // km²
lat: -23.5505,
lon: -46.6333
}
*/
Explicação: Este método processa os dados brutos da cidade e retorna um objeto estruturado com informações úteis. Extrai o nome da cidade do display_name, converte coordenadas para números, calcula a área aproximada usando os bounds, e organiza tudo em um formato fácil de usar.
#### updateStyle(newStyle)
Atualiza o estilo do polígono atual sem precisar recriá-lo.
`javascript`
cityLib.updateStyle({
color: '#ff0000',
weight: 5,
fillOpacity: 0.7
});
Explicação: Usa o método setStyle do Leaflet para atualizar o estilo do polígono existente. Útil para animações ou mudanças dinâmicas de cor.
#### removePolygon()
Remove o polígono atual do mapa e limpa as referências.
`javascript`
cityLib.removePolygon();
Explicação: Remove a camada do mapa usando removeLayer do Leaflet e limpa as variáveis currentPolygon e currentCityData. Útil para limpar o mapa antes de adicionar um novo polígono.
#### clear()
Limpa tudo e reseta a biblioteca ao estado inicial.
`javascript`
cityLib.clear();
Explicação: Chama removePolygon() internamente para limpar completamente o estado da biblioteca. Útil para resetar antes de uma nova busca.
#### zoomToPolygon()
Ajusta o zoom do mapa para enquadrar o polígono atual.
`javascript`
cityLib.zoomToPolygon();
Explicação: Usa fitBounds do Leaflet para ajustar o zoom e centralizar o mapa no polígono. Usa o zoomPadding configurado nas opções para adicionar espaço ao redor do polígono.
#### getApproximateArea()
Calcula e retorna a área aproximada do polígono em km².
`javascriptÁrea: ${area.toFixed(2)} km²
const area = cityLib.getApproximateArea();
console.log();`
Explicação: Calcula a área usando os bounds (limites) do polígono. Obtém os pontos nordeste e sudoeste, calcula a distância em metros usando o método distance do Leaflet, e converte para km². É uma aproximação, não uma medida exata, pois usa um retângulo ao invés do polígono real.
#### getBounds()
Retorna os bounds (limites) do polígono atual.
`javascript`
const bounds = cityLib.getBounds();
Explicação: Retorna um objeto L.LatLngBounds do Leaflet que contém os limites geográficos do polígono. Útil para cálculos de área, zoom, ou verificação de sobreposição.
#### getPolygon()
Retorna a camada L.GeoJSON do polígono atual.
`javascript`
const polygon = cityLib.getPolygon();
// Você pode usar métodos do Leaflet diretamente
polygon.on('click', () => console.log('Polígono clicado!'));
Explicação: Retorna a referência direta ao objeto GeoJSON do Leaflet, permitindo acesso a todos os métodos e eventos do Leaflet.
#### getCityData()
Retorna os dados brutos da cidade retornados pela API Nominatim.
`javascript`
const rawData = cityLib.getCityData();
console.log(rawData.place_id, rawData.osm_id);
Explicação: Retorna o objeto completo retornado pela API Nominatim, incluindo todos os campos como place_id, osm_id, boundingbox, address, etc. Útil quando você precisa de informações não processadas pela biblioteca.
#### setUsePrimaryPolygonOnly(value)
Define se deve usar apenas o polígono principal ao processar MultiPolygons.
`javascript`
cityLib.setUsePrimaryPolygonOnly(false); // Mostra todos os polígonos
Explicação: Altera a opção usePrimaryPolygonOnly que controla o comportamento do método processPrimaryPolygon. Quando true, apenas o primeiro polígono de um MultiPolygon é usado, melhorando a performance. Quando false, todos os polígonos são mantidos.
#### setAutoZoom(value)
Define se deve fazer zoom automático ao adicionar um polígono.
`javascript`
cityLib.setAutoZoom(false);
Explicação: Controla se o método addPolygonToMap deve automaticamente ajustar o zoom para enquadrar o polígono. Útil quando você quer controlar o zoom manualmente.
javascript
const map = L.map('map').setView([0, 0], 2);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);const cityLib = new CityPolygonLib(map);
await cityLib.loadCity('Brasília');
`$3
`javascript
const cityLib = new CityPolygonLib(map, {
style: {
color: '#ff0000',
weight: 4,
fillColor: '#ff6666',
fillOpacity: 0.4
}
});await cityLib.loadCity('Porto Alegre');
`$3
`javascript
const cityLib = new CityPolygonLib(map, {
autoZoom: false
});await cityLib.loadCity('Fortaleza');
`$3
`javascript
// Buscar coordenadas sem adicionar ao mapa
const coordinates = await cityLib.getCoordinates('São Paulo');
console.log('Coordenadas:', coordinates);// Usar todas as coordenadas de um MultiPolygon
const allCoordinates = await cityLib.getCoordinates('Rio de Janeiro', true);
console.log('Todas as coordenadas:', allCoordinates);
// Processar coordenadas manualmente
coordinates.forEach(ring => {
ring.forEach(point => {
const [lng, lat] = point;
console.log(
Lat: ${lat}, Lng: ${lng});
});
});
`$3
`javascript
// Adiciona apenas o primeiro polígono (padrão)
await cityLib.loadCity('São Paulo');// Adiciona todos os polígonos de um MultiPolygon
await cityLib.loadCity('Rio de Janeiro', {
useAllCoordinates: true
});
// Ou usando addPolygonToMap diretamente
const cityData = await cityLib.searchCity('Brasília');
cityLib.addPolygonToMap(cityData, {}, true); // true = todas as coordenadas
`$3
`javascript
// Desabilita remoção automática do polígono anterior
const cities = ['São Paulo', 'Rio de Janeiro', 'Belo Horizonte'];
const colors = ['#ff0000', '#00ff00', '#0000ff'];for (let i = 0; i < cities.length; i++) {
const cityData = await cityLib.searchCity(cities[i]);
// Cria uma nova instância para cada cidade
L.geoJSON(cityLib.processPrimaryPolygon(cityData.geojson), {
style: {
color: colors[i],
fillColor: colors[i],
weight: 2,
fillOpacity: 0.2
}
}).addTo(map);
}
`$3
`javascript
// Buscar apenas em um país específico
const cityData = await cityLib.searchCity('Londres', {
countrycodes: 'gb'
});
`$3
`javascript
const colors = ['#e74c3c', '#3498db', '#2ecc71', '#f39c12'];
let colorIndex = 0;function changeColor() {
cityLib.updateStyle({
color: colors[colorIndex],
fillColor: colors[colorIndex]
});
colorIndex = (colorIndex + 1) % colors.length;
}
setInterval(changeColor, 2000); // Muda cor a cada 2 segundos
`$3
`javascript
await cityLib.loadCity('Recife');const info = cityLib.getCityInfo();
console.log(
);
`$3
`javascript
try {
await cityLib.loadCity('CidadeQueNaoExiste123');
} catch (error) {
console.error('Erro ao carregar cidade:', error.message);
alert('Cidade não encontrada!');
}
`$3
`javascript
// Obter coordenadas e processar
const coordinates = await cityLib.getCoordinates('São Paulo', true);// Verificar se é MultiPolygon ou Polygon
if (Array.isArray(coordinates[0][0][0])) {
// É MultiPolygon
coordinates.forEach((polygon, index) => {
console.log(
Polígono ${index + 1}:, polygon);
});
} else {
// É Polygon
console.log('Coordenadas do polígono:', coordinates);
}
`🌐 Buscas Avançadas
A biblioteca usa a API Nominatim. Você pode passar parâmetros adicionais:
`javascript
await cityLib.searchCity('São Paulo', {
countrycodes: 'br', // Filtrar por país (código ISO)
limit: 5, // Número de resultados
'accept-language': 'en' // Idioma dos resultados
});
`Explicação: O método
searchCity aceita qualquer parâmetro válido da API Nominatim. Os parâmetros são mesclados com os padrões da biblioteca (como format: 'json', polygon_geojson: '1', etc.) e enviados na requisição.🎨 Personalização de Estilo
Todos os estilos do Leaflet são suportados:
`javascript
cityLib.updateStyle({
color: '#ff0000', // Cor da borda
weight: 5, // Espessura da borda
opacity: 1, // Opacidade da borda
fillColor: '#ff6666', // Cor do preenchimento
fillOpacity: 0.5, // Opacidade do preenchimento
dashArray: '5, 10', // Linha tracejada
lineCap: 'round', // Tipo de terminação
lineJoin: 'round' // Tipo de junção
});
`Explicação: Os estilos são passados diretamente para o Leaflet, então qualquer opção de estilo suportada pelo Leaflet funciona. Os estilos podem ser definidos no construtor, ao adicionar o polígono, ou atualizados dinamicamente.
🔧 Estrutura Interna da Biblioteca
$3
-
map: Referência ao mapa Leaflet
- currentPolygon: Referência ao polígono GeoJSON atual no mapa
- currentCityData: Dados brutos da cidade atual retornados pela API
- options: Objeto com todas as configurações da biblioteca$3
1. Busca:
searchCity → Faz requisição HTTP → Retorna dados brutos
2. Processamento: processPrimaryPolygon → Normaliza GeoJSON → Retorna GeoJSON processado
3. Visualização: addPolygonToMap → Cria camada Leaflet → Adiciona ao mapa
4. Coordenadas: getCoordinates → Processa GeoJSON → Retorna apenas coordenadas$3
A biblioteca lança erros descritivos em vários cenários:
- Mapa não fornecido no construtor
- Nome de cidade inválido
- Cidade não encontrada
- Dados de polígono não disponíveis
- Erros de rede ou HTTP
Todos os erros podem ser capturados com
try/catch ao usar métodos assíncronos.⚠️ Limitações
- A API Nominatim tem limite de requisições (1 req/segundo)
- Nem todas as cidades têm dados de polígono disponíveis
- Polígonos podem ser complexos e impactar performance
- Use
usePrimaryPolygonOnly: true para melhor performance
- A área calculada é aproximada (usa bounds, não o polígono real)📋 Requisitos
- Leaflet 1.7.0 ou superior
- Navegador com suporte a ES6+ (async/await)
- Conexão com internet (usa API Nominatim)
🧪 Testes
A biblioteca inclui testes automatizados usando Jest. Para executar:
`bash
npm test # Executa todos os testes
npm run test:watch # Modo watch
npm run test:coverage # Com relatório de cobertura
``Nota: Os testes não são incluídos no pacote npm, apenas os arquivos essenciais são publicados.
Sinta-se livre para contribuir com melhorias!
MIT License
Wallace - 2024
---
Nota: Esta biblioteca usa a API gratuita do OpenStreetMap (Nominatim). Respeite os limites de uso e considere fazer uma doação ao OpenStreetMap se usar intensivamente.