【GEE】10、使用 Google 地球引擎创建图形用户界面【GUI开发】

news2024/11/15 19:29:48

1简介

在本模块中,我们将讨论以下概念:

  1. 用于生成图形用户界面的 GEE 对象。
  2. 如何开发具有交互元素的面板。
  3. 如何将地理处理元素连接到交互式元素。

2背景

在过去的十个单元中,我们展示了 Google Earth Engine 可以成为一种重要且高效的资源,用于使用遥感数据进行可视化和运行分析。鉴于这些模块涉及的主题范围很明显,许多不同的科学和管理领域都可以从遥感数据的应用中受益。就是说,不是每个人都有时间或精力去做的事情,不是每个人都会有足够的时间或精力来参与 GEE 并创建自己的科学合理的分析。通常情况下,如果项目合作伙伴收到的最终交付物只是一堆代码,他们不会感到兴奋。在本模块中,我们将介绍创建图形用户界面 (GUI) 的过程,这将允许更直接、

2.1关于数据

对于这个模块,我们将改编一个由科罗拉多 NASA DEVELOP完成的预先存在的项目2018 年夏天的计划。DEVELOP 团队在 GEE 中创建了一个 GUI,使国家公园管理局的员工能够观察犹他州和科罗拉多州科罗拉多州、扬帕河和格林河沿岸洪泛平原的河流和植物形态的变化。该项目使用来自 Landsat 5、Landsat 7、Landsat 8 和 Sentinel 2 的数据来提供这些河流系统内 32 年形态变化的可视化。在代码中执行的分析相对简单,因为该项目的目标是为最终用户提供易于理解且足够灵活的资源,以允许他们使用数据提出自己的问题。在本模块中,我们将对这项工作进行调整,以可视化世界各地三个主要河流三角洲的植被变化。三角洲遥感可以提出许多有趣的科学问题。但是这个模块将侧重于 GUI 开发的技术解释,而不是所呈现的科学方法的细节。


 

3了解图形用户界面

Google 地球引擎代码编辑器本身就是一个图形用户界面。虽然我们无法更改基本元素(地图、任务窗格等),但我们可以添加它们的功能。根据需要,可以向现有 GUI 添加三种不同类别的元素(小部件、面板和事件)。这三个类元素是相互关联的。为了更多地了解每个元素,我们将使用一个示例,在该示例中,我们会询问一个关于 Landsat 图像功能的简单问题。
 

使用来自 Landsat 的 NDVI,我们能否在夏季有效地将柯林斯堡以西的猪背上的“A”与周围的植被区分开来?*

我们将从简化的示例开始,以提供对 GUI 开发元素如何在 GEE 中工作的概念性理解。一旦我们涵盖了这些元素,我们将通过复制河流形态评估工具箱 (RMET) 代码来绘制更复杂的材料演示,以绘制 Landsat 5-8 收集时间范围内河流三角洲的植被变化(1984 年至今)。

3.1小部件

小部件是用于向代码添加交互性的基本功能。有许多小部件可供选择。在继续本模块之前,我们强烈建议您访问小部件的文档,并通过将代码示例复制并粘贴到 GEE 脚本中来运行所有提供的示例。

为了演示一个小部件,我们将从下图中“A”周围的区域生成值的直方图,以查看由于“A”的白色而导致的分布是否存在双峰模式。

一张不到一米的高分辨率图像,位于科罗拉多州柯林斯堡郊区的山坡上,每年都会画一个大的白色 A。

 

// Load and display an NDVI image.
var ndvi = ee.ImageCollection('LANDSAT/LC8_L1T_8DAY_NDVI')
    .filterDate('2015-04-01', '2015-10-01')
    .median();
print(ndvi);
Map.addLayer(ndvi, {min:-0.1, max:0.6},'median NDVI ');
Map.setCenter(-105.147578,40.5593, 13); // Center on the Big A.
 
// Define a geometry feature that encompasses the "A".
var geometry = ee.Geometry.Polygon([[-105.154316,40.5593]
,[-105.147578,40.5593]
,[-105.147578,40.5640]
,[-105.154316,40.5640]
,[-105.154316,40.5593]]);
Map.addLayer(geometry, {}, 'geometry');
 
// Use the UI histogram function to produce a histogram of all values within the geometry feature.
var histo = ui.Chart.image.histogram({image:ndvi,region:geometry, scale:30});
print(histo);

黑色方形几何特征内的 NDVI 值直方图,包含 A.
 

我们脚本的最终结果是打印在控制台选项卡中的直方图。您可以更改几何对象的区域和图像的日期范围,以进一步细化数据并可能得到我们问题的答案。正如现在提供的数据所示,“A”似乎既不够大,也不够白,无法轻易地与周围的景观区分开来。分布极低端的小峰值是由水引起的。

3.2面板

Panels 和 Layouts用于在地图的不同位置生成容器,可以用来容纳特定的小部件。如果未指定面板,则大多数小部件会出现在控制台中,如前面的示例所示。通过应用面板,您可以创建一个强调使用地图而不是代码的布局。请看下面 RMET 工具中的示例。

RMET 工具使用一个面板来容纳多个小部件,这些小部件允许用户在不使用代码的情况下提问。
 

最终用户只需要点击运行按钮。然后,他们需要与代码互动的所有元素都将在面板上呈现和描述。他们可以有效地忽略代码和控制台并专注于地图。要了解更多信息,我们建议查看面板和布局的文档以了解更多信息。

正如我们上面提到的,将元素打印到控制台非常适合使用代码编辑器的个人。但对于不会编辑代码的个人,最好将感兴趣的元素放在地图上。在下面的脚本中,我们创建了一个面板来存储有关我们正在显示的内容的文本并保存我们的直方图以便于查看。将此代码复制到您的代码编辑器中并运行它以查看更改。

// Create a second histogram
var histo2 = ui.Chart.image.histogram({image:ndvi,region:geometry, scale:30});
 
// Create an empty panel in which to arrange widgets.
// The layout is vertical flow by default.
var panel = ui.Panel({style: {width: '400px'}})
     .add(ui.Label('We are attempting to differentiate the "A" from the surrounding landscape using NDVI values from Landsat Imagery. The histogram shows a distribution of all NDVI values found within the "geometry" layer on the map.'))
     .add(histo2);
ui.root.add(panel);

虽然小组在分析过程方面提供的东西不多,但它们是组织的重要资源。

添加到地图面板中的黑色方形几何特征内的 NDVI 值直方图。
 

我们可以看到直方图中的信息没有改变,只是呈现在不同的位置。

3.3事件

事件是在用户选择后执行预定义操作并与小部件或地图功能相关联的触发器。下面的示例包含一个小部件、面板和事件。面板用于显示事件的结果。在这种情况下,用户选择位置的平均 NDVI。有关事件的更多信息可以在事件文档中找到。

// Load and display an NDVI image.
var ls = ee.ImageCollection('LANDSAT/LC8_L1T_8DAY_NDVI')
    .filterDate('2015-04-01', '2015-10-01');
var vis = {min: 0, max: 1, palette: ['99c199', '006400']};
Map.addLayer(ls.median(), vis, 'NDVI');
 
 
// Configure the map.
Map.style().set('cursor', 'crosshair');
 
// Create a panel and add it to the map.
var inspector = ui.Panel([ui.Label('Click to get max NDVI')]);
Map.add(inspector);
 
Map.onClick(function(coords) {
  // Show the loading label.
  inspector.widgets().set(0, ui.Label({
    value: 'Loading...',
    style: {color: 'gray'}
  }));
 
  // Determine the mean NDVI, a server operation.
  var point = ee.Geometry.Point(coords.lon, coords.lat);
  var meanNdvi = ls.reduce('mean');
  var sample = meanNdvi.sample(point, 30);
  var computedValue = sample.first().get('NDVI_mean');
 
  // Request the value from the server.
  computedValue.evaluate(function(result) {
    // When the server returns the value, show it.
    inspector.widgets().set(0, ui.Label({
      value: 'Mean NDVI: ' + result.toFixed(2),
    }));
  });
});

当用户点击地图上的某个位置时,Map.onClick()事件会将坐标传递给函数。这将运行该函数的其余元素。第一个小部件inspector.widgets()用于确认已收到用户通过点击输入的信息。加载消息应该有助于阻止用户连续点击,从而防止他们重新启动事件进程。一旦基础地理处理代码返回了一个值,inspector.widgets()就会再次使用用户请求的值替换“加载”语句。

当用户点击感兴趣的位置时,on.Click()事件函数将返回该位置的平均 NDVI 值。
 

地理处理发生在函数内,这意味着函数内的变量(pointmeanNDVIsample等)仅存在于该函数内。您可以通过尝试打印computedValue变量来测试这一点。

<span style="color:#333333"><span style="background-color:#ffffff"><span style="color:#333333"><span style="background-color:#f8f8f8"><code><a id="cb4-1" class="sourceLine"><span style="color:#c4a000">print</span>(computedValue)<span style="color:#ce5c00"><strong>;</strong></span></a></code></span></span></span></span>

这将返回警告消息"computedValue is not defined in this scope."这意味着该变量未在本地环境中定义,因此 GEE 没有任何信息可打印。这可能会让人感到困惑,但它是使用 GUI 开发时需要理解的重要元素。

3.4将所有内容捆绑在一起

正如这些示例所示,在 GEE 中开发 GUI 很快就会变成一个复杂的过程。为了有效地进行 GUI 开发,对函数和客户端与服务器端环境有一个扎实的理解是很重要的。可以在在线文档中找到有关这两个主题的更多详细信息。;

客户端对象是存在于本地环境中的原生 JavaScript 元素。

服务器端对象是 JavaScript 包装器,用于将信息传递给我们在 Google 服务器上从未见过的功能。

为了说明这一点,让我们看一下之前定义感兴趣区域的代码卡盘。注意:您不需要运行这部分代码

// Define a geometry feature that encompasses the "A".
var geometry = ee.Geometry.Polygon([[-105.154316,40.5593]
,[-105.147578,40.5593]
,[-105.147578,40.5640]
,[-105.154316,40.5640]
,[-105.154316,40.5593]]);
Map.addLayer(geometry, {}, 'geometry');

坐标的数值和字符串“几何”文本甚至Map.addLayer函数都是客户端对象。当我们调用ee函数时,我们正在创建需要与服务器通信才能按预期工作的元素。在本例中,ee.Geometry.Polygon是一个服务器端对象。服务器端对象在 JavaScript 本身中没有任何意义。它们被用作参考,基本上是一组关于运行 GEE 的服务器应该在这个请求中做什么的指令。

例如,此文本是 JavaScript 字符串(“aString”)。然而,如果你试图在这个 JavaScript 字符串上调用ee.String()函数length(),你会得到一个错误“ JavaScriptString .length 不是函数”。您需要通过调用将客户端字符串“aString”更改为服务器端字符串对象,ee.String("aString")然后才能应用任何ee.String函数。

4 RMET:河流监测和评估工具

由 NASA DEVELOP 团队创建的 RMET 脚本是由以前使用 GEE 进行空间分析但在 GUI 开发方面经验有限的个人开发的,历时 10 周。代码本身有 1000 多行,包含 60 多个独特的功能。我们不会在这个模块中检查 RMET 代码的全部功能,但我们将从 RMET 代码中提取一些重要元素来构建我们自己的 GUI:Delta Watch。可以在此处找到 RMET 产品版本的完整代码。

4.1 RMET 组件

如果我们使用上面的链接加载 RMET 代码并运行它,我们可以在地图左侧的面板中看到许多用户定义的选项。需要注意的重要一点是,运行脚本后没有任何内容添加到地图中。实际上,RMET 代码的所有动作都依赖于用户的输入。

在深入研究代码之前,请查看 GUI 的每个元素。我们的第一个目标是尝试了解每个 GUI 元素实际在做什么。由此,我们将想要了解要使该元素按预期运行所需的条件。

选择感兴趣的领域

此按钮允许用户根据预加载的功能定义研究区域,并提供导入他们自己的研究区域的选项。

要求

  • 预定义研究区域的几何特征

  • 导入您自己的几何图形的机制

为 Landsat 30m 二进制地图选择年份

此步骤允许用户定义感兴趣的年份,然后创建二值图像并将其添加到地图中。用户可以调整二进制区分的阈值。

要求

  • 使用输入年份选择特定数据

  • 加载从 1984 年到 2018 年的所有 Landsat 数据

  • 根据所选索引生成特定图像

  • 根据用户定义的阈值重新制作图像

  • 可视化元素

选择 Landsat 30 m 更改地图的年份

此步骤通过比较给定索引的两个用户定义年份之间的图像差异来生成变化图。

要求

  • 使用输入年份选择特定数据

  • 加载从 1984 年到 2018 年的所有 Landsat 数据

  • 根据选定的年份和索引生成两张图像

  • 执行数据之间的差异。

  • 可视化元素

RMET GUI 的最后两个步骤实际上是相同的产品(二进制映射和变化映射),但用于不同的传感器。

为了保持井井有条,这里是我们需要合并到我们的 GUI 中以匹配 RMET 工具的功能的元素的一般列表

Delta Watch GUI所需元素的列表

  • 定义感兴趣的区域

  • 定义感兴趣的年份

  • 汇集图像

  • 从图像生成索引

  • 比较图像

  • 可视化图像

4.2接口实验

在深入了解代码如何运行的细节之前,我们需要花一些时间测试 GUI 中的不同元素。例如,如果您想清除已添加到地图的所有功能,只需刷新页面并重新运行或点击底部的重置地图按钮。

RMET 工具显示了 1985 年至 2018 年间恐龙国家公园的植被侵占区域。
 

上图显示了恐龙国家公园科罗拉多河最北端的一个区域,随着时间的推移,植被已经侵占了河流,有效地缩小了河道。这种类型的河道变窄极大地降低了河床的复杂性,并对这条河流中多种濒危鱼类的繁殖栖息地产生了负面影响。这类信息可以帮助公园管理者定性地评估附近火焰峡谷大坝的受控流量释放对濒危鱼类栖息地的影响。

4.3背后的逻辑

希望您在查看此工具的功能时获得了一些乐趣。它使用户有很大的权力用遥感数据提出自己的问题。当我们开始深入研究这一切是如何发生的时,您会注意到一个共同的主题:RMET 中的几乎每一段代码都是一个函数。函数是必不可少的,因为它们允许您进行灵活的输入。当我们通过下面的更多示例工作时,这样做的必要性将变得清晰。我们首先在模块 9中介绍了函数。

我们将调整现有的代码库来创建我们自己的 GUI,而不是简单地遍历 RMET 代码的元素,“ Delta Watch ”。该 GUI 将是 RMET 代码的简化版本,并将使用 Landsat 数据跟踪全球三个主要河流三角洲的 NDVI 随时间的变化。

5 “ Delta Watch ”,改编自 RMET。

RMET 代码的复杂性大部分来自于它做了很多事情。通过查看多个传感器的多个索引,您最终不得不写出许多独特的功能。对于我们的适应,我们将通过以下方式简化流程:

  • 定义感兴趣的区域:使用三个增量系统

  • 汇集大量图像:仅使用 Landsat 图像

  • 从图像生成索引:仅使用 NDVI

RMET 代码的其他元素在此新增内容中将保持不变。

  • 定义感兴趣的年/年

  • 比较图像

  • 可视化图像

在开始创建 GUI 之前,必须知道您希望 GUI 做什么。正如您将看到的,GUI 开发本质上是一个相互关联的过程。这意味着需要同时开发 GUI 和地理处理组件,以便解决它们如何协同工作的问题。您预先做的计划越多,从长远来看,它为您节省的时间就越多。通过本模块的其余部分,我们将同时描述每个 GUI 元素及其相互关联的地理处理方法。此过程反映了您将如何自己构建 GUI:添加一个元素,查看它是否有效,然后继续下一个元素。

5.1全局变量

将脚本元素可以仅存在于函数中的想法概念化有点棘手。然而,由于我们将非常依赖函数,因此必须了解何时需要将函数内部的元素与本地环境中的其他元素连接起来。建立这种关系的一种方法是在脚本中声明变量,然后在函数中定义这些变量。下面是在函数中定义但被本地环境中存在的脚本中的其他元素调用的变量的示例。

我们将这些元素称为全局变量,因为它们存在于服务器端和客户端环境中。

在代码编辑器中清除您的脚本,这样您就可以使用一个干净的状态。

/*
Global Variables
*/
var aoi,
    areaOfInterest,
    Fimageyr1,Fimageyr2,Fimageyr3,
    geometry,
    thresh,
    yr1,yr2,yr3;

如果您从头开始构建此代码,您将根据需要添加这些变量。所有这些变量都将在代码中的某个位置在函数中声明。

5.2可视化参数

就像全局变量一样,定义可视化参数是您将在代码开发时执行的操作。也就是说,为了清晰和组织目的,我们将在脚本开头声明所有可视化元素。

/*
Visualization parameters
*/
 
var VEG = {"opacity":1,"palette":["2ddc0d"],"min":0,"max":1};
var MG = {"opacity":1, "palette":["ffffff", "085a16"], "min":0, "max":2};
var LG = {"opacity":1, "palette":["ff7c0b", "ffffff"], "min":-2, "max":0};
var visNDVI = {min: -1, max: 1, palette: ['blue', 'white', 'green']};
var FLG = {"opacity":1, "palette":[ "ffffff", "ff7c0b"], "min":0, "max":2};

此时,所有将在代码中使用的预定义变量都已存在。我们现在将继续开发“ Delta Watch ”的功能。

5.3可视化图形用户界面。

作为规划过程的一部分,最好尽可能详细地画出您希望最终产品的外观。在这种情况下,我们可以创建一个简化的图表,显示我们将要创建的 GUI 的组成部分。

“Delta Watch”GUI 将做什么的示例概念图。
 

第一个组件“标题”和“描述”未连接到任何地理处理,因此我们将从那里开始。

/*
Create the panel
*/
 
// Generate main panel and add it to the map.
var panel = ui.Panel({style: {width:'33.333%'}});
ui.root.insert(0,panel);
 
// Define title and description.
var intro = ui.Label('Delta Watch: An Example GEE GUI ',
  {fontWeight: 'bold', fontSize: '24px', margin: '10px 5px'}
);
var subtitle = ui.Label('Use 30 m Landsat 5,7,8 imagery to'+
  ' visualize changes in delta vegetation patterns over time.'+
  ' Select from multiple area of interest and type of visualization; single year binary '+
  ' or change over time.', {});
 
// Add title and description to the panel.  
panel.add(intro).add(subtitle);

第一步是创建面板并将其添加到地图中。函数中的“0”ui.root.insert()会将面板放置在地图的左侧。值“1”将放在右侧。然后我们定义ui.Label包含我们想要的项目标题和描述文本的元素。最后一行将这些元素添加到面板和顺序问题。有了这个,我们可以运行代码,标题和描述将出现在地图的新面板上。

这是 GUI 开发的初始步骤。一个面板,带有关于我们正在构建的工具的功能的描述性文本。
 

5.4定义关注区域

对于这个项目,我们正在研究世界各地的三个三角洲

科罗拉多河三角洲,美国/墨西哥科罗拉多河是一个严重筑坝和过度占用的水体。这意味着河流往往无法到达三角洲。然而,近年来,已采取行动确保科罗拉多河的水到达科尔特斯海。完成“三角洲观察”后,我们可以检查这些流量是否对三角洲的植被产生了影响。

埃及尼罗河三角洲尼罗河三角洲是世界上种植最为一致的农业区之一,其独特之处在于其随着时间的推移水流的一致性。然而,1960 年代 Awsan 高坝的建设极大地改变了河流的流态,Landsat 图像可能会捕捉到由于 50 多年前流态变化而仍在发生的一些地貌事件.

马达加斯加贝齐博卡河三角洲贝齐博卡河是马达加斯加中北部的一条无坝水道。它以其独特的水色和非常大的三角洲而闻名。水的颜色和三角洲的特征是由于从 1950 年代开始在流域内的大规模森林砍伐造成的。由于沉积物负荷和自然流态的变化,我们可以期待在贝齐博卡河口看到一个非常动态的三角洲系统。

/*
Define study areas.
*/
// Generate polygons for each study region.
var colo = ee.Geometry.Polygon([[-115.29,32.20], [-114.89,31.17],
   [-114.40,31.64], [-115.14,32.19], [-115.29,32.20]]),
    nile = ee.Geometry.Polygon([[28.76,31.49],[30.69,29.91],
      [31.52, 29.96],[32.89,31.43],[31.00,31.76],[28.76,31.49]]),
    bets = ee.Geometry.Polygon([[46.19,-15.66],[46.25,-16.04],
      [46.51,-16.15],[46.70,-16.06],[46.19,-15.66]]);
 
// Define labels for each study region.
var COLO = 'Colorado River Delta',
    NILE = 'Nile River Delta',
    BETS = 'Betsiboka River Delta';

此代码生成简单的几何特征来定义我们感兴趣的区域,并生成我们将在感兴趣区域 (AOI) 选择的下拉框中使用的标签。

/*
Select Area of INTEREST
*/
 
// Define the select button for the AOI
var selectAoi = ui.Select({
  items:[COLO,NILE,BETS],
  placeholder:'Select area of interest',
  });
 
// Add a label.
var selectSIAOI = ui.Label({value:'Select area of interest',
style: {fontSize: '18px', fontWeight: 'bold'}});
 
// Add the select AOI panel to the map panel.
panel.add(selectSIAOI)
    .add(selectAoi);

在上面的脚本中,我们使用ui.select()小部件生成一个下拉框,其中列出了代表我们三个研究区域的三个变量。然后我们创建一个标签来确定在这一步应该做什么。在此之后,我们将我们的小部件和标签添加到现有的面板功能中。我们将元素添加到面板的顺序决定了它们在标签上的打印位置。

下拉元素将允许用户定义他们想要查看的增量。
 

此时,GUI 只是在引用局部变量;元素之间还没有真正的交互。因此,如果您运行该脚本,您可以选择一个 AOI。但这样做不会触发任何事件。在下一节中,我们将介绍作为此 GUI 基础的主要地理处理代码。

5.5嵌套函数

正如我们之前提到的,RMET 工具中的所有处理都需要某种类型的用户输入。这意味着大多数流程需要编写为函数,以便在它们接受的信息方面具有灵活性。这些函数通常嵌套在不同的函数中。了解嵌套函数的结构将需要起草一个工作流程来了解需要哪些元素以及何时需要。下面是我们第一个函数的嵌套组件示例,它为“Delta Watch” GUI 执行大部分计算。这是一个很大的功能,有很多活动部件,因此是图表。

“Delta Watch”代码中主要地理处理功能的概念图。
 

此过程的最终结果是一组与用户选择的年份和地区相匹配的图像。代码的其他方面将采用这些输出并产生我们感兴趣的附加值产品。基于选定年份的用户定义阈值和/或 NDVI 变化图的 NDVI 二进制图时间。

在这里,我们开始通过添加组件来构建此功能。

// Container function to create Image for mapping.
function applyFilter(){
 
}

第一步是创建一个包装器。包装器是一个术语,用于定义将包含此步骤中存在的多个函数的特征。通过将该applyFilter()函数用作包装器,我们可以将所有地理处理步骤嵌套在一个函数中。这将允许我们调用单个元素来执行完整的独特功能列表,并进一步增加代码的清晰度。

5.5.1定义感兴趣区域

我们功能的第一个元素applyFilter是定义感兴趣的区域。当您将这些代码夹添加到您的脚本时,请确保它们包含在函数的大括号“{}”中applyFilter

/*
Defining the area of interest
*/
 
function setAreaOfInterest(){
  aoi = selectAoi.getValue();
  if (aoi == COLO){
      areaOfInterest = colo;
  }//sets the area to nile river delta
  else if(aoi == NILE){
      areaOfInterest = nile;
  }//sets the area of interest to
  else if (aoi == BETS){
      areaOfInterest = bets;
  }
};
 
setAreaOfInterest();
 
geometry = areaOfInterest

setAreaOfInterest()函数使用该getValue()函数将预定义的位置之一声明为 aoi 对象。然后通过一系列子句语句,将局部定义的变量areaOfInterest设置为等于所选位置的几何形状。运行函数后,setAreaOfInterest()我们将geometry对象定义为等于areaOfInterest对象。我们将geometry在更多函数中使用全局定义的对象。

setAreaOfInterest()函数保存在applyFilter(). 接下来的几段代码也将出现在applyFilter()函数内部。
 

5.5.2定义年份
/*
Defining years for image selection
*/
    yr1 = selectYr1.getValue(); //Input for Landsat Binary Map
    yr2 = selectYr2.getValue(); //Input for Landsat Change Map Start Year
    yr3 = selectYr3.getValue(); //Input for Landsat Change Map End Year

在这种情况下,变量selectYr1, selectYr2selectYr3在 GUI 中定义并由用户设置。我们想要检索这些值并将它们声明为我们早期建立的全局变量之一。这是相对简单的。变量yr1, yr2yr3之前使用var yr1结构声明的。因此,我们可以将这些元素设置为等于函数返回的值getValue()

5.5.3收集图像

现在我们已经确定了几何和年份,我们可以编译我们的图像。我们将通过构建一个字典来做到这一点,该字典包含 Landsat 5,7 和 8 集合中每年的单个图像。这是我们在模块 8中首次使用的字典的改编版本。

对于每个传感器,我们每年都会生成一个中值缩小的图像,图像是在 4 月到 9 月之间拍摄的。这个时间框架代表了科罗拉多河沿岸的生长季节。它位于北纬 32 度,是距离赤道最远的地方,可能是我们河流三角洲生态系统中最明显的生长季节。也就是说,我们的工作假设我们的数据范围还将捕捉南半球马达加斯加贝齐博卡河三角洲的植被变化。

Landsat 词典的示例打印语句。有 35 年的可用数据,每年都有一个多波段图像。
 

5.5.4定义指数

在上面的步骤中,我们声明了一个函数。在下一步中,我们将生成一些附加函数,这些函数将用于根据constructLSDict函数的输出生成 NDVI 值。

/*
Generating indices
*/
// A function to compute NDVI for Landsat 5 and 7.
var NDVI57 = function(image) {
  return image.normalizedDifference(["B4","B3"]).rename("NDVI");
};
 
// A function to compute NDVI for Landsat 8.
var NDVI8 = function(image) {
  return image.normalizedDifference(["B5","B4"]).rename("NDVI");
};

在这部分脚本中,我们尝试利用从 5 到 8 的完整 Landsat 系列。这需要从可能具有不同波段排列的多个传感器中提取数据。Landsat 5 和 7 都具有相同的 NDVI 波段,因此我们可以为它们使用单​​个函数,但将为 Landsat 8 使用不同的函数。

5.5.5定义要使用的图像集合

我们现在将定义一个函数,该函数采用年份和几何形状并返回单个图像。

/*
Selecting bands based on years
*/
 
var selectYearAddBands = function(dictionary, year, geometry)
{
    if (year >= 2012) {
      var imageYear = ee.Image(dictionary.get(year)).clip(geometry)
      var outImage = imageYear.addBands(NDVI8(imageYear))
    } else if (year == 2011) {
      var imageYear = ee.Image(dictionary.get(year)).clip(geometry)
      var outImage = imageYear.addBands(NDVI57(imageYear))
    } else {
      var imageYear = ee.Image(dictionary.get(year)).clip(geometry)
      var outImage = imageYear.addBands(NDVI57(imageYear))
    }
  return (outImage)
};

此函数实际上是一个条件语句,它使用输入年份来确定要使用的年份,从而确定要应用的 NDVI 算法。在我们的案例中,我们使用的是单波段 NDVI,因此这不是最有效的方法,但它确实可以灵活地添加更多波段。

来自 RMET 代码的示例。如果需要更多索引,selectYearAddBands()可以轻松添加。只需定义函数以生成感兴趣的索引并使用addBands()函数添加它,就像我们对 NDVI 所做的那样。
 

5.5.6将感兴趣的图像设置为预定义的局部变量

最后,我们调用 final 函数来根据用户定义的三个年份生成三个图像。

 
// Make LS dictionary and select years of interest.
var dict_ls = constructLSDict(geometry);
  Fimageyr1 = selectYearAddBands(dict_ls, yr1, geometry); //LS BINARY
  Fimageyr2 = selectYearAddBands(dict_ls, yr2, geometry); //LS Change Start Year
  Fimageyr3 = selectYearAddBands(dict_ls, yr3, geometry); //LS Change End Year

结果是三个图像保存为可以被其他函数调用的全局变量。获得三个图像是一个漫长的过程,但是对于 GUI 开发,需要大量工作才能全面和灵活。

重要提示:过去的 6 个代码块都包含在第一个函数applyFilter()中,请确保它在您的代码编辑器中表示。这样做是为了让您可以调用单个函数来返回您的三个图像。

// Function to create Image for mapping.
function applyFilter(){
  // include all 6 previous code chucks within these brackets.
}

如果您此时要运行代码,则不会发生任何事情。applyFilter在调用该函数之前,不会发生任何处理。在此之前,我们需要确保 GUI 上有一个位置,用户可以在其中定义他们感兴趣的年份。

applyFilter()您可以通过选择旁边的箭头在代码编辑器中最小化函数来确保所有元素都在函数内。
 

5.6生成二进制 NDVI 地图

生成二进制和差异图的函数相当简单。在继续之前,仍然值得在概念图中定义所有组件和连接。

二值地图制作地理处理步骤的广义工作流程。
 

5.6.1建立二进制面板元素

在创建地理处理步骤之前,我们将生成保存它们的面板元素。

/*
Defining time frame for Binary MAP
*/
 
var durpanel = ui.Label({
  value:'Select Year for Landsat 30m Binary Map',
  style:{fontSize: '18px', fontWeight: 'bold'}});
 
var selectYr1 = ui.Textbox({placeholder: 'Year',  value: '2018',
  style: {width: '100px'}});
 
var datasetRange_label = ui.Label('Choose year from 1984 - 2018      ',
  {margin: '0 0 0 10px',fontSize: '12px',color: 'gray'});
 
panel.add(durpanel)
  .add(datasetRange_label)
  .add(selectYr1);

与前面的示例一样,我们正在创建标签并按照我们希望它们显示的顺序将它们添加到主面板。

我们希望用户能够通过改变在植被之间或不在 GUI 内绘制线的位置来调整被认为是“绿色”的植被。这是一个重要的步骤:允许用户根据所选河流三角洲的生态调整数据的呈现方式。NDVI 值基于连续比例,滑块是调整这些类型值的适当小部件。

// Add a slider bar widget.
var NDVIslider = ui.Slider();
// Set a default value.
NDVIslider.setValue(0.17);  
NDVIslider.onChange(function(value) {
  Map.layers().get(0);
});
// Create a button that will run the function that creates the NDVI binary object.
var NDVIBINMAP = ui.Button({label:'Landsat NDVI Map' , onClick:NDVIBIN});
// Add slider and Button to the map.
panel.add(NDVIslider)
  .add(NDVIBINMAP);

上面的代码块中列出了很多新功能。创建滑块遵循创建标签和文本框的形式。使用该setValue()函数,我们可以定义用户不进行更改时将使用的初始值。该函数是Event对象onChange()的第一个示例。事件会查找滑块上值的变化。如果发生此更改,它将获取该新值。该对象是地图图层,因为它位于地图的面板上。该函数在地图对象上调用并返回第一个也是唯一的值。onChange()NDVIslideronChange()NDVIslider

5.6.2生成二进制映射

函数中的最后一个变量创建ui.Button. 这个小部件函数中有一个内置的事件对象。单击发生后,该onClick参数将运行一个函数。下面我们将定义NDVIBIN()单击时函数将如何运行。


```cpp
//Function to create a binary NDVI map for a user selected year and AOI.
function NDVIBIN (){
    applyFilter();
    var Nimage = Fimageyr1.expression(
    "NDVI >= thresh1", {
      'NDVI': Fimageyr1.select('NDVI').clip(geometry),
      'thresh1' : NDVIslider.getValue()}); //Slider bar input
 
      var NDVIMimage = Nimage.updateMask(Nimage.gt(0)); //Mask 0 values
      Map.centerObject(geometry); //Center on AOI
      Map.addLayer(NDVIMimage, VEG, 'NDVI Landsat Binary Map(' + yr1+ ')');
}

   <p style="margin-left:0;"><span style="color:#333333;"><span style="background-color:#ffffff;">这个函数将创建我们的第一个实际地图输出。当用户按下“Landsat NDVI Map”按钮时,该功能运行。如果首先调用<code>applyFilter()</code>函数,则根据用户定义的年份从 Landsat 集合返回中值缩小图像。从那里我们使用表达式来确定 NDVI 大于阈值的所有位置,该阈值也由用户定义。在快速遮罩以删除所有低于阈值的值后,我们将图像添加到地图并将地图居中放置在图像上,以便用户可以立即查看它。</span></span></p> 
   <span style="color:#333333;"><span style="background-color:#ffffff;">有了这两个元素,我们可以看到我们的 GUI 开始增长。</span></span> 
   <p><img alt="" height="383" src="https://img-blog.csdnimg.cn/3aeb9e4cd8034301942da7eea5511339.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAR0VF5rC055Sf5oCB56m66Ze0,size_16,color_FFFFFF,t_70,g_se,x_16" width="634"></p> 
   <p style="margin-left:0;"><span style="color:#333333;"><span style="background-color:#ffffff;"><em>包含二进制映射选项的 GUI。</em></span></span><br> &nbsp;</p> 
   <p style="margin-left:0;"><span style="color:#333333;"><span style="background-color:#ffffff;">此时,如果您尝试生成它,它将失败,因为未定义函数中<code>Landsat NDVI Map</code>存在的变量。<code>applyFilter()</code>我们现在将讨论这些最后的元素。</span></span></p> 
  </div> 
 </div> 
 <div id="generating-the-ndvi-difference-maps"> 
  <h3><a name="t28"></a><span style="color:#333333;"><span style="background-color:#ffffff;"><strong><span style="color:inherit;">5.7生成 NDVI 差异图</span></strong></span></span></h3> 
  <p style="margin-left:0;"><span style="color:#333333;"><span style="background-color:#ffffff;">在这种情况下,差异图是通过减去两个图像来创建的。我们的最终代码会将差异图像和两个原始图像添加到地图中。</span></span></p> 

```cpp
/*
Change maps
*/
 
// Generate a change map based on a user defined STARTYEAR, ENDYEAR, AND AOI.   
function GREENER() {
    applyFilter();
    var date1 = Fimageyr2.select('NDVI'); //Select NDVI Values for Start Year
    var date2 = Fimageyr3.select('NDVI'); //Select NDVI values for End Year
    var diff = date2.subtract(date1); //Subtract rasters to get magnitude of change
    var trend = date2.gt(date1); //Creates a binary where pixels that have a greater NDVI in year two are assigned a value of 1
    var final = trend.multiply(diff).clip(geometry); //Shows magnitude for greening trend
 
    Map.centerObject(geometry); // Center on AOI
    Map.addLayer(final, MG, 'Areas that have become more green(' + yr2 + ')-(' + yr3 + ')');
    Map.addLayer(date1, visNDVI, 'NDVI (' + yr2 + ')', false); //Creates a layer with the index in the Start Year.
    Map.addLayer(date2, visNDVI, 'NDVI(' + yr3 + ')', false); //Creates a layer with the index in the End Year.
  }
// Same process for areas that decrease in greenness
function LESSGREEN() {
      applyFilter();
    var date1 = Fimageyr2.select('NDVI'); //Select NDVI Values for Start Year
    var date2 = Fimageyr3.select('NDVI'); //Select NDVI values for End Year
    var diff = date2.subtract(date1); //Subtract rasters to get maginitude of change
    var trend = date2.lt(date1); //Creates a binary where pixels that have a lesser NDVI in year two are assigned a value of 1
    var final = trend.multiply(diff).clip(geometry); //Shows magnitude for less green trend
 
    Map.centerObject(geometry); // Center on AOI
    Map.addLayer(final, LG, 'Areas that have become less green(' + yr2 + ')-(' + yr3 + ')');
    Map.addLayer(date1, visNDVI, 'NDVI(' + yr2 + ')', false); //Creates a layer with the index in the Start Year.
    Map.addLayer(date2, visNDVI, 'NDVI(' + yr3 + ')', false); //Creates a layer with the index in the End Year.
  };

就像二进制地图一样,我们调用该applyFilter()函数来收集我们的图像。gtlt函数阈值图像的值。这定义了经历 NDVI 值损失或增加的位置。此函数的二进制输出乘以两个值之间的差。这可确保在最终图像中仅找到表示变化方向的值。

5.7.1开发变更图的面板元素

接下来,我们将为更改图添加 GUI 元素。

// Define the textbox for the 1st and 2nd year.
var selectYr2 = ui.Textbox({placeholder: 'Year',  value: '1985',
  style: {width: '100px'}});
var selectYr3 = ui.Textbox({placeholder: 'Year',  value: '2018',
  style: {width: '100px'}});
// define the labels needed for GUI
var changepanel = ui.Label({
  value:'Select Years for Landsat 30m Change Map',
  style:{fontSize: '18px', fontWeight: 'bold'}});
var datasetRange_label2 = ui.Label('Start Year (1984 - )          ',
  {margin: '0 0 0 10px',fontSize: '12px',color: 'gray'});
var datasetRange_label3 = ui.Label('End Year (-2018)     ',
  {margin: '0 0 0 10px',fontSize: '12px',color: 'gray'});
// Create two buttons that will add the greener or lessGreen images to the map
var GRMAP = ui.Button('More Green Trend Map', GREENER);
var LGMAP = ui.Button('Less Green Trend Map', LESSGREEN);
// Add all elements to the panel in the correct order.
panel.add(changepanel)
  .add(ui.Panel([datasetRange_label2, datasetRange_label3],ui.Panel.Layout.flow('horizontal')))
  .add(ui.Panel([selectYr2, selectYr3],ui.Panel.Layout.flow('horizontal')))
  .add(GRMAP)
  .add(LGMAP);

虽然代码块中有很多步骤,但只有一个新元素,并且在最后出现。在整个过程中,我们创建了标签和按钮,单击时将调用特定的地理处理函数,允许用户确定他们想要观察的年份。出于易读性的目的,我们创建了一个新面板,将两个对象水平相邻放置,而不仅仅是添加数据范围和选择年份框。然后我们将该面板添加到现有面板中以创建有效的嵌套面板。这根本不会改变 GUI 的外观,因为我们没有将新面板添加到小睡中。

添加最终元素后,我们可以运行二进制文件并更改映射。在这里,我们展示了贝齐博卡三角洲 1990 年至 2018 年间绿化的区域。
 

5.8重置地图

GUI 的最后一个功能是休息按钮。

var resetButton = ui.Button('Reset Map', reset);
panel.add(resetButton);

和之前所有的按钮一样,这个元素reset()在被选中时调用一个函数。下面的reset()功能允许用户从地图中删除所有元素,为他们提供一个干净的石板来提出新问题。

/*
Reset Map
*/
function reset(){
  Map.clear();
}

我们有了它,一套完整的功能 GUI 代码,它为我们提供了一些关于我们世界上三个三角洲植被生长变化的重要信息。

6结论

在此过程结束时,您将拥有一个 GEE 脚本,该脚本生成一个 GUI,允许用户询问有关三个主要河流三角洲内 NDVI 随时间变化的问题。这种允许您的最终用户和合作伙伴使用遥感数据提出自己的问题的能力是将他们的专业知识整合到您的工作中的关键一步。虽然此模块中的方法并非旨在回答特定的管理问题,但调整此代码将允许您开始开发自己的 GUI,以针对与项目利益相关者相关的研究问题量身定制。由于此代码可用于查看 Web 浏览器,因此 GUI 的最终用户将使用交互式 Web 地图。由于易于使用,这些是人们想要的通信工具。所以,虽然需要大量的计划和努力才能实现它,但始终值得考虑添加这些动态元素是否值得。这是您的最终用户应该回答的问题,因为如果您可以让他们保持参与,那么您的内容更有可能被使用。

6.1 GUI的完整代码

/*
Global Variables
*/
var aoi,
    areaOfInterest,
    Fimageyr1,Fimageyr2,Fimageyr3,
    geometry,
    thresh,
    yr1,yr2,yr3
 
/*
Visualization parameters
*/
 
var VEG = {"opacity":1,"palette":["2ddc0d"],"min":0,"max":1}
var MG = {"opacity":1, "palette":["ffffff", "085a16"], "min":0, "max":2}
var LG = {"opacity":1, "palette":["ff7c0b", "ffffff"], "min":-2, "max":0}
var visNDVI = {min: -1, max: 1, palette: ['blue', 'white', 'green']};
var FLG = {"opacity":1, "palette":[ "ffffff", "ff7c0b"], "min":0, "max":2}
 
/*
Create the panel
*/
 
// Generate main panel and add it to the map.
var panel = ui.Panel({style: {width:'33.333%'}});
ui.root.insert(0,panel);
 
// Define title and description.
var intro = ui.Label('Delta Watch: An Example GEE GUI ',
  {fontWeight: 'bold', fontSize: '24px', margin: '10px 5px'}
);
var subtitle = ui.Label('Use 30 m Landsat 5,7,8 imagery to'+
  ' visualize changes in delta vegetation patterns over time.'+
  ' Select from multiple area of interest and type of visualization; single year binary '+
  ' or change over time.', {});
 
// Add title and description to the panel  
panel.add(intro).add(subtitle);
/*
Define study areas.
*/
// Generate polygons for each study region.
var colo = ee.Geometry.Polygon([[-115.29,32.20], [-114.89,31.17],
   [-114.40,31.64], [-115.14,32.19], [-115.29,32.20]]),
    nile = ee.Geometry.Polygon([[28.76,31.49],[30.69,29.91],
      [31.52, 29.96],[32.89,31.43],[31.00,31.76],[28.76,31.49]]),
    bets = ee.Geometry.Polygon([[46.19,-15.66],[46.25,-16.04],
      [46.51,-16.15],[46.70,-16.06],[46.19,-15.66]])
 
// Define labels for each study region.
var COLO = 'Colorado River Delta',
    NILE = 'Nile River Delta',
    BETS = 'Betsiboka River Delta'
 
/*
Select Area of INTEREST
*/
 
// Define the select button for the AOI
var selectAoi = ui.Select({
  items:[COLO,NILE,BETS],
  placeholder:'Select area of interest',
  });
 
// Add a label.
var selectSIAOI = ui.Label({value:'Select area of interest',
style: {fontSize: '18px', fontWeight: 'bold'}});
 
// Add the select AOI panel to the map panel.
panel.add(selectSIAOI)
    .add(selectAoi);
 
//Function to create a binary NDVI map for a user selected year and AOI.
function NDVIBIN (){
    applyFilter();
    var Nimage = Fimageyr1.expression(
    "NDVI >= thresh1", {
      'NDVI': Fimageyr1.select('NDVI').clip(geometry),
      'thresh1' : NDVIslider.getValue()}); //Slider bar input
 
      var NDVIMimage = Nimage.updateMask(Nimage.gt(0)); //Mask 0 values
      Map.centerObject(geometry); //Center on AOI
      Map.addLayer(NDVIMimage, VEG, 'NDVI Landsat Binary Map(' + yr1+ ')');
}
 
// Container function to create Image for mapping.
function applyFilter(){
/*
Defining the area of interested
*/
 
function setAreaOfInterest(){
  aoi = selectAoi.getValue();
  if (aoi == COLO){
      areaOfInterest = colo;
  }//sets the area to nile river delta
  else if(aoi == NILE){
      areaOfInterest = nile;
  }//sets the area of interest to
  else if (aoi == BETS){
      areaOfInterest = bets;
  }
}
 
setAreaOfInterest();
 
geometry = areaOfInterest
 
/*
Defining years for image selection
*/
    yr1 = selectYr1.getValue(); //Input for Landsat Binary Map
    yr2 = selectYr2.getValue(); //Input for Landsat Change Map Start Year
    yr3 = selectYr3.getValue(); //Input for Landsat Change Map End Year
 
/*
Creating image dictionaries.
*/
 
var constructLSDict = function(geometry)
  /*
  This function takes in a geometry feature from the GUI and uses it to
  generate a dictionary of median reduce images for the specified date
  range
  inputs
  geometry = defined by the GUI
  Features to change
  y_list = set based on sensor type
  imagecollection = unique id for collection type
  filterDate = cat() this is used to refine the month and day of start and end time
  With adaptation this should be flexible across sensors and times
  */
  {
  var startMonth = "-04-05"
  var endMonth = "-09-30"
  // Construct a dictionary for landsat 8 imagery.
  var y_list = ee.List.sequence(2013, 2018)
  var ystr_list = y_list.map(function(y){return ee.Number(y).format('%1d')})
  var ls8 = y_list.map(function(y){return ee.ImageCollection('LANDSAT/LC08/C01/T1_SR')
                                          .filterDate(ee.String(ee.Number(y).format('%1d')).cat(startMonth),
                                                      ee.String(ee.Number(y).format('%1d')).cat(endMonth))
                                          .filterBounds(geometry)
                                          .filterMetadata('CLOUD_COVER', 'less_than', 30)
                                          .median()})
  var ls8_dict = ee.Dictionary.fromLists(ystr_list, ls8);
 
  // Construct a dictionary for LS7 imagery
  var y_list7 = ee.List.sequence(2012, 2012)
  var ystr_list7 = y_list7.map(function(y){return ee.Number(y).format('%1d')})
  var ls7 = y_list7.map(function(y){return ee.ImageCollection('LANDSAT/LE07/C01/T1_SR')
                                          .filterDate(ee.String(ee.Number(y).format('%1d')).cat(startMonth),
                                                      ee.String(ee.Number(y).format('%1d')).cat(endMonth))
                                          .filterBounds(geometry)
                                          .filterMetadata('CLOUD_COVER', 'less_than', 30)
                                          .median()})
  var ls7_dict = ee.Dictionary.fromLists(ystr_list7, ls7);
 
  // Combine the LS8 and LS7 dictionaries.
  var ls8_ls7_dict = ls8_dict.combine(ls7_dict)
 
  // Construct a dictionary for Landsat 5.
  var y_list5 = ee.List.sequence(1984,2011)
  var ystr_list5 = y_list5.map(function(y){return ee.Number(y).format('%1d')})
  var ls5 = y_list5.map(function(y){return ee.ImageCollection('LANDSAT/LT05/C01/T1_SR')
                                          .filterDate(ee.String(ee.Number(y).format('%1d')).cat(startMonth),
                                                      ee.String(ee.Number(y).format('%1d')).cat(endMonth))
                                          .filterBounds(geometry)
                                          .filterMetadata('CLOUD_COVER', 'less_than', 30)
                                          .median()})
  var ls5_dict = ee.Dictionary.fromLists(ystr_list5, ls5);
  // Combine the LS5 with the LS8/7 dictionary
  var LS_Dict = ls8_ls7_dict.combine(ls5_dict)
 
  return(LS_Dict)
}
 
/*
Generating indices
*/
// A function to compute NDVI for Landsat 5 and 7.
var NDVI57 = function(image) {
  return image.normalizedDifference(["B4","B3"]).rename("NDVI");
};
 
// A function to compute NDVI for Landsat 8.
var NDVI8 = function(image) {
  return image.normalizedDifference(["B5","B4"]).rename("NDVI");
};
 
 
/*
Selecting bands based on years
*/
 
var selectYearAddBands = function(dictionary, year, geometry)
{
    if (year >= 2012) {
      var imageYear = ee.Image(dictionary.get(year)).clip(geometry)
      var outImage = imageYear.addBands(NDVI8(imageYear))
    } else if (year == 2011) {
      var imageYear = ee.Image(dictionary.get(year)).clip(geometry)
      var outImage = imageYear.addBands(NDVI57(imageYear))
    } else {
      var imageYear = ee.Image(dictionary.get(year)).clip(geometry)
      var outImage = imageYear.addBands(NDVI57(imageYear))
    }
  return (outImage)
};
 
 
// Make LS Dictionary and Select years of interest.
var dict_ls = constructLSDict(geometry);
  Fimageyr1 = selectYearAddBands(dict_ls, yr1, geometry); //LS BINARY
  Fimageyr2 = selectYearAddBands(dict_ls, yr2, geometry); //LS Change Start Year
  Fimageyr3 = selectYearAddBands(dict_ls, yr3, geometry); //LS Change End Year
 
}
 
 
/*
Defining time frame for Binary MAP
*/
 
var durpanel = ui.Label({
  value:'Select Year for Landsat 30m Binary Map',
  style:{fontSize: '18px', fontWeight: 'bold'}});
 
var selectYr1 = ui.Textbox({placeholder: 'Year',  value: '2018',
  style: {width: '100px'}});
 
var datasetRange_label = ui.Label('Choose year from 1984 - 2018      ',
  {margin: '0 0 0 10px',fontSize: '12px',color: 'gray'});
 
panel.add(durpanel)
  .add(datasetRange_label)
  .add(selectYr1);
 
// Add a slider bar widget
var NDVIslider = ui.Slider();
// Set a default value.
NDVIslider.setValue(0.17);  
NDVIslider.onChange(function(value) {
  Map.layers().get(0);
});
// Create a button that will run the function that creates the NDVI binary object
var NDVIBINMAP = ui.Button({label:'Landsat NDVI Map' , onClick:NDVIBIN});
// Add slider and Button to the map.
panel.add(NDVIslider)
  .add(NDVIBINMAP)
 
/*
Change maps
*/
 
// Generate a change map based on a user defined STARTYEAR, ENDYEAR, AND AOI.   
function GREENER() {
    applyFilter();
    var date1 = Fimageyr2.select('NDVI'); //Select NDVI Values for Start Year
    var date2 = Fimageyr3.select('NDVI'); //Select NDVI values for End Year
    var diff = date2.subtract(date1); //Subtract rasters to get maginitude of change
    var trend = date2.gt(date1); //Creates a binary where pixels that have a greater NDVI in year two are assigned a value of 1
    var final = trend.multiply(diff).clip(geometry); //Shows magnitude for greening trend
 
    Map.centerObject(geometry); // Center on AOI
    Map.addLayer(final, MG, 'Areas that have become more green(' + yr2 + ')-(' + yr3 + ')');
    Map.addLayer(date1, visNDVI, 'NDVI (' + yr2 + ')', false); //Creates a layer with the index in the Start Year.
    Map.addLayer(date2, visNDVI, 'NDVI(' + yr3 + ')', false); //Creates a layer with the index in the End Year.
  }
// Same process for areas that decrease in greenness
function LESSGREEN() {
      applyFilter();
    var date1 = Fimageyr2.select('NDVI'); //Select NDVI Values for Start Year
    var date2 = Fimageyr3.select('NDVI'); //Select NDVI values for End Year
    var diff = date2.subtract(date1); //Subtract rasters to get magnitude of change
    var trend = date2.lt(date1); //Creates a binary where pixels that have a lesser NDVI in year two are assigned a value of 1
    var final = trend.multiply(diff).clip(geometry); //Shows magnitude for less green trend
 
    Map.centerObject(geometry); // Center on AOI
    Map.addLayer(final, LG, 'Areas that have become less green(' + yr2 + ')-(' + yr3 + ')');
    Map.addLayer(date1, visNDVI, 'NDVI(' + yr2 + ')', false); //Creates a layer with the index in the Start Year.
    Map.addLayer(date2, visNDVI, 'NDVI(' + yr3 + ')', false); //Creates a layer with the index in the End Year.
  }
 
// Define the textbox for the 1st and 2nd year
var selectYr2 = ui.Textbox({placeholder: 'Year',  value: '1985',
  style: {width: '100px'}});
var selectYr3 = ui.Textbox({placeholder: 'Year',  value: '2018',
  style: {width: '100px'}});
// define the labels needed for GUI
var changepanel = ui.Label({
  value:'Select Years for Landsat 30m Change Map',
  style:{fontSize: '18px', fontWeight: 'bold'}});
var datasetRange_label2 = ui.Label('Start Year (1984 - )          ',
  {margin: '0 0 0 10px',fontSize: '12px',color: 'gray'});
var datasetRange_label3 = ui.Label('End Year (-2018)     ',
  {margin: '0 0 0 10px',fontSize: '12px',color: 'gray'});
// Create two buttons that will add the greener or lessGreen images to the map
var GRMAP = ui.Button('More Green Trend Map', GREENER);
var LGMAP = ui.Button('Less Green Trend Map', LESSGREEN);
// Add all elements to the panel in the correct order.
panel.add(changepanel)
  .add(ui.Panel([datasetRange_label2, datasetRange_label3],ui.Panel.Layout.flow('horizontal')))
  .add(ui.Panel([selectYr2, selectYr3],ui.Panel.Layout.flow('horizontal')))
  .add(GRMAP)
  .add(LGMAP);
 
var resetButton = ui.Button('Reset Map', reset);
panel.add(resetButton);
 
/*
Reset Map
*/
function reset(){
  Map.clear();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1201377.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数字双向码、密勒码、传号反转(CMI)码、AMI、HDB3的编码规则和功率谱解析+眼图

数字双向码、密勒码、传号反转&#xff08;CMI&#xff09;码、AMI、HDB3的编码规则和功率谱解析眼图 本文主要涉及数字双向码、密勒码、传号反转&#xff08;CMI&#xff09;码、AMI、HDB3的编码规则,优缺点和功率谱解析以及眼图的分析。关于简单二元码大家可以参考简单二元码…

Python---元组的相关操作方法

由于元组中的数据不允许直接修改&#xff0c;所以其操作方法大部分为查询方法。 编号函数作用1元组[索引]根据索引下标查找元素2index()查找某个数据&#xff0c;如果数据存在返回对应的下标&#xff0c;否则报错&#xff0c;语法和列表、字符串的index方法相同3count()统计某…

第十七章jQuery中的事件与动画

一。常用事件&#xff1a; 1.鼠标事件&#xff1a; mouseover()&#xff1a;在鼠标进入内容后一直显示事件 mouseout()&#xff1a;在鼠标离开内容后一直显示事件 mouseenter()&#xff1a;在进入刹那间显示事件 mouseleave()&#xff1a;在退出刹那间显示事件 案例&#xf…

v-bind和v-model

目录 前言 v-bind 作用 语法格式 编译原理 简写 v-model 作用 使用方法 v-bind和v-model的区别和联系 前言 本文我们来了解一下模板语法之指令语法中的v-bind和v-model v-bind 作用 v-bind可以让html标签的某个属性的值产生动态的效果 语法格式 <html标签 v-bin…

终端安全/SOC安全/汽车信息安全大课来袭-共计204节课

在近两年的时间里&#xff0c;我投入了大量的心血和精力&#xff0c;不仅创作了数千篇精美的图片&#xff0c;还编写了超过1000篇文章&#xff0c;以及数百篇内容丰富的PPT。经过这番努力我终于成功地构建出两套系统化的学习课程&#xff0c;它们分别是“Trustzone/TEE/安全从入…

【Android】Android apk 逆向编译

链接&#xff1a;https://pan.baidu.com/s/14r5s9EJwQgeLK5cCb1Gq1Q 提取码&#xff1a;qdqt 解压jadx 在 lib 文件内找到 jadx-gui-1.4.7.jar 打开cmd 执行 &#xff1a;java -jar jadx-gui-1.4.7.jar示列&#xff1a;

U-Mail邮件中继,让海外邮件沟通更顺畅

在海外&#xff0c;电子邮件是人们主要的通信工具&#xff0c;尤其是商务往来沟通&#xff0c;企业邮箱是标配。这主要是因为西方国家互联网发展较早&#xff0c;在互联网早期&#xff0c;电子邮件技术较为成熟&#xff0c;大家都用电子邮件交流&#xff0c;于是这成了一种潮流…

【debug】解决Kali虚拟机开机黑屏,左上角光标一直闪动无法开机问题

做网络攻防实验时&#xff0c;突然Kali无法打开&#xff0c;遇到这个问题。。。。。。 遇到的问题 突然kali虚拟机变成如下黑屏&#xff0c;无法开机&#xff0c;左上角光标闪动&#xff0c;重启无效。 解决办法 在上图界面&#xff0c;按Ctrl F3&#xff08;不同电脑快捷键…

设计模式-工厂方法

工厂方法是一种创建型设计模式&#xff0c;其在父类中提供一个创建对象的方法&#xff0c;允许子类决定实例化对象的类型。 问题 假设你开设了一个汽车工厂。创业初期工厂只能生产宝马这一款车&#xff0c;因此大部分代码都位于名为宝马的类中。 工厂效益非常好&#xff0c;为…

Spring Cloud学习(七)【Docker 容器】

文章目录 初识 DockerDocker 介绍Docker与虚拟机Docker架构安装 Docker Docker 基本操作镜像相关命令容器相关命令数据卷 Dockerfile 自定义镜像镜像结构Dockerfile DockerComposeDockerCompose介绍安装DockerCompose Docker镜像仓库常见镜像仓库服务私有镜像仓库 初识 Docker …

CSS花边001:无衬线字体和有衬线字体

网站中我们看到过很多字体&#xff0c;样子各有千秋。通常针对结构&#xff0c;区分为有衬字体&#xff08;serif&#xff09; 和无衬字体&#xff08;sans-serif&#xff09;。今天我们聊一下这个话题。 什么是有衬字体&#xff0c;什么是无衬字体&#xff1f; 衬线字体&…

【c++随笔12】继承

【c随笔12】继承 一、继承1、继承的概念2、3种继承方式3、父类和子类对象赋值转换4、继承中的作用域——隐藏5、继承与友元6、继承与静态成员 二、继承和子类默认成员函数1、子类构造函数 二、子类拷贝构造函数3、子类的赋值重载4、子类析构函数 三、单继承、多继承、菱形继承1…

[ASP]数据库编辑与管理V1.0

本地测试&#xff1a;需要运行 ASP专业调试工具&#xff08;自己搜索下载&#xff09; 默认登陆口令&#xff1a;admin 修改口令&#xff1a;打开index.asp找到第3行把admin"admin"改成其他&#xff0c;如admin"abc123" 程序功能齐全&#xff0c;代码精简…

CLIP:用文本作为监督信号训练可迁移的视觉模型

Radford A, Kim J W, Hallacy C, et al. Learning transferable visual models from natural language supervision[C]//International conference on machine learning. PMLR, 2021: 8748-8763. CLIP 是 OpenAI 在 2021 年初的工作&#xff0c;文章发表在 ICML-2021&#xff0…

并发安全问题之--事物失效问题

并发安全问题之–事物失效问题 事物失效常见的6种原因&#xff1a; 1、事物方法非public修饰 2、非事物方法调用事物方法 3、事物方法抛出的异常被捕获了 4、事物方法抛出的异常类型不对 5、事物传播行为不对&#xff08;事物发生嵌套时有事物传播&#xff09; 6、事物锁属类没…

IP多播需要使用两种协议(IGMP和多播路由选择协议)

目录 IGMP 多播路由选择协议 组播协议包括组成员管理协议和组播路由协议: 组成员管理协议用于管理组播组成员的加入和离开(IGMP) 组播路由协议负责在路由器之间交互信息来建立组播树(多播路由选择协议) IGMP 图中标有 IP 地址的四台主机都参加了一个多播组&#xff0c;其…

逻辑回归-癌症病预测与不均衡样本评估

1.注册相关库(在命令行输入&#xff09; pip install scikit-learn pip install pandas pip install numpy 2.导入相关库 import pandas as pd import numpy as np from sklearn.metrics import classification_report from sklearn.model_selection import train_test_split…

初始MySQL(三)(合计函数,分组函数,字符串相关函数,数字相关函数,时间日期函数,加密函数,流程控制函数)

目录 合计/统计函数 count 返回行的总数 sum 合计函数 - avg group by 字符串相关函数 数学相关函数 时间日期相关函数 加密函数 流程控制函数 合计/统计函数 count 返回行的总数 Select count(*) | count (列名) from tablename [WHERE where_definition] #演…

国际阿里云:Windows实例中数据恢复教程!!!

在处理磁盘相关问题时&#xff0c;您可能会碰到操作系统中数据盘分区丢失的情况。本文介绍了Windows系统下常见的数据盘分区丢失的问题以及对应的处理方法&#xff0c;同时提供了使用云盘的常见误区以及最佳实践&#xff0c;避免可能的数据丢失风险。 前提条件 已注册阿里云账…

C/C++满足条件的数累加 2021年9月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C满足条件的数累加 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 C/C满足条件的数累加 2021年9月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 现有n个整数&#xff0c;将其中个位数…