零、背景

这几天在选样点,发现GEE有强大的ui功能,于是应用在我的工作上。

下述代码实现了几个功能:

①用户可以自己勾勒多边形,随后程序会按面积比例在多边形中自动生成样点,同时根据改多边形的区域生成区域平均月NDVI曲线;可以实现选取多个多边形。

②设置【Export】ui按钮,将已选的多边形和生成的样点以shp文件形式导出。

③如果对刚才画的多边形不满意,点击【Delete】ui按钮,即可将最近画的一个多边形,以及其上生成的样点删除。

一、录了个小视频,先来感受一下效果吧!

用户自画多边形, 按面积比例在多边形中自动生成样点,导出多边形

二、代码

GEE链接:https://code.earthengine.google.com/31d8edcd71f7b5d27201b88fc46c1e05

/*-----------                        --------------
                    Sample
-------------                       --------------*/
// 创建地图
var map1 = ui.Map().setOptions('Hybrid').setZoom(4);
var map2 = ui.Map().setOptions('Hybrid').setZoom(4);

// map1 监听 map2 的缩放级别变化;map2 监听 map1 的缩放级别变
map1.onChangeZoom(function(zoom){
  zoom = parseInt(zoom); // 转换为整数类型
  map2.setZoom(zoom);
});

map2.onChangeZoom(function(zoom){
  zoom = parseInt(zoom);
  map1.setZoom(zoom);
});

// 当在一个地图上平移时,其他两个地图会同步平移。
var linker = ui.Map.Linker([map1, map2], 'change-center');

// 添加图层到地图面板
map1.centerObject(aoi,8);

map1.addLayer(lucc_img.clip(aoi), luccVis, year+'_Land Cover Map');//lucc_img可替换为自己的可视化数据

// 获取MODIS影像集合
var modisCollection = ee.ImageCollection('MODIS/061/MOD13Q1')
  .filterBounds(aoi)
  .filterDate(year+'-01-01', year+'-12-31'); // 修正日期范围

// 创建时间序列面板
var chartPanel = ui.Panel({
  style: {
    width: '34%'
  }
});

// 创建一个空的 FeatureCollection 来存储样本polygon
var polygonsCollection = ee.FeatureCollection([]);
// 创建一个空的 FeatureCollection 来存储样本points
var pointsCollection = ee.FeatureCollection([]);
var numPoints; // 全局变量

// 创建region 的NDVI时间序列图表
var plotNDVI = function(geometry, title) {
  // 选择NDVI波段并将值乘以0.0001
  var adjustedModisCollection = modisCollection.map(function(image) {
    return image.reproject('EPSG:4326').select('NDVI').multiply(0.0001)
      .copyProperties(image, ['system:time_start']);
  });

  // ee.Reducer.mean() 计算该区域内像素值的平均值
  var timeSeries = ui.Chart.image.series({
    imageCollection: adjustedModisCollection,
    region: geometry,
    reducer: ee.Reducer.mean(),
    scale: 250,
    xProperty: 'system:time_start'
  }).setOptions({
    title: title,
    vAxis: {title: 'NDVI'},
    hAxis: {title: 'Date'},
    lineWidth: 1,
    pointSize: 3
  });

  chartPanel.clear();
  chartPanel.add(timeSeries);
};

// 导出函数
function exportPolygons_points() {
  // 检查 FeatureCollection 是否包含元素
  if (polygonsCollection.size().getInfo() > 0) {
    // 导出 FeatureCollection 到 Google Drive 为 SHP 格式
    Export.table.toDrive({
      collection: polygonsCollection,
      description: 'Exported_Polygons',
      fileFormat: 'SHP' // 指定文件格式为 SHP
    });
    print('Exporting polygons to Google Drive as SHP...');
  } else {
    print('No polygons to export.');
  }
  if (pointsCollection.size().getInfo() > 0) {
  // 导出 FeatureCollection 到 Google Drive 为 SHP 格式
  Export.table.toDrive({
    collection: pointsCollection,
    description: 'Exported_Points',
    fileFormat: 'SHP' // 指定文件格式为 SHP
  });
  print('Exporting points to Google Drive as SHP...');
} else {
  print('No points to export.');
}
}
// 创建导出按钮
var exportPolygons_points_Button = ui.Button({
  label: 'Export Polygons/points',
  onClick: exportPolygons_points
});
// 将导出按钮添加到 map2
map2.add(exportPolygons_points_Button);

// Function to add drawing tools to the map
function addDrawingTools(map, mapTitle) {
  var drawingTools = map.drawingTools();
  drawingTools.addLayer([], 'geometry');
  drawingTools.setShape('polygon');
  drawingTools.draw();

  // Draw a polygon
  function onClickToDrawPoint(){
    var polygon = drawingTools.layers().get(0).getEeObject(); // Get the point drawn by the user
    drawingTools.layers().reset(); // Clear the drawing tools
    var areaInMeters = polygon.area();
    print('areaInMeters',areaInMeters);
    // Create a feature with the coordinates as properties
    var polygon_feature = ee.Feature(polygon);
    // Add the new point to the points collection
    polygonsCollection = polygonsCollection.merge(ee.FeatureCollection([polygon_feature])); // Merge the feature into the collection
    //print('polygonsCollection',polygonsCollection)
    // Calculate the number of random points based on the area
    numPoints = areaInMeters.divide(1572500).ceil();  // Area / 1572500 (in square meters)不加var 即更新全局变量 numPoints,而不是定义局部变量
    print('numPoints', numPoints);
    // Generate random points within the polygon
    var randomPoints = ee.FeatureCollection.randomPoints(polygon_feature.geometry(), numPoints);
    //print('randomPoints',randomPoints);
    pointsCollection = pointsCollection.merge(ee.FeatureCollection(randomPoints)); // Merge the feature into the collection
    //print('pointsCollection',pointsCollection)
    // Update the map with the new points collection
    map1.layers().set(4, ui.Map.Layer(polygonsCollection, {color: 'FF0000'}, 'Sample Polygons'));
    map2.layers().set(0, ui.Map.Layer(polygonsCollection, {color: 'FF0000'}, 'Sample Polygons'));
    map1.layers().set(5, ui.Map.Layer(pointsCollection, {color: '000000'}, 'Sample Points'));
    map2.layers().set(1, ui.Map.Layer(pointsCollection, {color: '000000'}, 'Sample Points'));
    // Redraw the NDVI chart (or any other chart you're displaying)
    plotNDVI(polygon_feature, mapTitle);
    drawingTools.draw();
  }

  drawingTools.onDraw(onClickToDrawPoint); // Enable drawing tools
}

// Add drawing tools and delete button
addDrawingTools(map1, 'Click map 1 and add polygons!');
addDrawingTools(map2, 'Click map 2 and add polygons!');
addDeleteButton();  // Add the delete button to the UI

// Function to add a delete button to remove the last drawn Polygon/Points
function addDeleteButton() {
  // Create the delete button
  var deleteButton = ui.Button({
    label: 'Delete Last Polygon/Points',
    onClick: function() {
      // Delete last polygon
      if (polygonsCollection.size().getInfo() > 0) {
        // Get the index of the last feature
        var lastPolygon = polygonsCollection.toList(polygonsCollection.size()).get(-1);  // Get the last polygon
        // Remove the last polygon by filtering it out
        polygonsCollection = polygonsCollection.filter(ee.Filter.neq('system:index', ee.Feature(lastPolygon).get('system:index')));
        print('Last polygon deleted');
      } else {
        print('No polygon to delete');  // Print if no polygon exists
      }

      // Delete last points
      if (pointsCollection.size().getInfo() > 0) {
        // Calculate the size of the collection
        var collectionSize = pointsCollection.size();
        // Convert FeatureCollection to list
        var pointsList = pointsCollection.toList(collectionSize);
        //var n = numPoints // assuming `numPoints` is defined elsewhere
        //print('numPoints2',numPoints)
        // Slice the list to exclude the last `n` points
        var slicedPointsList = pointsList.slice(0, ee.Number(collectionSize).subtract(numPoints));  // Slice to remove last `numPoints` points
        // Convert the sliced list back to a FeatureCollection
        pointsCollection = ee.FeatureCollection(slicedPointsList);
        // Update the map with the new polygon/points collection
        map1.layers().set(4, ui.Map.Layer(polygonsCollection, {color: 'FF0000'}, 'Sample Polygons'));
        map2.layers().set(0, ui.Map.Layer(polygonsCollection, {color: 'FF0000'}, 'Sample Polygons'));
        map1.layers().set(5, ui.Map.Layer(pointsCollection, {color: '000000'}, 'Sample Points'));
        map2.layers().set(1, ui.Map.Layer(pointsCollection, {color: '000000'}, 'Sample Points'));
        print('Last ' + numPoints.getInfo() + ' points deleted');
      } else {
        print('No points to delete.');
      }
    }
  });
  // Add the delete button to map2
  map2.add(deleteButton);
}

// 创建水平分割面板(宽度为整个用户界面的66%),其中 map1 和 map2 分别位于左侧和右侧
var split1 = ui.Panel(ui.SplitPanel({
  firstPanel: map1,
  secondPanel: map2,
  orientation: 'horizontal',
  wipe: false,
}), null, {width: '66%', height:'100%'});

// 创建水平分割面板(宽度为整个用户界面的34%),其中 chartPanel 位于map1和map2的右侧
var split2 = ui.Panel(ui.SplitPanel({
  firstPanel: split1,
  secondPanel: chartPanel,
  orientation: 'horizontal',
  wipe: false,
}), null, {width: '100%', height: '100%'});

map2.setControlVisibility(false);//设置 map2 地图的控制面板(如缩放、平移、图层选择等控制元素)不显示
ui.root.clear(); // 清空当前用户界面上的所有内容
ui.root.insert(0, split2); // 将新的布局 split2 插入到用户界面的根容器中,位置索引为 0(即第一个位置)

完结撒花!

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部