DataTable 图表

ui.Chart 函数会根据客户端 JSON 对象渲染图表,该对象遵循与 Google 图表 DataTable 类相同的结构,但缺少 DataTable 方法和可变性。它本质上是一个二维表格,其中行代表观测结果,列代表观测属性。它提供了一个灵活的基础界面,可用于在 Earth Engine 中绘制图表。如果需要高度自定义图表,则此方法是一个不错的选择。

DataTable 个架构

您可以通过以下两种方式在 Earth Engine 中定义伪 DataTable:JavaScript 二维数组和 JavaScript 字面量对象。对于大多数应用,构建二维数组是最简单的方法。在这两种情况下,传递给 ui.Chart 的表都必须是客户端对象。手动编码的表格本质上是客户端的,而计算对象需要使用 evaluate 传输到客户端。如需详细了解服务器端对象与客户端对象之间的区别,请参阅客户端与服务器页面。

JavaScript 数组

二维 DataTable 由行和列数组组成。行是观察结果,列是属性。第 1 列用于定义 x 轴的值,而其他列用于定义 y 轴系列的值。第一行应为列标题。最简单的标题是一系列列标签,如以下按所选州显示人口的数组 DataTable 所示。

var dataTable = [
  ['State', 'Population'],
  ['CA', 37253956],
  ['NY', 19378102],
  ['IL', 12830632],
  ['MI', 9883640],
  ['OR', 3831074],
];

(可选)除了定义域 (x 轴) 和数据 (y 轴系列) 之外,您还可以为列指定其他角色,例如注释、间隔、提示或样式。在以下示例中,标题数组显示为一系列对象,其中明确定义了每个列的角色。如需了解每种 Google 图表类型的可接受列角色,请参阅相应文档,例如柱状图数据格式

var dataTable = [
  [{role: 'domain'}, {role: 'data'}, {role: 'annotation'}],
  ['CA', 37253956, '37.2e6'],
  ['NY', 19378102, '19.3e6'],
  ['IL', 12830632, '12.8e6'],
  ['MI', 9883640, '9.8e6'],
  ['OR', 3831074, '3.8e6'],
];

列属性的指定方式如下:

参数 类型 定义
type 字符串,推荐 列数据类型:'string''number''boolean''date''datetime''timeofday'
label 字符串,推荐 图表图例中列的标签,系列标签。
role 字符串,推荐 列的角色(例如柱形图的角色)。
pattern 字符串,可选 数字(或日期)格式字符串,用于指定如何显示列值。

JavaScript 对象

DataTable 可以采用 JavaScript 字面量对象的格式,其中提供行和列对象的数组。如需了解如何指定列和行参数,请参阅此指南

var dataTable = {
  cols: [{id: 'name', label: 'State', type: 'string'},
         {id: 'pop', label: 'Population', type: 'number'}],
  rows: [{c: [{v: 'CA'}, {v: 37253956}]},
         {c: [{v: 'NY'}, {v: 19378102}]},
         {c: [{v: 'IL'}, {v: 12830632}]},
         {c: [{v: 'MI'}, {v: 9883640}]},
         {c: [{v: 'OR'}, {v: 3831074}]}]
};

手动DataTable图表

假设您有少量静态数据要显示在图表中。使用 JavaScript 数组对象规范构建要传递给 ui.Chart 函数的输入。在这里,2010 年美国人口普查中选定的州人口被编码为 JavaScript 数组,其中包含用于定义列属性的列标题对象。请注意,第三列被指定为 'annotation' 的角色,它会将人口数作为注释添加到图表中的每个观察结果。

// Define a DataTable using a JavaScript array with a column property header.
var dataTable = [
  [
    {label: 'State', role: 'domain', type: 'string'},
    {label: 'Population', role: 'data', type: 'number'},
    {label: 'Pop. annotation', role: 'annotation', type: 'string'}
  ],
  ['CA', 37253956, '37.2e6'],
  ['NY', 19378102, '19.3e6'],
  ['IL', 12830632, '12.8e6'],
  ['MI', 9883640, '9.8e6'],
  ['OR', 3831074, '3.8e6']
];

// Define the chart and print it to the console.
var chart = ui.Chart(dataTable).setChartType('ColumnChart').setOptions({
  title: 'State Population (US census, 2010)',
  legend: {position: 'none'},
  hAxis: {title: 'State', titleTextStyle: {italic: false, bold: true}},
  vAxis: {title: 'Population', titleTextStyle: {italic: false, bold: true}},
  colors: ['1d6b99']
});
print(chart);

计算的 DataTable 图表

可以通过 evaluate 从服务器传递给客户端的二维 ee.List 创建 DataTable 数组。常见场景是将 ee.FeatureCollectionee.ImageCollection 的属性或这些属性的元素级缩减转换为 DataTable。以下示例中应用的策略会将函数映射到用于求和给定元素的 ee.ImageCollection,从求和结果中组装 ee.List,并将列表作为名为 'row' 的属性附加到返回的元素。新集合的每个元素都有一个 1 维 ee.List,表示 DataTable 中的一行。aggregate_array() 函数用于将所有 'row' 属性汇总到父 ee.List 中,以创建 DataTable 所需形状的二维服务器端 ee.List。系统会将自定义列标题连接到表格,并使用 evaluate 将结果传输到客户端,在客户端使用 ui.Chart 函数渲染该结果。

按地区划分的时间序列

此示例显示了某个森林生态区的 MODIS 派生 NDVI 和 EVI 植被指数的时间序列。系统会按生态区缩减系列中的每个图片,并将其结果组装为 'row' 属性,然后将该属性汇总为 DataTable,以便传递给客户端并使用 ui.Chart 绘制图表。请注意,此代码段生成的图表与 ui.Chart.image.series 图表示例生成的图表相同。

// Import the example feature collection and subset the forest feature.
var forest = ee.FeatureCollection('projects/google/charts_feature_example')
                 .filter(ee.Filter.eq('label', 'Forest'));

// Load MODIS vegetation indices data and subset a decade of images.
var vegIndices = ee.ImageCollection('MODIS/061/MOD13A1')
                     .filter(ee.Filter.date('2010-01-01', '2020-01-01'))
                     .select(['NDVI', 'EVI']);

// Define a function to format an image timestamp as a JavaScript Date string.
function formatDate(img) {
  var millis = img.date().millis().format();
  return ee.String('Date(').cat(millis).cat(')');
}

// Build a feature collection where each feature has a property that represents
// a DataFrame row.
var reductionTable = vegIndices.map(function(img) {
  // Reduce the image to the mean of pixels intersecting the forest ecoregion.
  var stat = img.reduceRegion(
      {reducer: ee.Reducer.mean(), geometry: forest, scale: 500});

  // Extract the reduction results along with the image date.
  var date = formatDate(img);   // x-axis values.
  var evi = stat.get('EVI');    // y-axis series 1 values.
  var ndvi = stat.get('NDVI');  // y-axis series 2 values.

  // Make a list of observation attributes to define a row in the DataTable.
  var row = ee.List([date, evi, ndvi]);

  // Return the row as a property of an ee.Feature.
  return ee.Feature(null, {'row': row});
});

// Aggregate the 'row' property from all features in the new feature collection
// to make a server-side 2-D list (DataTable).
var dataTableServer = reductionTable.aggregate_array('row');

// Define column names and properties for the DataTable. The order should
// correspond to the order in the construction of the 'row' property above.
var columnHeader = ee.List([[
  {label: 'Date', role: 'domain', type: 'date'},
  {label: 'EVI', role: 'data', type: 'number'},
  {label: 'NDVI', role: 'data', type: 'number'}
]]);

// Concatenate the column header to the table.
dataTableServer = columnHeader.cat(dataTableServer);

// Use 'evaluate' to transfer the server-side table to the client, define the
// chart and print it to the console.
dataTableServer.evaluate(function(dataTableClient) {
  var chart = ui.Chart(dataTableClient).setOptions({
    title: 'Average Vegetation Index Value by Date for Forest',
    hAxis: {
      title: 'Date',
      titleTextStyle: {italic: false, bold: true},
    },
    vAxis: {
      title: 'Vegetation index (x1e4)',
      titleTextStyle: {italic: false, bold: true}
    },
    lineWidth: 5,
    colors: ['e37d05', '1d6b99'],
    curveType: 'function'
  });
  print(chart);
});

间隔图表

此图表利用 DataTable'role' 属性生成了区间图表。该图表显示了加利福尼亚州蒙特雷附近某个像素的年 NDVI 数据和年际差异。年际中位数显示为线条,而绝对值和四分位范围显示为带状。通过将 'role' 列属性设置为 'interval',可将表示每个时间段的表格列分配为此类。通过将 intervals.style 图表属性设置为 'area',系统会围绕中位数线绘制条带。

// Define a point to extract an NDVI time series for.
var geometry = ee.Geometry.Point([-121.679, 36.479]);

// Define a band of interest (NDVI), import the MODIS vegetation index dataset,
// and select the band.
var band = 'NDVI';
var ndviCol = ee.ImageCollection('MODIS/006/MOD13Q1').select(band);

// Map over the collection to add a day of year (doy) property to each image.
ndviCol = ndviCol.map(function(img) {
  var doy = ee.Date(img.get('system:time_start')).getRelative('day', 'year');
  // Add 8 to day of year number so that the doy label represents the middle of
  // the 16-day MODIS NDVI composite.
  return img.set('doy', ee.Number(doy).add(8));
});

// Join all coincident day of year observations into a set of image collections.
var distinctDOY = ndviCol.filterDate('2013-01-01', '2014-01-01');
var filter = ee.Filter.equals({leftField: 'doy', rightField: 'doy'});
var join = ee.Join.saveAll('doy_matches');
var joinCol = ee.ImageCollection(join.apply(distinctDOY, ndviCol, filter));

// Calculate the absolute range, interquartile range, and median for the set
// of images composing each coincident doy observation group. The result is
// an image collection with an image representative per unique doy observation
// with bands that describe the 0, 25, 50, 75, 100 percentiles for the set of
// coincident doy images.
var comp = ee.ImageCollection(joinCol.map(function(img) {
  var doyCol = ee.ImageCollection.fromImages(img.get('doy_matches'));

  return doyCol
      .reduce(ee.Reducer.percentile(
          [0, 25, 50, 75, 100], ['p0', 'p25', 'p50', 'p75', 'p100']))
      .set({'doy': img.get('doy')});
}));

// Extract the inter-annual NDVI doy percentile statistics for the
// point of interest per unique doy representative. The result is
// is a feature collection where each feature is a doy representative that
// contains a property (row) describing the respective inter-annual NDVI
// variance, formatted as a list of values.
var reductionTable = comp.map(function(img) {
  var stats = ee.Dictionary(img.reduceRegion(
      {reducer: ee.Reducer.first(), geometry: geometry, scale: 250}));

  // Order the percentile reduction elements according to how you want columns
  // in the DataTable arranged (x-axis values need to be first).
  var row = ee.List([
    img.get('doy'),            // x-axis, day of year.
    stats.get(band + '_p50'),  // y-axis, median.
    stats.get(band + '_p0'),   // y-axis, min interval.
    stats.get(band + '_p25'),  // y-axis, 1st quartile interval.
    stats.get(band + '_p75'),  // y-axis, 3rd quartile interval.
    stats.get(band + '_p100')  // y-axis, max interval.
  ]);

  // Return the row as a property of an ee.Feature.
  return ee.Feature(null, {row: row});
});

// Aggregate the 'row' properties to make a server-side 2-D array (DataTable).
var dataTableServer = reductionTable.aggregate_array('row');

// Define column names and properties for the DataTable. The order should
// correspond to the order in the construction of the 'row' property above.
var columnHeader = ee.List([[
  {label: 'Day of year', role: 'domain'},
  {label: 'Median', role: 'data'},
  {label: 'p0', role: 'interval'},
  {label: 'p25', role: 'interval'},
  {label: 'p75', role: 'interval'},
  {label: 'p100', role: 'interval'}
]]);

// Concatenate the column header to the table.
dataTableServer = columnHeader.cat(dataTableServer);

// Use 'evaluate' to transfer the server-side table to the client, define the
// chart and print it to the console.
dataTableServer.evaluate(function(dataTableClient) {
  var chart = ui.Chart(dataTableClient).setChartType('LineChart').setOptions({
    title: 'Annual NDVI Time Series with Inter-Annual Variance',
    intervals: {style: 'area'},
    hAxis: {
      title: 'Day of year',
      titleTextStyle: {italic: false, bold: true},
    },
    vAxis: {title: 'NDVI (x1e4)', titleTextStyle: {italic: false, bold: true}},
    colors: ['0f8755'],
    legend: {position: 'none'}
  });
  print(chart);
});

表示间隔的方法有很多种。在以下示例中,通过将 intervals.style 属性更改为 'boxes' 并使用相应的方框样式,使用方框(而非条带)。

dataTableServer.evaluate(function(dataTableClient) {
  var chart = ui.Chart(dataTableClient).setChartType('LineChart').setOptions({
    title: 'Annual NDVI Time Series with Inter-Annual Variance',
    intervals: {style: 'boxes', barWidth: 1, boxWidth: 1, lineWidth: 0},
    hAxis: {
      title: 'Day of year',
      titleTextStyle: {italic: false, bold: true},
    },
    vAxis: {title: 'NDVI (x1e4)', titleTextStyle: {italic: false, bold: true}},
    colors: ['0f8755'],
    legend: {position: 'none'}
  });
  print(chart);
});