npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

city-polygon

v1.0.0

Published

Biblioteca JavaScript para buscar e exibir polígonos de cidades no Leaflet usando a API do OpenStreetMap (Nominatim)

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 coordenada

Parâmetros:

  • cityData (Object): Dados da cidade retornados por searchCity
  • styleOptions (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.

// 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á buscados
  • searchOptionsOrUseAll (Object|boolean): Se cityNameOrData for string: objeto com opções de busca. Se for objeto: boolean indicando se deve usar todas as coordenadas
  • useAllCoordinates (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> - 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 GeoJSON
  • useAllCoordinates (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.

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í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.

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 coordenadas

Exemplo 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 segundos

Exemplo 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 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

Fluxo de Dados

  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

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: 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:

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.

🤝 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.