Переход между классами воды

Слой перехода воды фиксирует изменения между тремя классами наличия воды (не вода, сезонная вода и постоянная вода), а также двумя дополнительными классами для эфемерной воды (эфемерная постоянная и эфемерная сезонная).

В этом разделе руководства вы узнаете:

  1. добавить слой карты для визуализации перехода воды;
  2. создать сгруппированный редуктор для суммирования площади каждого класса перехода в пределах указанной интересующей области;
  3. создайте диаграмму, на которой будет показана площадь по классам перехода.

Базовая визуализация

В разделе Asset List (Список объектов) скрипта добавьте следующую инструкцию, которая создает объект изображения с одной полосой под названием transition:

Редактор кода (JavaScript)

var transition = gsw.select('transition');

Изображения GSW содержат метаданные о номерах и названиях классов переходов, а также палитру по умолчанию для стилизации классов переходов. Когда слой перехода будет добавлен на карту, эти параметры визуализации будут использоваться автоматически.

В нижней части раздела Map Layers (Слои карты) скрипта добавьте следующую инструкцию, которая добавляет новый слой карты, отображающий классы перехода:

Редактор кода (JavaScript)

Map.setCenter(105.26, 11.2134, 9);     // Mekong River Basin, SouthEast Asia
Map.addLayer({
  eeObject: transition,
  name: 'Transition classes (1984-2015)',
});

При запуске скрипта будет показан слой перехода.

Классы перехода поверхностных вод (Surface Water Transition Classes)
Рисунок 10. Скриншот дельты реки Меконг, показывающий широкие вариации в переходах классов поверхностных вод.

Ключ карты для классов перехода:

Значение Символ Ярлык
0 Не вода
1 Постоянная
2 Новый постоянный
3 Потеряно навсегда
4 сезонные релизы.
5 Новый сезонный контент
6 Сезонные товары, которых нет в наличии
7 Сезонные и постоянные
8 Постоянная на сезонную
9 Временное или постоянное
10 Временные сезонные

Суммирование площади по классу перехода

В этом разделе мы снова воспользуемся инструментом "Многоугольник", чтобы определить интересующий нас регион. Если вы хотите проанализировать новое местоположение, сначала выберите и удалите исходный многоугольник, чтобы не получить результаты для объединенных областей. Информацию о том, как изменять геометрию, можно найти в разделе Инструменты геометрии документации по редактору кода.

В этом примере мы нарисуем новый многоугольник в дельте реки Меконг.

Переходные классы с ROI
Рисунок 11. Дельта реки Меконг во Вьетнаме с интересующим регионом, созданным с помощью инструмента рисования многоугольников в редакторе кода.

Чтобы рассчитать площадь, покрытую частями изображения, мы добавим дополнительную полосу к объекту переходного изображения, которая определяет размер каждого пикселя в квадратных метрах с помощью метода ee.Image.pixelArea.

Редактор кода (JavaScript)

var area_image_with_transition_class = ee.Image.pixelArea().addBands(transition);

Полученный объект изображения (area_image_with_transition_class) – это двухполосное изображение, где первая полоса содержит информацию о площади в квадратных метрах (созданную методом ee.Image.pixelArea ), а вторая полоса содержит информацию о классе перехода.

Затем мы обобщаем переходы между классами в интересующем регионе (roi) с помощью метода ee.Image.reduceRegion и сгруппированного редуктора , который суммирует площадь в пределах каждого класса перехода:

Редактор кода (JavaScript)

var reduction_results = area_image_with_transition_class.reduceRegion({
  reducer: ee.Reducer.sum().group({
    groupField: 1,
    groupName: 'transition_class_value',
  }),
  geometry: roi,
  scale: 30,
  bestEffort: true,
});
print('reduction_results', reduction_results);

На вкладке консоли появится reduction_results. Чтобы увидеть сводные данные по региону, вам нужно будет развернуть дерево на несколько уровней.

Результаты группового снижения
Рисунок 12. Вывод на вкладке Console, показывающий результаты сгруппированного сокращения.

Хотя объект reduction_results содержит информацию о площади, покрытой каждым классом перехода, его не очень легко читать. В следующем разделе мы упростим просмотр результатов.

Как создать сводную диаграмму

В этом разделе мы создадим диаграмму, чтобы лучше обобщить результаты. Для начала мы извлекаем список классов перехода с областями следующим образом:

Редактор кода (JavaScript)

var roi_stats = ee.List(reduction_results.get('groups'));

Результатом сгруппированного редуктора (reduction_results) является словарь, содержащий список словарей. В списке есть по одному словарю для каждого класса перехода. В этих инструкциях используется метод ee.Dictionary.get , чтобы извлечь сгруппированные результаты редуктора из этого словаря и привести их к типу данных ee.List, чтобы мы могли получить доступ к отдельным словарям.

Чтобы использовать функции построения диаграмм в редакторе кода, мы создадим FeatureCollection, содержащую необходимую информацию. Для этого мы сначала создаем два словаря поиска и две вспомогательные функции. Код, создающий словари поиска, можно разместить в верхней части раздела Calculations (Вычисления):

Редактор кода (JavaScript)

//////////////////////////////////////////////////////////////
// Calculations
//////////////////////////////////////////////////////////////

// Create a dictionary for looking up names of transition classes.
var lookup_names = ee.Dictionary.fromLists(
    ee.List(gsw.get('transition_class_values')).map(numToString),
    gsw.get('transition_class_names')
);
// Create a dictionary for looking up colors of transition classes.
var lookup_palette = ee.Dictionary.fromLists(
    ee.List(gsw.get('transition_class_values')).map(numToString),
    gsw.get('transition_class_palette')
);

Словарь lookup_names связывает значения классов переходов с их названиями, а словарь lookup_palette – значения классов переходов с определениями цветов.

Две вспомогательные функции можно поместить в новый раздел кода под названием "Вспомогательные функции".

Редактор кода (JavaScript)

//////////////////////////////////////////////////////////////
// Helper functions
//////////////////////////////////////////////////////////////

// Create a feature for a transition class that includes the area covered.
function createFeature(transition_class_stats) {
  transition_class_stats = ee.Dictionary(transition_class_stats);
  var class_number = transition_class_stats.get('transition_class_value');
  var result = {
      transition_class_number: class_number,
      transition_class_name: lookup_names.get(class_number),
      transition_class_palette: lookup_palette.get(class_number),
      area_m2: transition_class_stats.get('sum')
  };
  return ee.Feature(null, result);   // Creates a feature without a geometry.
}

// Create a JSON dictionary that defines piechart colors based on the
// transition class palette.
// https://developers.google.com/chart/interactive/docs/gallery/piechart
function createPieChartSliceDictionary(fc) {
  return ee.List(fc.aggregate_array("transition_class_palette"))
    .map(function(p) { return {'color': p}; }).getInfo();
}

// Convert a number to a string. Used for constructing dictionary key lists
// from computed number objects.
function numToString(num) {
  return ee.Number(num).format();
}

Функция createFeature принимает словарь (содержащий область и класс перехода воды) и возвращает объект, подходящий для построения диаграммы. Функция createPieChartSliceDictionary создает список цветов, соответствующих классам переходов, в формате, который используется в круговой диаграмме.

Затем мы применим функцию createFeature к каждому словарю в списке (roi_stats), используя ee.List.map, чтобы применить вспомогательную функцию к каждому элементу списка.

Редактор кода (JavaScript)

var transition_fc = ee.FeatureCollection(roi_stats.map(createFeature));
print('transition_fc', transition_fc);

Теперь, когда у нас есть FeatureCollection, содержащий атрибуты, которые мы хотим отобразить на диаграмме, мы можем создать объект диаграммы и вывести его в консоль.

Редактор кода (JavaScript)

// Add a summary chart.
var transition_summary_chart = ui.Chart.feature.byFeature({
    features: transition_fc,
    xProperty: 'transition_class_name',
    yProperties: ['area_m2', 'transition_class_number']
  })
  .setChartType('PieChart')
  .setOptions({
    title: 'Summary of transition class areas',
    slices: createPieChartSliceDictionary(transition_fc),
    sliceVisibilityThreshold: 0  // Don't group small slices.
  });
print(transition_summary_chart);

Вариант slices позволяет раскрасить сектора круговой диаграммы в соответствии с палитрой по умолчанию, заданной для классов переходов (см. таблицу условных обозначений выше). Параметр sliceVisibilityThreshold позволяет не объединять небольшие сектора в категорию "Другое". Полученная диаграмма должна быть похожа на ту, что показана на рисунке 13.

Сводная диаграмма классов перехода воды
Рисунок 13. Диаграмма, на которой показаны относительные размеры классов перехода воды.

Окончательный сценарий

Полный скрипт для этого раздела:

Редактор кода (JavaScript)

//////////////////////////////////////////////////////////////
// Asset List
//////////////////////////////////////////////////////////////

var gsw = ee.Image('JRC/GSW1_0/GlobalSurfaceWater');
var occurrence = gsw.select('occurrence');
var change = gsw.select("change_abs");
var transition = gsw.select('transition');
var roi = ee.Geometry.Polygon(
        [[[105.531921, 10.412183],
          [105.652770, 10.285193],
          [105.949401, 10.520218],
          [105.809326, 10.666006]]]);
//////////////////////////////////////////////////////////////
// Constants
//////////////////////////////////////////////////////////////

var VIS_OCCURRENCE = {
    min: 0,
    max: 100,
    palette: ['red', 'blue']
};
var VIS_CHANGE = {
    min: -50,
    max: 50,
    palette: ['red', 'black', 'limegreen']
};
var VIS_WATER_MASK = {
  palette: ['white', 'black']
};

//////////////////////////////////////////////////////////////
// Helper functions
//////////////////////////////////////////////////////////////

// Create a feature for a transition class that includes the area covered.
function createFeature(transition_class_stats) {
  transition_class_stats = ee.Dictionary(transition_class_stats);
  var class_number = transition_class_stats.get('transition_class_value');
  var result = {
      transition_class_number: class_number,
      transition_class_name: lookup_names.get(class_number),
      transition_class_palette: lookup_palette.get(class_number),
      area_m2: transition_class_stats.get('sum')
  };
  return ee.Feature(null, result);   // Creates a feature without a geometry.
}

// Create a JSON dictionary that defines piechart colors based on the
// transition class palette.
// https://developers.google.com/chart/interactive/docs/gallery/piechart
function createPieChartSliceDictionary(fc) {
  return ee.List(fc.aggregate_array("transition_class_palette"))
    .map(function(p) { return {'color': p}; }).getInfo();
}

// Convert a number to a string. Used for constructing dictionary key lists
// from computed number objects.
function numToString(num) {
  return ee.Number(num).format();
}

//////////////////////////////////////////////////////////////
// Calculations
//////////////////////////////////////////////////////////////

// Create a dictionary for looking up names of transition classes.
var lookup_names = ee.Dictionary.fromLists(
    ee.List(gsw.get('transition_class_values')).map(numToString),
    gsw.get('transition_class_names')
);
// Create a dictionary for looking up colors of transition classes.
var lookup_palette = ee.Dictionary.fromLists(
    ee.List(gsw.get('transition_class_values')).map(numToString),
    gsw.get('transition_class_palette')
);

// Create a water mask layer, and set the image mask so that non-water areas
// are transparent.
var water_mask = occurrence.gt(90).mask(1);

// Generate a histogram object and print it to the console tab.
var histogram = ui.Chart.image.histogram({
  image: change,
  region: roi,
  scale: 30,
  minBucketWidth: 10
});
histogram.setOptions({
  title: 'Histogram of surface water change intensity.'
});
print(histogram);

// Summarize transition classes in a region of interest.
var area_image_with_transition_class = ee.Image.pixelArea().addBands(transition);
var reduction_results = area_image_with_transition_class.reduceRegion({
  reducer: ee.Reducer.sum().group({
    groupField: 1,
    groupName: 'transition_class_value',
  }),
  geometry: roi,
  scale: 30,
  bestEffort: true,
});
print('reduction_results', reduction_results);

var roi_stats = ee.List(reduction_results.get('groups'));

var transition_fc = ee.FeatureCollection(roi_stats.map(createFeature));
print('transition_fc', transition_fc);

// Add a summary chart.
var transition_summary_chart = ui.Chart.feature.byFeature({
    features: transition_fc,
    xProperty: 'transition_class_name',
    yProperties: ['area_m2', 'transition_class_number']
  })
  .setChartType('PieChart')
  .setOptions({
    title: 'Summary of transition class areas',
    slices: createPieChartSliceDictionary(transition_fc),
    sliceVisibilityThreshold: 0  // Don't group small slices.
  });
print(transition_summary_chart);

//////////////////////////////////////////////////////////////
// Initialize Map Location
//////////////////////////////////////////////////////////////

// Uncomment one of the following statements to center the map on
// a particular location.
// Map.setCenter(-90.162, 29.8597, 10);   // New Orleans, USA
// Map.setCenter(-114.9774, 31.9254, 10); // Mouth of the Colorado River, Mexico
// Map.setCenter(-111.1871, 37.0963, 11); // Lake Powell, USA
// Map.setCenter(149.412, -35.0789, 11);  // Lake George, Australia
Map.setCenter(105.26, 11.2134, 9);     // Mekong River Basin, SouthEast Asia
// Map.setCenter(90.6743, 22.7382, 10);   // Meghna River, Bangladesh
// Map.setCenter(81.2714, 16.5079, 11);   // Godavari River Basin Irrigation Project, India
// Map.setCenter(14.7035, 52.0985, 12);   // River Oder, Germany & Poland
// Map.setCenter(-59.1696, -33.8111, 9);  // Buenos Aires, Argentina
// Map.setCenter(-74.4557, -8.4289, 11);  // Ucayali River, Peru

//////////////////////////////////////////////////////////////
// Map Layers
//////////////////////////////////////////////////////////////

Map.addLayer({
  eeObject: water_mask,
  visParams: VIS_WATER_MASK,
  name: '90% occurrence water mask',
  shown: false
});
Map.addLayer({
  eeObject: occurrence.updateMask(occurrence.divide(100)),
  name: "Water Occurrence (1984-2015)",
  visParams: VIS_OCCURRENCE,
  shown: false
});
Map.addLayer({
  eeObject: change,
  visParams: VIS_CHANGE,
  name: 'occurrence change intensity',
  shown: false
});
Map.addLayer({
  eeObject: transition,
  name: 'Transition classes (1984-2015)',
});

На этом руководство по набору данных Global Surface Water завершено. Обратите внимание, что в этом руководстве показано, как работать только с тремя слоями данных (наличие, интенсивность изменений и переход), доступными в наборе данных Global Surface Water. О других доступных слоях данных можно узнать в руководстве пользователя по данным (версия 2).

Желаем успехов!