Download simultâneo de várias imagens de SAR (como Sentinel-1) via Python
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:
- O que é o Alaska Satellite Facility (ASF, para quem não conhece)?
- Acessando dados do ASF, pelo portal Vertex ou pelo pacote asf_search (no Python);
- 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_hullaoi.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