Além da Latitude e Longitude: Criando Embeddings Espaciais para RAG
- João Ataide
- há 2 horas
- 6 min de leitura
Quando comecei a misturar modelos de linguagem (LLMs) com dados geográficos, a primeira tentação foi tratar latitude e longitude como “apenas mais duas features” e seguir adiante. Logo percebi o problema, coordenadas são ótimas, mas não capturam contexto. Latitude e longitude não dizem nada sobre vizinhança. Elas não capturam bem a noção de vizinhança, não funcionam como identidades discretas e não oferecem uma estrutura hierárquica clara, como bairro, rua e quarteirão, dificultando então fazer essa conexão.
Neste artigo, compartilho um caminho simples e prático para transformar localização em algo que se comporte como um embedding, isto é, uma representação vetorial que pode ser indexada, comparada e utilizada em retrieval. O melhor é que esse processo é totalmente determinístico, não exige treinamento, tem baixo custo e escala bem. Para isso, vamos usar o H3, biblioteca de indexação espacial criada pela Uber, muito usada quando fazemos operações e análise de camadas vetoriais.
Definindo o recorte: o Recife como exemplo
Para tornar a explicação mais concreta, escolhi trabalhar com um recorte do Recife e pontos de interesse do OpenStreetMap (OSM). A partir desse conjunto, construí um retrieval geográfico básico e, em seguida, dei o passo para combinar geografia e texto e criar uma base consistente de RAG geográfico, algo que uma LLM consegue utilizar de forma eficaz.
Antes de buscar os dados, foi necessário definir o universo do experimento. Em geoprocessamento, isso é mais relevante do que parece, pois o recorte influencia a densidade, os tipos de lugares e até o comportamento do retrieval. No meu caso, defini um bounding box aproximado do Recife.
Com o recorte estabelecido, consultei os pontos do OSM por meio da Overpass API. Selecionei categorias comuns em ambientes urbanos, como hospital, escola, farmácia, polícia e abrigo. Essas classes são fáceis de validar e representam bem a dinâmica da cidade. Assim, obtive um dataset realista e, ao mesmo tempo, interpretável.
1250Estruturando os dados para indexação
Com os dados coletados, organizei tudo em um formato simples de indexar e depurar, gerando um DataFrame com colunas essenciais, como id, nome, categoria, latitude e longitude. Parece bobagem, mas esse passo facilita muito depois. Principalmente quando você quer evoluir o pipeline, por exemplo, ao trocar node por way ou relation e utilizar center no lugar das coordenadas diretas.
Ao analisar a distribuição das classes, observei que as escolas predominam no conjunto. Isso é positivo, pois cria uma situação próxima da realidade. Ao buscar uma classe como hospital, não quero receber escolas próximas apenas porque são maioria. Em outras palavras, o retrieval precisa compreender intenção, e não apenas densidade.
Até aqui, eu tinha apenas um conjunto de pontos com latitude e longitude. A partir deste ponto, vem a mudança central, no qual iremos tratar as coordenadas como números contínuos e convertê-las em tokens espaciais com o H3.
Convertendo coordenadas em tokens espaciais com H3

A ideia principal do H3 é dividir o mapa em hexágonos e atribuir um identificador único a cada célula, como na imagem ao lado. Para a LLM, esse identificador funciona como um token espacial.
Para utilizar esse índice, é possível escolher diferentes resoluções, representando o espaço em múltiplas escalas. Utilizei as resoluções 7, 9 e 11 para capturar desde regiões mais amplas até detalhes locais.
A partir daqui cada local deixa de ser apenas um ponto somente e passa a ser representado por um conjunto de identificadores discretos. É nesse momento que o embedding começa a tomar forma, sem necessidade de treinar uma rede.
Lidando com o problema das bordas
No mundo real, bordas importam. Dois pontos podem estar muito próximos, mas cair em hexágonos diferentes. Se esses hexágonos forem tratados como completamente distintos, o retrieval tende a falhar justamente onde deveria acertar.
Para lidar com isso, adotei uma estratégia simples, além do token da célula principal, incluí também os tokens das células vizinhas. No H3, esse conjunto é chamado de k-ring, ou grid disk. Utilizei a resolução 9 como base urbana e k = 1 para incluir os vizinhos imediatos.
Primeiro, gerei uma assinatura multiescala. Em seguida, expandi essa assinatura com as células vizinhas. Assim, o conceito de “perto” deixa de se restringir à distância e passa a incorporar a adjacência topológica, o que melhora o desempenho em áreas densas.
sig
0 [878183980ffffff, 89818398053ffff, 8b818398051...
1 [878183980ffffff, 89818398057ffff, 8b818398057...
2 [878183981ffffff, 8981839810fffff, 8b81839810e...
3 [878183981ffffff, 89818398107ffff, 8b818398105...
4 [878183981ffffff, 898183981cfffff, 8b8183981cf...
E depois expande com vizinhos:
sig_k1
0 878183980ffffff<br>89818398053ffff<br>8b818398...
1 878183980ffffff<br>89818398057ffff<br>8b818398...
2 878183981ffffff<br>8981839810fffff<br>8b818398...
3 878183981ffffff<br>89818398107ffff<br>8b818398...
4 878183981ffffff<br>898183981cfffff<br>8b818398...
Convertendo tudo em vetores (hashing trick)
Com os tokens definidos, o passo seguinte foi convertê-los em vetores numéricos de dimensão fixa. Isso permite utilizar similaridade por cosseno, índices vetoriais como FAISS ou HNSW e, consequentemente, aplicar RAG.
Para essa conversão, utilizei o hashing trick, onde cada token é mapeado para uma posição do vetor por meio de uma função hash, no caso MD5, acumulando contagens nessa posição. Ao final, normalizo o vetor. O resultado foi um conjunto de 1.250 locais do Recife convertidos em vetores prontos para retrieval.
(1250, 1024)Visualizando o retrieval no mapa
Após transformar os pontos em vetores e estruturar o índice, busquei observar o comportamento espacial do sistema. Para isso, defini um ponto de consulta e visualizei no mapa quais locais o mecanismo de retrieval considerava mais próximos.
Utilizei uma função em Python com matplotlib:
Retrieval “perto de mim” similaridade vetorial
Retrieval é o processo de recuperar informações relevantes a partir de um conjunto de dados indexado. Em sistemas vetoriais, isso significa identificar os itens mais semelhantes a uma consulta com base em uma métrica, como similaridade por cosseno ou distância euclidiana.
Primeiro vamos criar o retrieve geográfico, para iniciar os testes de recuperação por proximidade, por meio do cálculo a similaridade em relação ao conjunto dos dados.
Essa abordagem não busca medir distâncias em metros com precisão absoluta (o que seria lento caso muitos pontos), o objetivo é oferecer uma noção prática de proximidade que seja rápida, passível de indexação e menos sensível a efeitos de borda. Em muitos contextos urbanos, esse nível de aproximação é suficiente, em especial casos de uso como "Qual hospital mais próximos dessa coordenada?".

Obviamente, isso não é perfeito, veja que ela ainda selecionou algumas classes que não são hospitais. Além disso, caso um usuário fizesse a busca bem na divisa entre dois bairros, poderia rolar uma confusão. Mas na prática, esse é um processo que pode funcionar bem o suficiente para maioria dos casos do dia a dia.
Combinando texto e geografia: o passo que cria o RAG geográfico
Até aqui, eu respondia à pergunta “o que está perto desta coordenada?”. No entanto, a dúvida real do usuário costuma ser outra “quais hospitais estão perto de mim?”.
Para incorporar a intenção semântica à busca, gerei embeddings de texto a partir do nome e da categoria utilizando o modelo SentenceTransformer. Optei pelo modelo all-MiniLM-L6-v2, que para mim ofereceu equilíbrio entre desempenho e custo computacional, bem leve e funcionou bem na minha máquina. Em seguida, concatenei os embeddings o textual e o geográfico.
Quando analisei o resultado o ganho foi imediato. O sistema deixa de ser apenas geográfico e passa a considerar simultaneamente localização e intenção. Em vez de retornar qualquer ponto próximo, ele passa a priorizar o que está próximo e semanticamente alinhado à consulta.
Agora sim temos um RAG geográfico de verdade. O retriever seleciona o contexto mais adequado, e a LLM assume a etapa linguística, explicando, comparando ou recomendando com base nos resultados recuperados.

Conclusão
Aqui eu parti de uma limitação clara das coordenadas. Quando usadas isoladamente, latitude e longitude não representam adequadamente o contexto. Ao converter o espaço em tokens H3 multiescala, incorporar a adjacência com k-ring e transformar essas assinaturas em vetores por meio do hashing trick, construí um embedding geográfico simples, determinístico e eficiente. Esse processo dispensa treinamento, tem baixo custo computacional e escala de forma previsível.
O ponto mais relevante, porém, não é apenas técnico. Ao transformar a localização em uma representação vetorial compatível com mecanismos modernos de retrieval, passamos a tratar o espaço como parte da linguagem do sistema. A geografia deixa de ser um filtro externo e passa a integrar o próprio raciocínio do modelo.
Quando esse embedding é combinado a representações textuais geradas pelo SentenceTransformer, surge um retriever capaz de compreender simultaneamente o que se busca e onde se busca. Nesse estágio, o RAG geográfico deixa de ser uma adaptação pontual e passa a operar como uma arquitetura integrada, unindo localização e intenção semântica de forma prática e aplicável a contextos reais, da busca contextual urbana a sistemas inteligentes de apoio à decisão.
No fim, isso vai além de indexação espacial, é uma forma de fazer LLMs entenderem geografia de verdade, não só descreverem coordenadas, o que é um passo essencial para que LLMs deixem de apenas descrever o espaço e passem, de fato, a compreendê-lo.