You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Earth Engine中Image.select is not a function错误排查与修复

解决GEE中Image.select is not a function报错问题

错误原因

  • 直接触发点:当某年月的Landsat影像集合为空时,collection.median()会返回null而非合法的ee.Image对象。后续将这个null传入measure_width函数调用image.select('NDWI')时,就会抛出Image.select is not a function错误——因为null不是Image实例,没有该方法。
  • 附带问题:原代码中generate_points_along_river函数用cutLines()生成的是线段,不是点,导致后续的cross sections方向完全错误,根本不是垂直于河流的断面。

解决方案

1. 处理空影像集合

在生成月度median影像前,先判断集合是否有元素,为空则返回null并在后续过滤掉,避免传入无效值。

2. 修复沿河流生成点的逻辑

用ee.Geometry.LineString的interpolate方法,按指定间隔生成沿河流的点,而不是用cutLines生成线段。

3. 生成垂直于河流的断面

通过计算河流路径的方向角,生成垂直于河流的断面线,而不是固定的对角线。

修正后的完整代码

var aoi = ee.Geometry.Polygon(
    [[
      [74.36670, 32.79014],
      [74.36670, 30.67386],
      [76.81411, 30.67386],
      [76.81411, 32.79014]
    ]]
  );
  
// Define start and end points for the river
var start_point = ee.Geometry.Point([74.36670, 32.79014]);
var end_point = ee.Geometry.Point([76.81411, 30.67386]);

// Create a line between the start and end points
var river_path = ee.Geometry.LineString([start_point, end_point]);

// Function to generate points at 50 km intervals along the river
var generate_points_along_river = function(river, interval_km) {
  var length_m = river.length();
  var interval_m = interval_km * 1000;
  var num_points = length_m.divide(interval_m).int().add(1); // Include start point
  
  var points = ee.List.sequence(0, num_points.subtract(1)).map(function(i) {
    var distance = ee.Number(i).multiply(interval_m);
    return river.interpolate(distance, 0); // 0 means linear interpolation
  });
  
  return ee.FeatureCollection(points.map(function(point) {
    return ee.Feature(point);
  }));
};

// Generate points along the river at 50 km intervals
var interval_km = 50;
var points_fc = generate_points_along_river(river_path, interval_km);

// Function to create a perpendicular line (cross-section) at each point
var create_cross_section = function(feature) {
  var point = feature.geometry();
  // Get the direction of the river path at this point
  var line_segment = river_path.cutLines(point.buffer(1000)).get(0);
  var segment_coords = ee.Geometry(line_segment).coordinates();
  var dx = ee.Number(segment_coords.get(1).get(0)).subtract(segment_coords.get(0).get(0));
  var dy = ee.Number(segment_coords.get(1).get(1)).subtract(segment_coords.get(0).get(1));
  // Perpendicular direction: (-dy, dx) and (dy, -dx)
  var perp_dx = dy.multiply(-1);
  var perp_dy = dx;
  // Normalize the perpendicular vector to set fixed length
  var length = ee.Number(perp_dx.pow(2).add(perp_dy.pow(2))).sqrt();
  var norm_dx = perp_dx.divide(length).multiply(500); // 500m half-length
  var norm_dy = perp_dy.divide(length).multiply(500);
  
  var point_coords = point.coordinates();
  var x = ee.Number(point_coords.get(0));
  var y = ee.Number(point_coords.get(1));
  
  // Create perpendicular line
  var line = ee.Geometry.LineString([
    [x.subtract(norm_dx), y.subtract(norm_dy)], 
    [x.add(norm_dx), y.add(norm_dy)]
  ]);
  return ee.Feature(line).copyProperties(feature);
};

var cross_sections = points_fc.map(create_cross_section);

// Function to mask clouds using the quality band for Landsat 8 and 9
var mask_clouds = function(image) {
  var qa = image.select('QA_PIXEL');
  var cloud = qa.bitwiseAnd(1 << 5).eq(0);
  var cloud_shadow = qa.bitwiseAnd(1 << 3).eq(0);
  return image.updateMask(cloud.and(cloud_shadow));
};

// Function to mask clouds using the quality band for Landsat 5 and 7
var mask_clouds_ls5_ls7 = function(image) {
  var qa = image.select('QA_PIXEL');
  var cloud = qa.bitwiseAnd(1 << 4).eq(0);
  var cloud_shadow = qa.bitwiseAnd(1 << 2).eq(0);
  return image.updateMask(cloud.and(cloud_shadow));
};

// Function to calculate NDWI
var calculate_ndwi = function(image) {
  var ndwi = image.normalizedDifference(['SR_B3', 'SR_B5']).rename('NDWI');
  return image.addBands(ndwi);
};

// Function to get the monthly collection
var get_monthly_collection = function(start_date, end_date) {
  var landsat5 = ee.ImageCollection('LANDSAT/LT05/C02/T1_L2')
    .filterBounds(aoi)
    .filterDate(start_date, end_date)
    .map(mask_clouds_ls5_ls7)
    .map(calculate_ndwi);
  var landsat7 = ee.ImageCollection('LANDSAT/LE07/C02/T1_L2')
    .filterBounds(aoi)
    .filterDate(start_date, end_date)
    .map(mask_clouds_ls5_ls7)
    .map(calculate_ndwi);
  var landsat8 = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
    .filterBounds(aoi)
    .filterDate(start_date, end_date)
    .map(mask_clouds)
    .map(calculate_ndwi);
  var landsat9 = ee.ImageCollection('LANDSAT/LC09/C02/T1_L2')
    .filterBounds(aoi)
    .filterDate(start_date, end_date)
    .map(mask_clouds)
    .map(calculate_ndwi);
  return landsat5.merge(landsat7).merge(landsat8).merge(landsat9);
};

// Generate monthly collections
var start_year = 1995;
var end_year = 2023;
var months = ee.List.sequence(1, 12);

var monthly_collections = ee.List.sequence(start_year, end_year).map(function(year) {
  return months.map(function(month) {
    var start_date = ee.Date.fromYMD(year, month, 1);
    var end_date = start_date.advance(1, 'month');
    var collection = get_monthly_collection(start_date, end_date);
    // Only generate median if collection is not empty
    return ee.Algorithms.If(collection.size().gt(0),
      collection.median().set('system:time_start', start_date.millis()),
      null // Return null for empty collections
    );
  });
}).flatten().removeAll([null]); // Filter out null values

// Function to measure width at cross-sections
var measure_width = function(image) {
  var time_start = image.get('system:time_start');
  var get_width = function(feature) {
    var cross_section = feature.geometry();
    // Sample NDWI along the cross-section
    var samples = image.select('NDWI').sample({
      region: cross_section,
      scale: 30,
      geometries: true
    });
    // Filter water pixels (NDWI > 0)
    var water_samples = samples.filter(ee.Filter.gt('NDWI', 0));
    // Calculate distance between first and last water pixel to get width
    var width = ee.Algorithms.If(water_samples.size().gt(1),
      water_samples.first().geometry().distance(water_samples.last().geometry()),
      0 // If no water or single pixel, width is 0
    );
    return ee.Feature(feature.geometry(), {
      'width': width,
      'time_start': time_start
    });
  };
  return cross_sections.map(get_width);
};

// Apply the width measurement function to each monthly NDWI image
var widths_time_series = monthly_collections.map(function(image) {
  return measure_width(ee.Image(image)); // Ensure we cast to Image
});

// Flatten the collection of widths to export
var widths_flat = ee.FeatureCollection(widths_time_series).flatten();

// Export the widths time series data
Export.table.toDrive({
  collection: widths_flat,
  description: 'Satluj_River_Widths_Time_Series',
  fileFormat: 'CSV'
});

// Visualize NDWI and cross-sections for the first month (as an example)
if (monthly_collections.size().gt(0)) {
  var ndwi_image = ee.Image(monthly_collections.get(0)).clip(aoi);
  var ndwi_vis = {min: -1, max: 1, palette: ['00FFFF', '0000FF']};

  Map.centerObject(aoi, 8);
  Map.addLayer(ndwi_image.select('NDWI'), ndwi_vis, 'NDWI');
  Map.addLayer(aoi, {}, 'AOI');
  Map.addLayer(points_fc, {}, 'Points');
  Map.addLayer(cross_sections, {}, 'Cross Sections');
}

额外说明

  • 修正后的代码会自动过滤没有影像的月份,避免空值报错
  • 断面生成逻辑改为垂直于河流方向,宽度计算改为沿断面采样后取首尾水像素的距离,更符合实际河流宽度测量逻辑
  • 增加了时间戳属性到输出结果中,方便后续分析时间序列

内容的提问来源于stack exchange,提问作者AYUSH RATHORE

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 11:15:54