Curso

Neste tutorial, você vai conhecer dois pacotes populares para trabalhar com dados geoespaciais: geopandas e Shapely. Em seguida, vai aplicar esses pacotes para ler os dados geoespaciais usando Python e plotar a trilha do furacão Florence de 30 de agosto a 18 de setembro.
O que são dados geoespaciais?
Dados espaciais, dados geoespaciais, dados GIS ou geodados são nomes para dados numéricos que identificam a localização geográfica de um objeto físico, como um prédio, uma rua, um bairro, uma cidade, um país etc., de acordo com um sistema de coordenadas geográficas. A partir dos dados espaciais, você pode descobrir não só a localização, mas também o comprimento, o tamanho, a área ou a forma de qualquer objeto. Um exemplo de dado espacial que você pode obter são as coordenadas de um objeto, como latitude, longitude e altitude. Sistemas de Informações Geográficas (GIS) ou outros softwares especializados podem ser usados para acessar, visualizar, manipular e analisar dados geoespaciais.
Por que dados geoespaciais?
Dados geoespaciais têm inúmeras aplicações no nosso dia a dia. Um exemplo é o app de mapas que você usa para ir de um lugar a outro. Outro, que você provavelmente vê todos os dias, é no canal de previsão do tempo.

Sim, dados geoespaciais representam a posição de algo em relação ao que está ao seu redor: sua casa no mapa da cidade, o furacão no mapa-múndi e por aí vai. Então, neste tutorial, vamos olhar para o destrutivo furacão Florence e acompanhar sua localização.
Requisitos de pacotes
Não pule esta parte. Ela é importante. Se você não tem certeza se atendeu aos requisitos, confira de novo. Primeiro de tudo, você vai precisar ter todos os pacotes abaixo instalados.
- Pandas: fornece estruturas de dados e ferramentas de análise
- Numpy: pacote fundamental para computação científica com Python
- SciPy: (pronuncia-se “Sai Pai”) é um ecossistema em Python de software open source para matemática, ciência e engenharia
- RTree: wrapper em ctypes do libspatialindex que oferece diversos recursos avançados de indexação espacial
- GDAL: biblioteca tradutora para formatos de dados geoespaciais raster e vetoriais
- Fiona: leitura e escrita de arquivos de dados espaciais
- Shapely: objetos geométricos, predicados e operações
- GeoPandas: estende os tipos de dados do pandas para permitir operações espaciais em tipos geométricos
- PySAL: biblioteca de funções de análise espacial em Python para apoiar o desenvolvimento de aplicações de nível superior
- Matplotlib: biblioteca de plotagem 2D em Python
- Missingno: módulo de visualização de dados ausentes para Python
Instale todos os pacotes seguindo a ordem acima para garantir que tudo funcione. Alguns pacotes são pré-requisitos de outros. Por exemplo: para instalar o GeoPandas, é necessário ter o Shapely, e para instalar o Shapely, RTree, GDAL e Fiona devem estar instalados. A forma mais simples de instalar um pacote é: pip install PACKAGE_NAME
Se você usa Windows e não conseguir instalar desse jeito, confira este site para baixar os pacotes e rode pip install PATH_TO_PACKAGE.
Sobre os dados
Como você deve saber, o assustador furacão Florence passou por parte da Costa Leste dos Estados Unidos, deixando um prejuízo estimado de 17 bilhões de dólares. Este tutorial vai ajudar você a descobrir de onde ele veio, quando e onde ficou mais forte e a entender melhor esse desastre natural, analisando tudo em Python. Existem muitos sites com informações sobre esse furacão. Por exemplo, este site oferece dados de várias tempestades e furacões nos EUA de 1902 a 2018 — tem muita coisa para você explorar depois. Para este tutorial, vamos usar apenas os dados do furacão Florence
Você também vai usar os dados geoespaciais do mapa dos EUA disponíveis na internet. O post do Eric Celeste reúne vários arquivos de limites de condados e estados dos EUA. O dado usado aqui é o arquivo US States, 5m, GeoJSON.
# Load all importance packages
import geopandas
import numpy as np
import pandas as pd
from shapely.geometry import Point
import missingno as msn
import seaborn as sns
import matplotlib.pyplot as plt
% matplotlib inline
Primeiro, vamos olhar para o primeiro geodataframe: Geodados dos estados dos EUA
# Getting to know GEOJSON file:
country = geopandas.read_file("data/gz_2010_us_040_00_5m.json")
country.head()

Verificando o tipo do dataframe que você acabou de carregar, dá para ver que é um GeoDataFrame, que tem todas as características normais de um DataFrame do Pandas.
type(country)
geopandas.geodataframe.GeoDataFrame
Checando o tipo da coluna que contém as coordenadas: é uma GeoSeries.
type(country.geometry)
geopandas.geoseries.GeoSeries
Cada valor na GeoSeries é um objeto Shapely. Pode ser:
- Point
- Line
- Polygon
- MultiPolygon
Cada objeto pode representar um tipo diferente de entidade física, por exemplo: Point para prédio, Line para rua, Polygon para cidade e MultiPolygon para país com várias áreas. Para saber mais sobre cada objeto geométrico, leia este artigo: https://shapely.readthedocs.io/en/stable/manual.html#geometric-objects
type(country.geometry[0])
shapely.geometry.multipolygon.MultiPolygon
Assim como um DataFrame do Pandas, um GeoDataFrame também tem o atributo plot, que usa a geometria dentro do dataframe para desenhar um mapa:
country.plot()
<matplotlib.axes._subplots.AxesSubplot at 0x1cfe68c1358>

Como dá para ver, o mapa dos EUA fica relativamente pequeno no quadro. Isso acontece porque as informações incluem Alasca, Havaí e Porto Rico, que ficam afastados. Para este tutorial, você pode excluir Alasca e Havaí, já que o furacão não passou perto desses estados. Você também pode ajustar o tamanho da figura e a cor para personalizar seu gráfico:
# Exclude Alaska and Hawaii for now
country[country['NAME'].isin(['Alaska','Hawaii']) == False].plot(figsize=(30,20), color='#3B3C6E');

Tranquilo, né? Agora que você tem o mapa dos EUA, vamos carregar os dados do furacão:
florence = pd.read_csv('data/florence.csv')
florence.head()

Análise exploratória de dados
Este é sempre o primeiro passo ao carregar qualquer dataset:
- Verificar informações e tipos de dados
- Procurar valores ausentes
- Estatísticas descritivas
florence.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 105 entries, 0 to 104
Data columns (total 11 columns):
AdvisoryNumber 105 non-null object
Date 105 non-null object
Lat 105 non-null float64
Long 105 non-null float64
Wind 105 non-null int64
Pres 105 non-null int64
Movement 105 non-null object
Type 105 non-null object
Name 105 non-null object
Received 105 non-null object
Forecaster 104 non-null object
dtypes: float64(2), int64(2), object(7)
memory usage: 9.1+ KB
Conferindo valores ausentes com o pacote missingno. Ele é útil para visualizar dados faltantes. Como você vê abaixo, há apenas um valor ausente na coluna "Forecaster", que não será usada aqui. Podemos ignorar por enquanto.
# Notice you can always adjust the color of the visualization
msn.bar(florence, color='darkolivegreen');

Dê uma olhada nas estatísticas descritivas — algumas são bem úteis, como velocidade média dos ventos, máximas e mínimas deste furacão, etc.
# Statistical information
florence.describe()
| Lat | Long | Wind | Pres | |
|---|---|---|---|---|
| count | 105.000000 | 105.000000 | 105.000000 | 105.000000 |
| mean | 25.931429 | 56.938095 | 74.428571 | 981.571429 |
| std | 7.975917 | 20.878865 | 36.560765 | 22.780667 |
| min | 12.900000 | 18.400000 | 25.000000 | 939.000000 |
| 25% | 18.900000 | 41.000000 | 40.000000 | 956.000000 |
| 50% | 25.100000 | 60.000000 | 70.000000 | 989.000000 |
| 75% | 33.600000 | 76.400000 | 105.000000 | 1002.000000 |
| max | 42.600000 | 82.900000 | 140.000000 | 1008.000000 |
Na maioria dos casos, você vai limpar os dados e manter só o que precisa. Aqui, você só precisa do horário, das coordenadas (latitude e longitude), da velocidade do vento, da pressão e do nome. Movement e Type são opcionais; o restante pode ser descartado.
# dropping all unused features:
florence = florence.drop(['AdvisoryNumber', 'Forecaster', 'Received'], axis=1)
florence.head()

Normalmente, se você plota os dados sozinhos, não precisa se preocupar com sinal das coordenadas. Mas, se quiser que pareça com o mapa, é importante checar longitude e latitude. Aqui a longitude é oeste, então precisamos adicionar "-" na frente do número para plotar corretamente:
# Add "-" in front of the number to correctly plot the data:
florence['Long'] = 0 - florence['Long']
florence.head()

Depois, você pode combinar latitude e longitude para criar as coordenadas do furacão, que serão transformadas em GeoPoint para visualização.
# Combining Lattitude and Longitude to create hurricane coordinates:
florence['coordinates'] = florence[['Long', 'Lat']].values.tolist()
florence.head()

# Change the coordinates to a geoPoint
florence['coordinates'] = florence['coordinates'].apply(Point)
florence.head()

Verificando o tipo do dataframe florence e da coluna coordinates dos dados de florence. São, respectivamente, um DataFrame do pandas e uma Series do pandas.
type(florence)
pandas.core.frame.DataFrame
type(florence['coordinates'])
pandas.core.series.Series
Após converter os dados em geoespaciais, vamos checar novamente o tipo do dataframe florence e da coluna coordinates. Agora são GeoDataFrame e GeoSeries.
# Convert the count df to geodf
florence = geopandas.GeoDataFrame(florence, geometry='coordinates')
florence.head()

type(florence)
geopandas.geodataframe.GeoDataFrame
type(florence['coordinates'])
geopandas.geoseries.GeoSeries
Perceba que, mesmo sendo agora um GeoDataFrame e uma GeoSeries, eles ainda se comportam como um DataFrame e uma Series normais. Ou seja, você pode filtrar, fazer groupby ou extrair valores mínimos, máximos ou médios da coluna.
# Filtering from before the hurricane was named.
florence[florence['Name']=='Six']

# Groupping by name to see how many names it has in the data set:
florence.groupby('Name').Type.count()
Name
FLORENCE 6
Florence 85
SIX 4
Six 10
Name: Type, dtype: int64
Calculando a velocidade média dos ventos do furacão Florence:
print("Mean wind speed of Hurricane Florence is {} mph and it can go up to {} mph maximum".format(round(florence.Wind.mean(),4),
florence.Wind.max()))
Mean wind speed of Hurricane Florence is 74.4286 mph and it can go up to 140 mph maximum
Ou seja, a velocidade média do vento do furacão Florence é de 74,43 milhas por hora (119,78 km/h) e a máxima é de 140 milhas por hora (225,308 km/h). Para ter ideia do quão assustador é, a Escala de Beaufort, desenvolvida pela Marinha Real do Reino Unido, mostra os efeitos do vento na água e em terra. Com velocidades de 48 a 55 milhas por hora, já é possível quebrar e arrancar árvores e causar "danos estruturais consideráveis".

Você não vai querer estar lá nessa hora.
Visualização
Assim como no DataFrame do pandas, um GeoDataFrame também tem o atributo .plot. A diferença é que esse atributo usa as coordenadas do GeoDataFrame para mapear os pontos. Vamos ver:
florence.plot(figsize=(20,10));

O que aconteceu? Só dá para ver um monte de pontos sem referência. Tem algo errado?
Não, está tudo certo. Como esse dataframe tem apenas as coordenadas (localização) do furacão Florence em cada instante, só conseguimos plotar as posições em um “mapa” em branco.
Então, o próximo passo é plotar a posição do furacão sobre o mapa dos EUA para ver onde ele atingiu e quão forte estava em cada momento. Para isso, você vai usar as coordenadas do mapa dos EUA (os dados carregados no começo) como base e desenhar por cima as posições do Florence.
# Plotting to see the hurricane overlay the US map:
fig, ax = plt.subplots(1, figsize=(30,20))
base = country[country['NAME'].isin(['Alaska','Hawaii']) == False].plot(ax=ax, color='#3B3C6E')
# plotting the hurricane position on top with red color to stand out:
florence.plot(ax=base, color='darkred', marker="*", markersize=10);

Ficou ótimo! Agora vamos finalizar com mais detalhes, como:
- Adicionar título
- Colorir a posição do furacão pela velocidade do vento para ver a intensidade em cada cidade
- Remover os eixos
- Adicionar legenda
- Salvar o resultado em uma imagem para usar depois
fig, ax = plt.subplots(1, figsize=(20,20))
base = country[country['NAME'].isin(['Alaska','Hawaii']) == False].plot(ax=ax, color='#3B3C6E')
florence.plot(ax=base, column='Wind', marker="<", markersize=10, cmap='cool', label="Wind speed(mph)")
_ = ax.axis('off')
plt.legend()
ax.set_title("Hurricane Florence in US Map", fontsize=25)
plt.savefig('Hurricane_footage.png',bbox_inches='tight');

Perceba que o furacão ficou mais forte quando estava no mar, perto da costa leste. Ao se aproximar do continente, começou a perder força, mas com ventos entre 60 e 77 milhas por hora ainda é capaz de causar estragos enormes.
Conclusão
Mandou bem! Você aprendeu os passos essenciais para trabalhar com dados geoespaciais em Python. Também viu como plotar esses dados e personalizar forma, cor e sobreposição dos gráficos para contar uma história. Se quiser praticar, há uma infinidade de dados geoespaciais online para você explorar. Um exemplo é o site de clima que citei acima.
Para se aprofundar, dê uma olhada no livro: “Python Geospatial Development Essentials”, de Karim Bahgat (2015).
Se quiser falar comigo, me manda um e-mail em dqvu.ubc@gmail.com ou me adiciona no LinkedIn. Bons estudos e se cuide!
Se você quiser aprender mais sobre dados geoespaciais em Python, faça o curso Visualizing Geospatial Data in Python da DataCamp.
Abaixo estão os dados usados neste tutorial:

