目录
一、前言
最近由于工作需要,需要下载部分landsat数据的元数据,老板大手一挥,给了十几年的landsat的path、row以及日期等,就算交待完了。于是我就开始准备吭哧吭哧到USGS官网上去一个个找。程序员应该是世界上最懒的人,懒到哪怕只有几百个也不愿意一个个手动弄,于是在官网上翻腾半天,终于找到这么一个页面(https://landsat.usgs.gov/landsat-bulk-metadata-service),能够给定范围批量下载元数据,这是个好东西,基本几下就解决问题了。懒劲又升级了,这么一批一批的下下来我不是还要手工整理,于是就想写个程序自动下载然后筛选出我想要的部分。
二、landsat数据情况简介
我相信接触过landsat的人这块应该都很清楚了,百度百科介绍如下:
美国NASA的陆地卫星(Landsat)计划(1975年前称为地球资源技术卫星 — ERTS ),从1972年7月23日以来, 已发射8颗(第6颗发射失败)。目前Landsat1—4均相继失效,Landsat 5仍在超期运行(从1984年3月1日发射至今)。 Landsat 7于1999年4月15日发射升空。Landsat8[1] 于2013年2月11日发射升空,经过100天测试运行后开始获取影像。
具体信息可以自行查询,landsat的数据都是以带号进行命名的,信息包含path、row以及数据日期等。我们就要根据这些来实现批量下载landsat元数据。
三、下载元数据
3.1 分析下载元数据页面
仔细分析上述usgs官网中的下载元数据页面,不难发现其是将行列号一定范围内以及日期一定范围内的元数据打包成一个文件发送到前台。并且发送的是一个GET请求,请求格式如下:
https://earthexplorer.usgs.gov/EE/InventoryStream/pathrow?start_path=131&end_path=140&start_row=35&end_row=38&sensor=LANDSAT_TM_C1&start_date=2011-06-01&end_date=2011-11-01&format=CSV
参数名称也都通俗易懂,于是一切就豁然开朗了,我只需要写个程序根据需求拼接出此url,然后发送请求,就能得到结果。一切就是这么easy,不过拿到结果后你会发现事情稍微复杂一点,因为请求的区域及时间等都是范围,这就导致结果中有很多不是我们需要的,于是再完善程序循环遍历与我们输入数据的行列号逐一匹配,取出需要的结果即可。
3.2 程序实现
程序整体界面如图所示:
程序比较简单,只需要提供一个下载范围文件(csv格式),每行一个,选择下载的landsat的dataset即可。
程序读取用户给定的范围自动算出行列号的范围以及日期范围,代码如下:
var lines = File.ReadAllLines(fileName);
var data = lines.Select(s => s.Split(',')).Select(s => new { Path = int.Parse(s[0]), Row = int.Parse(s[1]), Date = CommonHelper.ParseDate(s[2]) });
StartPath = data.Min(s => s.Path);
StartRow = data.Min(s => s.Row);
StartDate = data.Min(s => s.Date);
EndPath = data.Max(s => s.Path);
EndRow = data.Max(s => s.Row);
EndDate = data.Max(s => s.Date);
之后拼接请求的URL,代码如下:
$"https://earthexplorer.usgs.gov/EE/InventoryStream/pathrow?start_path={StartPath}&end_path={EndPath}&start_row={StartRow}&end_row={EndRow}&sensor={DatasetType}&start_date={CommonHelper.FormatDate(StartDate)}&end_date={CommonHelper.FormatDate(EndDate)}&format=CSV"
其中DatasetType是用户选择的landsat数据源,CommonHelper.FormatDate函数完成日期到字符串格式的转化,代码如下:
public static string FormatDate(DateTime date)
{
return date.ToString("yyyy-MM-dd");
}
然后发送请求,获取结果,这块在网络爬虫之密码破解一文中已经介绍过,不再赘述。获取到结果后,将其与用户想要的结果一一比对,取出需要的结果,代码如下:
var orginResultList = orginResult.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).Skip(1);
var source = File.ReadAllLines(file);
var data = source.Select(s => s.Split(',')).Select(s => new { Path = int.Parse(s[0]), Row = int.Parse(s[1]), Date = CommonHelper.ParseDate(s[2]) });
var endResult = new List<string>();
data.ToList().ForEach(s =>
{
var temp = orginResultList.Where(re =>
{
var arr = re.Split(',');
return int.Parse(arr[7]) == s.Path && int.Parse(arr[8]) == s.Row /*&& CommonHelper.ParseDate(arr[5]) == s.Date*/;
});
if (temp != null && temp.Count() > 0)
endResult.Add(temp.First());
});
File.WriteAllLines(Path.Combine(Path.GetDirectoryName(file), "res.csv"), endResult);
其中orginResult表示请求返回的结果,这里面存在一个问题就是往往用户想要的元数据日期与返回的元数据日期不一致(原因可能有很多,用户输入不准确,或者有什么我未考虑到的因素),如果时间也进行匹配的话基本取不到结果,所以目前采用的方式只比对行列号。
四、总结
通过以上方式即可实现批量下载landsat元数据,需要下载程序的可以直接点击这里,当然由于刚做出的程序,难免在代码逻辑或者业务逻辑上有BUG或者未考虑到的地方,欢迎批评指正,后续完善之后可能会将源代码开放到Github上,以供需要的人使用。