📖 Glossário - Análise Geoespacial

A

Atributo (Feature Attribute): Informação descritiva associada a uma feição geográfica (nome, população, área, etc).

B

Bounding Box: Retângulo que delimita a extensão mínima de um conjunto de geometrias.

Buffer: Operação que cria área ao redor de geometria com distância específica.

C

Centroide: Ponto central de uma geometria.

CRS (Coordinate Reference System): Sistema de coordenadas que define como posições geográficas são representadas.

CSV (Comma-Separated Values): Formato de arquivo tabular com valores separados por vírgula.

D

DataFrame: Estrutura de dados tabular do pandas com linhas e colunas.

Dissolve: Operação que combina geometrias adjacentes em uma única.

E

EPSG: Código numérico que identifica sistemas de coordenadas (ex: EPSG:4326 = WGS84).

Extent: Limites geográficos (min/max X e Y) de uma área.

F

Feature (Feição): Entidade geográfica com geometria e atributos (ponto, linha ou polígono).

Feature Collection: Coleção de múltiplas feições GeoJSON.

G

Geocodificação: Processo de converter endereços em coordenadas.

GeoDataFrame: DataFrame do GeoPandas com coluna de geometrias.

Geografia: Ciência que estuda a superfície terrestre e distribuição espacial de fenômenos.

GeoJSON: Formato JSON para codificar estruturas de dados geográficos.

GeoPandas: Biblioteca Python para manipular dados geoespaciais.

Geometria: Representação matemática de forma espacial (ponto, linha, polígono).

GIS (Geographic Information System): Sistema para capturar, armazenar, analisar e gerenciar dados espaciais.

I

Interseção (Intersection): Operação que retorna geometria comum entre duas outras.

L

Latitude: Coordenada que indica posição norte-sul (0° no Equador, -90° a +90°).

Layer (Camada): Conjunto de dados geográficos relacionados.

LineString: Geometria GeoJSON representando linha conectando pontos.

Longitude: Coordenada que indica posição leste-oeste (0° em Greenwich, -180° a +180°).

M

Mapa: Representação visual de informações geográficas.

Mapa de calor (Heatmap): Visualização que usa cor para mostrar densidade/intensidade.

Marker (Marcador): Ícone que indica localização em mapa.

Mercator: Projeção cilíndrica comum em mapas web.

MultiPoint/MultiLineString/MultiPolygon: Geometrias compostas por múltiplas partes.

O

Overlay: Operação que combina duas camadas espaciais.

P

Plotly: Biblioteca Python para visualizações interativas.

Point (Ponto): Geometria mais simples representando localização (X, Y).

Polygon (Polígono): Geometria fechada representando área.

Projeção: Transformação de coordenadas 3D (Terra) para 2D (mapa).

R

Raster: Dados espaciais em formato de grade de pixels (imagens, elevação).

Reprojetar: Converter dados de um CRS para outro.

S

Shapefile: Formato popular de arquivo para dados vetoriais GIS (.shp, .shx, .dbf).

SIG (Sistema de Informação Geográfica): Termo em português para GIS.

Spatial Join: Operação que une atributos baseado em relação espacial.

T

Tiles (Ladrilhos): Pequenas imagens que compõem mapa web em diferentes zooms.

Topology (Topologia): Relações espaciais entre geometrias (adjacência, interseção, etc).

U

Union: Operação que combina geometrias em uma única.

V

Vetor: Dados geográficos representados por pontos, linhas e polígonos.

Visualização: Representação gráfica de dados para facilitar compreensão.

W

WGS84 (World Geodetic System 1984): Sistema de coordenadas geográficas padrão (EPSG:4326).

Z

Zoom: Nível de detalhe de visualização em mapa (maior número = mais próximo).

Exemplos Práticos

import geopandas as gpd
from shapely.geometry import Point, Polygon

# Criar GeoDataFrame de pontos
dados = {
    'nome': ['Ingleses', 'Lagoa', 'Centro'],
    'geometry': [
        Point(-48.4, -27.4),
        Point(-48.5, -27.6),
        Point(-48.55, -27.58)
    ]
}
gdf = gpd.GeoDataFrame(dados, crs="EPSG:4326")

# Criar buffer de 1 km
gdf_buffer = gdf.to_crs("EPSG:31982")  # Projetar para metros
gdf_buffer['geometry'] = gdf_buffer.buffer(1000)

# Ler shapefile
praias = gpd.read_file("praias.shp")

# Interseção espacial
praias_proximas = gpd.sjoin(praias, gdf_buffer, how='inner')

# Salvar como GeoJSON
praias_proximas.to_file("praias_proximas.geojson", driver='GeoJSON')

# Plotar
praias.plot(figsize=(10, 10), color='blue')

Sistemas de Coordenadas Comuns

EPSG Nome Uso
4326 WGS84 GPS, Google Maps, coordenadas geográficas
3857 Web Mercator Mapas web (Google, OpenStreetMap)
31982 SIRGAS 2000 / UTM 22S Brasil - SC, RS (coordenadas métricas)
4674 SIRGAS 2000 Brasil - coordenadas geográficas

💡 Dica: Use sempre gdf.crs para verificar o sistema de coordenadas do seu GeoDataFrame!

🗺️ GeoJSON - Dados Geográficos Básicos

O que é GeoJSON?

GeoJSON = formato de arquivo para armazenar dados geográficos em JSON.

Por que GeoJSON é importante?

Excel/CSV           GeoJSON
├─ Dados tabulares  ├─ Dados + Geografia
├─ Sem localização  ├─ Coordenadas incluídas
└─ Sem mapas        └─ Pronto para mapear

Usado em: Leaflet, Folium, QGIS, ArcGIS Online, Google Maps


📍 Coordenadas Geográficas

Antes de GeoJSON, você precisa entender coordenadas!

Latitude e Longitude

Latitude  (Y) → Norte/Sul → -90° a +90°
Longitude (X) → Leste/Oeste → -180° a +180°

Brasil:
├─ Latitude: -33° a +5° (negativo = Sul)
└─ Longitude: -73° a -34° (negativo = Oeste)

Santa Catarina (exemplo):
├─ Florianópolis: -27.5969°, -48.5495°
├─ Praia dos Ingleses: -27.4374°, -48.3923°
└─ Laguna: -28.4833°, -48.7833°

Formato padrão: [longitude, latitude] ⚠️ Longitude vem PRIMEIRO!


📐 Tipos de Geometria

1. Point (Ponto)

Um único local.

{
  "type": "Point",
  "coordinates": [-48.5495, -27.5969]
}

Uso: Estação de coleta, localização de espécie

2. LineString (Linha)

Sequência de pontos conectados.

{
  "type": "LineString",
  "coordinates": [
    [-48.5495, -27.5969],
    [-48.5500, -27.6000],
    [-48.5505, -27.6030]
  ]
}

Uso: Transecto, rota de barco, corrente marinha

3. Polygon (Polígono)

Área fechada.

{
  "type": "Polygon",
  "coordinates": [
    [
      [-48.5495, -27.5969],
      [-48.5500, -27.5969],
      [-48.5500, -27.6000],
      [-48.5495, -27.6000],
      [-48.5495, -27.5969]
    ]
  ]
}

⚠️ Primeiro e último ponto devem ser iguais!

Uso: Área de proteção, zona de coleta, habitat de espécie


🎯 Estrutura Completa de GeoJSON

Feature (Um objeto geográfico)

{
  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [-48.5495, -27.5969]
  },
  "properties": {
    "nome": "Praia dos Ingleses",
    "especie": "Ulva lactuca",
    "profundidade_m": 5.2,
    "temperatura_c": 22.5,
    "data_coleta": "2025-01-06"
  }
}

Partes: - geometry: Localização geográfica - properties: Dados descritivos (qualquer coisa!)

FeatureCollection (Múltiplos objetos)

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [-48.5495, -27.5969]
      },
      "properties": {
        "id": 1,
        "especie": "Ulva lactuca",
        "profundidade_m": 5.2
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [-48.3923, -27.4374]
      },
      "properties": {
        "id": 2,
        "especie": "Gracilaria",
        "profundidade_m": 7.8
      }
    }
  ]
}

🐍 Criar GeoJSON com Python

Instalar Bibliotecas

pip install geojson

Exemplo 1: Criar Ponto Simples

import json

# Criar Feature (ponto)
ponto_coleta = {
    "type": "Feature",
    "geometry": {
        "type": "Point",
        "coordinates": [-48.5495, -27.5969]  # [lon, lat]
    },
    "properties": {
        "nome": "Estação 1 - Praia dos Ingleses",
        "especie": "Ulva lactuca",
        "profundidade_m": 5.2,
        "temperatura_c": 22.5,
        "salinidade_psu": 35.0
    }
}

# Salvar em arquivo
with open("ponto_coleta.geojson", "w", encoding="utf-8") as f:
    json.dump(ponto_coleta, f, indent=2, ensure_ascii=False)

print("✅ GeoJSON criado: ponto_coleta.geojson")

Exemplo 2: Múltiplas Estações de Coleta

import json

# Dados de coleta (exemplo LAFIC)
coletas = [
    {"nome": "Ingleses - Norte", "lon": -48.3923, "lat": -27.4374, 
     "especie": "Ulva lactuca", "prof": 5.2, "temp": 22.5},
    {"nome": "Ingleses - Sul", "lon": -48.3950, "lat": -27.4450, 
     "especie": "Gracilaria", "prof": 7.8, "temp": 23.1},
    {"nome": "Barra da Lagoa", "lon": -48.4200, "lat": -27.5750, 
     "especie": "Sargassum", "prof": 3.1, "temp": 22.8},
    {"nome": "Laguna", "lon": -48.7833, "lat": -28.4833, 
     "especie": "Laminaria", "prof": 10.5, "temp": 21.2}
]

# Criar FeatureCollection
features = []
for i, coleta in enumerate(coletas, start=1):
    feature = {
        "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": [coleta["lon"], coleta["lat"]]
        },
        "properties": {
            "id": i,
            "nome": coleta["nome"],
            "especie": coleta["especie"],
            "profundidade_m": coleta["prof"],
            "temperatura_c": coleta["temp"]
        }
    }
    features.append(feature)

geojson_collection = {
    "type": "FeatureCollection",
    "features": features
}

# Salvar
with open("coletas_LAFIC.geojson", "w", encoding="utf-8") as f:
    json.dump(geojson_collection, f, indent=2, ensure_ascii=False)

print(f"✅ GeoJSON criado com {len(features)} estações")

Exemplo 3: Criar Polígono (Área de Estudo)

import json

# Área de proteção marinha (exemplo)
area_protecao = {
    "type": "Feature",
    "geometry": {
        "type": "Polygon",
        "coordinates": [[
            [-48.5495, -27.5969],  # Ponto 1
            [-48.5400, -27.5969],  # Ponto 2
            [-48.5400, -27.6100],  # Ponto 3
            [-48.5495, -27.6100],  # Ponto 4
            [-48.5495, -27.5969]   # Fecha (igual ao primeiro)
        ]]
    },
    "properties": {
        "nome": "Área de Proteção Marinha - Ingleses",
        "tipo": "Zona de coleta proibida",
        "area_km2": 12.5,
        "criacao": "2020-01-01"
    }
}

# Salvar
with open("area_protecao.geojson", "w", encoding="utf-8") as f:
    json.dump(area_protecao, f, indent=2, ensure_ascii=False)

print("✅ Área de proteção criada")

📊 Ler GeoJSON

Exemplo: Carregar e Processar

import json

# Carregar arquivo
with open("coletas_LAFIC.geojson", "r", encoding="utf-8") as f:
    dados = json.load(f)

# Acessar features
print(f"Total de pontos: {len(dados['features'])}")

# Iterar sobre features
for feature in dados["features"]:
    props = feature["properties"]
    coords = feature["geometry"]["coordinates"]

    print(f"\n📍 {props['nome']}")
    print(f"   Espécie: {props['especie']}")
    print(f"   Localização: {coords[1]:.4f}, {coords[0]:.4f}")
    print(f"   Profundidade: {props['profundidade_m']}m")

# Filtrar por espécie
ulvas = [f for f in dados["features"] 
         if f["properties"]["especie"] == "Ulva lactuca"]
print(f"\n🌿 Encontradas {len(ulvas)} amostras de Ulva lactuca")

🌐 Validar GeoJSON Online

Antes de usar em mapas, valide seu GeoJSON:

Ferramentas: 1. geojson.io - http://geojson.io - Cole seu GeoJSON - Visualize no mapa - Edite geometrias

  1. GeoJSONLint - https://geojsonlint.com
  2. Valida sintaxe
  3. Mostra erros

🎯 Exemplo Prático: Sistema de Cadastro

Crie arquivo cadastro_coletas.py:

import json
from datetime import datetime

def criar_ponto_coleta(nome, lon, lat, especie, profundidade, temperatura):
    """Cria um Feature GeoJSON para coleta"""
    return {
        "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": [lon, lat]
        },
        "properties": {
            "nome": nome,
            "especie": especie,
            "profundidade_m": profundidade,
            "temperatura_c": temperatura,
            "data_cadastro": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }
    }

def adicionar_coleta(arquivo, nova_coleta):
    """Adiciona uma coleta ao GeoJSON existente"""
    try:
        # Tentar carregar arquivo existente
        with open(arquivo, "r", encoding="utf-8") as f:
            dados = json.load(f)
    except FileNotFoundError:
        # Criar novo se não existe
        dados = {
            "type": "FeatureCollection",
            "features": []
        }

    # Adicionar nova coleta
    dados["features"].append(nova_coleta)

    # Salvar
    with open(arquivo, "w", encoding="utf-8") as f:
        json.dump(dados, f, indent=2, ensure_ascii=False)

    return len(dados["features"])

def listar_coletas(arquivo):
    """Lista todas as coletas"""
    try:
        with open(arquivo, "r", encoding="utf-8") as f:
            dados = json.load(f)

        print("=" * 60)
        print("📊 COLETAS CADASTRADAS")
        print("=" * 60)

        for i, feature in enumerate(dados["features"], start=1):
            props = feature["properties"]
            coords = feature["geometry"]["coordinates"]

            print(f"\n{i}. {props['nome']}")
            print(f"   Espécie: {props['especie']}")
            print(f"   Coordenadas: {coords[1]:.4f}°, {coords[0]:.4f}°")
            print(f"   Profundidade: {props['profundidade_m']}m")
            print(f"   Temperatura: {props['temperatura_c']}°C")

        print("\n" + "=" * 60)
        print(f"Total: {len(dados['features'])} coletas")

    except FileNotFoundError:
        print("❌ Nenhum arquivo encontrado")

# Sistema interativo
def menu():
    arquivo = "minhas_coletas.geojson"

    while True:
        print("\n🌊 SISTEMA DE CADASTRO DE COLETAS")
        print("1 - Adicionar coleta")
        print("2 - Listar coletas")
        print("3 - Sair")

        opcao = input("\nEscolha: ")

        if opcao == "1":
            print("\n📝 Nova Coleta:")
            nome = input("Nome da estação: ")
            lat = float(input("Latitude: "))
            lon = float(input("Longitude: "))
            especie = input("Espécie: ")
            prof = float(input("Profundidade (m): "))
            temp = float(input("Temperatura (°C): "))

            coleta = criar_ponto_coleta(nome, lon, lat, especie, prof, temp)
            total = adicionar_coleta(arquivo, coleta)
            print(f"✅ Coleta adicionada! Total: {total}")

        elif opcao == "2":
            listar_coletas(arquivo)

        elif opcao == "3":
            print("👋 Até logo!")
            break

if __name__ == "__main__":
    menu()

Execute:

python cadastro_coletas.py

🗺️ Visualizar no geojson.io

Depois de criar seus GeoJSON:

  1. Abra: http://geojson.io
  2. Arraste seu arquivo .geojson
  3. Veja no mapa interativo! 🗺️

Ou cole o JSON diretamente no editor.


🎓 Boas Práticas

Faça: - Use [longitude, latitude] nesta ordem - Feche polígonos (primeiro = último ponto) - Valide GeoJSON antes de usar - Use UTF-8 para acentos

Evite: - Coordenadas invertidas - Polígonos não fechados - Muitos decimais (4-6 é suficiente) - Arquivos gigantes (use simplificação)


🎓 Checklist desta Lição

  • [ ] Entendo o que é GeoJSON
  • [ ] Sei a diferença entre Point, LineString e Polygon
  • [ ] Consigo criar GeoJSON com Python
  • [ ] Sei ler e processar arquivos GeoJSON
  • [ ] Validei pelo menos um GeoJSON no geojson.io
  • [ ] Criei um sistema de cadastro

Se marcou tudo, você está pronto para GeoPandas! 🎉


➡️ Próximo Tópico

👉 02-GeoPandas-Intro.html

Lá você aprenderá: - Usar GeoPandas para análise geoespacial - Operações com geometrias - Análise espacial - Juntar dados geográficos


📝 Resumo de Tipos

Tipo Geometria Uso
Point Um ponto Estação de coleta
LineString Linha Transecto, rota
Polygon Área Zona de estudo
MultiPoint Vários pontos Cluster de coletas

Você agora domina GeoJSON básico! 🗺️ Próximo: GeoPandas! 🐼

🐼 GeoPandas - Análise Geoespacial com Python

O que é GeoPandas?

GeoPandas = Pandas + Geografia

Pandas              GeoPandas
├─ DataFrames       ├─ GeoDataFrames (DataFrames + geometria)
├─ Tabelas          ├─ Tabelas + mapas
└─ Análise dados    └─ Análise espacial

Por que usar: Análise geoespacial com sintaxe familiar de Pandas!


🚀 Instalação

pip install geopandas
pip install matplotlib  # Para gráficos
pip install folium      # Para mapas interativos (próxima lição)

📊 GeoDataFrame - A Base

Criar GeoDataFrame Simples

import geopandas as gpd
from shapely.geometry import Point

# Dados de coleta
dados = {
    'nome': ['Ingleses Norte', 'Ingleses Sul', 'Barra da Lagoa'],
    'especie': ['Ulva lactuca', 'Gracilaria', 'Sargassum'],
    'profundidade_m': [5.2, 7.8, 3.1],
    'temperatura_c': [22.5, 23.1, 22.8]
}

# Criar pontos (geometria)
geometrias = [
    Point(-48.3923, -27.4374),  # Ingleses Norte
    Point(-48.3950, -27.4450),  # Ingleses Sul
    Point(-48.4200, -27.5750)   # Barra da Lagoa
]

# Criar GeoDataFrame
gdf = gpd.GeoDataFrame(dados, geometry=geometrias, crs="EPSG:4326")

print(gdf)

Resultado:

              nome       especie  profundidade_m  temperatura_c                    geometry
0   Ingleses Norte  Ulva lactuca             5.2           22.5  POINT (-48.3923 -27.4374)
1    Ingleses Sul    Gracilaria             7.8           23.1  POINT (-48.3950 -27.4450)
2  Barra da Lagoa     Sargassum             3.1           22.8  POINT (-48.4200 -27.5750)

O que é CRS?

CRS (Coordinate Reference System) = sistema de coordenadas.

EPSG:4326   WGS84 (GPS padrão - graus decimais)
EPSG:3857   Web Mercator (Google Maps, Leaflet)
EPSG:31982  SIRGAS 2000 / UTM 22S (Brasil Sul)

Sempre use EPSG:4326 para dados geográficos básicos!


📥 Carregar Dados Geoespaciais

De GeoJSON

import geopandas as gpd

# Carregar GeoJSON
gdf = gpd.read_file("coletas_LAFIC.geojson")

# Informações
print(gdf.info())
print(gdf.head())

# Ver CRS
print(gdf.crs)

De Shapefile

# Shapefile (formato antigo mas comum)
gdf = gpd.read_file("municipios_sc.shp")

De CSV com Coordenadas

import pandas as pd
import geopandas as gpd
from shapely.geometry import Point

# Carregar CSV
df = pd.read_csv("coletas.csv")
# Colunas: nome, latitude, longitude, especie

# Converter para GeoDataFrame
geometry = [Point(lon, lat) for lon, lat in zip(df['longitude'], df['latitude'])]
gdf = gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326")

🗺️ Visualizar Dados

Plot Simples

import geopandas as gpd
import matplotlib.pyplot as plt

# Criar dados
gdf = gpd.read_file("coletas_LAFIC.geojson")

# Plot básico
gdf.plot(figsize=(10, 6), marker='o', color='blue', markersize=50)
plt.title("Estações de Coleta - LAFIC")
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.grid(True)
plt.show()

Plot com Cores por Categoria

# Colorir por espécie
gdf.plot(column='especie', 
         figsize=(10, 6), 
         legend=True,
         markersize=100,
         cmap='Set1')  # Esquema de cores
plt.title("Distribuição de Espécies")
plt.show()

Plot com Tamanho Variável

# Tamanho proporcional à profundidade
gdf.plot(column='profundidade_m',
         figsize=(10, 6),
         legend=True,
         markersize=gdf['profundidade_m'] * 20,  # Multiplicar para visualizar
         cmap='YlOrRd')  # Amarelo a Vermelho
plt.title("Profundidade das Coletas")
plt.show()

🔧 Operações Básicas

Filtrar Dados

# Filtrar por espécie
ulvas = gdf[gdf['especie'] == 'Ulva lactuca']
print(f"Total de Ulva lactuca: {len(ulvas)}")

# Filtrar por profundidade
rasas = gdf[gdf['profundidade_m'] < 5]
print(f"Coletas rasas (<5m): {len(rasas)}")

# Múltiplas condições
validas = gdf[(gdf['temperatura_c'] >= 20) & 
              (gdf['temperatura_c'] <= 25) & 
              (gdf['profundidade_m'] < 10)]

Adicionar Coluna

# Classificar profundidade
def classificar_profundidade(prof):
    if prof < 5:
        return "Rasa"
    elif prof < 20:
        return "Intermediária"
    else:
        return "Profunda"

gdf['classificacao'] = gdf['profundidade_m'].apply(classificar_profundidade)

📐 Operações Geométricas

Calcular Distâncias

from shapely.geometry import Point

# Ponto de referência (LAFIC - UFSC)
LAFIC = Point(-48.5495, -27.5969)

# Calcular distância de cada coleta ao LAFIC
# Nota: distância em graus (aproximação!)
gdf['distancia_graus'] = gdf.geometry.distance(LAFIC)

# Converter para km (1 grau ≈ 111 km)
gdf['distancia_km'] = gdf['distancia_graus'] * 111

print(gdf[['nome', 'distancia_km']])

Criar Buffer (Área ao Redor)

# Criar buffer de 5km ao redor de cada ponto
# Primeiro converter para sistema métrico
gdf_utm = gdf.to_crs("EPSG:31982")  # UTM para Santa Catarina

# Buffer de 5000 metros (5km)
gdf_utm['buffer_5km'] = gdf_utm.geometry.buffer(5000)

# Plotar
fig, ax = plt.subplots(figsize=(10, 6))
gdf_utm.plot(ax=ax, color='red', markersize=50)
gdf_utm['buffer_5km'].plot(ax=ax, alpha=0.3, color='blue')
plt.title("Áreas de Influência (5km)")
plt.show()

Verificar se Ponto está Dentro de Polígono

from shapely.geometry import Polygon

# Criar área de estudo (polígono)
area_estudo = Polygon([
    (-48.6, -27.4),
    (-48.3, -27.4),
    (-48.3, -27.7),
    (-48.6, -27.7)
])

# Verificar quais pontos estão dentro
gdf['dentro_area'] = gdf.geometry.within(area_estudo)

# Filtrar
dentro = gdf[gdf['dentro_area']]
print(f"Coletas dentro da área: {len(dentro)}")

🎯 Análise Espacial

Encontrar Vizinhos Mais Próximos

import numpy as np

def encontrar_mais_proximo(gdf, idx):
    """Encontra ponto mais próximo a um dado índice"""
    ponto = gdf.loc[idx, 'geometry']

    # Calcular distâncias
    distancias = gdf.geometry.distance(ponto)

    # Remover próprio ponto
    distancias[idx] = np.inf

    # Encontrar mínimo
    idx_proximo = distancias.idxmin()
    dist_km = distancias[idx_proximo] * 111

    return gdf.loc[idx_proximo, 'nome'], dist_km

# Testar
nome, dist = encontrar_mais_proximo(gdf, 0)
print(f"Ponto mais próximo: {nome} ({dist:.2f} km)")

Agrupar por Região

# Dividir em regiões por latitude
gdf['regiao'] = pd.cut(gdf.geometry.y, 
                       bins=3, 
                       labels=['Norte', 'Centro', 'Sul'])

# Estatísticas por região
resumo = gdf.groupby('regiao').agg({
    'profundidade_m': 'mean',
    'temperatura_c': 'mean',
    'especie': 'count'
})
resumo.columns = ['Prof. Média (m)', 'Temp. Média (°C)', 'Num. Coletas']
print(resumo)

💾 Salvar Dados

Para GeoJSON

# Salvar como GeoJSON
gdf.to_file("resultado_analise.geojson", driver="GeoJSON")

Para Shapefile

# Salvar como Shapefile
gdf.to_file("resultado_analise.shp")

Para CSV (sem geometria)

# Adicionar colunas de coordenadas
gdf['longitude'] = gdf.geometry.x
gdf['latitude'] = gdf.geometry.y

# Salvar só os dados
gdf.drop(columns=['geometry']).to_csv("resultado.csv", index=False)

🌍 Exemplo Completo: Análise de Distribuição de Espécies

import geopandas as gpd
import matplotlib.pyplot as plt
import pandas as pd
from shapely.geometry import Point

# 1. CRIAR DADOS DE EXEMPLO (LAFIC)
dados = {
    'id': range(1, 11),
    'local': [
        'Ingleses N', 'Ingleses S', 'Barra Lagoa', 'Laguna', 
        'Garopaba', 'Armação', 'Campeche', 'Pântano Sul',
        'Joaquina', 'Mole'
    ],
    'especie': [
        'Ulva lactuca', 'Gracilaria', 'Sargassum', 'Ulva lactuca',
        'Gracilaria', 'Ulva lactuca', 'Sargassum', 'Laminaria',
        'Ulva lactuca', 'Gracilaria'
    ],
    'profundidade_m': [5.2, 7.8, 3.1, 10.5, 6.0, 4.5, 8.2, 12.0, 5.5, 7.0],
    'temperatura_c': [22.5, 23.1, 22.8, 21.2, 22.0, 23.5, 22.9, 20.5, 23.0, 22.7],
    'longitude': [-48.39, -48.40, -48.42, -48.78, -48.62, -48.51, -48.48, -48.50, -48.44, -48.43],
    'latitude': [-27.44, -27.45, -27.58, -28.48, -28.02, -27.75, -27.67, -27.78, -27.63, -27.58]
}

df = pd.DataFrame(dados)

# 2. CRIAR GEODATAFRAME
geometry = [Point(lon, lat) for lon, lat in zip(df['longitude'], df['latitude'])]
gdf = gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326")

print("=" * 70)
print("🌊 ANÁLISE DE DISTRIBUIÇÃO DE MACROALGAS - LAFIC/UFSC")
print("=" * 70)

# 3. ESTATÍSTICAS GERAIS
print(f"\n📊 Dados Gerais:")
print(f"   Total de coletas: {len(gdf)}")
print(f"   Espécies únicas: {gdf['especie'].nunique()}")
print(f"   Profundidade média: {gdf['profundidade_m'].mean():.1f}m")
print(f"   Temperatura média: {gdf['temperatura_c'].mean():.1f}°C")

# 4. ANÁLISE POR ESPÉCIE
print(f"\n🌿 Distribuição por Espécie:")
especies_count = gdf['especie'].value_counts()
for especie, count in especies_count.items():
    print(f"   {especie}: {count} coletas")

# 5. CALCULAR CENTROIDE (centro geográfico)
centroide = gdf.geometry.unary_union.centroid
print(f"\n📍 Centro Geográfico das Coletas:")
print(f"   Latitude: {centroide.y:.4f}°")
print(f"   Longitude: {centroide.x:.4f}°")

# 6. FILTRAR E ANALISAR
print(f"\n✓ Coletas Validadas (20-25°C, <10m):")
validas = gdf[(gdf['temperatura_c'] >= 20) & 
              (gdf['temperatura_c'] <= 25) & 
              (gdf['profundidade_m'] < 10)]
print(f"   {len(validas)}/{len(gdf)} coletas válidas ({len(validas)/len(gdf)*100:.0f}%)")

# 7. VISUALIZAÇÃO
fig, axes = plt.subplots(1, 3, figsize=(18, 5))

# Subplot 1: Todas as coletas
gdf.plot(ax=axes[0], marker='o', color='blue', markersize=100, alpha=0.6)
axes[0].set_title("Todas as Estações de Coleta", fontsize=12)
axes[0].set_xlabel("Longitude")
axes[0].set_ylabel("Latitude")
axes[0].grid(True, alpha=0.3)

# Subplot 2: Por espécie
gdf.plot(ax=axes[1], column='especie', legend=True, 
         markersize=100, cmap='Set1', alpha=0.7)
axes[1].set_title("Distribuição por Espécie", fontsize=12)
axes[1].set_xlabel("Longitude")
axes[1].grid(True, alpha=0.3)

# Subplot 3: Por profundidade
gdf.plot(ax=axes[2], column='profundidade_m', legend=True,
         markersize=100, cmap='YlOrRd', alpha=0.7)
axes[2].set_title("Profundidade das Coletas", fontsize=12)
axes[2].set_xlabel("Longitude")
axes[2].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig("analise_distribuicao.png", dpi=300, bbox_inches='tight')
print(f"\n💾 Gráfico salvo: analise_distribuicao.png")

# 8. SALVAR RESULTADOS
gdf.to_file("coletas_analisadas.geojson", driver="GeoJSON")
print(f"💾 GeoJSON salvo: coletas_analisadas.geojson")

print("\n" + "=" * 70)
print("✅ Análise concluída com sucesso!")

🎓 Checklist desta Lição

  • [ ] Entendo o que é GeoDataFrame
  • [ ] Consigo criar GeoDataFrame de dados
  • [ ] Sei carregar e salvar GeoJSON
  • [ ] Posso fazer operações geométricas (buffer, distância)
  • [ ] Consigo visualizar dados em mapas
  • [ ] Realizei análise espacial completa

Se marcou tudo, você está pronto para Mapas Interativos! 🎉


➡️ Próximo Tópico

👉 03-Mapas-Folium.html

Lá você aprenderá: - Criar mapas interativos com Folium - Adicionar marcadores e popups - Camadas e clusters - Publicar mapas online


📝 Resumo de Funções

Função Uso
gpd.read_file() Carregar GeoJSON/Shapefile
gdf.plot() Visualizar mapa
gdf.to_file() Salvar arquivo geoespacial
geometry.distance() Calcular distância
geometry.buffer() Criar área ao redor
geometry.within() Verificar se está dentro

Você domina GeoPandas básico! 🐼 Próximo: Mapas interativos com Folium! 🗺️

🗺️ Mapas Interativos com Folium

O que é Folium?

Folium = biblioteca Python para criar mapas interativos web (Leaflet.js).

GeoPandas          Folium
├─ Análise         ├─ Visualização
├─ Mapas estáticos ├─ Mapas interativos
└─ PNG/PDF         └─ HTML (web)

Use para: Publicar resultados, apresentações, dashboards web


🚀 Instalação

pip install folium
pip install geopandas  # Se ainda não tem

🎯 Criar Mapa Básico

Exemplo Mínimo

import folium

# Criar mapa centrado em Florianópolis
mapa = folium.Map(
    location=[-27.5969, -48.5495],  # [latitude, longitude]
    zoom_start=12,
    tiles='OpenStreetMap'
)

# Salvar como HTML
mapa.save("meu_primeiro_mapa.html")
print("✅ Mapa criado: meu_primeiro_mapa.html")

Abra o arquivo HTML no navegador! 🌐

Tipos de Tiles (Camadas Base)

import folium

# OpenStreetMap (padrão)
mapa1 = folium.Map(location=[-27.5969, -48.5495], zoom_start=12, 
                   tiles='OpenStreetMap')

# Imagem de satélite
mapa2 = folium.Map(location=[-27.5969, -48.5495], zoom_start=12,
                   tiles='https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
                   attr='Esri')

# CartoDB Positron (minimalista)
mapa3 = folium.Map(location=[-27.5969, -48.5495], zoom_start=12,
                   tiles='CartoDB positron')

# Stamen Terrain (topografia)
mapa4 = folium.Map(location=[-27.5969, -48.5495], zoom_start=12,
                   tiles='Stamen Terrain')

📍 Adicionar Marcadores

Marcador Simples

import folium

# Criar mapa
mapa = folium.Map(location=[-27.5969, -48.5495], zoom_start=12)

# Adicionar marcador
folium.Marker(
    location=[-27.4374, -48.3923],
    popup="Praia dos Ingleses",
    tooltip="Clique para mais informações",
    icon=folium.Icon(color='blue', icon='info-sign')
).add_to(mapa)

mapa.save("mapa_com_marcador.html")

Marcadores com Ícones Personalizados

import folium

mapa = folium.Map(location=[-27.5969, -48.5495], zoom_start=11)

# Ícone padrão - vermelho
folium.Marker(
    [-27.4374, -48.3923],
    popup="Ulva lactuca - Profundidade: 5.2m",
    icon=folium.Icon(color='red', icon='leaf', prefix='fa')
).add_to(mapa)

# Ícone verde
folium.Marker(
    [-27.4450, -48.3950],
    popup="Gracilaria - Profundidade: 7.8m",
    icon=folium.Icon(color='green', icon='seedling', prefix='fa')
).add_to(mapa)

# Ícone azul
folium.Marker(
    [-27.5750, -48.4200],
    popup="Sargassum - Profundidade: 3.1m",
    icon=folium.Icon(color='blue', icon='water', prefix='fa')
).add_to(mapa)

mapa.save("mapa_especies.html")

Cores disponíveis: red, blue, green, purple, orange, darkred, lightred, beige, darkblue, darkgreen, cadetblue, darkpurple, white, pink, lightblue, lightgreen, gray, black, lightgray


🎨 Popups e Tooltips Avançados

import folium

mapa = folium.Map(location=[-27.5969, -48.5495], zoom_start=12)

# HTML formatado
html = """
<div style="font-family: Arial; width: 200px;">
    <h4 style="color: #2E86AB;">Estação 1 - Ingleses</h4>
    <hr>
    <b>Espécie:</b> <i>Ulva lactuca</i><br>
    <b>Profundidade:</b> 5.2m<br>
    <b>Temperatura:</b> 22.5°C<br>
    <b>Salinidade:</b> 35.0 PSU<br>
    <hr>
    <small>Data: 06/01/2025</small>
</div>
"""

folium.Marker(
    [-27.4374, -48.3923],
    popup=folium.Popup(html, max_width=300),
    tooltip="Clique para detalhes"
).add_to(mapa)

mapa.save("mapa_popup_html.html")
import folium
import base64
from io import BytesIO
import matplotlib.pyplot as plt

mapa = folium.Map(location=[-27.5969, -48.5495], zoom_start=12)

# Criar mini-gráfico
fig, ax = plt.subplots(figsize=(4, 2))
temps = [22.5, 23.1, 22.8, 23.4, 22.9]
ax.plot(temps, marker='o', color='#2E86AB')
ax.set_title("Temperatura (últimos 5 dias)")
ax.set_ylabel("°C")
ax.grid(True, alpha=0.3)

# Salvar como PNG em memória
buffer = BytesIO()
plt.savefig(buffer, format='png', bbox_inches='tight', dpi=100)
buffer.seek(0)
image_base64 = base64.b64encode(buffer.read()).decode()
plt.close()

# Criar HTML com imagem
html = f"""
<div style="font-family: Arial;">
    <h4>Estação 1</h4>
    <img src="data:image/png;base64,{image_base64}" width="300">
</div>
"""

folium.Marker(
    [-27.4374, -48.3923],
    popup=folium.Popup(html, max_width=350)
).add_to(mapa)

mapa.save("mapa_popup_grafico.html")

🌐 Adicionar Múltiplos Pontos de GeoDataFrame

Integração com GeoPandas

import folium
import geopandas as gpd
from shapely.geometry import Point

# Criar dados
dados = {
    'nome': ['Ingleses N', 'Ingleses S', 'Barra Lagoa', 'Laguna'],
    'especie': ['Ulva lactuca', 'Gracilaria', 'Sargassum', 'Laminaria'],
    'profundidade_m': [5.2, 7.8, 3.1, 10.5],
    'temperatura_c': [22.5, 23.1, 22.8, 21.2]
}

geometry = [
    Point(-48.3923, -27.4374),
    Point(-48.3950, -27.4450),
    Point(-48.4200, -27.5750),
    Point(-48.7833, -28.4833)
]

gdf = gpd.GeoDataFrame(dados, geometry=geometry, crs="EPSG:4326")

# Criar mapa
mapa = folium.Map(location=[-27.8, -48.5], zoom_start=9)

# Adicionar cada ponto
for idx, row in gdf.iterrows():
    # Popup com informações
    popup_html = f"""
    <b>{row['nome']}</b><br>
    Espécie: <i>{row['especie']}</i><br>
    Profundidade: {row['profundidade_m']}m<br>
    Temperatura: {row['temperatura_c']}°C
    """

    folium.Marker(
        location=[row.geometry.y, row.geometry.x],
        popup=folium.Popup(popup_html, max_width=200),
        tooltip=row['nome'],
        icon=folium.Icon(color='blue', icon='leaf', prefix='fa')
    ).add_to(mapa)

mapa.save("mapa_geodataframe.html")
print("✅ Mapa criado com dados GeoPandas")

🎨 Marcadores com Cores Dinâmicas

Colorir por Categoria

import folium
import geopandas as gpd
from shapely.geometry import Point

# Dados
gdf = gpd.GeoDataFrame({
    'nome': ['Local 1', 'Local 2', 'Local 3', 'Local 4'],
    'especie': ['Ulva', 'Gracilaria', 'Ulva', 'Sargassum'],
    'geometry': [Point(-48.39, -27.44), Point(-48.40, -27.45),
                 Point(-48.42, -27.58), Point(-48.78, -28.48)]
}, crs="EPSG:4326")

# Mapa de cores por espécie
cores_especies = {
    'Ulva': 'green',
    'Gracilaria': 'red',
    'Sargassum': 'blue'
}

mapa = folium.Map(location=[-27.8, -48.5], zoom_start=9)

for idx, row in gdf.iterrows():
    cor = cores_especies.get(row['especie'], 'gray')

    folium.Marker(
        [row.geometry.y, row.geometry.x],
        popup=f"{row['nome']}: {row['especie']}",
        icon=folium.Icon(color=cor, icon='leaf', prefix='fa')
    ).add_to(mapa)

mapa.save("mapa_cores_especies.html")

⭕ CircleMarkers (Círculos)

Tamanho Proporcional a Valor

import folium
import geopandas as gpd
from shapely.geometry import Point

gdf = gpd.GeoDataFrame({
    'nome': ['Est. 1', 'Est. 2', 'Est. 3'],
    'profundidade_m': [5.2, 12.8, 3.1],
    'geometry': [Point(-48.39, -27.44), Point(-48.40, -27.45), Point(-48.42, -27.58)]
}, crs="EPSG:4326")

mapa = folium.Map(location=[-27.5, -48.4], zoom_start=11)

for idx, row in gdf.iterrows():
    # Raio proporcional à profundidade
    raio = row['profundidade_m'] * 2  # Multiplicar para visualizar melhor

    folium.CircleMarker(
        location=[row.geometry.y, row.geometry.x],
        radius=raio,
        popup=f"{row['nome']}<br>Prof: {row['profundidade_m']}m",
        color='blue',
        fill=True,
        fillColor='cyan',
        fillOpacity=0.6
    ).add_to(mapa)

mapa.save("mapa_circle_markers.html")

🗂️ Grupos e Camadas

Controle de Camadas

import folium

mapa = folium.Map(location=[-27.5969, -48.5495], zoom_start=11)

# Criar grupos de camadas
grupo_ulva = folium.FeatureGroup(name='Ulva lactuca')
grupo_gracilaria = folium.FeatureGroup(name='Gracilaria')

# Adicionar marcadores aos grupos
folium.Marker(
    [-27.4374, -48.3923],
    popup="Ulva - Local 1",
    icon=folium.Icon(color='green')
).add_to(grupo_ulva)

folium.Marker(
    [-27.4450, -48.3950],
    popup="Ulva - Local 2",
    icon=folium.Icon(color='green')
).add_to(grupo_ulva)

folium.Marker(
    [-27.5750, -48.4200],
    popup="Gracilaria - Local 1",
    icon=folium.Icon(color='red')
).add_to(grupo_gracilaria)

# Adicionar grupos ao mapa
grupo_ulva.add_to(mapa)
grupo_gracilaria.add_to(mapa)

# Adicionar controle de camadas
folium.LayerControl().add_to(mapa)

mapa.save("mapa_com_camadas.html")

📊 Heatmap (Mapa de Calor)

import folium
from folium.plugins import HeatMap

# Dados: [latitude, longitude, intensidade]
dados_calor = [
    [-27.4374, -48.3923, 0.8],
    [-27.4450, -48.3950, 0.6],
    [-27.5750, -48.4200, 0.9],
    [-27.4400, -48.3930, 0.7],
    [-27.4500, -48.4000, 0.5]
]

mapa = folium.Map(location=[-27.5, -48.4], zoom_start=11)

# Adicionar heatmap
HeatMap(dados_calor, radius=20, blur=25, max_zoom=13).add_to(mapa)

mapa.save("mapa_heatmap.html")

🎯 Exemplo Completo: Dashboard de Coletas

import folium
import geopandas as gpd
import pandas as pd
from shapely.geometry import Point
from folium.plugins import MarkerCluster

# 1. PREPARAR DADOS
dados = {
    'id': range(1, 11),
    'local': ['Ingleses N', 'Ingleses S', 'Barra Lagoa', 'Laguna', 
              'Garopaba', 'Armação', 'Campeche', 'Pântano Sul',
              'Joaquina', 'Mole'],
    'especie': ['Ulva lactuca', 'Gracilaria', 'Sargassum', 'Ulva lactuca',
                'Gracilaria', 'Ulva lactuca', 'Sargassum', 'Laminaria',
                'Ulva lactuca', 'Gracilaria'],
    'profundidade_m': [5.2, 7.8, 3.1, 10.5, 6.0, 4.5, 8.2, 12.0, 5.5, 7.0],
    'temperatura_c': [22.5, 23.1, 22.8, 21.2, 22.0, 23.5, 22.9, 20.5, 23.0, 22.7],
    'longitude': [-48.39, -48.40, -48.42, -48.78, -48.62, -48.51, -48.48, -48.50, -48.44, -48.43],
    'latitude': [-27.44, -27.45, -27.58, -28.48, -28.02, -27.75, -27.67, -27.78, -27.63, -27.58]
}

df = pd.DataFrame(dados)
geometry = [Point(lon, lat) for lon, lat in zip(df['longitude'], df['latitude'])]
gdf = gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326")

# 2. CONFIGURAR MAPA
mapa = folium.Map(
    location=[-27.8, -48.5],
    zoom_start=9,
    tiles='OpenStreetMap'
)

# 3. DEFINIR CORES POR ESPÉCIE
cores_especies = {
    'Ulva lactuca': 'green',
    'Gracilaria': 'red',
    'Sargassum': 'blue',
    'Laminaria': 'purple'
}

# 4. CRIAR GRUPOS DE CAMADAS
grupos = {}
for especie in gdf['especie'].unique():
    grupos[especie] = folium.FeatureGroup(name=especie)

# 5. ADICIONAR MARCADORES
for idx, row in gdf.iterrows():
    # Determinar status de validação
    valida = (20 <= row['temperatura_c'] <= 25) and (row['profundidade_m'] < 10)
    status = "✅ VÁLIDA" if valida else "⚠️ ATENÇÃO"

    # HTML do popup
    popup_html = f"""
    <div style="font-family: Arial; width: 220px;">
        <h4 style="color: #2E86AB; margin-bottom: 8px;">
            📍 {row['local']}
        </h4>
        <hr style="margin: 5px 0;">
        <table style="width: 100%; font-size: 12px;">
            <tr>
                <td><b>ID:</b></td>
                <td>{row['id']}</td>
            </tr>
            <tr>
                <td><b>Espécie:</b></td>
                <td><i>{row['especie']}</i></td>
            </tr>
            <tr>
                <td><b>Profundidade:</b></td>
                <td>{row['profundidade_m']}m</td>
            </tr>
            <tr>
                <td><b>Temperatura:</b></td>
                <td>{row['temperatura_c']}°C</td>
            </tr>
            <tr>
                <td><b>Coordenadas:</b></td>
                <td>{row['latitude']:.4f}°, {row['longitude']:.4f}°</td>
            </tr>
        </table>
        <hr style="margin: 5px 0;">
        <div style="text-align: center; padding: 5px; background-color: {'#d4edda' if valida else '#fff3cd'}; border-radius: 3px;">
            <b>{status}</b>
        </div>
    </div>
    """

    # Criar marcador
    cor = cores_especies.get(row['especie'], 'gray')
    marcador = folium.Marker(
        location=[row['latitude'], row['longitude']],
        popup=folium.Popup(popup_html, max_width=300),
        tooltip=f"{row['local']} - {row['especie']}",
        icon=folium.Icon(color=cor, icon='leaf', prefix='fa')
    )

    # Adicionar ao grupo correspondente
    marcador.add_to(grupos[row['especie']])

# 6. ADICIONAR GRUPOS AO MAPA
for grupo in grupos.values():
    grupo.add_to(mapa)

# 7. ADICIONAR CONTROLE DE CAMADAS
folium.LayerControl(position='topright', collapsed=False).add_to(mapa)

# 8. ADICIONAR TÍTULO
titulo_html = '''
<div style="position: fixed; 
            top: 10px; 
            left: 50px; 
            width: 400px; 
            height: auto; 
            background-color: white; 
            border: 2px solid #2E86AB;
            border-radius: 5px;
            z-index: 9999; 
            padding: 10px;
            font-family: Arial;">
    <h3 style="margin: 0; color: #2E86AB;">
        🌊 Distribuição de Macroalgas - LAFIC/UFSC
    </h3>
    <p style="margin: 5px 0; font-size: 12px;">
        Total de coletas: <b>{}</b> | Espécies: <b>{}</b>
    </p>
</div>
'''.format(len(gdf), gdf['especie'].nunique())

mapa.get_root().html.add_child(folium.Element(titulo_html))

# 9. SALVAR
mapa.save("dashboard_coletas_LAFIC.html")
print("✅ Dashboard criado: dashboard_coletas_LAFIC.html")
print(f"📊 {len(gdf)} coletas mapeadas")
print(f"🌿 {gdf['especie'].nunique()} espécies diferentes")

Execute e abra o arquivo HTML! 🚀


🎓 Checklist desta Lição

  • [ ] Criei mapa básico com Folium
  • [ ] Adicionei marcadores com popups
  • [ ] Integrei GeoPandas com Folium
  • [ ] Usei cores e ícones personalizados
  • [ ] Criei camadas controláveis
  • [ ] Desenvolvi dashboard completo

Se marcou tudo, você completou Análise Geoespacial! 🎉


➡️ Próximos Passos

Parabéns! Você domina análise geoespacial com Python! 🗺️

Próximos módulos: 1. Visualização Web (HTML, JavaScript avançado) 2. Casos Práticos (Aplicações reais em Oceanografia)


📝 Resumo de Funções Folium

Função Uso
folium.Map() Criar mapa base
folium.Marker() Adicionar marcador
folium.CircleMarker() Círculo proporcional
folium.Popup() Popup HTML
folium.FeatureGroup() Grupo de camadas
folium.LayerControl() Controle de camadas
HeatMap() Mapa de calor

Você é um expert em mapas interativos! 🗺️✨

Abra seus mapas HTML no navegador e compartilhe com sua equipe do LAFIC! 🌊