还在担心卫星遥感影像数据下载的问题么?诚心奉献Sentinel哨兵数据高速下载方法(适用于其它卫星数据下载)
The following article is from Satellive Author 善睐-黄伟
点击图片上方蓝色字体“慧天地”即可订阅
(点击图片即可查看详细信息)
卫星遥感影像下载是挡在地学及相关学科科研道路上的拦路虎,在国外可以通过对Google Earth Engine(GEE)中的Landsat,MODIS,哨兵二号卫星(Sentinel-2)等众多卫星影像数据直接进行云上处理获取感兴趣区域结果。它是如此之好,只可惜啊,像我这样的人,囊中羞涩怎会为其支付昂贵费用呢?特别是当领导要结果时,还是需要经过Google Driver box下载到本地的,而这也又是一笔不菲的费用。当然,这里就不点名那些同行们利用这个心理大势宣传GEE如何牛逼的复杂经济背景问题了。做研究嘛,还是要看在哪个国家的。至于道理,大家都懂,在国内下载影像数据,会有成千上万个草泥马从脑袋里面跑出来。不得不说,国家队的XX图发布的高分免费下载服务带来了巨大震撼,但那又如何,像我这样的科研贫民不还是搞不到被他们宣传成极品的数据么?怎么干掉这个拦路虎,怎么不再担心遥感数据下载、质量检查的事情呢?团队成员一直鼓励我探索,我想现在是时候诚心奉献哨兵影像数据高速下载方法了。
欧空局的哨兵数据自然是精品,如果非要杠精地说美国航空航天局出品的Landsat系列卫星数据也很好,那也无可厚非。废话不多说,本文后续包含四个部分。在第一部分,在简要介绍哨兵影像数据的概况之后,以Sentinel-2数据为例,介绍如何通过ESA的API hub查询指定地理空间范围的Sentinel-2影像数据。在第二部分介绍如何通过通过配置aria2下载工具下载查询到的Sentinel-2影像数据,重点介绍python守护进程用于监控和管理aria2工具的重启。第三部分,介绍如何通过python程序更新Aria2的Sentinel-2数据下载清单。最后,一部分是题外话部分,不感兴趣可以不看。(篇幅较长,需多点耐心)
第一部分:哨兵数据介绍及使用API查询的两种方式(含代码)
哨兵一号(Sentinel-1)是由两颗极轨卫星组成的,通过C波段合成孔径雷达成像仪,不受天气影响情况下获取图像。它是欧空局为哥白尼计划开发的五个任务中的第一个卫星计划。
哨兵二号(Sentinel-2)也是由两颗极轨卫星组成的星座,两颗卫星在同一太阳同步轨道上,主要任务是监控陆地地表变化。幅宽是290km²,两颗卫星在无云条件下重访中纬度地区需要2~3天时间。官网上给出的覆盖区域描述:
The Sentinel-2 mission will provide systematic coverage over the following areas:
all continental land surfaces (including inland waters) between latitudes 56° south and 84° north(在北纬56° 和南纬84°之间的地表区域)
all coastal waters up to 20 km from the shore
all islands greater than 100 km²
all EU islands(整个欧洲大陆区域)
the Mediterranean Sea (地中海)
all closed seas (e.g. Caspian Sea).
In addition, the Sentinel-2 observation scenario includes observations following member states or Copernicus Services requests (e.g. Antarctica, Baffin Bay).
图1 Sentinel-2(哨兵二号)卫星的重访周期
(图片来源:https://sentinel.esa.int/web/sentinel/user-guides/sentinel-2-msi/revisit-coverage)
两颗卫星Sentinel-2A和Sentinel-2B在相同视角条件下,每5天重访陆地地表的大部分区域。由于相邻轨道的不同条带会重叠,重访部分区域的周期在不同视角条件下会不相同,但最多不超过10天。
图2 因邻近轨道重叠导致的重访周期差异
(图片来源:https://sentinel.esa.int/web/sentinel/user-guides/sentinel-2-msi/revisit-coverage)
哨兵三号(Sentinel-3)是有欧空局和欧洲气象组织联合运营,提供可操作的海洋和陆地观测服务,主要目标是以高精度和可靠性测量海面地形、海陆表面温度和海陆表面颜色,以支持海洋预报系统、环境监测和气候监测。在哨兵三号卫星上通过海洋和陆地彩色仪器(OLCI)、海洋和陆地表面温度辐射计(SLSTR)、合成孔径雷达高度计(SRAL)、微波辐射计(MWR)和精密定轨(POD)仪器获得图像。
哨兵五号(Sentinel-5)先驱者主要用于进行大气测量,用于空气质量、臭氧和紫外线辐射以及气候监测和预报,该卫星采用推扫式成像,对地球表面的扫描带宽度约为2600公里。除了UV1波段(7x28km²)和SWIR波段(7x7km²),所有光谱波段的典型像素大小(接近最低点)约为7x3.5km²。
欧空局通过OpenHub和的API Hub向外提供数据服务,前面一个提供交互式地图查询数据,后一个通过API服务查询哨兵数据。这里重点介绍后面一个。第一种通过指定的Tile编号查询,直接上完整的python程序,首先用在欧空局官网上注册的账号密码作为参数传递到SentinelAPI函数中。
敲重点:
import argparse
import datetime
import glob
import numpy as np
import os
import re
import time
import sentinelsat
import zipfile
import time
import pdb
import pandas
from sentinelsat import SentinelAPI, read_geojson, geojson_to_wkt
from pandas import DataFrame
import pandas as pd
import hashlib
#用于获取下载哨兵二指定tile或者指定矢量的哨兵影像所需的URL地址、
# md5校验码及长度
#用于检验那些未下载完的链接,
#以获取2019年全年武汉覆盖武汉区域的哨兵二号卫星2A级产品为例,
# 黑色标注的startdate ,enddate ,level ,specify_tiles
# 分别代表了所要查询的数据开始时间、结束时间、影像产品级别、指定的空间范围。(武汉刚好跨越5个tiles)
def getSentinelUrlByTiles():
scihub_api = SentinelAPI('用户名', '密码',
'scihub.copernicus.eu/dh')
startdate = sentinelsat.format_query_date('20190101')
enddate = sentinelsat.format_query_date('20191231')
# Search data, filtering by options.
level = '2A'
# tile = '50RLV'
specify_tiles = ['49RGQ', '49RGP', '50RKU', '50RKV', '50RLV']
# 指定需要下载的tiles
final_urls = DataFrame()
url_list = []
md5_list = []
for tile in specify_tiles:
products = scihub_api.query(beginposition=(startdate, enddate),
platformname='Sentinel-2',
producttype='S2MSI%s' % level,
cloudcoverpercentage=(0, 100), filename='*T%s*' % tile)
products_df = scihub_api.to_dataframe(products)
#使用print(products_df)会看到下面的结果,其中id,md5,url最为重要
# {'id': 'd9c11a08-9548-423a-9d5b-3790bdd1949b',
# 'title': 'S2B_MSIL1C_20180322T031539_N0206_R118_T49SCS_20180322T080731',
# 'size': 557305492,
# 'md5': 'A4DEE331BABF5D81FEEFED7E8F152E1A',
# 'date': datetime.datetime(2018, 3, 22, 3, 15, 39, 27000),
# 'footprint': 'POLYGON((109.12450894645511 32.434247217341294,109.15551946020656 32.55886040012398,109.19500574164893 32.7064511260414,109.23422411890954 32.85418162235375,109.27416742227145 33.00170389409306,109.31402177718599 33.149262433169554,109.35311404383268 33.297123960618194,109.38830946718173 33.42770696377061,110.02964494156487 33.435778798872015,110.04040193865697 32.44547285973725,109.12450894645511 32.434247217341294))', 'url': "scihub.copernicus.eu/dh('d9c11a08-9548-423a-9d5b-3790bdd1949b')/$value", 'Online': False, 'Creation Date': datetime.datetime(2018, 3, 22, 11, 36, 6, 645000), 'Ingestion Date': datetime.datetime(2018, 3, 22, 11, 25, 45, 119000)}
for i in range(len(products_df)):
product = scihub_api.get_product_odata(products_df.iloc[i]['uuid'])
print(product['url'])
url_list.append(product['url'])
print(product['md5'])
md5_list.append((product['md5']))
final_urls['url'] = url_list
final_urls['md5'] = md5_list
final_urls.to_csv("finale_urls_md5.csv", index=False)
# 这里的md5一定要记录下来
# 第二种方式是,通过geojson指定的空间范围对影像数据进行查询,
# 如何获得geojson这个事情就不在这里介绍了。
# 同样得到一个pandas products_df 和之前是样的。
# 最后的记过可以输出到csv文件中。
# footprint = geojson_to_wkt(read_geojson('test.geojson'))
# products = scihub_api.query(footprint,
# beginposition=(startdate, enddate),
# platformname='Sentinel-2')
# convert to Pandas DataFrame
# products_df = scihub_api.to_dataframe(products)
# print("............")
# print(products_df)
# pass
## '#'开头为注释内容, 选项都有相应的注释说明, 根据需要修改 ##
## 被注释的选项填写的是默认值, 建议在需要修改时再取消注释 ##
## 文件保存相关 ##
# 文件的保存路径(可使用绝对路径或相对路径), 默认: 当前启动位置
dir=GPP
input-file=5368574491-download_GPP.txt
# 启用磁盘缓存, 0为禁用缓存, 需1.16以上版本, 默认:16M
disk-cache=32M
# 文件预分配方式, 能有效降低磁盘碎片, 默认:prealloc
# 预分配所需时间: none < falloc < trunc < prealloc
# NTFS建议使用falloc
file-allocation=none
# 断点续传
continue=true
allow-overwrite=false
## 下载连接相关 ##
# 最大同时下载任务数, 运行时可修改, 默认:5
max-concurrent-downloads=10
# 同一服务器连接数, 添加时可指定, 默认:1
max-connection-per-server=5
# 最小文件分片大小, 添加时可指定, 取值范围1M -1024M, 默认:20M
# 假定size=10M, 文件为20MiB 则使用两个来源下载; 文件为15MiB 则使用一个来源下载
min-split-size=10M
# 单个任务最大线程数, 添加时可指定, 默认:5
split=20
# 整体下载速度限制, 运行时可修改, 默认:0
#max-overall-download-limit=0
# 单个任务下载速度限制, 默认:0
#max-download-limit=0
# 整体上传速度限制, 运行时可修改, 默认:0
max-overall-upload-limit=1M
# 单个任务上传速度限制, 默认:0
#max-upload-limit=1000000
# 禁用IPv6, 默认:false
disable-ipv6=false
max-download-result=1000000
max-tries=50
http-user=XXXXX
http-passwd=YYYY
# 从会话文件中读取下载任务
always-resume=true
max-file-not-found=100
# 在Aria2退出时保存`错误/未完成`的下载任务到会话文件
save-session=aria2.session
# 定时保存会话, 0为退出时才保存, 需1.16.1以上版本, 默认:0
#save-session-interval=60
## RPC相关设置 ##
# 启用RPC, 默认:false
enable-rpc=true
# 允许所有来源, 默认:false
rpc-allow-origin-all=true
# 允许非外部访问, 默认:false
rpc-listen-all=true
# 事件轮询方式, 取值:[epoll, kqueue, port, poll, select],
# 不同系统默认值不同
#event-poll=select
# RPC监听端口, 端口被占用时可以修改, 默认:6800
#rpc-listen-port=6800
# 设置的RPC授权令牌, v1.18.4新增功能,
# 取代 --rpc-user 和 --rpc-passwd 选项
#rpc-secret=mivm.cn
# 设置的RPC访问用户名, 此选项新版已废弃, 建议改用 --rpc-secret 选项
#rpc-user=<USER>
# 设置的RPC访问密码, 此选项新版已废弃, 建议改用 --rpc-secret 选项
#rpc-passwd=<PASSWD>
## BT/PT下载相关 ##
# 当下载的是一个种子(以.torrent结尾)时, 自动开始BT任务, 默认:true
follow-torrent=true
# BT监听端口, 当端口被屏蔽时使用, 默认:6881-6999
listen-port=51413
# 单个种子最大连接数, 默认:55
#bt-max-peers=55
# 打开DHT功能, PT需要禁用, 默认:true
enable-dht=true
# 打开IPv6 DHT功能, PT需要禁用
#enable-dht6=false
# DHT网络监听端口, 默认:6881-6999
#dht-listen-port=6881-6999
# 本地节点查找, PT需要禁用, 默认:false
#bt-enable-lpd=true
# 种子交换, PT需要禁用, 默认:true
enable-peer-exchange=true
# 每个种子限速, 对少种的PT很有用, 默认:50K
#bt-request-peer-speed-limit=50K
# 客户端伪装, PT需要
peer-id-prefix=-TR2770-
user-agent=Transmission/2.77
# 当种子的分享率达到这个数时, 自动停止做种, 0为一直做种, 默认:1.0
seed-ratio=0.1
# 强制保存会话, 即使任务已经完成, 默认:false
# 较新的版本开启后会在任务完成后依然保留.aria2文件
#force-save=false
# BT校验相关, 默认:true
#bt-hash-check-seed=true
# 继续之前的BT任务时, 无需再次校验, 默认:false
bt-seed-unverified=true
# 保存磁力链接元数据为种子文件(.torrent文件), 默认:false
#bt-save-metadata=true
以本地电脑的D盘目录为例,说明下载步骤,首先要点击和运行EasyWebSvr.exe这个可执行程序,做一个web服务层。按照之前的配置文件配置好aria2.conf,如图3所示:
图3. aria2文件夹中的有关文件
打开EasyWebSrc.exe的配置界面,如图4所示,设置主目录为当前aria2配置文件所在目录,确定其已打开。
图4.EasyWebSrc.exe的配置界面
之前提到过,通过api hub访问哨兵数据集会有一些约束:The API Hub is managed with the same quota restrictions, ie. a limit of two parallel downloads per user. The site is publishing precisely the same data content as the Scientific Data Hub (both Sentinel-1 and Sentinel-2). (也就是一个用户最大的并行下载速度是有限制的,而且还有约束条件,例如经常用一个机器一个账户下载,机器的IP会被锁定,账号会受到限制)
aria2虽然下载速度挺快,但是在尝试多次下载失败后,不会重新下载对应的链接了。因此,我在这里使用一个downloadermanagy.py的文件,这个文件在图3所示的目录中有显示。具体给出python代码,这个代码通过子进程模块,调用aria2c.exe这个可执行文件,从子进程的标准输出中查看下载数据的打印信息,如果发现最后一条打印信息一直不发生变化,则调用terminate()函数终止该子进程,然后重新执行循环。(需要注意的是python没有goto语句,可以安装一个with-go模块实现循环跳转)
#!/usr/bin/env python
#coding : utf-8
import subprocess
import hashlib
import os,time
import sys
from subprocess import Popen, PIPE
import time
from goto import with_goto
def getMd5OfFile(fname):
if not os.path.exists(fname):
return None
try:
f = file(fname, 'rb')
md5 = hashlib.md5()
while True:
# 每次读16K
d = f.read(16384)
if not d:
break
md5.update(d)
f.close()
return md5.hexdigest()
except Exception :
return None
@with_goto
def start_download():
label .begin
input_file = "user2.txt"
image_count = len(open(input_file, 'rU').readlines())
last_line = ""
sleep_total_second = 60
#print(image_count)
with Popen("D:\\aria2\\aria2c.exe --conf-path=aria2.conf",
shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
bufsize=20000, universal_newlines=True) as p:
for line in p.stdout:
#如果最后一行一直没有发生变化,则向原来的进程发中止信号,并退出
#print(line)
#print(len(line))
if (line == '\n' and last_line==""):
continue
else:
if last_line == "":
last_line = line
continue
else:
#line = line.strip()
if last_line == line:
sleep_total_second = sleep_total_second - 1
time.sleep(1)
if sleep_total_second == 0:
p.terminate()
break
else:
if line == '\n':
continue
else:
print(line)
last_line = line
continue
goto .begin
if __name__ == "__main__":
#cmd = 'cmd.exe D:\\aria2\\Start.bat'
#首先看需要下载多少个文件
start_download()
图5 aria2数据下载界面
经过这样的折腾,aira2下载工具就能快速下载那些失败的链接了。ESA会定期维护数据集下载服务器,因此,链接暂时失效是难以避免的。我们认为,这个小程序会比较好的解决这个问题。
第三部分:更新aria2下载工具的下载清单。
aria2的失效链接自动重启配置好后,另一个比较重要的问题是,它内部循环迭代查询是否还有未下载的影像。这就使得一个包含一万个链接的文件,总会从开头一直扫描到结尾,比较浪费时间。因此,继续用python程序进行判断,将已经完成的哨兵数据链接从未完成的哨兵数据链接中剔除,代码如下:
import argparse
import datetime
import glob
import numpy as np
import os
import re
import time
import sentinelsat
import zipfile
import time
import pdb
import pandas
from sentinelsat import SentinelAPI, read_geojson, geojson_to_wkt
from pandas import DataFrame
import pandas as pd
import hashlib
#printPath功能是打印所有当前目录下的文件名
def printPath(level, path):
allFileNum = 0
dirList = []
fileList = []
files = os.listdir(path)
dirList.append(str(level))
for f in files:
if (os.path.isdir(path + '/' + f)):
if (f[0] == '.'):
pass
else:
dirList.append(f)
if (os.path.isfile(path + '/' + f)):
fileList.append(f)
i_dl = 0
for dl in dirList:
if (i_dl == 0):
i_dl = i_dl + 1
else:
printPath((int(dirList[0]) + 1), path + '/' + dl)
final_list=[]
for fl in fileList:
allFileNum = allFileNum + 1
final_list.append(os.path.join(path, fl))
return final_list
#checkwhichURL_not_done接受两个参数,第一个参数是通过上一个函数获得
#的影像文件名
#第二个参数是包含ur和mdf信息的csv文件,
#之前第一个python代码就是获得了这两个有效信息,
#使用md5_hash = hashlib.md5()进行计算文件的md5值,
#然后设置要对比的链接下载文件csv中是否有该md5信息,
#如果有则将dataframe的flag例置为1,
#最后打印出来的url文件就是未下载完的哨兵影像链接,
#继续按照第二和第三部分的说明进行下载即可。
#
def checkwhichURL_not_done(tmp_filelist, urls_list_df):
not_download_url=[] #最后未被下载的url
df = urls_list_df.copy()
for file in tmp_filelist:
md5_hash = hashlib.md5()
with open(file, "rb") as f:
# Read and update hash in chunks of 4K
for byte_block in iter(lambda: f.read(40960), b""):
md5_hash.update(byte_block)
md5=str.upper(md5_hash.hexdigest())
#print(".................")
#print(md5)
mask = (df['md5'] == md5)
df.loc[mask, 'flag'] = 1
df = df.copy()
df=df.fillna(0).copy()
#最后找到所有那些flag=0.0的
mask = (df['flag'] =="0.0")
df_extra = df.loc[mask]
df_extra = df.copy()
print(df_extra['url'])
pass
if __name__ == "__main__":
strPath=r"D:\test_MODIS"
tmp_filelist=printPath(1,r"D:\test_MODIS")
#print(tmp_filelist)
urls_list_df = pd.read_csv("finale_urls_md5.csv")
checkwhichURL_not_done(tmp_filelist,urls_list_df )
pass
在详细介绍了如何快速下载哨兵卫星数据之后,我们聊点别的。实际上对遥感数据的获取和查询一直是个火热的话题。例如,当我们想研究全球湖泊水库的面积变化时,我想这种难度是极其高的,主要原因是,数据的下载会浪费我们足够多的时间。另外一方面,小范围小区域的遥感影像数据查询下载其实更加重要。例如,我们只需要武汉东湖的卫星遥感影像数据,结果不得不下载整个整个武汉区域甚至更大范围的数据。也许对于GIS和遥感专业的人来说,遥感数据本身就是大范围的,但站在普通用户的角度,有可能我们需要的仅仅是某块小试验田的单像素长序列数据。这样的话,本身的那种大范围的思想就不一样了。可以很明确的告诉大家,时至完稿日我大天朝尚无一个卫星遥感机构考虑到这个问题。为了解决这个问题,我们团队研发一套卫星遥感数据服务平台,致力于解决遥感数据下载难使用难的问题。我们的目标是:让小范围遥感影像数据下载如泉涌,让小区域遥感影像数据交易成主流。诚挚希望大家继续关注我们善睐(Satellive)团队。最后,愿大家收获数据下载技能的同时帮忙转载此文。与此同时,我们呼吁大家都能把下载的数据分享在善睐(Satellive)卫星遥感数据服务平台上,本平台预计2020年5月4日青年节上线,敬请关注。我们的愿望是让其他科研用户不再痛苦地等待数据下载。在转载前,记得联系我们关注本公众号(Satellive)并在后台留言或邮件至huangwei0316@hust.edu.cn。我们保留对所转载图文内容和代码的所有权利。谢谢大家!
内容转载、商务活动、投稿等合作请联系
微信号:huitiandi321
邮箱:geomaticshtd@163.com
欢迎关注慧天地同名新浪微博:
ID:慧天地_geomaticser
将卫星遥感图像分析拓展至商业领域,以色列卫星数据AI分析公司Oneview获数百万美元融资
【蝗虫监测】“珠海一号”遥感卫星监测巴基斯坦蝗灾区域
《慧天地》敬告
《慧天地》公众号聚焦国内外时空信息科技前沿、行业发展动态、跨界融合趋势,探索企业核心竞争力,传播测绘地理信息文化,为测绘、地信、遥感等相关专业的同学提供日常学习、考研就业一站式服务,旨在打造政产学研用精准对接的平台。《慧天地》高度重视版权,对于原创、委托发布的稿件,会烦请作者、委托方亲自审核通过后才正式推发;对于来自网站、期刊、书籍、微博、微信公众号等媒介的稿件,会在作者栏或者文章开头显著标明出处,以表达对作者和推文引用平台版权的充分尊重和感谢;对于来源于网络作者不明的优质作品,转载时如出现侵权,请后台留言,我们会及时删除。感谢大家一直以来对《慧天地》的关注和支持!