Download simultâneo de várias imagens de SAR (como Sentinel-1) via Python

Erli Pinto dos Santos
5 min readJul 14, 2022

Esse tipo de texto (ou quem sabe tutorial) já existe na lingua inglesa, portanto, o meu objetivo é compartilhar, em lingua portuguesa, com colegas uma funcionalidade que pode otimizar o trabalho com Sensoriamento Remoto. Segue um resumo do que você lerá aqui:

  1. O que é o Alaska Satellite Facility (ASF, para quem não conhece)?
  2. Acessando dados do ASF, pelo portal Vertex ou pelo pacote asf_search (no Python);
  3. Um breve exemplo de como usar as ferramentas do pacote asf_search: exemplo voltado à busca geográfica de imagens (usando polígonos e pontos amostrais).

Todo cientista — ou aspirante a cientista, ou analista, etc. — de sensoriamento remoto que trabalha com (ou tem interesse em) dados de sensores de micro-ondas, sabe que um dos maiores repositórios desses dados é o Alaska Satelite Facility (carinhosamente nominalizado daqui em diante de ASF). O ASF é uma instalação de processamento de dados e estação de rastreamento de satélites e é parte do Geophysical Institute da University of Alaska Fairbanks. O objetivo deles é tornar possível o acesso a dados de sensoriamento remoto, e essa é uma definição importante para nós nesse pequeno texto.

O acesso aos dados do repositório é feito através do Vertex, um portal o qual se você não conhece pode consultar aqui: https://search.asf.alaska.edu/. Mas recentemente eles desenvolveram (no final de 2021) um pacote implementado na linguagem de programação Python, para que o usuário dessa linguagem possa buscar por imagens de forma criativa e ainda mais automatizada. O pacote se chama asf_search.

Sobre o asf_search

O asf_search é um módulo do Python para fazer buscas no catálogo da ASF. Além da busca, ele oferece funcionalidades para fazer diretamente o download de um único produto ou vários produtos (em lote). O pacote está disponível via PyPi e repositório do Anaconda (Conda).

A partir de agora eu vou mostrar um estudo de caso de uma necessidade minha, a qual eu aprimorei automatizando via Python (e Spyder). No entanto se você quiser, e eu aconselho que queira, mais detalhes sobre os mecanismos de busca e download e das funcionalidades do pacote, sugiro que navegue pelos links:

Da própria ASF (ASF SAR Data Search Manual): https://docs.asf.alaska.edu/asf_search/basics/

E do pacote depositado no GitHub: https://github.com/asfadmin/Discovery-asf_search

Dito isso vou te mostrar como ele pode ser usado!

Primeiro de tudo, instale o asf_search. Você pode fazer isso via pip (o que eu, particularmente, prefiro) ou usando o conda:

pip install asf_search
# ou
conda install -c conda-forge asf_search

Feito isso, preciso resumir para você o que pretendo fazer, assim você foca no que te interessar mais:

Dito isso carregue os pacotes necessários para lidar com os objetos:

# Para manipular dados:
import pandas as pd
# Para visualizar dados:
import matplotlib.pyplot as plt
# Para busca e download das imagens no Alaska Satellite Facility API:
import asf_search as asf
# Para lidar com dados geoespaciais:
import geopandas as gpd
from shapely.geometry import MultiPoint, shape

Daí eu fiz a importação da planilha contendo os pontos amostrais do meu interesse:

df = pd.read_excel(io = "C:/Users/erlis/Documents/arquivo.xlsx")
df['month'] = df['DATA'].dt.month
# E filtrei as linhas do data frame que me interessavam:
SOC_BW_July = df[df.month == 7]

Em seguida transformei, usando o geopandas, o data frame num GeoDataFrame. Com funções do geopandas, também calculei um polígono envolvente aos meus pontos. A busca geográfica no asf_search deve ser feita com um polígono da Área de Interesse (aoi).

SOC_BW_July = gpd.GeoDataFrame(
SOC_BW_July,
geometry = gpd.points_from_xy(SOC_BW_July.LON, SOC_BW_July.LAT)
)
aoi = gpd.GeoSeries(MultiPoint(SOC_BW_July['geometry']))
aoi = aoi.convex_hull
aoi.plot()
SOC_BW_July.plot()
plt.show()

Esses sãos os pontos no espaço:

E esse é o polígono que o asf_search usará para procurar pelas imagens:

Agora podemos fazer a busca!

Repare no chunk abaixo que criei um objeto com as opções da busca: nele informo que a plataforma deve ser o Sentinel-1; as datas de início e fim são os dias 01 de julho e 01 de agosto de 2017; a órbita relativa do Sentinel-1 são os caminhos 126 e 24 (isso eu já conhecia previamente para essa minha área); e por fim, eu queria produtos GRD (Ground Range Detected) do Sentinel-1.

opts = {
'platform': asf.PLATFORM.SENTINEL1,
'start': '2017-07-01T00:00:00Z',
'end': '2017-08-01T23:59:59Z',
'relativeOrbit': [126, 24],
'processingLevel': 'GRD_HD' #'SLC'
}

Na sequência, uso o objeto anterior com a função geo_search() do asf_search para buscar. Nessa etapa, o polígono referido deve ser em formato de WKT (Well-Known-Text)

results = asf.geo_search(intersectsWith = str(aoi[0]), **opts)
print(f'{len(results)} results found')

Para essa busca 4 resultados foram encontrados. Mas eu quis visualizar o footprint das imagens encontradas sobre os pontos amostrais. Por isso fiz a rotina:

# Visualizando o footprint das imagensresults_index = 3footprint = gpd.GeoSeries(shape(results[results_index].geometry))f, ax = plt.subplots(1)
gpd.plotting.plot_series(footprint, ax = ax)
for points in SOC_BW_July['geometry']:
gpd.plotting.plot_point_collection(
ax, SOC_BW_July['geometry'],
facecolor = "darkblue", alpha = 0.3)
#print(f'Granule search example: {results}')
print(f'ASFSearchResults serializes to geojson: {results[results_index]}')

Com esse rotina o Python plota para mim o footprint e os pontos amostrais, além de exibir detalhes do produto:

Estes são os dados da imagem selecionada:

ASFSearchResults serializes to geojson: {
"geometry": {
"coordinates": [
[
[
-44.164528,
-13.164262
],
[
-46.465927,
-12.635384
],
[
-46.10643,
-11.133487
],
[
-43.816441,
-11.656668
],
[
-44.164528,
-13.164262
]
]
],
"type": "Polygon"
},
"properties": {
"beamModeType": "IW",
"browse": [
"https://datapool.asf.alaska.edu/BROWSE/SA/S1A_IW_GRDH_1SDV_20170706T083547_20170706T083612_017348_01CF83_B8D5.jpg"
],
"bytes": "997703549",
"centerLat": "-12.15",
"centerLon": "-45.1386",
"faradayRotation": null,
"fileID": "S1A_IW_GRDH_1SDV_20170706T083547_20170706T083612_017348_01CF83_B8D5-GRD_HD",
"fileName": "S1A_IW_GRDH_1SDV_20170706T083547_20170706T083612_017348_01CF83_B8D5.zip",
"flightDirection": "DESCENDING",
"frameNumber": "632",
"granuleType": "SENTINEL_1A_FRAME",
"groupID": "S1A_IWDV_0632_0637_017348_126",
"insarStackId": null,
"md5sum": "5d7158ddf208fa1ccbcc0915ef0d068e",
"offNadirAngle": null,
"orbit": "17348",
"pathNumber": "126",
"perpendicularBaseline": null,
"platform": "Sentinel-1A",
"pointingAngle": null,
"polarization": "VV+VH",
"processingDate": "2017-07-06T08:35:47.000000",
"processingLevel": "GRD_HD",
"sceneName": "S1A_IW_GRDH_1SDV_20170706T083547_20170706T083612_017348_01CF83_B8D5",
"sensor": "C-SAR",
"startTime": "2017-07-06T08:35:47.000000",
"stopTime": "2017-07-06T08:36:12.000000",
"temporalBaseline": null,
"url": "https://datapool.asf.alaska.edu/GRD_HD/SA/S1A_IW_GRDH_1SDV_20170706T083547_20170706T083612_017348_01CF83_B8D5.zip"
},
"type": "Feature"
}

Explorados os resultados das imagens? Vamos baixá-las!

No exemplo anterior eu escolhi duas imagens, das quatro encontradas. Para fazer o download você precisa ter conta no Vertex, por isso, se ainda não tem volta no link (lá em cima) e cria o seu!

Log in ASF Data Vertex para baixar as imagens de interessesession = asf.ASFSession()
try:
user_pass_session = asf.ASFSession().auth_with_creds(
"Insira aqui o seu nome de login",
"Insira a sua senha aqui")
except asf.ASFAuthenticationError as e:
print(f'Autenticacao falhou: {e}')
else:
print('Autenticacao bem sucedida!')

Tendo sucesso no log in, podemos seguir para o download em lote usando a função download. Repare no chunk abaixo que eu irei realizar o download de duas imagens, e por isso eu preciso configurar o argumento processes da função download em 2 processos a serem realizados paralelamente:

from os import listdirresults[2:3].download(path = "C:/Users/erlis/Documents",
session = user_pass_session,
processes = 2)
listdir("C:/Users/erlis/Documents")

E voilà, cá estão as imagens baixadas na minha pasta:

['.ipynb_checkpoints',
'.spyproject',
'S1A_IW_GRDH_1SDV_20170706T083547_20170706T083612_017348_01CF83_B8D5.zip',
'S1A_IW_GRDH_1SDV_20170706T083612_20170706T083637_017348_01CF83_0966.zip',

'S1A_IW_SLC__1SDV_20170706T083546_20170706T083613_017348_01CF83_245F.zip',
'Sentinel1_Imagery_Download.py',
'Sentinel1_JulyImages_Download.py',
'SMAP_L1A_RADAR_39762_D_20220712T081450_R18290_001.h5',
'Untitled.ipynb']

Sinta-se à vontade para comentar, criticar, tirar dúvidas e conversar a respeito. O meu contato é: erlipinto@gmail.com

--

--