GEE ui界面实现:用户自画多边形, 按面积比例在多边形中自动生成样点,导出多边形和样点shp,以及删除上一组多边形和样点(有视频效果展示)
零、背景
这几天在选样点,发现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(即第一个位置)
完结撒花!