---
title: "GeoPandas com Python: Mapas e Dados Geoespaciais"
url: "https://python.dev.br/blog/geopandas-dados-geoespaciais-python/"
markdown_url: "https://python.dev.br/blog/geopandas-dados-geoespaciais-python.MD"
description: "Aprenda a usar GeoPandas com Python para analisar dados geoespaciais, cruzar CSV com shapefiles, criar mapas e montar projetos de portfólio no Brasil."
date: "2026-05-31"
author: "Equipe Python Brasil"
---

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


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](/glossario/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](/blog/introducao-ao-pandas/), [APIs públicas brasileiras](/blog/apis-publicas-brasileiras-python/), [DuckDB](/blog/python-e-duckdb-analytics/) e [testes com pytest](/guias/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:

| Pergunta | Operaçã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`:

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

Com `pip`:

```bash
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](/blog/python-e-duckdb-analytics/) 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:

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

```python
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`.

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

```python
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á.

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

```python
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.

```python
# 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:

```python
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.

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

```text
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.

```python
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](/vagas/);
- 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, <a href="https://golang.com.br/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go</a> costuma aparecer em serviços concorrentes e binários simples, enquanto Python continua excelente para análise, notebooks e automação geoespacial.
