Como fazer filtros de distância em camadas para buscas locais
Vou ser direto sobre um problema que vejo todo dia nos fóruns técnicos: gente tentando mostrar restaurantes próximos aos usuários e acabando com interfaces lentas, resultados inconsistentes ou APIs que estouram o quota. O cenário mais comum que encuentro é o restaurante dentro de 800 m dentro de 400 m — uma busca em duas etapas que, mal implementada, pode destruir a experiência do produto.
restaurante dentro de 800 m dentro de 400 m
O conceito é simples na teoria: você primeiro busca um raio amplo (800 metros) para garantir cobertura, depois refina para um raio mais apertado (400 metros) usando os dados já coletados ou uma nova consulta. Na prática, existem armadilhas que quase ninguém menciona nos tutoriais básicos. A primeira coisa que você precisa decidir é como calcular a distância. O Haversine é o padrão para distâncias em superfícies esféricas. Ele funciona bem para a maioria dos casos urbanos, mas perde precisão em escalas muito grandes ou próximo aos polos. Se seu aplicativo é usado majoritariamente em cidades brasileiras, o erro é marginal — da ordem de alguns metros. O problema real aparece quando você começa a misturar diferentes fontes de dados: algumas APIs retornam coordenadas arredondadas, outras usam sistemas de referência diferentes.
Aqui vai algo que aprendi na marra: quando fiz uma dashboard interna para uma rede de cafés, implementamos a busca em duas fases com PostgreSQL e PostGIS. A fase de 800 metros retornava cerca de 200 estabelecimentos. A fase de 400 metros deveria filtrar esses 200, mas estávamos fazendo uma segunda consulta completa à API externa em vez de filtrar localmente. Isso triplicava o tempo de resposta e queimava três vezes mais requisições no limite do Google Places API. A solução foi armazenar as coordenadas e dados básicos em uma tabela temporária após a primeira consulta, aplicar o filtro geométrico do PostGIS diretamente no banco e só então enrichcer os registros que passaram no segundo raio com os dados completos da API. Outro detalhe técnico importante: a forma como você lida com a geocodificação inversa. Se o usuário está se movendo, pedir coordenadas a cada atualização de posição gera ruído. A posição GPS fluctúa entre 3 e 15 metros naturalmente, mesmo em smartphones bons. Meu conselho prático é implementar um filtro de suavização simples — uma média móvel exponencial com fator de 0.3 já resolve 90% dos casos — antes de enviar qualquer consulta de distância.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Sobre a escolha entre approaches: se você está construindo algo pequeno ou um MVP, usar a Google Places API com rankBy=distance e depois filtrar no cliente até 400 metros pode ser suficiente. O custo é previsível e a precisão é boa. Se o volume cresce — digamos, mais de mil buscas por dia — essa abordagem fica cara rapidamente. O Google Places cobra por sessão, e sessões de busca com distância podem acumular. A alternativa que recomendo para escala maior é uma combinação de Overpass API para dados brutos do OpenStreetMap com cálculo local de distância. Você baixa os pontos de interesse num raio de 800 metros uma única vez, armazena num GeoJSON, e filtra para 400 metros sem custo de API. A desvantagem é que os dados não têm informações em tempo real como horário de funcionamento, avaliações ou telefone atualizado. Você precisa cross-referenciar com alguma outra fonte se precisar desses detalhes.
Um edge case que merece atenção: restaurantes que estão fisicamente dentro dos 400 metros mas aparecem nos resultados de 800 metros com coordenadas imprecisas. Isso acontece principalmente com estabelecimentos em shoppings centers ou complexos edilícios grandes, onde o ponto de referência do OSM ou do Google Maps pode estar na entrada principal do edifício, não no restaurante em si. Nesses casos, a distância calculada pode ser de 350 metros na realidade mas aparecer como 450 metros no sistema. A correção prática é adicionar uma margem de segurança de 20 a 30 metros ao seu raio de 400 metros, ou permitir que o usuário ajuste o raio manualmente na interface. Se você está construindo isso do zero e quer um ponto de partida concreto, o fluxo básico é: obter a localização do usuário com a Geolocation API do navegador, calcular um bounding box alrededor das coordenadas usando a fórmula aproximada de que 1 grau de latitude equivale a cerca de 111 mil metros e 1 grau de longitude varia conforme a latitude, fazer a consulta ao raio de 800 metros dentro desse bounding box, aplicar o filtro de 400 metros com Haversine no lado do servidor, e só então formatar os resultados para exibição. Isso evita enviar dados desnecessários pela rede e mantém a latência baixa.
O código para o Haversine em JavaScript é trivial — umas dez linhas. O que realmente faz diferença é a estratégia de cache. Resultados de busca por distância são frequentemente repetidos: usuários que buscam o mesmo bairro em horários diferentes vão gerar consultas similares. Um cache de 5 minutos com chave baseada nas coordenadas arredondadas para 4 casas decimais reduz drasticamente a carga nas APIs externas sem prejudicar a relevância dos resultados.