GEE_API Docs_Tutorials_1.编程基础和Earth Engine API入门

API Docs_Tutorials_1.编程基础和Earth Engine API入门

  • 一.Introduction to JavaScript for Earth Engine(JavaScript导引)
    • 1.Introduction
      • 1.1Hello World
      • 1.2Basic JavaScript data types基本数据类型
    • 2.Earth Engine Objects and Methods(Earth Engine对象和方法)
      • 2.1Strings对象
      • 2.2Numbers对象
      • 2.3Methods on Earth Engine objects对象上的方法
      • 2.4Lists
      • 2.5Casting转换
      • 2.6Dictionaries
      • 2.7Dates日期
      • 2.8Digression: passing parameters by name按名称传递参数
    • 3.Functional Programming Concepts函数式编程的概念
      • 3.1For Loops for循环
      • 3.2If/Else Conditions条件判断
      • 3.3Cumulative Iteration累计迭代
  • 二.The Earth Engine API
    • 1.Introduction to the Earth Engine JavaScript API引入
    • 2.Visualizing Images and Image Bands可视化图像和图像波段
      • 2.1Image Constructor图像构造函数
      • 2.2Configuring the Map配置地图
      • 2.3Adding a layer to the Map在地图上添加一个图层
      • 2.4Digression: Images in Earth Engine
      • 2.5Customizing layer visualization自定义图层可视化
      • 2.6Digression: Palettes调色板
    • 3.Computations using Images用图像计算
      • 3.1Image math图像计算
      • 3.2Image statistics影像统计
      • 3.3Digression: Scale in Earth Engine尺度/比例尺
    • 4.Image Collections图像集
      • 4.1Filtering image collections过滤图像集合
      • 4.2Digression: Displaying RGB images显示RGB图像
      • 4.3Displaying image collections显示图像集合
    • 5.Compositing, Masking, and Mosaicking合成、掩膜和镶嵌
      • 5.1Compositing with Reducers用Reducers做影像合成
      • 5.2Masking掩膜
      • 5.3Mosaicking镶嵌/拼接
    • 6.NDVI, Mapping a Function over a Collection, Quality Mosaicking (NDVI,映射函数到集合上,质量拼接)
      • 6.1Mapping a Function over a Collection在集合上映射函数
      • 6.2Make a greenest pixel composite制作最绿色的像素合成
    • 7.Exporting Charts and Images导出图表和图像
      • 7.1Charting图表
      • 7.2Digression: Simple Cloud Masking for Landsat陆地卫星云掩膜
      • 7.3Exporting Images导出图像

一.Introduction to JavaScript for Earth Engine(JavaScript导引)

1.Introduction

1.1Hello World

要获得更详细的JavaScript教程,请参阅这些Mozilla开发人员资源:https://developer.mozilla.org/en-US/docs/Web/JavaScript
有关编程的介绍和JavaScript示例,请参阅Eloquent JavaScript:https://eloquentjavascript.net/
有关JavaScript编码风格,参阅 Google JavaScript Style Guide:http://google.github.io/styleguide/javascriptguide.xml
print('Hello World!');

单击Run,就会看到 “Hello world!” 被打印到 Console tab上。
在JavaScript中,语句以分号结尾。Earth Engine程序由一组类似这样的语句组成。您可以通过注释阻止代码运行而不删除代码。
注释掉代码的一种方法是在不想运行的代码之前放置两个前斜杠 // 。例如:

// print('Hello World!');

在代码中添加大量注释来描述您要做的事情是一种很好的做法。删除不再做任何事情的注释代码也是很好的。这两种做法都将提高代码的可读性。

1.2Basic JavaScript data types基本数据类型

  1. Strings
    使用变量(variables)来存储对象和图元(primitives)有助于提高代码的可读性。例如,存储字符串对象的变量定义为单引号 ‘’ 或双引号 “” (但不要混合使用它们),首选单引号。创建一个新的字符串并将其存储在一个名为greetString的变量中:
// Use single (or double) quotes to make a string.
var greetString = 'Ahoy there!';
// Use parentheses to pass arguments to functions.使用括号将参数传递给函数
print(greetString);
  1. Numbers
    注意变量是用关键字var定义的。变量也可以存储数字:
// Store a number in a variable.
var number = 42;
print('The answer is:', number);

在这个例子中,注意当 print() 被给出两个用逗号分隔的参数时,每个参数被打印在不同的行上。
3. Lists
用方括号 [] 定义列表。 A list of numbers(数字的列表),例如

// Use square brackets [] to make a list.
var listOfNumbers = [0, 1, 1, 2, 3, 5];
print('List of numbers:', listOfNumbers);

列表还可以存储字符串或其他对象。例如:

// Make a list of strings.
var listOfStrings = ['a', 'b', 'c', 'd'];
print('List of strings:', listOfStrings);
  1. Objects
    JavaScript中的对象是键:值对的字典 (dictionaries of key value) 。使用大括号 {} 创建一个对象(或字典),所以JavaScript中的对象就是指字典。
    例如:
// Use curly brackets {} to make a dictionary of key:value pairs.
var object = {
  foo: 'bar',
  baz: 13,
  stuff: ['this', 'that', 'the other thing']
};
print('Dictionary:', object);
// Access dictionary items using square brackets.使用方括号(键)访问字典项(值)。
print('Print foo:', object['foo']);
// Access dictionary items using dot notation.使用点符号(键)访问字典项(值)
print('Print stuff:', object.stuff);

GEE_API Docs_Tutorials_1.编程基础和Earth Engine API入门_第1张图片
打印结果表明:这里定义的 object 包含3个property(属性)(可以说,只有Objects/字典包含的内容叫做属性property)。object中包含的stuff属性是一个list,又包含3个内容。

注意,您可以通过提供键从字典中获取值。稍后您将学习如何为Earth Engine服务器上的字典执行此操作。
5. Functions
函数是通过对操作集(grouping sets of operations.)进行分组来提高代码可读性和可重用性的另一种方法。
使用function关键字定义一个函数。函数名以字母开头,末尾有一对括号。函数通常接收参数来告诉函数要做什么。这些参数放在括号 () 中。组成函数的语句集放在花括号 {} 内。return关键字表示函数输出是什么。
有几种方法来声明一个函数,但这里我们将使用像这样:

var myFunction = function(parameter1, parameter2, parameter3) {
  statement;
  statement;
  statement;
  return statement;
};

逐行解析上面的那些行。第一行创建了一个新函数,并将其分配给变量myFunction。这个变量可以取任何名字。它定义了以后如何调用函数。函数名后的圆括号中的项(即parameter1、parameter2、parameter3)是参数名,它们也可以被命名为任何名称,不过最好给它们指定与函数外部代码不同的惟一名称。参数的值一旦被传递到函数中,就称为参数。虽然函数可以使用在函数外部声明的变量(全局变量),但是函数参数在函数外部是不可见的。函数可以接受任意多的参数,甚至是零。这里有一个简单的例子,函数只返回它的参数:

// The reflect function takes a single parameter: element.反射函数只接受一个参数
var reflect = function(element) {
  // Return the argument.
  return element;
};
print('A good day to you!', reflect('Back at you!'));

这是一个用户自定义函数的例子。还有很多内置的Earth Engine函数。浏览Guides_Earth Engine Code Editor,了解这些内置函数。下面是一个Earth Engine函数的非常简单的例子:

var aString = ee.Algorithms.String(42);

2.Earth Engine Objects and Methods(Earth Engine对象和方法)

学习如何将JavaScript对象和原语(objects and primitives)放入到Earth Engine容器中( Earth Engine containers),以便发送到服务器并在谷歌进行处理。
反之,如果只是像之前一样不将JavaScript对象和原语放到Earth Engine容器中,那就只是在用网页上的JavaScript处理,而不会发送到服务器并在谷歌进行处理。区别就是仅仅在客户端处理,还是发送到服务器端处理。

2.1Strings对象

例如,定义一个字符串,然后将其放入 ee.String() 容器中,以发送到Earth Engine:

// Define a string, then put it into an EE container.
var aString = 'To the cloud!';
var eeString = ee.String(aString);
print('Where to?', eeString);

ee.Thing作为存在于服务器上的事物的容器。在本例中,首先定义字符串,然后将其放入容器中。您还可以一次性定义容器及其内容。例如:

// Define a string that exists on the server.
var serverString = ee.String('This is on the server.');
print('String on the server:', serverString);

尽管 print() 上的第一个参数只是客户端(client)上的一个字符串,但第二个参数实际上被发送到服务器进行计算,然后再返回。

2.2Numbers对象

使用ee.Number() 在服务器上创建数字对象。例如,使用Math.E JavaScript方法在服务器上创建常量:

// Define a number that exists on the server.
var serverNumber = ee.Number(Math.E);
print('e=', serverNumber);

ee.String()ee.Number() 方法是构造函数。构造函数接受它的参数(可能还有其他参数),将其放在容器中,并将容器及其内容作为Earth Engine对象返回,您可以在代码中操作该对象。任何以ee开始的构造函数都返回一个Earth Engine对象。

2.3Methods on Earth Engine objects对象上的方法

请注意,一旦创建了Earth Engine对象,就必须使用Earth Engine方法来处理它。
在本例中,不能使用JavaScript的 Math.log() (Math.log()是JavaScript的方法,而不是Earth Engine的方法)来处理Earth Engine对象。您必须使用为ee.Numbe定义的等效方法(即Earth Engine的方法):

// Use a built-in function to perform an operation on the number.使用内置函数对数字执行操作
var logE = serverNumber.log();
print('log(e)=', logE);

在本例中,log()ee.Number对象的一个方法。(使用代码编辑器左侧的Docs tab可以查看每个Earth Engine对象类型的所有方法列表,例如ee.Number > log()。请注意,Earth Engine对象的方法返回其他Earth Engine对象。

2.4Lists

将JavaScript列表制作成在服务器上的ee.List对象,可以将JavaScript文字放入容器中,就像数字和字符串一样。Earth Engine还提供了用于制作数字序列的服务器端的便利方法(可理解为Earth Engine方法)。例如:

// Make a sequence the hard way.用笨方法做出一个序列
var eeList = ee.List([1, 2, 3, 4, 5]);
// Make a sequence the easy way!
var sequence = ee.List.sequence(1, 5);
print('Sequence:', sequence);

因为ee.List对象仅存在于服务器上,使用Earth Engine提供的函数与之交互。
例如,要从列表中获取内容,可以使用ee.list对象的 get() 方法:

// Use a method on an ee.List to extract a value.
var value = sequence.get(2);
print('Value at index 2:', value);

2.5Casting转换

有时候,Earth Engine不知道从方法返回的对象(returned from a method)的类型。
作为程序员,您知道前面示例中的value变量是一个number对象。但是如果您尝试使用ee.Numberadd() 方法,你会得到一个错误,像:

value.add is not a function

这在使用 get() 函数时很常见,get() 函数可以返回各种Earth Engine对象,导致返回结果不再是原来的对象类型(使用 get() 函数后,返回的结果不再是原来类型的对象,需要再转换回原来类型)。
要纠正它,请使用ee.Number构造函数转换结果之前的结果value(cast the result):

// Cast the return value of get() to a number.
print('No error:', ee.Number(value).add(3));

2.6Dictionaries

您可以从JavaScript对象构造一个Earth Engine Dictionary,就像字符串、数字和列表一样( strings, numbers and lists)。在构造时,可以使用JavaScript功能初始化Earth Engine对象。在这种情况下,ee.Dictionary 是直接从JavaScript文字对象构造:

// Make a Dictionary on the server.
var dictionary = ee.Dictionary({
  e: Math.E,
  pi: Math.PI,
  phi: (1 + Math.sqrt(5)) / 2
});

// Get some values from the dictionary.
print('Euler:', dictionary.get('e'));
print('Pi:', dictionary.get('pi'));
print('Golden ratio:', dictionary.get('phi'));

// Get all the keys:
print('Keys: ', dictionary.keys());

GEE_API Docs_Tutorials_1.编程基础和Earth Engine API入门_第2张图片
在本例中,注意一旦你有了ee.Dictionary,你必须使用ee.Dictionary上的方法去获取values (与上一课中的JavaScript字典不同)。具体来说,get(key) 返回与key关联的value。由于 get() 返回的对象类型(type of object)可以是任何类型,如果您打算对对象执行任何操作,然后打印它,则需要将其转换为正确的类型(cast it to the right type)。还要注意, keys() 方法返回一个 ee.List

2.7Dates日期

Date对象是Earth Engine表示时间的方式。与前面的示例一样,区分JavaScript Dates对象和Earth Engine ee.Date很重要。构造一个ee.Date可以是从一个字符串,或是从一个JavaScript Date,或是使用ee.Date类提供的静态方法
(详情请参阅 Docs tab中的Date部分)。这个例子演示了从字符串或一个JavaScript date(表示从1970年1月1日午夜开始的毫秒)构造日期的过程:

// Define a date in Earth Engine.
var date = ee.Date('2015-12-31');
print('Date:', date);

// Get the current time using the JavaScript Date.now() method.
var now = Date.now();// JavaScript方法,不是Earth Engine方法 
print('Milliseconds since January 1, 1970', now);

// Initialize an ee.Date object.初始化一个ee.Date对象
var eeNow = ee.Date(now);
print('Now:', eeNow);

Dates对于筛选集合(filtering collections)非常有用,特别是作为 filterDate() 方法的参数。有关集合排序/整理(sorting collections)的更多信息,请参见Get Started的Filtering and Sorting部分。

2.8Digression: passing parameters by name按名称传递参数

Earth Engine方法的参数可以按顺序传递,例如从年、月、日创建一个ee.Date,开始,你可以按照年、月、日顺序传递 fromYMD() 静态方法的参数:

var aDate = ee.Date.fromYMD(2017, 1, 13);
print('aDate:', aDate);

或者,也可以按名称、按任何顺序传递参数。虽然它可能有更多的代码,但它可以提高可读性和可重用性。要按名称传递参数,传入一个JavaScript对象,其中对象的键是方法参数的名称,值是方法的参数。例如:

var theDate = ee.Date.fromYMD({
  day: 13,
  month: 1,
  year: 2017
});
print('theDate:', theDate);

注意,对象属性的名称(键)与ee.Date.fromYMD() docs中指定的名称相匹配。还要注意,作为参数传递的对象可以保存在变量中以供重用,如JavaScript对象示例所示。

3.Functional Programming Concepts函数式编程的概念

Introduction to functional programming函数程序设计

Earth Engine使用并行处理系统在大量机器上进行计算。为了实现这样的处理,Earth Engine利用函数语言常用的标准技术,比如引用透明性和惰性求值(referential transparency和lazy evaluation),以实现显著的优化和效率提高。

函数式编程与过程式编程的主要区别在于没有副作用。它的意思是,您编写的函数不依赖或更新函数外的数据。正如您将在下面的示例中看到的,可以重新构造问题,以便使用没有副作用的函数来解决问题——这些函数更适合并行执行。

3.1For Loops for循环

在Earth Engine中不鼓励使用for循环。使用 map() 操作也可以获得相同的结果,其中指定可以独立应用于每个元素的函数。这允许系统将处理(运算)分配给不同的机器。
下面的示例演示了如何使用 map() 获取一个数字列表和用每个数字的平方创建另一个列表:

// This generates a list of numbers from 1 to 10.
var myList = ee.List.sequence(1, 10);

// The map() operation takes a function that works on each element independently
// and returns a value. You define a function that can be applied to the input.
var computeSquares = function(number) {
  // We define the operation using the EE API.
  return ee.Number(number).pow(2);
};

// Apply your function to each item in the list by using the map() function.
var squares = myList.map(computeSquares);
print(squares);  // [1, 4, 9, 16, 25, 36, 49, 64, 81]

3.2If/Else Conditions条件判断

习惯过程式编程范式的新用户面临的另一个常见问题是在Earth Engine中正确使用if/else条件操作符。
虽然API确实提供了ee.Algorithms.If() 算法,但是强烈建议不使用它,而使用更具函数性的map() 和过滤器方法(filters)。
Earth Engine使用延迟执行,这意味着表达式的求值被延迟到它的实现值被实际需要时。在某些情况下,这种类型的执行模型将评估 ee.Algorithms.If() 语句的真选项和假选项。这可能导致额外的计算和内存使用,具体取决于表达式和执行它们所需的资源。
假设您想解决上述示例的一个变体,其中的任务是计算奇数的平方。在没有if/else条件的情况下,函数方法可以解决这个问题,如下所示:

// The following function determines if a number is even or odd.  
// 确定一个数是偶数还是奇数
// The mod(2) function returns 0 if the number is even and 1 if it is odd (the remainder after dividing by 2).
// mod(2)函数的作用是:如果数字是偶数,则返回0;如果数字是奇数,则返回1(除以2后的余数)。
// The input is multipled by this remainder so even numbers get set to 0 and odd numbers are left unchanged.
// 输入被这个余数相乘,因此偶数被设为0,奇数不变。
var getOddNumbers = function(number) {
  number = ee.Number(number);   
  // Cast the input to a Number so we can use mod.
  // 将输入转换为数字
  var remainder = number.mod(2);
  return number.multiply(remainder);
};

var newList = myList.map(getOddNumbers);

// Remove the 0 values.
var oddNumbers = newList.removeAll([0]);

var squares = oddNumbers.map(computeSquares);
print(squares);  // [1, 9, 25, 49, 81]

这个范例在处理集合时特别适用。
如果希望根据某些条件对集合应用不同的算法,首选方法是首先根据条件筛选集合,然后将不同的函数 map() 到每个子集(subsets)。这允许系统并行化操作。例如:

var collection = ee.ImageCollection('LANDSAT/LC08/C01/T1_TOA');

// Divide the collection into 2 subsets and apply a different algorithm on them.
// 将集合分为两个子集,并对其应用不同的算法。
var subset1 = collection.filter(ee.Filter.lt('SUN_ELEVATION', 40));
var subset2 = collection.filter(ee.Filter.gte('SUN_ELEVATION', 40));

var processed1 = subset1.map(function(image) {
  return image.multiply(2);
});
var processed2 = subset2;

// Merge the collections to get a single collection.
// 合并集合以获得单个集合
var final = processed1.merge(processed2);
print('Original collection size', collection.size());
print('Processed collection size', final.size());

3.3Cumulative Iteration累计迭代

您可能需要执行顺序操作(sequential operation),其中每次迭代的结果将用于后续迭代(subsequent iteration)。Earth Engine为此类任务提供了一个 iterate() 方法。记住,iterate() 是按顺序( sequential manner)执行的,因此对于大型操作来说会很慢。
仅当您不能使用 map() 和过滤器来实现所需的输出时才使用它。

iterate() 的一个很好的演示是用于创建斐波那契数字( Fibonacci number)序列。这里,级数中的每一个数都是前两个数的和。iterate() 函数接受两个参数,一个函数(算法)和一个起始值(function (algorithm) and a starting value.)。函数本身传递两个值,即迭代中的当前值和前一次迭代的结果。下面的示例演示如何在Earth Engine中实现斐波那契序列(ibonacci sequence)。

var algorithm = function(current, previous) {
  previous = ee.List(previous);
  var n1 = ee.Number(previous.get(-1));
  var n2 = ee.Number(previous.get(-2));
  return previous.add(n1.add(n2));
};

// Compute 10 iterations.
var numIteration = ee.List.repeat(1, 10);
var start = [0, 1];
var sequence = numIteration.iterate(algorithm, start);
print(sequence);  // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

二.The Earth Engine API

1.Introduction to the Earth Engine JavaScript API引入

Prerequisites预备知识

  • 注册地球引擎(Signup for Earth Engine)。一旦你被录取(accepted),你将会收到一封电子邮件,里面有更多的信息。
  • 如果您不熟悉JavaScript,请在继续之前查看JavaScript for Earth Engine tutorial。
  • 熟悉 Earth Engine Code Editor,这是在web浏览器中编写Earth Engine JavaScript代码的IDE。了解有关代码编辑器的更多信息(https://developers.google.com/earth-engine/playground)。

2.Visualizing Images and Image Bands可视化图像和图像波段

现在,您已经准备好开始编写Earth Engine JavaScript,开始通过复制以下代码到代码编辑器:

// Instantiate an image with the Image constructor.:使用图像构造函数实例化一个图像
var image = ee.Image('CGIAR/SRTM90_V4');

// Zoom to a location.缩放到一个位置
Map.setCenter(-112.8598, 36.2841, 9); // Center on the Grand Canyon.以the Grand Canyon为中心

// Display the image on the map.
Map.addLayer(image);

单击代码编辑器顶部的Run按钮,就会看到地图上出现一个非常灰色的图像。别担心,很快就会好的。

2.1Image Constructor图像构造函数

影像构造函数ee.Image()。提供给构造函数的参数是Earth Engine数据目录中的图像的字符串ID。
请参阅代码编辑器左侧的Docs选项卡,以查看图像构造函数可能的参数的完整列表。Docs选项卡提供了有关Earth Engine功能的最新信息。
GEE_API Docs_Tutorials_1.编程基础和Earth Engine API入门_第3张图片
要发现一个图像ID,请使用代码编辑器顶部的搜索工具在Earth Engine数据目录中搜索。
例如,在搜索区域中键入“elevation”,并注意将返回一个栅格列表。单击“SRTM Digital Elevation Data Version 4”条目,查看有关该数据集的更多信息。数据集描述的右侧是一个图像ID字段。示例中的图像ID是从数据集描述中显示的图像ID复制过来的。
复制和粘贴图像id的另一种方法是使用数据集描述上的Import按钮或搜索结果右侧的Import链接。如果单击import链接或按钮,会在脚本顶部的一个名为 “Imports” 的特殊区域中自动创建一个变量。您可以通过在import部分中单击变量的名称来重命名该变量。
GEE_API Docs_Tutorials_1.编程基础和Earth Engine API入门_第4张图片

2.2Configuring the Map配置地图

这个示例的第二个新部分是Map.setCenter() 调用。Map对象(表示代码编辑器中显示的地图)上的此方法将地图以给定的经度、纬度(十进制度数)和缩放级别(zoom level )为中心显示。
缩放级别为1,以便地图显示整个地球表面。更大的数字则从中心那里放大。通过检查代码编辑器左侧Docs tab中的Map部分,可以发现Map对象上的所有方法。

2.3Adding a layer to the Map在地图上添加一个图层

示例中的最后一行说:使用 Map objectaddLayer() 方法在代码编辑器中向地图添加图像。

2.4Digression: Images in Earth Engine

Earth Engine中的图像由一个或多个波段(bands)组成。图像中的每个波段都有自己的名称、像素值( pixel values)、像素分辨率(pixel resolution)和投影(projection)。你很快就会发现,SRTM图像只有一个波段(band):“elevation”。

当使用 map . addlayer() 向map添加图像时,Earth Engine需要确定如何将图像波段中的值(map the values in the image band(s))映射到显示的颜色(colors on the display)。如果将单波段图像(single-band image)添加到地图(map)中,默认情况下,Earth Engine将以灰度显示该波段,其中最小值为黑色,最大值为白色。

如果你没有指定最小值和最大值应该是多少,Earth Engine将使用默认值。
例如,您刚刚添加到map中的图像将显示为扩展到全部数据范围的灰度图像,或有符号的16位整数[-32768,32767]。(默认情况下,float bands被扩展到[0,1],byte bands被扩展到[0,255])。

您可以通过打印图像并检查Console tab中的图像对象来发现图像的数据类型。例如,在粘贴前面代码后,粘贴以下代码:

print('SRTM image', image);

GEE_API Docs_Tutorials_1.编程基础和Earth Engine API入门_第5张图片
单击run时,请注意 console中将出现一个对象。要研究对象属性,单击对象或属性左侧的 zippy() 展开它。展开image对象、“bands”属性、index(索引)“0”处的“elevation”波段以及“elevation”波段的“data_type”属性,发现它是带符号的int16(signed int16)数据类型。
GEE_API Docs_Tutorials_1.编程基础和Earth Engine API入门_第6张图片

2.5Customizing layer visualization自定义图层可视化

要更改拉伸数据的方式,可以向Map.addLayer() 调用提供另一个参数。具体来说,第二个参数visParams允许您指定要显示的最小值和最大值。要发现使用什么值,激活Inspector tab并在地图上点击以了解像素值的范围(range of pixel values)。或者,使用Layer manager交互式地拉伸数据,然后观察与百分比或标准偏差拉伸( percentiles or standard deviation stretches)对应的最小值和最大值。假设通过这样的实验,您决定将数据拉伸到[0,3000]。要使用此范围显示图像,请使用:

Map.addLayer(image, {min: 0, max: 3000}, 'custom visualization');

注意,visParams参数是一个对象,具有指定minmax的属性。

从JavaScript tutorials或 this external reference中了解更多关于JavaScript对象的信息)

请注意,Map.addLayer() 的第三个参数是图层管理器中显示的图层的名称。结果应该如下图所示。将鼠标悬停在右边的Layers box上,可以看到重命名该图层的效果。
GEE_API Docs_Tutorials_1.编程基础和Earth Engine API入门_第7张图片
灰度图高程图像,拉伸到[0,3000]。

要使用调色板(color palette)显示单个波段(single band),请向visParams对象添加调色板属性(palette property ):

Map.addLayer(image, {min: 0, max: 3000, palette: ['blue', 'green', 'red']},
    'custom palette');

GEE_API Docs_Tutorials_1.编程基础和Earth Engine API入门_第8张图片
带颜色的高程图像:颜色渐变从蓝色到红色,拉伸到[0,3000]。由于图像本身是单波段图像,所以这里显示的什么颜色就是什么颜色,没有合成效果。

2.6Digression: Palettes调色板

Palettes允许您设置单波段图像(single-band images)的配色方案(color scheme)。调色板是一个由逗号分隔的颜色字符串列表( list),它在可视化参数的最大值和最小值之间进行线性插值(或者根据前面描述的波段类型band type的默认值)。例如,小于或等于最小值的像素(pixels less than or equal to the minimum)将以列表中的第一种颜色显示;大于或等于最大值的像素(pixels greater than or equal to the maximum)将以列表中的最后一种颜色显示。中间颜色线性拉伸到中间像素值。

这些颜色是使用web标准CSS颜色值方案(web standard CSS color value scheme)定义的。颜色可以通过名称指定,也可以作为表示红、绿、蓝组合的十六进制字符串( hexadecimal strings )指定。这三个位置中的最小值是00(表示十进制数0),最大的是FF(表示十进制数255)。字符串“000000”代表黑色,“FFFFFF”是白色,“FF0000”是红色,“00FF00”是绿色,“0000FF”是蓝色。有关更多细节,请参阅颜色调色板部分( Color palettes section )。其他拉伸也可以通过使用样式层描述规范(Styled Layer Descriptors)实现。

web standard CSS color value scheme:https://en.wikipedia.org/wiki/Web_colors

3.Computations using Images用图像计算

例如,你可以计算出地形的坡度:将SRTM高程图像(SRTM elevation image)传递给ee.Terrain包的slope 方法。(slope method of the ee.Terrain package

// Load the SRTM image.加载影像
var srtm = ee.Image('CGIAR/SRTM90_V4');

// Apply an algorithm to an image.将算法应用到影像上
var slope = ee.Terrain.slope(srtm);

// Display the result.
Map.setCenter(-112.8598, 36.2841, 9); // Center on the Grand Canyon.
Map.addLayer(slope, {min: 0, max :60}, 'slope');

请注意,在代码 ee. Terrain.slope(srtm) 中,srtm图像是作为坡度算法的参数提供的。

3.1Image math图像计算

ee.Image类中也有一些可以在image对象上调用的方法。
例如,假设您想使用图像波段(image bands)进行一些数学运算(有时称为波段数学band math或映射代数map algebra)。
例如,您可能对aspect image(方位图)上的三角函数运算(trigonometric operations)感兴趣。为此,首先将一个 aspect image(方位图)转换为弧度(radians),然后对其调用sin()。重用我们的srtm图像:

// Get the aspect (in degrees).
var aspect = ee.Terrain.aspect(srtm);

// Convert to radians, compute the sin of the aspect.转换成弧度,计算角度的sin值
var sinImage = aspect.divide(180).multiply(Math.PI).sin();

// Display the result.
Map.addLayer(sinImage, {min: -1, max: 1}, 'sin');

看一下aspect.divide(180).multiply(Math.PI).sin() 代码。像这样将多个方法链接起来。
代码表示,‘aspect除以180,将结果乘以π,最后得到sin’。
通过以这种方式将方法组合起来,您可以对图像执行复杂的数学操作。
Image docs中可以看到完整的数学运算列表,如add(), subtract(), multiply()

3.2Image statistics影像统计

对图像的另一操作class涉及计算图像区域的像素统计(computing pixel statistics),或栅格向量覆盖(raster-vector overlays.)。
要在Earth Engine中计算统计信息,请使用 ee.Reducer package中的类表示的reducer。
例如,假设你对某个地区的平均海拔感兴趣。属性可以用geometry drawing tools绘制多边形来定义区域。要交互式地绘制一个区域,请使用多边形绘制工具(polygon drawing tool),然后在感兴趣的区域上数字化一个多边形,完成后单击退出。
注意:结果的ee.Geometry对象被自动命名为geometry,并作为导入(import)添加到脚本的顶部。通过在导入中单击变量名并输入新名称,将该变量重命名为 “polygon”

接下来,使用以下代码得到多边形(polygon)中的平均像素值(mean pixel value):

// Compute the mean elevation in the polygon.计算多边形的平均高程。
var meanDict = srtm.reduceRegion({
  reducer: ee.Reducer.mean(),
  geometry: polygon,
  scale: 90
});

// Get the mean from the dictionary and print it.
var mean = meanDict.get('elevation');
print('Mean elevation', mean);

这里有几点需要注意。首先,reduceRegion() 是一个可用于Image对象的方法。

更多关于reducing regions的信息:https://developers.google.com/earth-engine/reducers_reduce_region

第二,方法参数(method arguments)在作为单个参数传递的JavaScript对象中提供。(具体来说,对象的键keys是方法参数的名称,值是方法的参数)。第三,reducer参数指定要计算的统计量类型和geometry参数指定要计算统计量的区域。scale参数是要使用的以米为单位的像素大小(pixel size in meters to use)。为了避免歧义,您应该在进行缩小(reductions)时始终指定scale ,因为Earth Engine可能无法从输入中自动确定适当的scale。

scale参考:https://developers.google.com/earth-engine/scale

第三,reduceRegion() 的返回值是一个字典,其中键是频带名(keys are band names),值是频带的像素统计信息(values are the pixel statistics for the bands)。字典上的 get() 方法返回值(对应被作为一个参数提供的键)。在本例中,srtm图像有一个波段“elevation”,因此示例代码从字典中获取统计数据并打印它。

当你运行这段代码,如果你得到一个如下错误:
在这里插入图片描述
您可以做几件事来解决错误。reduceRegion() 方法进行了一个检查,以确保您考虑是否真的要在计算中包含这么多像素。这是为了防止您意外地做一些错误事情,比如尝试计算世界上每一米像素的平均值( mean of every one-meter pixel)(不要这样做)。要解决此错误,通过在参数字典中添加bestEffort:True或设置maxPixels参数为高于默认值1000万像素( default of 10 million pixels)的值或者两者同时设置来设置bestEffort参数为true。如果bestEffort为true,Earth Engine将自动重新计算scale,这样就不会超过maxPixels。

3.3Digression: Scale in Earth Engine尺度/比例尺

在前面的示例中,scale被设置为近似SRTM图像的本机分辨率( native resolution )。你可以发现一个图像的原生分辨率:

var scale = srtm.projection().nominalScale();
print('SRTM scale in meters', scale);

如果指定的scale小于本机分辨率(native resolution),Earth Engine将使用最近邻算法(nearest neighbor)对输入图像重采样(resample),然后在计算中包括所有较小的像素。如果你将scale 设置得更大,Earth Engine将使用来自聚合版本( aggregated version)的输入像素( input pixels )(例如,从图像金字塔 image pyramid)的更高层次获取像素)。

到目前为止,您一直在处理单个波段的单个图像(single image with a single band.)。现在是学习多波段图像和图像集合(multi-band images and image collections.)的时候了。

4.Image Collections图像集

一个image collection指的是一组Earth Engine图像。例如,所有Landsat 8图像的集合(collection)是一个ee.ImageCollection。与您正在使用的SRTM图像一样,image collections也有一个ID。与单个图像一样,您可以通过从Code Editor中搜索Earth Engine data catalog并查看数据集的详细信息页面来发现一个image collection的ID。例如,搜索“landsat 8 toa”并点击第一个结果,它应该与 USGS Landsat 8 Collection 1 Tier 1 TOA Reflectance反射数据集对应。使用import按钮导入数据集并将其重命名为l8,或者将ID复制到image collection构造函数中(image collection constructor):

var l8 = ee.ImageCollection('LANDSAT/LC08/C01/T1_TOA');

4.1Filtering image collections过滤图像集合

这个集合(this collection)代表了地球上的每个Landsat 8 scene 。
通常,提取一个图像或图像子集来测试算法是有用的。
通过时间或空间限制集合的方法来过滤它。例如,要筛选集合到覆盖特定位置的图像,首先使用几何绘图工具(geometry drawing tools)用点(或线或多边形)定义感兴趣的区域。移动到感兴趣的区域,在Geometry Imports(几何体导入)上悬停(如果已经定义了一个或多个几何图形),然后单击 +new layer (如果没有任何导入,进入下一步)。使用点绘制工具(point drawing tool)并在您感兴趣的区域中绘制点。命名import point。现在,过滤l8 collection进行,只得到与该点相交的图像,然后添加第二个filter,将l8 collection限制为仅在2015年采集的图像:

var spatialFiltered = l8.filterBounds(point);
print('spatialFiltered', spatialFiltered);

var temporalFiltered = spatialFiltered.filterDate('2015-01-01', '2015-12-31');
print('temporalFiltered', temporalFiltered);

在这里,filterBounds()filterDate() 是 image collections上更通用的 filter() 方法的快捷方法,该方法接受 ee.Filter() 作为参数。 filterBounds() 的参数是数字化的点,而filterDate() 的参数是两个表示为字符串的日期。

注意,您可以 print() 过滤后的集合( filtered collections)。你不能一次打印超过5000件东西。
例如,打印整个l8 collection。执行 print() 方法后,可以在console中检查打印的collections。注意,当使用zippy() 展开ImageCollection,然后展开feature 列表时,您将看到一个images列表,每个图片都可以展开和检查。这是发现单个图像ID的一种方法。另一种获得用于分析的单个图像的更程序化的方法是对集合进行排序(sort the collection),以获得相对于某些元数据属性最近的、最老的或最优的图像( recent, oldest, or optimal image)。

例如,通过检查打印的图像集合中的图像对象( image objects in the printed image collections),您可能已经观察到了一个名为CLOUD_COVER 的元数据属性。
你可以使用该属性在你感兴趣的地区获得2015年最少的云图像(least cloudy image):

// This will sort from least to most cloudy.排序:从最少云到最多云
var sorted = temporalFiltered.sort('CLOUD_COVER');

// Get the first (least cloudy) image.
var scene = sorted.first();

4.2Digression: Displaying RGB images显示RGB图像

当向地图添加多波段图像(multi-band image)时,Earth Engine选择图像的前三个波段,并默认将它们显示为红色、绿色和蓝色,并根据数据类型拉伸它们。通常情况下,这是不行的。
例如,如果将 Landsat image(上例中的scene)添加到地图中,结果并不理想:

Map.centerObject(scene, 9);
Map.addLayer(scene, {}, 'default RGB');

首先请注意,地图缩放到scale 9且以导入图像为中心。然后为visParams参数使用空对象 ({}) 来显示图像(详细信息请参阅Map.addLayer() docs)。因此,图像以默认的可视化方式显示:前三个波段分别映射到R、G、B,并拉伸到[0,1],因为这些波段是浮点数据类型(bands are float data type)。这意味着海岸气溶胶带(coastal aerosol band (‘B1’))以红色表示,蓝色带(“B2”)以绿色表示,而绿色带(“B3”)以蓝色表示。要将图像渲染为真色合成(true-color composite),需要告诉Earth Engine使用Landsat 8 bands ‘B4’、'B3’和’B2’来分别表示R、G和B。用visParams对象的bands属性指定要使用的波段。

了解更多landsat bands:
What are the band designations for the Landsat satellites?
https://www.usgs.gov/faqs/what-are-band-designations-landsat-satellites?qt-news_science_products=0#qt-news_science_products

您还需要提供适合显示典型地表目标反射率( reflectance from typical Earth surface targets.)的min最小值和max最大值。虽然可以使用列表为每个频带指定不同的值,但是在这里,将0.3指定为max并使用默认值0作为min参数就足够了。将可视化参数组合成一个对象并显示:

var visParams = {bands: ['B4', 'B3', 'B2'], max: 0.3};
Map.addLayer(scene, visParams, 'true-color composite');

请注意,此代码将 可视化参数对象(object of visualization parameters) 分配给一个变量,以便将来可能使用。您很快就会发现,该对象在可视化图像集合时非常有用!
运行上面代码:landsat8 TOA反射率图像为真彩色合成,拉伸到[0,0.3]

试着可视化不同的bands。另一个最受欢迎的组合是“B5”、“B4”和“B3”,这被称为假色合成( false-color composite)。

其他一些有趣的伪色组合:Common Landsat Band RGB Composites
https://www.usgs.gov/media/images/common-landsat-band-rgb-composites

Earth Engine被设计用来进行大规模的分析(large-scale analyses),所以您不局限于只处理一个scene。现在是时候将整个collection显示为RGB组合了!

4.3Displaying image collections显示图像集合

向映射中添加image collection类似于向映射中添加image。例如,使用l8 collection中的2016年图像和前面定义的visParams对象:

var landsat2016 = l8.filterDate('2016-01-01', '2016-12-31');
Map.addLayer(landsat2016, visParams, 'l8 collection');

注意,现在你可以缩放( zoom out)并看到一个连续的整合Landsat imagery的 mosaic(镶嵌图像)。还请注意,当您使用Inspector tab并单击图像时,您将在像素部分( Pixels section)看到一列像素值(或图表)( list of pixel values (or a chart)),在检查器的对象部分(Objects section of the inspector)看到一个图像对象列表( list of image objects )

如果你zoomed out的足够,你可能会在mosaic里注意到一些云。在向映射中添加ImageCollection时,它将显示为新值组合( recent-value composite,),这意味着只显示最近的像素(比如在 collection上调用mosaic())。这就是为什么你可以看到不同时间获得的路径之间的不连续性( discontinuities between paths which were acquired at different times)。这也是许多地区出现多云的原因。在下一节中,学习如何改变图像的合成方式,以摆脱那些讨厌的云!

5.Compositing, Masking, and Mosaicking合成、掩膜和镶嵌

将 Landsat 8 TOA reflectance collection 加载到一个名为l8的变量(variable)中,您会看到以下代码生成一个新值复合(recent-value composite):

var landsat2016 = l8.filterDate('2016-01-01', '2016-12-31');
Map.addLayer(landsat2016, visParams, 'l8 collection');

这个合成物(composite)的一个问题是它充满了云。您可以减少(reduce the ImageCollection )ImageCollection,而不是只获取集合中的最后一个像素(当您向地图添加一个集合collection时,Earth Engine会对其隐式调用mosaic())。

了解关于reducing image collections的更多信息
https://developers.google.com/earth-engine/reducers_image_collection

5.1Compositing with Reducers用Reducers做影像合成

用于在影像区域中获取统计信息的简化器( reducers for getting statistics in an image region)。这是空间缩减( spatial reduction)。
当集合表示一段时间内的图像时,将图像集合减少为图像( Reducing an image collection to an image)是一种时间缩减( temporal reduction)。
使用的Reducer类型定义了Earth Engine如何处理重叠像素(overlapping pixels)。
Landsat 8每16天访问地球上同一地点一次。这意味着在6个月的时间里,将会有大约12张图片(以及更多scenes重叠的地方)。地图上的每个像素都来自一堆像素(a stack of pixels)——集合中显示的每个图像中的一个(one from each image in the collection )。

仅仅将集合添加到映射中(adding the collection to the map),就会选择最近的像素——集合中最新图像中的一个像素。这一行为可以使用Earth Engine reducers来改变。例如,可以指示Earth Engine选择堆栈中的中值(the median value in the stack),而不是从堆栈中获取最近的像素(the most recent pixel from the stack)。这样做的好处是可以移除云(高值)和阴影(低值)。当使用中值减速器(median reducer)减少图像集合( image collection )时,复合值(composite value )是每个波段随时间的中值(the median in each band)。
例如,使用2016年的Landsat scenes:

// Get the median over time, in each band, in each pixel.
var median = l8.filterDate('2016-01-01', '2016-12-31').median();

// Make a handy variable of visualization parameters.为可视化参数创建一个方便的变量。
var visParams = {bands: ['B4', 'B3', 'B2'], max: 0.3};

// Display the median composite.显示中值复合
Map.addLayer(median, visParams, 'median');

这段代码中的新内容是应用于image collection的median() 方法。与过滤方法一样,这是image collections上更通用的reduce() 方法的快捷方式,该方法采用ee.Reducer() 作为参数。在代码编辑器的Docs tab中查看ee.Reducer 包来查看所有Earth Engine reducers的列表。当为image collection考虑一个reducer时,请注意输出是一个图像,因此具有非数字输出的reducers(例如histogramtoList reducers)将不能用于 image collection。

这看起来应该比你之前做出的最近的值组合(recent value composite)要好得多。此时,我们有必要回过头来考虑中值合成( median composite)的过程。Earth Engine已经在美国大陆加载了整个Landsat 8,并计算了每个像素的中值。这可是很多数据啊!当然,您可以像前面那样,通过首先过滤集合(filtering the collection)来计算年度中位数(annual medians)。关键是,如果必须下载所有图像并进行合成,这将是一个大项目。用Earth Engine,你将在几秒钟得到一个结果!

学些更多合成和镶嵌(compositing and mosaicking):https://developers.google.com/earth-engine/ic_composite_mosaic

5.2Masking掩膜

尽管中值复合(median composite)比新值复合(recent-value composite)有所改进,但您可能希望屏蔽(mask)图像的某些部分。在一个图像上掩蔽像素(Masking pixels)使那些像素透明,并从分析中排除他们。图像的每个波段中的每个像素(Each pixel in each band of an image)都有一个掩码(mask)。那些mask值为0或以下(mask value of 0 or below )的像素将是透明的。那些蒙版值大于0(mask of any value above 0 )的将被渲染。通过调用 image1.mask(image2) 来设置图像的mask。这个调用接受image2的值并将它们作为image1的掩码。image2中值为0的任何像素在image1中都是透明的。

例如,假设您想要掩模(mask)中值复合(median composite)中的所有水像素(water pixels)。可以使用Hansen等人(2013)描述的数据集创建水面罩(water mask),该数据集位于Earth Engine data catalog中。在这个数据集中,water的值为2,land值为1,no data值为0。使用一点逻辑(logic)来创建一个有零的且没有土地的遮罩图像(mask image):

Hansen等人(2013)描述的数据集:
https://science.sciencemag.org/content/342/6160/850
// Load or import the Hansen et al. forest change dataset.加载或导入Hansen等人的forest change数据集。
var hansenImage = ee.Image('UMD/hansen/global_forest_change_2015');

// Select the land/water mask.
var datamask = hansenImage.select('datamask');

// Create a binary mask.二值掩膜
var mask = datamask.eq(1);

// Update the composite mask with the water mask.
var maskedComposite = median.updateMask(mask);
Map.addLayer(maskedComposite, visParams, 'masked');

这段代码中:首先,select() 函数用于从图像中提取感兴趣的波段(extracting the bands)。这里,我们只选择我们关心的波段:datamask。下一个新东西是逻辑操作符(logical operator)eq(),它代表“equals”。我们使用eq(1) 来创建一个二值图像(binary image),其中datamask波段中所有值不是1的像素(那些是水或没有数据的像素)在结果图像中获取值为0。

由于这种 masking,中值合成(median composite)中所有在陆地上的像素(根据Hansen等人的数据集)都是可见的,但是在水面上的像素(或者没有数据)是透明的,并且将被排除在你对 maskedComposite 图像所做的任何分析之外。

5.3Mosaicking镶嵌/拼接

通过结合image collections、logical operators, masking and compositing的概念,您可以获得有趣的制图结果。例如,假设你想要一幅图像,其中土地像素( land pixels)显示为真颜色(true-color),所有其他像素显示为蓝色,你可以做如下的事情:

// Make a water image out of the mask.
var water = mask.not();

// Mask water with itself to mask all the zeros (non-water).用水自己来掩膜谁来掩盖所有的零
water = water.mask(water);

// Make an image collection of visualization images.制作可视化图像的image collection
var mosaic = ee.ImageCollection([
  median.visualize(visParams),
  water.visualize({palette: '000044'}),
]).mosaic();

// Display the mosaic.
Map.addLayer(mosaic, {}, 'custom mosaic');

代码说明:
首先,我们使用 not() 逻辑运算符(logical operator)反转前面创建的mask。具体来说,not() 将所有的0转换为1,将所有的非0转换为0。称它为变量水(variable water)并不完全正确,因为它也包含了一些nodata像素,但在目前的地图环境中它是可以的。下一件事是用它自己来掩盖“水”(mask the “water” with itself)。这将产生一个图像,其中所有的水像素(water pixels)都是1,并且其他的都被掩盖了。
最后一步是使用mosaic() 组合图像(combine the images)。由于mosaic() 作用于一个图像集合( image collection),所以我们传递一个我们希望将之合并到图像集合构造函数(image collection constructor)中的图像列表(list of images),然后在最后一步调用mosaic()
该列表中图像的顺序很重要。具体来说,输出图像将包含输入集合中图像堆栈( stack of images)中最后一个未屏蔽的像素(last unmasked pixel)。在本例中,这是有效的,因为水层(water layer)是集合中最后的(顶部)图像(last (top) image in the collection),并且只包含出现水的未遮罩像素(un-masked pixels )。

注意,集合中的图像是可视化图像(visualization images)。当您对图像调用 visualize() 时,根据您传入的可视化参数( visualization parameters),它将转换为3波段、8位的图像(3-band, 8-bit image)。默认可视化参数(default visualization parameters )适用于3波段8位图像(3-band, 8-bit images),因此在将图像添加到映射时不需要可视化参数。

至此,您已经看到了将图像集合(image collections)可视化为新值组合(recent-value composites)的方法,使用简化器(reducers)组合图像集合(compositing image collections)的方法,以及通过屏蔽和镶嵌图像集合( masking and mosaicking a collection of images)来创建自定义组合(custom composites)的方法。

6.NDVI, Mapping a Function over a Collection, Quality Mosaicking (NDVI,映射函数到集合上,质量拼接)

之前,您学习了如何通过如下操作获得单独的Landsat scene,其中l8point是代表Landsat 8 TOA collection和 area-of-interest geometry(感兴趣的几何区域)的导入:

// Get the least cloudy image in 2015.
var image = ee.Image(
  l8.filterBounds(point)
    .filterDate('2015-01-01', '2015-12-31')
    .sort('CLOUD_COVER')
    .first()
);

假设现在您想要从Landsat图像中计算一个归一化植被指数(Normalized Difference Vegetation Index (NDVI) )图像。植被在电磁波谱(electromagnetic spectrum)的近红外(near-infrared (NIR) )部分反射光线,在红色部分吸收光线。

了解更多植被的近红外反射:
https://science.nasa.gov/ems/08_nearinfraredwaves

NDVI利用这个来创建一个单一值( single value),单一值粗略地反映一个像素处发生的光合作用活动(photosynthetic activity)。计算结果为(NIR - red) / (NIR + red)。这就产生了一个介于1和-1之间的数字,其中具有高光合活性(high photosynthetic activity)的像素具有高的NDVI。这是 Earth Engine中计算NDVI的一种方法:

// Compute the Normalized Difference Vegetation Index (NDVI).
var nir = image.select('B5');
var red = image.select('B4');
var ndvi = nir.subtract(red).divide(nir.add(red)).rename('NDVI');

// Display the result.
Map.centerObject(image, 9);
var ndviParams = {min: -1, max: 1, palette: ['blue', 'white', 'green']};
Map.addLayer(ndvi, ndviParams, 'NDVI image');

注意,我们使用 select() 函数获取近红外和红色波段( NIR and red bands,),然后使用图像数学运算符(image mathematical operators)计算NDVI,这些运算符在前面的图像数学部分中也见过。最后,使用调色板(palette)显示图像。这里我们在调色板中使用颜色名称而不是十六进制字符串。

有关CSS颜色的详细信息:
https://developer.mozilla.org/en-US/docs/Web/CSS/color_value

归一化差分运算(normalized difference operation)在遥感中普遍存在,在ee.Image上有一个快捷函数(shortcut function )。快捷函数有助于简化前面示例中的代码:()

var ndvi = image.normalizedDifference(['B5', 'B4']).rename('NDVI');

6.1Mapping a Function over a Collection在集合上映射函数

假设现在您希望将NDVI添加到 image collection中的每个图像中。在Earth Engine中实现这一点的方法是在集合上映射map() 一个函数。不要将map()map对象混淆。前者是一个collection上的方法,在并行计算的意义上( parallel computing sense —将函数应用于集合中的每个元素)使用map。该函数定义将应用于集合中每个元素的操作。您已经在JavaScript教程中看到了一个简单的函数,但是现在我们将创建一个包含Earth Engine功能的函数。例如,将之前的NDVI代码复制到一个函数中,返回带有NDVI带的输入图像:

var addNDVI = function(image) {
  var ndvi = image.normalizedDifference(['B5', 'B4']).rename('NDVI');
  return image.addBands(ndvi);
};

// Test the addNDVI function on a single image.在单个映像上测试addNDVI函数。
var ndvi = addNDVI(image).select('NDVI');

对于计算单个图像的NDVI,此代码可能效率不高,但可以将此函数用作 map() 的参数,以便向集合中的每个图像添加NDVI band。首先在单个影像上测试函数,以确保函数的行为符合预期,这通常很有用。一旦你在一个单独的图像上测试了这个函数,并确定它做了你想要的,你可以将它映射到集合中:

var withNDVI = l8.map(addNDVI);

要验证这确实是将NDVI band放入到这个集合中的每个图像中,可以将withNDVI collection添加到地图中,并使用Inspector tab查询一个随机位置。您应该注意到集合中的每个图像现在都有一个名为NDVI的波段。

6.2Make a greenest pixel composite制作最绿色的像素合成

现在已经创建了一个图像集合(image collection),其中每个图像都有一个NDVI波段,我们可以探索一种新的方法来创建复合( make composites):qualityMosaic()。你可能已经注意到 Landsat 路径之间的不连续性,甚至在中值像素合成(median pixel composite)中也是如此。造成这种情况的部分原因可能是由于在不同时间(特别是相隔8天)收集邻近路径上的图像造成了物候(phenology)上的差异。减少这种情况的一种方法是尝试从大致相同的物候阶段设置合成物中的像素值(pixel values in the composite),例如植物达到最大绿化率的时间(当叶片处于开启状态且光合作用活跃时)。如果我们让最大绿色度由最大NDVI定义,我们可以使用qualityMosaic() 来制作一个合成(composite),其中每个像素包含集合中的最大NDVI像素。现在你可以利用你的withNDVI集合中添加的NDVI波段在:

// Make a "greenest" pixel composite.制作一个“最绿色”像素合成。
var greenest = withNDVI.qualityMosaic('NDVI');

// Display the result.
var visParams = {bands: ['B4', 'B3', 'B2'], max: 0.3};
Map.addLayer(greenest, visParams, 'Greenest pixel composite');

这段代码的结果应该如图9所示。将图9与图6中值组合(median composite)进行比较,可以发现最绿的像素组合(greenest pixel composite)确实更绿。然而,对水体(water bodies)的仔细检查应使另一个问题变得明显。具体来说,水体现在呈现多云状态(cloudy)。这是由于 qualityMosaic() 方法的工作方式:在每个位置,对整个时间序列(time series)进行检查,将NDVI波段中值最大的像素(pixel with the maximum value)设置为合成值(composite value)。由于NDVI在云上高于水( NDVI is higher over clouds than water),水区域的像素是多云的(water areas get cloudy pixels),而植被区域都是绿色的,因为当像素中的植被(vegetation in the pixel)是光合活动活跃期时,NDVI是最高的。

现在您已经看到了几种在Earth Engine中合成和镶嵌图像(composite and mosaic images)的方法。您可以从按时间和地点过滤出来的图像或集合中的所有图像来创建新值、中值或绿色像素组合( make recent-value, median, or greenest-pixel composites)。

7.Exporting Charts and Images导出图表和图像

Earth Engine是一个强大的分析工具,但是您可能需要导出分析结果,以便将图表、图像、地图等嵌入到报告或出版物中。在本节中,您将学习如何创建可在其他软件中导出和查看的图表和图像(charts and images)。

// Map a function over the Landsat 8 TOA collection to add an NDVI band.
var withNDVI = l8.map(function(image) {
  var ndvi = image.normalizedDifference(['B5', 'B4']).rename('NDVI');
  return image.addBands(ndvi);
});

7.1Charting图表

假设你想要的是一个在给定位置的随时间变化的NDVI图表。制作这样一个图表,第一步是选择一个感兴趣的地点。通过点绘制工具(point drawing tool)创建一个点并且在你感兴趣的区域做一个单点几何图形(single point geometry)。如果你已经导入了,悬停在几何体导入( Geometry Imports)上,然后点击 + new layer )。把地点定位在农业、落叶林、一年生草地或其他具有年循环的土地覆盖的区域。将导入命名为roi

有关以编程方式创建几何图形的信息,请参阅此页
https://developers.google.com/earth-engine/geometries

现在让我们使用roi点来制作一个在该点下像素的NDVI随时间变化的图表。在Earth Engine中制作图表的方法是使用ui.Chart包。具体来说,要制作一个随时间变化的图表,可以使用 ui.Chart.image.series() 方法:

// Create a chart.
var chart = ui.Chart.image.series({
  imageCollection: withNDVI.select('NDVI'),
  region: roi,
  reducer: ee.Reducer.first(),
  scale: 30
}).setOptions({title: 'NDVI over time'});

// Display the chart in the console.
print(chart);

对于roi几何图形,我们选择了农业区域中的一个点,得到的图表如图10所示。注意,图表构造函数(chart constructor)的参数包括一个reducer和scale(如reduceRegion())。因为我们提供的作为区域的点只能相交一个像素,所以使用“first”reducer就足够了。如果有较大的区域,则应该使用“mean”或其他指定如何聚合像素(aggregate pixels)的reducer。还要注意,要使图表可视化,您所要做的就是打印它。

图10. 在某个点geometrry上,Landsat NDVI 随时间变化的图表。

7.2Digression: Simple Cloud Masking for Landsat陆地卫星云掩膜

关于这个图表,你可能已经注意到NDVI值的时间序列在这个点上看起来有点嘈杂。这可能是由云层造成的。为了改善这一效果,Earth Engine包括了一个云掩膜算法(cloud-masking algorithm),用于有热波段的陆地卫星传感器(Landsat sensors with a thermal band):ee.Algorithms.Landsat.simpleCloudScore()
它接收一个Landsat TOA reflectance image作为输入,并添加一个名为cloud的band,该波段是像素中的云度指数:从0到100、从最少到最多云。通过修改映射到集合上的函数,可以对云索引使用任意阈值(arbitrary threshold (20) )来稍微清理一下图表:

var cloudlessNDVI = l8.map(function(image) {
  // Get a cloud score in [0, 100].
  // 得到一个云分数,范围是[0,100]
  var cloud = ee.Algorithms.Landsat.simpleCloudScore(image).select('cloud');

  // Create a mask of cloudy pixels from an arbitrary threshold.
  // 以一个任意阈值创建一个多云像素的mask
  var mask = cloud.lte(20);

  // Compute NDVI.
  var ndvi = image.normalizedDifference(['B5', 'B4']).rename('NDVI');

  // Return the masked image with an NDVI band.
  // 返回带有NDVI波段的屏蔽图像。
  return image.addBands(ndvi).updateMask(mask);
});

print(ui.Chart.image.series({
  imageCollection: cloudlessNDVI.select('NDVI'),
  region: roi,
  reducer: ee.Reducer.first(),
  scale: 30
}).setOptions({title: 'Cloud-masked NDVI over time'}));

被云掩膜的结果( cloud-masked result)如图11所示。注意,时间序列看起来更平滑一些,但可能仍然包含受云影响的像素( pixels affected by clouds)。调整云索引的阈值(threshold on the cloud index)并观察图表时间序列,了解该阈值如何影响结果。

图11. 云掩膜的NDVI(cloud-masked NDVI )在某点几何(point geometry.)随时间变化的图表。

7.3Exporting Images导出图像

您已经了解了导出由Earth Engine计算的数据图表的方法,但是如何导出整个图像呢?例如,假设你已经构建了一个绿色像素组合(greenest-pixel composite),如前一节所述的:

var greenest = cloudlessNDVI.qualityMosaic('NDVI');

这段代码与您以前所做的惟一区别是,我们现在使用的是云掩膜集合( cloud masked collection)。您可以使用Export包导出其中的子集(由区域定义的)。

了解更多关于从Earth Engine导出栅格和矢量数据:(exporting raster and vector data):Exporting Data
https://developers.google.com/earth-engine/exporting

例如,要导出一个可以很容易嵌入到其他文档中的图像,让我们创建一个可视化图像,就像你之前做的那样,导出到你的谷歌驱动器文件夹(Google Drive folder):

// Create a 3-band, 8-bit, color-IR composite to export.生成一个3波段,8位,color-IR的复合来导出
var visualization = greenest.visualize({
  bands: ['B5', 'B4', 'B3'],
  max: 0.4
});

// Create a task that you can launch from the Tasks tab.创建可以从Tasks tab启动的任务。
Export.image.toDrive({
  image: visualization,
  description: 'Greenest_pixel_composite',
  scale: 30
});

运行此代码时,请注意在Tasks选项卡中创建了一个新任务。要启动导出配置对话框(export configuration dialog,),单击Tasks选项卡中的RUN按钮。配置完任务后,单击对话框中的Run按钮启动导出。但在你这么做之前,请注意:
在这里插入图片描述
注意:如果不指定区域,脚本运行时的映射边界(Map bounds)将被使用!

在没有region参数时的导出要谨慎的原因是,如果您设置了一个相对较小的比例值(value of scale),而地图被放大(zoomed out)为较大的区域,那么您将导出一个可能非常大的图像到驱动器文件夹(Drive folder)。查看Docs选项卡中的 Export.image.toDrive() 文档,了解更多细节和可能的配置参数列表。

阅读:Guides:https://developers.google.com/earth-engine
在论坛搜索问题:https://groups.google.com/forum/#!forum/google-earth-engine-developers

你可能感兴趣的:(遥感)