Google Earth Engine 中带有 map() 函数的双循环
Double loop with map() function in Google Earth Engine
在Google Earth Engine Developer's Guide中,有一个避免for()
循环的建议。他们建议使用 map()
函数作为这个例子:
// to avoid
var clientList = [];
for(var i = 0; i < 8; i++) {
clientList.push(i + 1);
}
print(clientList);
// to use
var serverList = ee.List.sequence(0, 7);
serverList = serverList.map(function(n) {
return ee.Number(n).add(1);
});
print(serverList);
我正在尝试 select 每个 month/year 的 MODIS 场景,然后再计算 VCI。因此,我采用的方法是使用双循环:
modis = ee.ImageCollection("MODIS/MYD13A1");
var modis_list = [];
for(var i = 1; i <13; i++) {
for(var j = 2000; j <2018; j++){
modis_list.push(modis.filter(ee.Filter.calendarRange(i, i, 'month'))
.filter(ee.Filter.calendarRange(j, j, 'year')));
}
}
print(modis_list);
有没有办法用 map()
函数复制这样的双循环以达到服务器端方法?
首先声明我对 Google Earth Engine 一无所知,我的信息来自函数式编程知识。
map
的独特之处在于它不会生成循环遍历的内容。您从一个列表开始,然后 map
遍历该列表中的每个项目并对其进行转换。如果您没有该列表,那么 map 就不太合适。
您似乎正在创建一个列表,其中包含每个 month/year 组合。我会把它分成几个步骤。构建月份和年份列表,构建表示 2 个列表的笛卡尔积的列表,然后转换为 ee
对象。
var range = (from, to) => new Array(end-start+1).fill(0).map((_,i)=>i+from)
var cartesianProduct = (a, b) => // not gonna do this here but it returns pairs [ [ a[1], b[1] ], ... ]
var getEE = ([month, year]) => modis
.filter(ee.Filter.calendarRange(month, month, 'month'))
.filter(ee.Filter.calendarRange(year, year, 'year'));
var months = range(1,12);
var years = range(2000, 2017);
var data = cartesianProduct(months, years)
.map(getEE)
可能有更好的方法(比如每次我们想要一个月份对象(使用字典)时不遍历 modis
的整个列表)但要点是相同的。
假设你只是想了解 GEE 的 map()
功能,以及如何相当于一个普通的 js for loop
,代码将是:
var map_m = function(i) {
i = ee.Number(i)
var years = ee.List.sequence(2000, 2017)
var filtered_col = years.map(function(j) {
var filtered = modis.filter(ee.Filter.calendarRange(i, i, 'month'))
.filter(ee.Filter.calendarRange(j, j, 'year'))
return filtered
})
return filtered_col
}
var months = ee.List.sequence(1, 12)
var modis_list2 = months.map(map_m).flatten()
此代码复制了一个正常的 for loop
。首先,它逐项进入年列表,然后逐项进入月列表,然后,一旦你有了年份和月,过滤集合并将其添加到列表中(map
自动执行)。当您使用 2 个 map
函数(一个超过几年,另一个超过几个月)时,您会得到一个列表列表,因此要获得 ImageCollection
的列表,请使用 flatten()
函数。不知何故打印的对象有点不同,但我相信结果是一样的。
最简单的方法是在您关心的 "months" 上使用一张地图。
// Collect images for each month, starting from 2000-01-01.
var months = ee.List.sequence(0, 18*12).map(function(n) {
var start = ee.Date('2000-01-01').advance(n, 'month')
var end = start.advance(1, 'month')
return ee.ImageCollection("MODIS/MYD13A1").filterDate(start, end)
})
print(months.get(95))
这将 return ImageCollections 列表。大多数月份只有 1 张图片,因为 MYD13A1 包含 16 天的图片,但有些月份会有两张。第 95 个月是 2008 年 1 月,有两个。
或者,您可以加入包含日期集合的集合,但这更简单。
您应该尽可能选择 filterDate 而不是 calendarRange,因为它已经过优化。
在Google Earth Engine Developer's Guide中,有一个避免for()
循环的建议。他们建议使用 map()
函数作为这个例子:
// to avoid
var clientList = [];
for(var i = 0; i < 8; i++) {
clientList.push(i + 1);
}
print(clientList);
// to use
var serverList = ee.List.sequence(0, 7);
serverList = serverList.map(function(n) {
return ee.Number(n).add(1);
});
print(serverList);
我正在尝试 select 每个 month/year 的 MODIS 场景,然后再计算 VCI。因此,我采用的方法是使用双循环:
modis = ee.ImageCollection("MODIS/MYD13A1");
var modis_list = [];
for(var i = 1; i <13; i++) {
for(var j = 2000; j <2018; j++){
modis_list.push(modis.filter(ee.Filter.calendarRange(i, i, 'month'))
.filter(ee.Filter.calendarRange(j, j, 'year')));
}
}
print(modis_list);
有没有办法用 map()
函数复制这样的双循环以达到服务器端方法?
首先声明我对 Google Earth Engine 一无所知,我的信息来自函数式编程知识。
map
的独特之处在于它不会生成循环遍历的内容。您从一个列表开始,然后 map
遍历该列表中的每个项目并对其进行转换。如果您没有该列表,那么 map 就不太合适。
您似乎正在创建一个列表,其中包含每个 month/year 组合。我会把它分成几个步骤。构建月份和年份列表,构建表示 2 个列表的笛卡尔积的列表,然后转换为 ee
对象。
var range = (from, to) => new Array(end-start+1).fill(0).map((_,i)=>i+from)
var cartesianProduct = (a, b) => // not gonna do this here but it returns pairs [ [ a[1], b[1] ], ... ]
var getEE = ([month, year]) => modis
.filter(ee.Filter.calendarRange(month, month, 'month'))
.filter(ee.Filter.calendarRange(year, year, 'year'));
var months = range(1,12);
var years = range(2000, 2017);
var data = cartesianProduct(months, years)
.map(getEE)
可能有更好的方法(比如每次我们想要一个月份对象(使用字典)时不遍历 modis
的整个列表)但要点是相同的。
假设你只是想了解 GEE 的 map()
功能,以及如何相当于一个普通的 js for loop
,代码将是:
var map_m = function(i) {
i = ee.Number(i)
var years = ee.List.sequence(2000, 2017)
var filtered_col = years.map(function(j) {
var filtered = modis.filter(ee.Filter.calendarRange(i, i, 'month'))
.filter(ee.Filter.calendarRange(j, j, 'year'))
return filtered
})
return filtered_col
}
var months = ee.List.sequence(1, 12)
var modis_list2 = months.map(map_m).flatten()
此代码复制了一个正常的 for loop
。首先,它逐项进入年列表,然后逐项进入月列表,然后,一旦你有了年份和月,过滤集合并将其添加到列表中(map
自动执行)。当您使用 2 个 map
函数(一个超过几年,另一个超过几个月)时,您会得到一个列表列表,因此要获得 ImageCollection
的列表,请使用 flatten()
函数。不知何故打印的对象有点不同,但我相信结果是一样的。
最简单的方法是在您关心的 "months" 上使用一张地图。
// Collect images for each month, starting from 2000-01-01.
var months = ee.List.sequence(0, 18*12).map(function(n) {
var start = ee.Date('2000-01-01').advance(n, 'month')
var end = start.advance(1, 'month')
return ee.ImageCollection("MODIS/MYD13A1").filterDate(start, end)
})
print(months.get(95))
这将 return ImageCollections 列表。大多数月份只有 1 张图片,因为 MYD13A1 包含 16 天的图片,但有些月份会有两张。第 95 个月是 2008 年 1 月,有两个。
或者,您可以加入包含日期集合的集合,但这更简单。
您应该尽可能选择 filterDate 而不是 calendarRange,因为它已经过优化。