GEE基本概念(二)——3. filter&map&reducer

2.1 过滤筛选filter

过滤主要分为:空间过滤、时间过滤、属性过滤

下面例子中的ee.Filter.lt(name,value)和ee.ImageCollection().filter过滤是等价的。
过滤是在数据几何的基础上过滤的。

var point = ee.Geometry.Point(117.089, 36.744); 
var start= ee.Date('2020-08-01');
var finish = ee.Date('2020-09-01');
// 云量筛选
var  img = ee.ImageCollection('LANDSAT/LC08/C01/T1')
  .filterBounds(point)                  //空间过滤filterBounds()
  .filterDate(start, finish)            //时间过滤filterDate()
  // .sort('CLOUD_COVER', True)            //云量排序
  // .first()
  .filter(ee.Filter.lt('CLOUD_COVER', 5))       //云量过滤,ee.Filter.lt()筛选到小于给定值的元数据。返回构造好的过滤器
var vizParams = {bands: ['B5', 'B4', 'B3'], min: 5000, max: 15000, gamma: 1.3};
Map.centerObject(img, 7);
Map.addLayer(img, vizParams, 'Landsat 8 false color'); 

2.2 数据整合Reducer

数据整合Reducer是在EE中按照时间、空间、波段、矩阵或其他数据结构整合数据的方法。ee.Reducer()这一大类指定了数据整合的方式。Reducer是将数据整合起来的某种简单的统计算法(如最大最小值、平均值等等),或是对数据进行更为复杂的总结(如直方图、线性回归、列表等)。依据:

1.时间:imageCollection.Reducer();

2.空间:image.ReducerReigon();image.reducerNeighborhood();

3.波段:image.reducer();

4.FeatureColection的属性空间:FeatureColection.reducerColumns()或者以"aggregate_"开头的FeatureColection的方法。

example: 实现计算NDVI感兴趣区均值的例子。源于课本

var roi = ee.Geometry.Polygon(        
      [[[114.62959747314449, 33.357067677774594],  
       [114.63097076416011, 33.32896028884253],  
       [114.68315582275386, 33.33125510961763],  
       [114.68178253173824, 33.359361757948754]]]);  
Map.centerObject(roi, 9);  
var image = ee.Image("LANDSAT/LC08/C01/T1_TOA/LC08_123037_20180611");  
var ndvi = image.normalizedDifference(["B5", "B4"]).rename("NDVI");  
var visParam = {  
  min: -0.2,   
  max: 0.8,  
  palette: ["FFFFFF", "CE7E45", "DF923D", "F1B555", "FCD163",   
            "99B718", "74A901", "66A000", "529400", "3E8601",   
            "207401", "056201", "004C00", "023B01", "012E01",   
            "011D01", "011301"]  
};  
Map.addLayer(ndvi, visParam, "NDVI");  
Map.addLayer(roi, {color: "red"}, "roi");    
var mean = ndvi.reduceRegion({  
   reducer: ee.Reducer.mean(),      //reducer均值计算器
   geometry: roi,                   //geometry计算区域
   scale: 30                        //分辨率选择:30m
});  // 这部分是最重要的
print("reduceRegion value is: ", mean); 

上述例子的链接:

https://code.earthengine.google.com/039f13adeedd57a36a1d36334a69598c

**Example:**Reducer实现多时相NDVI数据中值的计算。来源于培训教材的例子

var getNDVI = function(image) {
  return image.normalizedDifference(['B5', 'B4']).select(['nd'],['NDVI']);
}

var NDVIs = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR')
    .filter(ee.Filter.eq('WRS_PATH',122))
    .filter(ee.Filter.eq('WRS_ROW',34))
    .filterDate('2019-01-01','2020-10-01')
    .map(getNDVI)
var reduceNDVI2=NDVIs.reduce(ee.Reducer.median())   //在结果上使用Reducer命令,进行NDVI中值的计算
Map.addLayer(reduceNDVI2,{min:1,max:1,palette:['0000FF','00FF00']},'ndvi2');

上述例子只是进行了简单的介绍,但是,reducer的理解较为复杂。有几十个,接下来具体介绍GEE中常用的各个Reducer的意思,其他的也可以在docs中查询。下面来自于知乎。

命令解释
ee.Reducer.allNonZero()检查输入是否都是非0值,如果都是非0值,则返回1,否则0。
ee.Reducer.anyNonZero()检测输入是否包含非0值,如果包含非0值,则返回1,否则0
ee.Reducer.first()返回输入的第一个元素
ee.Reducer.firstNonNull()返回输入的第一个非null元素
ee.Reducer.last()返回输入的最后一个元素
ee.Reducer.lastNonNull()返回输入的最后一个非null元素
ee.Reducer.min()获取输入的最小值。如果输入是数值列表,获取数值的最小值;如果是影像集合,获取所有匹配波段的每一个像素的最小值;如果是单张影像,获取波段中的像素最小值。
ee.Reducer.max()获取输入的最大值。如果输入是数值列表,获取数值的最大值;如果是影像集合,获取所有匹配波段的每一个像素的最大值;如果是单张影像,获取波段中的像素最大值。
ee.Reducer.minMax()获取输入的最小值和最大值,如果输入是数值列表,获取数值的最小值和最大值;如果是影像集合,获取所有匹配波段的每一个像素的最小值和最大值;如果是单张影像,获取波段中的像素最小值和最大值
ee.Reducer.mode()获取输入的众数
ee.Reducer.mean()获取输入的均值
//1. ee.Reducer.allNonZero()
function allNonZero() {
  //ee.Reducer.allNonZero(),
  var a = ee.List([1,0,2,3]);
  //输出结果为0,因为包含0
  print("result1", a.reduce(ee.Reducer.allNonZero()));
  var b = ee.List([1,2,3,4]);
  //输出结果为1,因为全是非0值
  print("result2", b.reduce(ee.Reducer.allNonZero()));
}
allNonZero();

/2.ee.Reducer.anyNonZero()
function anyNonZero() {
  //ee.Reducer.anyNonZero()
  var a = ee.List([1,0,2,3]);
  //因为包含非0值,输出1
  print("result1", a.reduce(ee.Reducer.anyNonZero()));
  var b = ee.List([0,0]);
  //值全为0,输出0
  print("result2", b.reduce(ee.Reducer.anyNonZero()));  
}
anyNonZero();

//3.ee.Reducer.first()
var roi = /* color: #d63000 */ee.Geometry.Polygon(
        [[[116.11220202824006, 38.133561577393166],
          [116.37569631837516, 38.1696689233409],
          [116.32936651641012, 38.35173534234094],
          [116.07083980910647, 38.31789056147068]]]),
    l8 = ee.ImageCollection("LANDSAT/LC08/C01/T1_RT_TOA");
 
function generateImage() {
  Map.centerObject(roi, 10);
  var img = l8.filterDate("2018-6-1", "2018-8-1")
              .filterBounds(roi)
              .map(ee.Algorithms.Landsat.simpleCloudScore)
              .map(function(image){
                image = image.updateMask(image.select("cloud").lte(20));
                return image;
              })
              .map(function(image) {
                return image.addBands(image.normalizedDifference(["B5", "B4"]).rename("NDVI"));
              })
              .select("NDVI")
              .mosaic()
              .clip(roi);
  var visParam = {
    min: -0.2,
    max: 0.8,
    palette: 'FFFFFF, CE7E45, DF923D, F1B555, FCD163, 99B718, 74A901, 66A000, 529400,' +
      '3E8601, 207401, 056201, 004C00, 023B01, 012E01, 011D01, 011301'
  };
  Map.addLayer(img, visParam, "NDVI");
  return img;
}
var img = generateImage();


function first() {
  //ee.Reducer.first()
  //list
  var a = ee.List([1,2,3,4,5]);
  //返回列表的第一个元素 1
  print("result", a.reduce(ee.Reducer.first()));
 
  //image
  var firImg = ee.Image(l8.reduce(ee.Reducer.first()));
  //返回集合中每一景影像的波段第一个像素,同时重命名波段名称
  print("firImg", firImg);
}
first();

//4.ee.Reducer.firstNonNull()
function firstNonNull() {
  //ee.Reducer.firstNonNull()
  //list
  var a = ee.List([null, 1,2,3,4,5]);
  //返回第二个元素 1
  print("result", a.reduce(ee.Reducer.firstNonNull()));
}
firstNonNull();
function firstNonNull() {
  //ee.Reducer.firstNonNull()
  //list
  var a = ee.List([null, 1,2,3,4,5]);
  //返回第二个元素 1
  print("result", a.reduce(ee.Reducer.firstNonNull()));
}
firstNonNull();

//5.ee.Reducer.last()
function last() {
  //ee.Reducer.last()
  //list
  var a = ee.List([1,2,3,4,5]);
  //返回最后一个元素 5
  print("result", a.reduce(ee.Reducer.last()));
}
last();

//6.ee.Reducer.lastNonNull()
function lastNonNull() {
  //ee.Reducer.lastNonNull()
  //list
  var a = ee.List([0, 1,2,3,4, null]);
  //返回倒数第二个元素 4
  print("result", a.reduce(ee.Reducer.lastNonNull()));
}
lastNonNull();

其他详细的资料看下面第二个链接,代码都是上述第三个例子为基础:

ee.Reducer()介绍详细参考资料:

https://zhuanlan.zhihu.com/p/45335579

https://zhuanlan.zhihu.com/p/46683686

2.3 循环遍历map

循环遍历是依次对列表或者集合中每一个元素做相同的处理,在其他编程语言中是for或while,同样,gee也可以,但是由于其特殊性,推荐map。和之前的Map映射不一样。

map通常搭配函数一起使用,提取landsat最大ndvi并添加为新波段,请看下例:

var getNDVI = function(image) {
  var ndvi = image.normalizedDifference(['B5', 'B4']).rename('NDVI');
  return image.addBands(ndvi);      //定义添加ndvi新波段的函数,.rename()是将变量更名
    
}
// .select(['nd'],['NDVI'])         //和.rename()一个意思,是将nd更名为NDVI
var maxNDVI = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR')
    .filter(ee.Filter.eq('WRS_PATH',122))
    .filter(ee.Filter.eq('WRS_ROW',34))
    .filterDate('2019-01-01','2020-10-01')      //按照路径和block提取的数据集
    .map(getNDVI)
    .max()                          //.max()为提取ndvi为最大值的那张影像

print(maxNDVI)
Map.addLayer(maxNDVI.select('NDVI'),{min:-1,max:1,palette:['0000FF','00FF00']},'ndviDiff');

这个代码是map的另一个案例,结合了Reducer():

https://code.earthengine.google.com/9d9b72c002fe16ba406f6fe388b120c1

2.4 ee.Image().mask()

掩膜,使用一个数据度另一个数据进行裁剪,类似于ee.filterBounds():

// This function gets NDVI from Landsat 5 imagery.
var getNDVI= function(image) {
return image.normalizedDifference(['B4', 'B3']);
};
// Load two Landsat 5 images, 20 years apart.
var image1 = ee.Image('LANDSAT/LT05/C01/T1_TOA/LT05_044034_19900604');
var image2 = ee.Image('LANDSAT/LT05/C01/T1_TOA/LT05_044034_20100611');
// Compute NDVI from the scenes.
var ndvi1 = getNDVI(image1);
var ndvi2 = getNDVI(image2);
// Compute the difference in NDVI.
var ndviDifference= ndvi2.subtract(ndvi1);



// Load the land mask from the SRTM DEM.
var landMask= ee.Image('CGIAR/SRTM90_V4').mask(); //准备掩膜数据,dem陆地部分为1,水体为0,用为1的部分掩膜
// Update the NDVI difference mask with the land mask.
var maskedDifference= ndviDifference.updateMask(landMask);//掩膜
// Display the masked result.
var vizParams= {min: -0.5, max: 0.5, palette: ['FF0000', 'FFFFFF', '0000FF']};
Map.setCenter(-122.2531, 37.6295, 9);

Map.addLayer(ndviDifference, vizParams, 'ndviDifference');
Map.addLayer(landMask, vizParams, 'landMask');
Map.addLayer(maskedDifference, vizParams, 'NDVI difference');

//2.另一种掩膜,也就是大于0.1的部分作为mask进行掩膜,结果就是将NDVI大于0.1的显示出来
var mask = maskedDifference.gt(0.1)
var vegtationNDVI = maskedDifference.updateMask(mask)
Map.addLayer(mask,{min:-1,max:1,palette:['0000FF','00FF00']},'mask');
Map.addLayer(vegtationNDVI,{min:-1,max:1,palette:['0000FF','00FF00']},'NDVI');

2.5发布APP

APP是指把EE中编写的代码发布成为可在线运行的网页端应用,其他用户可以直接通过我们发布的链接地址访问、使用APP。具体操作如下:

var center = /* color: #d63000 */ee.Geometry.Point([116.387928, 40.00649]);  //定义初始化地图并显示位置;
var zoom = 9;  //定义初始化地图及缩放级别;
var leftMap = ui.Map();  

//初始化左地图和右地图,并用linker将其联系起来;
leftMap.centerObject(center, zoom);  
var rightMap = ui.Map();  
rightMap.centerObject(center, zoom);  
leftMap.setControlVisibility(false);  
rightMap.setControlVisibility(false);  
leftMap.setControlVisibility({zoomControl: true});  
var linker = new ui.Map.Linker([leftMap, rightMap]);  

//定义滑动界面组件,左右布局,中间显示滑动条;
var splitPanel = ui.SplitPanel({  
 firstPanel: leftMap,  
 secondPanel: rightMap,  
 orientation: 'horizontal',  
 wipe: true  
});  
//清空原始的地图界面内容
ui.root.clear();  
ui.root.add(splitPanel);  

//RGB  左地图显示
var landsat = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR')  
                .filterDate('2017-01-01', '2018-01-01')  
                .median();  
landsat = landsat.addBands(landsat.normalizedDifference(['B5', 'B4']).rename("NDVI"));  
var vis = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000};  
leftMap.addLayer(landsat, vis, "rgb");  
//NDVI  右地图显示
var visNDVI = {  
 min: 0,  
 max: 1,  
 palette: 'FFFFFF,CE7E45,DF923D,F1B555,FCD163,99B718,74A901,66A000,529400,' + 
     '3E8601,207401,056201,004C00,023B01,012E01,011D01,011301'  
};  
rightMap.addLayer(landsat.select("NDVI"), visNDVI, 'NDVI');  

但是我貌似发布不了,不知道为什么,链接:

https://code.earthengine.google.com//1fc5d33fa0121736f7fd71a2ea44501c

<think>我们注意到用户在使用GEE时遇到了一个错误:`filterBounds is not a function`。根据引用[1]中的解释,这个错误通常是因为在`ee.Image`对象上调用`filterBounds`方法,而`filterBounds`是`ee.ImageCollection`的方法。在用户提供的原始代码中,我们看到: ```javascript var idasoil = ee.Image("ISDASOIL/Africa/v1/carbon_organic") .filterBounds(points) // 这里错误:ee.Image没有filterBounds方法 .filterDate(&#39;2015-08-01&#39;, &#39;2017-09-08&#39;) ``` 所以,我们需要将代码修改为对影像集合(ImageCollection)进行操作,而不是单个影像(Image)。但是,这里我们只有一个影像(`ee.Image`),所以不能使用`filterBounds`。实际上,对于单个影像,我们不需要进行空间过滤,因为它已经是一个覆盖整个区域的影像(通常是分块或镶嵌的)。如果我们想裁剪到研究区域,应该使用`clip`方法。 然而,在用户的问题中,他想要从影像中提取点位的值。正确的做法是: 1. 使用`ee.Image`的`reduceRegions`方法直接提取点位的值,而不需要先进行空间过滤(因为影像已经覆盖了点位所在区域,如果点位不在影像范围内,则提取不到值)。 因此,修改后的代码应该是: ```javascript var points = ee.FeatureCollection("projects/gee2erainfall/assets/arable_devices_metadata"); var idasoil = ee.Image("ISDASOIL/Africa/v1/carbon_organic"); // 这是一个单幅影像 // 注意:单幅影像不需要filterBounds和filterDate(除非它是时间序列中的一幅,但这里是一个静态影像) // 直接使用reduceRegions提取点值 var triplets = idasoil.select(&#39;mean_0_20&#39;).reduceRegions({ collection: points.select([&#39;arable_barcode&#39;]), reducer: ee.Reducer.mean(), scale: 30 }).filter(ee.Filter.neq(&#39;mean&#39;, null)); // 过滤掉没有值的点 print(triplets.first()); ``` 但是,如果用户的数据是影像集合(ImageCollection),那么就需要使用`filterBounds`和`filterDate`,然后使用`map`来逐幅影像提取点值。例如: ```javascript var idasoilCollection = ee.ImageCollection("ISDASOIL/Africa/v1/carbon_organic") .filterBounds(points) // 对集合进行空间过滤 .filterDate(&#39;2015-08-01&#39;, &#39;2017-09-08&#39;); // 时间过滤 var triplets = idasoilCollection.map(function(image) { return image.select(&#39;mean_0_20&#39;).reduceRegions({ collection: points.select([&#39;arable_barcode&#39;]), reducer: ee.Reducer.mean(), scale: 30 }).filter(ee.Filter.neq(&#39;mean&#39;, null)) .map(function(feature) { return feature.set(&#39;imageId&#39;, image.id()); }); }).flatten(); ``` 因此,用户需要根据数据源的类型(是单个影像还是影像集合)来选择正确的处理方法。 现在,针对用户的问题:“Google Earth Engine ee.ImageCollection chain filterDate filterBounds select map filter groupby error: is not a function”,这个错误通常是因为在链式调用中,某个方法作用的对象不正确。例如,在`map`之后返回的可能是`FeatureCollection`,而接下来的操作(如`filter`或`groupby`)可能不是该对象的方法。 下面我们提供一个通用的处理流程,并解释每一步: 1. 初始化影像集合:`ee.ImageCollection(...)` 2. 空间过滤:`.filterBounds(geometry)` 3. 时间过滤:`.filterDate(start, end)` 4. 选择波段:`.select(bandNames)` 5. 映射函数(对每幅影像进行处理):`.map(function(image){...})`,在函数内部可以对每幅影像进行操作,比如裁剪、计算等。 6. 过滤特征集合(如果映射返回的是特征集合):注意,在影像集合上使用`map`后,如果返回的是特征集合,那么整个结果就是一个`ee.FeatureCollection`的集合(即每个影像对应一个特征集合)。我们可以使用`.flatten()`将其展平为一个大的特征集合,然后再进行过滤。 但是,如果我们在`map`之后想要对每个特征集合进行过滤,那么应该在`map`内部的函数里进行(即对每个影像处理时,对提取的特征集合进行过滤),而不是在外部对整个集合过滤。 例如,下面的代码是安全的: ```javascript var collection = ee.ImageCollection(...) .filterBounds(geometry) .filterDate(start, end) .map(function(image) { // 对每幅影像进行一些操作,返回一个特征集合 var features = image.reduceRegions(...); // 在内部进行过滤 return features.filter(ee.Filter.neq(&#39;property&#39;, null)); }); ``` 然后,如果我们想要一个大的特征集合,可以展平: ```javascript var flattened = collection.flatten(); ``` 如果要对展平后的特征集合进行过滤,可以使用: ```javascript var filtered = flattened.filter(ee.Filter.eq(&#39;property&#39;, value)); ``` 但是,如果我们在`map`之后直接调用`filter`,那么作用的对象是影像集合(每个元素是一个特征集合),而特征集合的`filter`方法并不是影像集合的方法,所以会报错。 因此,总结常见的错误和解决方案: - **错误1**:在`ee.Image`上调用`ee.ImageCollection`的方法(如`filterBounds`, `filterDate`, `map`等)。解决方案:确认对象类型,如果是单幅影像,则使用影像的方法(如`clip`,`reduceRegions`等);如果是影像集合,则使用集合的方法。 - **错误2**:在链式调用中,前一步返回的对象类型与下一步调用的方法不匹配。例如,在`map`之后返回的是特征集合,然后试图在影像集合上调用特征集合的方法。解决方案:在`map`内部完成对特征集合的操作,或者将结果展平(`flatten`)后再进行特征集合的操作。 下面我们提供一个完整的示例代码,展示如何从影像集合中提取多个时间点的点位值,并导出为表格: ```javascript // 定义研究区域(点集合)和影像集合 var points = ee.FeatureCollection("users/your_points"); var collection = ee.ImageCollection("MODIS/006/MOD13A2") .filterBounds(points.geometry()) // 根据点位集合的几何进行空间过滤 .filterDate(&#39;2010-01-01&#39;, &#39;2020-12-31&#39;) .select(&#39;NDVI&#39;); // 选择NDVI波段 // 对每幅影像提取点位的NDVI值 var triplets = collection.map(function(image) { // 对当前影像提取点位的值 var date = image.date().format(&#39;YYYY-MM-dd&#39;); var features = image.reduceRegions({ collection: points, reducer: ee.Reducer.mean(), scale: 250 // MODIS的分辨率 }); // 给每个特征添加一个时间属性 features = features.map(function(f) { return f.set(&#39;date&#39;, date); }); // 过滤掉没有值的点位 return features.filter(ee.Filter.neq(&#39;NDVI&#39;, null)); }).flatten(); // 展平为一个大的特征集合 // 导出为CSV Export.table.toDrive({ collection: triplets, description: &#39;NDVI_time_series&#39;, fileFormat: &#39;CSV&#39; }); ``` 在这个示例中,我们首先对影像集合进行空间和时间过滤,然后对每幅影像使用`reduceRegions`提取点位的NDVI值,并给每个特征添加影像的日期。最后,我们将所有结果展平并导出。 如果用户遇到`groupby`错误,请注意`groupby`是特征集合(`ee.FeatureCollection`)的方法,而不是影像集合的方法。因此,在展平后的特征集合上可以调用`groupby`进行分组统计。 例如,我们想按点位ID分组,计算每个点位的NDVI时间序列的平均值: ```javascript var grouped = triplets.groupby(&#39;point_id&#39;).mean(); // 假设点位有一个属性&#39;point_id&#39;作为标识 ``` 但是,如果我们在影像集合上直接调用`groupby`,就会报错,因为影像集合没有`groupby`方法。 因此,请确保在正确的对象类型上调用方法。如果遇到`is not a function`错误,首先检查该对象是否具有该方法,并检查链式调用中每一步返回的对象类型。</think>### Google Earth Engine (GEE) 链式调用错误分析与解决方案 您在 GEE 中遇到的 `is not a function` 错误通常是由于**对象类型与方法不匹配**导致的。根据引用[1]的分析,`filterBounds` 是 `ee.ImageCollection` 的方法,不能直接应用于 `ee.Image` 对象。以下是常见错误场景和解决方案: #### 常见错误模式与修复方案 1. **在 `ee.Image` 上调用集合方法**(如您的案例): ```javascript // 错误:ee.Image 没有 filterBounds 方法 var image = ee.Image("ISDASOIL/Africa/v1/carbon_organic").filterBounds(points); // 正确:将单幅影像转为集合 var imageCol = ee.ImageCollection([ee.Image("ISDASOIL/...")]).filterBounds(points); ``` 2. **方法链顺序错误**: ```javascript // 错误:select() 返回 ee.Image,不再有集合方法 var wrong = col.select(&#39;NDVI&#39;).filterBounds(aoi); // 正确:先过滤再选择 var correct = col.filterBounds(aoi).select(&#39;NDVI&#39;); ``` 3. **误用 `mosaic()`**(如引用[3]): ```javascript // 错误:reduce() 返回 ee.Image,没有 mosaic 方法 var ETmean = evapotranspiration.reduce(ee.Reducer.mean()).mosaic(); // 正确:直接使用 reduce 结果 var ETImage = ETmean.clip(geometry); ``` #### 正确的链式调用模板 ```javascript var processed = ee.ImageCollection(&#39;MODIS/006/MOD13A2&#39;) // 1. 先应用空间过滤 .filterBounds(aoi) // 2. 再应用时间过滤 .filterDate(&#39;2020-01-01&#39;, &#39;2020-12-31&#39;) // 3. 选择所需波段 .select(&#39;NDVI&#39;) // 4. 逐影像处理 .map(function(img) { return img .clip(aoi) .multiply(0.0001) .set(&#39;date&#39;, img.date().format()); }) // 5. 分组聚合(返回ee.ImageCollection) .groupby(&#39;date&#39;) .mean(); ``` #### 关键原则总结 1. **对象类型决定可用方法**: - `ee.Image` 方法:`clip()`, `rename()`, `multiply()` - `ee.ImageCollection` 方法:`filterBounds()`, `filterDate()`, `map()`, `groupby()` - `ee.FeatureCollection` 方法:`reduceRegions()`, `filter()` 2. **链式调用顺序**: ```mermaid graph LR A[ImageCollection] --> B[空间过滤 filterBounds] B --> C[时间过滤 filterDate] C --> D[波段选择 select] D --> E[逐影像处理 map] E --> F[分组聚合 groupby] ``` 3. **类型转换技巧**: ```javascript // 单幅影像 -> 集合 var imgCol = ee.ImageCollection([singleImage]); // 集合 -> 单幅影像(镶嵌) var mosaicImg = imgCol.mosaic(); // 集合 -> 单幅影像(聚合) var meanImg = imgCol.mean(); ``` ### 修复后的完整示例 ```javascript // 正确链式调用示例 var ndviResidual = ee.ImageCollection(&#39;MODIS/006/MOD13A2&#39;) .filterBounds(aoi) // 先空间过滤 .filterDate(&#39;2010-01-01&#39;, &#39;2020-12-31&#39;) // 再时间过滤 .select(&#39;NDVI&#39;) // 选择波段 .map(function(img) { var precip = getPrecipitation(img.date()); // 自定义降水获取函数 return img .multiply(0.0001) .addBands(precip) .set(&#39;month&#39;, img.date().get(&#39;month&#39;)); }) .groupby(&#39;month&#39;) // 按月份分组 .mean(); // 计算月均值 // 残差计算函数 function calculateResidual(img) { var pred = img.select(&#39;precip&#39;).multiply(slope).add(intercept); return img.select(&#39;NDVI&#39;).subtract(pred).rename(&#39;NDVI_res&#39;); } ``` ### 调试建议 1. 使用 `print()` 检查对象类型: ```javascript print(&#39;Type:&#39;, imgCol); // 控制台显示对象类型 ``` 2. 分步执行链式操作,避免长链调用 3. 参考 GEE 文档确认方法适用对象类型[^1][^3]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值