GeoPandas com Python: Mapas e Dados Geoespaciais

Aprenda a usar GeoPandas com Python para analisar dados geoespaciais, cruzar CSV com shapefiles, criar mapas e montar projetos de portfólio no Brasil.

8 min de leitura Equipe Python Brasil

Dados geoespaciais aparecem em muito mais projetos do que parece. Uma base de escolas tem endereço. Uma lista de unidades de saúde tem município. Um arquivo de entregas tem latitude e longitude. Dados de chuva, imóveis, crime, transporte, saneamento, queimadas e cobertura de internet quase sempre têm alguma dimensão territorial. Para quem estuda Python no Brasil, saber transformar esses dados em mapa e análise espacial é uma habilidade forte para portfólio, vagas de dados, automação pública e produtos locais.

O GeoPandas é uma das bibliotecas mais práticas para esse trabalho porque leva a experiência do Pandas para dados com geometria. Em vez de tratar um polígono, uma linha ou um ponto como texto solto, você trabalha com um GeoDataFrame: uma tabela comum, mas com uma coluna especial que entende operações espaciais.

Neste guia, vamos montar um fluxo realista: ler dados tabulares, transformar coordenadas em pontos, cruzar esses pontos com polígonos de municípios, calcular métricas e gerar um mapa simples. A ideia não é substituir ferramentas GIS completas, e sim mostrar como Python entra no dia a dia de análise de dados públicos, projetos de portfólio e automações que combinam Pandas, APIs públicas brasileiras, DuckDB e testes com pytest.

Quando GeoPandas faz sentido

Use GeoPandas quando a pergunta depende de localização, distância, área ou pertencimento territorial. Alguns exemplos brasileiros:

PerguntaOperação geoespacial
Quais escolas ficam em áreas com maior risco de alagamento?interseção entre pontos e polígonos
Quantas vagas Python aparecem por estado?agregação por unidade federativa
Quais bairros têm menos equipamentos públicos por habitante?junção espacial e cálculo por área
Onde estão os clientes fora da região de atendimento?filtro por distância ou polígono
Quais municípios concentram ocorrências em uma base aberta?pontos dentro de limites municipais

Se a base é pequena e você só precisa desenhar um gráfico rápido, uma planilha ou ferramenta de BI pode bastar. Se você precisa automatizar o processo, versionar código, repetir a análise todo mês ou combinar várias fontes, Python começa a fazer diferença.

Instalando o ambiente

GeoPandas depende de bibliotecas nativas de geometria. Em 2026, a instalação costuma ser bem mais tranquila do que era alguns anos atrás, mas ainda vale criar um ambiente limpo.

Com uv:

uv init analise-geoespacial
cd analise-geoespacial
uv add geopandas pandas matplotlib pyarrow

Com pip:

python -m venv .venv
source .venv/bin/activate
pip install geopandas pandas matplotlib pyarrow

Para mapas interativos, você pode adicionar folium. Para volumes maiores, combine com DuckDB ou Parquet. Para análises científicas mais pesadas, vale conhecer rasterio, xarray e osmnx, mas não comece por eles se o objetivo é aprender o fluxo principal.

Lendo pontos a partir de um CSV

Imagine um CSV com unidades de atendimento, escolas, clientes ou ocorrências. O arquivo tem latitude e longitude:

nome,cidade,latitude,longitude,atendimentos
Unidade Centro,São Paulo,-23.5505,-46.6333,1280
Unidade Campinas,Campinas,-22.9056,-47.0608,640
Unidade Recife,Recife,-8.0476,-34.8770,710

Você lê com Pandas e transforma em GeoDataFrame:

import geopandas as gpd
import pandas as pd


df = pd.read_csv("data/unidades.csv")

unidades = gpd.GeoDataFrame(
    df,
    geometry=gpd.points_from_xy(df["longitude"], df["latitude"]),
    crs="EPSG:4326",
)

print(unidades[["nome", "cidade", "geometry"]])

O crs="EPSG:4326" informa que as coordenadas estão em latitude/longitude, o padrão mais comum em APIs e arquivos CSV. CRS significa Coordinate Reference System. Se você ignorar isso, distâncias e áreas podem sair erradas.

Uma regra prática: use EPSG:4326 para armazenar e integrar dados globais, mas reprojete antes de calcular distância ou área. No Brasil, análises por estado ou município podem exigir projeções específicas. Para um projeto de portfólio, documente essa decisão no README em vez de deixar implícita.

Lendo limites municipais ou estaduais

Para cruzar pontos com território, você precisa de polígonos. Muitas bases públicas brasileiras usam shapefile, GeoJSON ou GeoPackage. Um exemplo comum é baixar limites de municípios do IBGE e salvar em data/municipios.gpkg.

import geopandas as gpd


municipios = gpd.read_file("data/municipios.gpkg")

print(municipios.columns)
print(municipios.crs)

Arquivos geoespaciais nem sempre vêm com nomes de coluna amigáveis. Pode aparecer NM_MUN, CD_MUN, SIGLA_UF ou variações. Antes de escrever a análise completa, rode uma inspeção simples:

colunas_interesse = ["CD_MUN", "NM_MUN", "SIGLA_UF", "geometry"]
municipios = municipios[colunas_interesse].rename(
    columns={
        "CD_MUN": "codigo_municipio",
        "NM_MUN": "municipio",
        "SIGLA_UF": "uf",
    }
)

Essa limpeza parece burocrática, mas melhora muito a leitura do notebook, do script e do README. Também evita que o projeto dependa de siglas misteriosas que só fazem sentido para quem conhece o arquivo original.

Fazendo junção espacial

A junção espacial responde uma pergunta muito comum: em qual polígono cada ponto cai? No exemplo, queremos descobrir em qual município cada unidade está.

unidades = unidades.to_crs(municipios.crs)

unidades_com_municipio = gpd.sjoin(
    unidades,
    municipios,
    how="left",
    predicate="within",
)

print(
    unidades_com_municipio[
        ["nome", "cidade", "municipio", "uf", "atendimentos"]
    ]
)

O predicate="within" significa que o ponto precisa estar dentro do polígono. Em outros cenários, você pode usar intersects, contains, touches ou nearest. Para pontos e municípios, within costuma ser a escolha mais clara.

Depois da junção, fica fácil agregar por estado:

resumo_uf = (
    unidades_com_municipio
    .groupby("uf", as_index=False)
    .agg(
        unidades=("nome", "count"),
        atendimentos=("atendimentos", "sum"),
    )
    .sort_values("atendimentos", ascending=False)
)

print(resumo_uf)

Esse padrão se repete em muitos projetos: transformar localização em território, agregar, comparar e visualizar. É útil para portfólio porque mostra domínio de dados tabulares e espaciais ao mesmo tempo.

Calculando distância corretamente

Um erro comum é calcular distância diretamente em latitude/longitude. Como graus não são metros, o resultado não representa uma distância real. Antes de medir, reprojete para um CRS métrico adequado.

# SIRGAS 2000 / Brazil Polyconic, útil para análises nacionais aproximadas.
unidades_m = unidades.to_crs("EPSG:5880")

referencia = unidades_m.iloc[0].geometry
unidades_m["distancia_km"] = unidades_m.geometry.distance(referencia) / 1000

print(unidades_m[["nome", "distancia_km"]])

Para uma análise local, como uma cidade específica, pesquise a projeção mais adequada para a região. Se a precisão for crítica, valide com alguém da área de geoprocessamento. Para análise exploratória e portfólio, o mais importante é mostrar que você sabe que CRS afeta distância e área.

Criando um mapa estático

GeoPandas integra bem com Matplotlib para mapas rápidos:

import matplotlib.pyplot as plt


sp = municipios[municipios["uf"] == "SP"]
unidades_sp = unidades_com_municipio[unidades_com_municipio["uf"] == "SP"]

fig, ax = plt.subplots(figsize=(10, 8))
sp.plot(ax=ax, color="#f3f4f6", edgecolor="#9ca3af", linewidth=0.4)
unidades_sp.plot(
    ax=ax,
    color="#306998",
    markersize=unidades_sp["atendimentos"] / 20,
    alpha=0.75,
)

ax.set_title("Unidades e atendimentos em São Paulo")
ax.set_axis_off()
plt.tight_layout()
plt.savefig("outputs/unidades-sp.png", dpi=160)

Esse mapa já pode entrar em um README, relatório ou apresentação. Para produto web, o caminho muda: você pode exportar GeoJSON, servir via API e renderizar no frontend. Para exploração rápida, folium permite criar HTML interativo com poucas linhas.

import folium


mapa = folium.Map(location=[-23.55, -46.63], zoom_start=7)

for _, row in unidades_sp.to_crs("EPSG:4326").iterrows():
    folium.CircleMarker(
        location=[row.geometry.y, row.geometry.x],
        radius=6,
        popup=f"{row['nome']}{row['atendimentos']} atendimentos",
        color="#306998",
        fill=True,
    ).add_to(mapa)

mapa.save("outputs/unidades-sp.html")

Estrutura de projeto para portfólio

Um projeto geoespacial fica mais profissional quando separa dados brutos, dados processados, código e saídas:

analise-geoespacial/
  data/
    raw/
      unidades.csv
      municipios.gpkg
    processed/
      unidades_com_municipio.parquet
  notebooks/
    exploracao.ipynb
  src/
    preparar_dados.py
    gerar_mapa.py
  outputs/
    unidades-sp.png
    unidades-sp.html
  tests/
    test_geometria.py
  README.md

No README, explique três coisas:

  • qual pergunta o projeto responde;
  • quais fontes de dados foram usadas;
  • quais decisões geoespaciais importam, como CRS, filtros e limitações.

Isso diferencia um mapa bonito de uma análise confiável. Recrutadores e pessoas técnicas conseguem entender o raciocínio, não apenas ver uma imagem final.

Testando regras simples

Nem tudo em dados precisa de teste automatizado, mas algumas regras valem a pena. Por exemplo: todo ponto deve cair em algum município, não pode haver geometria vazia e a coluna de atendimentos precisa ser não negativa.

import geopandas as gpd


def test_unidades_processadas_tem_municipio():
    dados = gpd.read_parquet("data/processed/unidades_com_municipio.parquet")

    assert dados["municipio"].notna().all()
    assert dados.geometry.notna().all()
    assert (dados["atendimentos"] >= 0).all()

Esse tipo de teste pega erros comuns: coordenada invertida, arquivo de limites errado, mudança de schema no CSV ou carga incompleta. Para um pipeline mensal, vale rodar esses testes no CI antes de publicar relatório ou dashboard.

Erros comuns com GeoPandas

O primeiro erro é inverter latitude e longitude. Em points_from_xy, a ordem é longitude, latitude, porque x representa eixo horizontal e y representa eixo vertical. Se você inverter, pontos brasileiros podem cair no oceano ou em outro continente.

O segundo erro é misturar CRS. Antes de sjoin, overlay, distance ou area, confira gdf.crs. Se dois GeoDataFrames usam sistemas diferentes, reprojete com .to_crs().

O terceiro erro é carregar dados demais cedo demais. Se o arquivo tem milhões de linhas, filtre colunas, use Parquet, leia por partes ou empurre agregações tabulares para DuckDB antes da etapa espacial. GeoPandas é prático, mas não é mágica: geometria custa CPU e memória.

O quarto erro é não registrar fonte e data de captura. Dados públicos mudam. Um limite municipal, uma base de equipamentos ou uma lista de ocorrências pode ser atualizada. Escreva no README quando os dados foram baixados e como reproduzir.

Ideias de projetos brasileiros

Algumas ideias boas para portfólio:

  • mapa de equipamentos públicos por município usando bases abertas e população do IBGE;
  • análise de tempo de deslocamento aproximado entre unidades e regiões atendidas;
  • visualização de ocorrências por bairro com agregação por período;
  • comparação de vagas de dados por estado usando dados da seção de vagas Python;
  • painel simples de escolas, hospitais ou bibliotecas por região administrativa.

Escolha um recorte pequeno. Um projeto sobre todo o Brasil pode ficar pesado e superficial. Um projeto sobre uma cidade, estado ou tema específico costuma ser mais fácil de explicar e validar.

Conclusão

GeoPandas é uma ponte excelente entre análise de dados e geoprocessamento. Ele permite usar a lógica familiar de DataFrames para responder perguntas territoriais: onde está, dentro de qual região, a que distância, com qual concentração e com qual padrão espacial.

Para quem busca vaga ou quer fortalecer portfólio, a combinação é forte: Python, Pandas, dados públicos brasileiros, mapas, testes e uma explicação clara no README. Comece com CSV e limites municipais, faça uma junção espacial, gere um mapa simples e documente as limitações. Depois evolua para pipelines, dashboards, APIs ou modelos preditivos.

Se o projeto crescer para processamento de muitos arquivos, serviços de mapas ou APIs de baixa latência, pode fazer sentido combinar Python com outras linguagens. Em arquiteturas mistas, Go costuma aparecer em serviços concorrentes e binários simples, enquanto Python continua excelente para análise, notebooks e automação geoespacial.

E

Equipe Python Brasil

Contribuidor do Python Brasil — Aprenda Python em Português