city-polygon
v1.0.0
Published
Biblioteca JavaScript para buscar e exibir polígonos de cidades no Leaflet usando a API do OpenStreetMap (Nominatim)
Maintainers
Readme
CityPolygonLib 🗺️
Biblioteca JavaScript para buscar e exibir polígonos de cidades no Leaflet usando a API do OpenStreetMap (Nominatim).
📦 Instalação
Simplesmente inclua o script na sua página HTML após o Leaflet:
<!-- Leaflet -->
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
<!-- CityPolygonLib -->
<script src="city-polygon-lib.js"></script>🚀 Uso Básico
// 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');📚 API Completa
Construtor
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:
{
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.
Métodos Principais
loadCity(cityName, options)
Busca e adiciona a cidade ao mapa (método mais conveniente). Este método combina searchCity e addPolygonToMap em uma única chamada.
const result = await cityLib.loadCity('Rio de Janeiro');
// Retorna: { cityData, polygon }Com opções:
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).
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.
const cityData = await cityLib.searchCity('Salvador');
const polygon = cityLib.addPolygonToMap(cityData, {
color: '#00ff00',
fillColor: '#00cc00'
}, false); // false = usa apenas primeira coordenadaParâmetros:
cityData(Object): Dados da cidade retornados porsearchCitystyleOptions(Object): Opções de estilo personalizadas (opcional)useAllCoordinates(boolean): Setrue, usa todas as coordenadas; sefalse, 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.
// 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): SecityNameOrDatafor string: objeto com opções de busca. Se for objeto: boolean indicando se deve usar todas as coordenadasuseAllCoordinates(boolean): Setrue, usa todas as coordenadas; sefalse, usa apenas a primeira (padrão:false). Apenas usado quandocityNameOrDatafor string
Retorna: Promise<Array> - 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.
// 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): Setrue, mantém todas as coordenadas; sefalse, usa apenas a primeira (padrão:false)
Explicação: Este método processa diferentes tipos de geometrias GeoJSON:
- Polygon: Retorna como está
- MultiPolygon: Se
useAllCoordinatesforfalse, extrai apenas o primeiro polígono e converte para Polygon. Setrue, 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.
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.
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.
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.
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.
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².
const area = cityLib.getApproximateArea();
console.log(`Área: ${area.toFixed(2)} km²`);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.
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.
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.
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.
Métodos de Configuração
setUsePrimaryPolygonOnly(value)
Define se deve usar apenas o polígono principal ao processar MultiPolygons.
cityLib.setUsePrimaryPolygonOnly(false); // Mostra todos os polígonosExplicaçã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.
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.
💡 Exemplos de Uso
Exemplo 1: Básico
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');Exemplo 2: Com estilo customizado
const cityLib = new CityPolygonLib(map, {
style: {
color: '#ff0000',
weight: 4,
fillColor: '#ff6666',
fillOpacity: 0.4
}
});
await cityLib.loadCity('Porto Alegre');Exemplo 3: Sem auto zoom
const cityLib = new CityPolygonLib(map, {
autoZoom: false
});
await cityLib.loadCity('Fortaleza');Exemplo 4: Obter apenas coordenadas
// 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}`);
});
});Exemplo 5: Usar todas as coordenadas ao adicionar ao mapa
// 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 coordenadasExemplo 6: Comparar múltiplas cidades
// 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);
}Exemplo 7: Buscar com filtros
// Buscar apenas em um país específico
const cityData = await cityLib.searchCity('Londres', {
countrycodes: 'gb'
});Exemplo 8: Mudar cores dinamicamente
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 segundosExemplo 9: Exibir informações
await cityLib.loadCity('Recife');
const info = cityLib.getCityInfo();
console.log(`
Cidade: ${info.name}
País: ${info.country}
Área: ${info.area.toFixed(2)} km²
Coordenadas: ${info.lat}, ${info.lon}
`);Exemplo 10: Tratamento de erros
try {
await cityLib.loadCity('CidadeQueNaoExiste123');
} catch (error) {
console.error('Erro ao carregar cidade:', error.message);
alert('Cidade não encontrada!');
}Exemplo 11: Processar coordenadas manualmente
// 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:
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:
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
Propriedades da Classe
map: Referência ao mapa LeafletcurrentPolygon: Referência ao polígono GeoJSON atual no mapacurrentCityData: Dados brutos da cidade atual retornados pela APIoptions: Objeto com todas as configurações da biblioteca
Fluxo de Dados
- Busca:
searchCity→ Faz requisição HTTP → Retorna dados brutos - Processamento:
processPrimaryPolygon→ Normaliza GeoJSON → Retorna GeoJSON processado - Visualização:
addPolygonToMap→ Cria camada Leaflet → Adiciona ao mapa - Coordenadas:
getCoordinates→ Processa GeoJSON → Retorna apenas coordenadas
Tratamento de Erros
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: truepara 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:
npm test # Executa todos os testes
npm run test:watch # Modo watch
npm run test:coverage # Com relatório de coberturaNota: Os testes não são incluídos no pacote npm, apenas os arquivos essenciais são publicados.
🤝 Contribuindo
Sinta-se livre para contribuir com melhorias!
📄 Licença
MIT License
👨💻 Autor
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.
