Aguarde...

11 de outubro de 2024

Usando geocodificação com o Leaflet

Usando geocodificação com o Leaflet

Quando comecei a falar sobre o Leaftlet, mencionei como ele era “apenas” uma biblioteca de mapas, e com isso quero dizer, capaz apenas de apresentar uma visualização/wrapper em torno de blocos que representam dados de mapas. Há uma porção de serviços que o Google Maps, HERE e assim por diante adicionam por cima que não estarão presentes, coisas como roteamento, geocodificação e muito mais. Considerando o fato de que o Leaflet é, novamente, “apenas” uma biblioteca JavaScript do lado do cliente, isso é apenas um fato da vida. Mas tenho pensado em como poderia integrar o Leaflet com esses serviços, e pensei em compartilhar uma demonstração disso mesmo – adicionando geocodificação ao Leaflet.

O que é geocodificação?

Simplificando, geocodificação é converter um endereço em texto para um local preciso. Então, por exemplo, uma cidade como “Seattle, Washington” pode ser geocodificada para latitude 47.61 e longitude -122.33. Um local como “3901 Johnston St, Lafayette, LA 70503” (um restaurante local que é incrível) pode ser geocodificado para 30.198, -92.055.

A geocodificação reversa permite que você forneça uma latitude e longitude e a API lhe dirá o que está lá. Então, por exemplo, 38.9002898,-76.9990361 mapeia para 508 H St NE, Washington, DC 20002.

Você precisa disso?

Antes de considerar adicionar geocodificação a um aplicativo do lado do cliente, pergunte a si mesmo se você realmente precisa disso. Em algumas das minhas demonstrações do Leaflet, usei o exemplo de lojas para uma empresa. Se sua empresa tivesse 5, 10, caramba, 50 locais, provavelmente esse seria um conjunto bastante estático de locais que não mudam com frequência. Abra seu navegador, acesse qualquer número de sites de geocodificação, insira um por um e copie e cole os resultados em um banco de dados, arquivo local ou pergaminho. Não há necessidade de cada visitante do seu site acessar alguma API de terceiros para localizar uma loja que seja exatamente o mesmo local para todos os outros visitantes do seu site.

Ok, mas e se você tiver cinco mil? Nesse caso, considere automatizar, mas faça isso localmente. Escreva um script que passe pelos seus dados, geolocalize com base em um endereço e então armazene.

Agora, nesse caso de uso, você vai querer verificar as regras de uso para seu provedor de API. Algumas impedem isso e isso é meio triste na minha opinião.

Mas a principal lição disso é que, antes mesmo de considerar usar meu código abaixo, certifique-se de que realmente precisa dele primeiro.

Não acredito que estou sendo prático. No meu blog. Talvez eu devesse verificar minha temperatura.

Geocodificação via API

Em preparação para este post, eu fiz uma pesquisa, e por pesquisa eu pesquisei no Google por “API de geocodificação”, porque eu estava procurando especificamente por algo fora do Google e AQUI. Eu encontrei um muito bom, geocodio. Ele tem algumas ótimas vantagens, com apenas uma desvantagem. No lado positivo:

  • Ótimo nível gratuito, sem necessidade de adicionar um cartão de crédito, a menos que você queira fazer processamento em lote.
  • Uma API simples, uma das mais amigáveis ​​que já vi. (E compartilharei um exemplo abaixo.)
  • A capacidade de bloquear sua chave por IP para maior segurança.
  • Você tem permissão “legal” para salvar os resultados da geocodificação. Veja o que mencionei acima sobre alguns serviços que impedem você de fazer isso.

No entanto, o lado negativo…

  • Eles só geocodificam nos EUA e Canadá, embora possam fazer a geocodificação reversa no México.

Isso é bem significativo, mas se você sabe que está trabalhando nessa área, tenho que dizer que todos os pontos positivos realmente fazem deste um ótimo serviço. O nível gratuito deles suporta 2.500 pesquisas por dia, o que é bem alto. Além disso, quando me deparei com essa limitação pela primeira vez, não tinha visto nos documentos e procurei suporte. Eles me responderam em uma hora, o que é sempre um bom sinal.

A API de geocodificação deles (e eles incluem reverso e batching também) suporta uma série de opções tanto de como pesquisar quanto de como lidar com os resultados. Como um exemplo básico, isso tentará geocodificar minha cidade:

Isso retorna:

{
    "input": {
        "address_components": {
            "city": "Lafayette",
            "state": "LA",
            "country": "US"
        },
        "formatted_address": "Lafayette, LA"
    },
    "results": [
        {
            "address_components": {
                "city": "Lafayette",
                "county": "Lafayette Parish",
                "state": "LA",
                "zip": "70501",
                "country": "US"
            },
            "formatted_address": "Lafayette, LA 70501",
            "location": {
                "lat": 30.24171,
                "lng": -91.991044
            },
            "accuracy": 1,
            "accuracy_type": "place",
            "source": "TIGER/Line® dataset from the US Census Bureau"
        },
        {
            "address_components": {
                "city": "Lafayette",
                "county": "Lafayette Parish",
                "state": "LA",
                "zip": "70502",
                "country": "US"
            },
            "formatted_address": "Lafayette, LA 70502",
            "location": {
                "lat": 30.319799,
                "lng": -92.026969
            },
            "accuracy": 1,
            "accuracy_type": "place",
            "source": "TIGER/Line® dataset from the US Census Bureau"
        },
        {
            "address_components": {
                "city": "Lafayette",
                "county": "Lafayette Parish",
                "state": "LA",
                "zip": "70503",
                "country": "US"
            },
            "formatted_address": "Lafayette, LA 70503",
            "location": {
                "lat": 30.163949,
                "lng": -92.055824
            },
            "accuracy": 1,
            "accuracy_type": "place",
            "source": "TIGER/Line® dataset from the US Census Bureau"
        },
        {
            "address_components": {
                "city": "Lafayette",
                "county": "Lafayette Parish",
                "state": "LA",
                "zip": "70504",
                "country": "US"
            },
            "formatted_address": "Lafayette, LA 70504",
            "location": {
                "lat": 30.21385,
                "lng": -92.01866
            },
            "accuracy": 1,
            "accuracy_type": "place",
            "source": "TIGER/Line® dataset from the US Census Bureau"
        },
        {
            "address_components": {
                "city": "Lafayette",
                "county": "Lafayette Parish",
                "state": "LA",
                "zip": "70505",
                "country": "US"
            },
            "formatted_address": "Lafayette, LA 70505",
            "location": {
                "lat": 30.202251,
                "lng": -92.01877
            },
            "accuracy": 1,
            "accuracy_type": "place",
            "source": "TIGER/Line® dataset from the US Census Bureau"
        },
        {
            "address_components": {
                "city": "Lafayette",
                "county": "Lafayette Parish",
                "state": "LA",
                "zip": "70506",
                "country": "US"
            },
            "formatted_address": "Lafayette, LA 70506",
            "location": {
                "lat": 30.195474,
                "lng": -92.081292
            },
            "accuracy": 1,
            "accuracy_type": "place",
            "source": "TIGER/Line® dataset from the US Census Bureau"
        },
        {
            "address_components": {
                "city": "Lafayette",
                "county": "Lafayette Parish",
                "state": "LA",
                "zip": "70507",
                "country": "US"
            },
            "formatted_address": "Lafayette, LA 70507",
            "location": {
                "lat": 30.2528,
                "lng": -92.038679
            },
            "accuracy": 1,
            "accuracy_type": "place",
            "source": "TIGER/Line® dataset from the US Census Bureau"
        },
        {
            "address_components": {
                "city": "Lafayette",
                "county": "Lafayette Parish",
                "state": "LA",
                "zip": "70508",
                "country": "US"
            },
            "formatted_address": "Lafayette, LA 70508",
            "location": {
                "lat": 30.181866,
                "lng": -92.026859
            },
            "accuracy": 1,
            "accuracy_type": "place",
            "source": "TIGER/Line® dataset from the US Census Bureau"
        },
        {
            "address_components": {
                "city": "Lafayette",
                "county": "Lafayette Parish",
                "state": "LA",
                "zip": "70509",
                "country": "US"
            },
            "formatted_address": "Lafayette, LA 70509",
            "location": {
                "lat": 30.156506,
                "lng": -92.000019
            },
            "accuracy": 1,
            "accuracy_type": "place",
            "source": "TIGER/Line® dataset from the US Census Bureau"
        },
        {
            "address_components": {
                "city": "Lafayette",
                "county": "Lafayette Parish",
                "state": "LA",
                "zip": "70598",
                "country": "US"
            },
            "formatted_address": "Lafayette, LA 70598",
            "location": {
                "lat": 30.20812,
                "lng": -92.095109
            },
            "accuracy": 1,
            "accuracy_type": "place",
            "source": "TIGER/Line® dataset from the US Census Bureau"
        },
        {
            "address_components": {
                "city": "Lafayette",
                "county": "Lafayette Parish",
                "state": "LA",
                "zip": "70593",
                "country": "US"
            },
            "formatted_address": "Lafayette, LA 70593",
            "location": {
                "lat": 30.20812,
                "lng": -92.095109
            },
            "accuracy": 1,
            "accuracy_type": "place",
            "source": "TIGER/Line® dataset from the US Census Bureau"
        },
        {
            "address_components": {
                "city": "Lafayette",
                "county": "Lafayette Parish",
                "state": "LA",
                "zip": "70596",
                "country": "US"
            },
            "formatted_address": "Lafayette, LA 70596",
            "location": {
                "lat": 30.20812,
                "lng": -92.095109
            },
            "accuracy": 1,
            "accuracy_type": "place",
            "source": "TIGER/Line® dataset from the US Census Bureau"
        }
    ]
}

Isso é bastante, e você pode limitar o número de resultados e solicitar ainda mais campos, como congressional e distritos escolares. Dê uma olhada no documento que eu vinculei na frase anterior, pois ele é bem extenso.

Mas o que eu realmente gosto é que ele suporta um formatatributo que assume um valor, simple.

Isso retorna:

{
    "address": "Lafayette, LA 70501",
    "lat": 30.24171,
    "lng": -91.991044,
    "accuracy": 1,
    "accuracy_type": "place",
    "source": "TIGER/Line® dataset from the US Census Bureau"
}

Isso é… excelente! Vamos usar isso!

Folheto e Geocodificação

Eu preparei uma demonstração super rápida do Leaflet que, embora provavelmente não seja muito realista, mostra a integração da geocodificação com mapas. Para minha demonstração, eu simplesmente usei um campo de texto no qual você pode inserir um endereço. Abaixo dele está o HTML para o mapa.

<input type="search" id="address" placeholder="Address">
<div id="map"></div>

Agora para o código. Primeiro, o mapa, centralizado nos EUA:

let map = L.map('map').setView([39.8097343, -98.5556199], 4);
let marker;

L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
    maxZoom: 10,
    attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);

Em seguida, reconhecendo as alterações no campo de pesquisa e iniciando uma solicitação de geocodificação:

document.querySelector('#address').addEventListener('change', geocode, false);

async function geocode(e) {
	let address = e.target.value.trim();
	if(address === '') return;
	console.log(`Geocode ${address}`);
	
	let req = await fetch(`https://api.geocod.io/v1.7/geocode?q=${encodeURIComponent(address)}&format=simple&api_key=${KEY}`);
	let resp = await req.json();
	
	if(resp.error) {
		alert(resp.error);	
	}
	
	if(resp.lat && resp.lng) {
		if(marker) map.removeLayer(marker);
		marker = L.marker([resp.lat, resp.lng]).addTo(map);
		marker.bindPopup(`Geocoded to ${resp.lat},${resp.lng}`);
		centerLeafletMapOnMarker(map, marker);
	}
	
	console.log(resp);
}
// Credit - https://jeffreymorgan.io/articles/how-to-center-a-leaflet-map-on-a-marker/
function centerLeafletMapOnMarker(map, marker) {
  var latLngs = [ marker.getLatLng() ];
  var markerBounds = L.latLngBounds(latLngs);
  map.flyToBounds(markerBounds, { maxZoom: 6});
}

Eu não tinha visto essas APIs antes, mas elas fizeram sentido imediatamente. Modifiquei um pouco o código do que o blog mostrou para adicionar o efeito ‘fly’.

E é isso. Você pode jogar com a demo abaixo.

Postado em BlogTags:
Escreva um comentário