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
相关产品推荐
相关产品推荐

