Переход класса воды

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

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

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

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

В разделе «Список активов» скрипта добавьте следующий оператор, который создает однополосный объект изображения с именем transition :

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

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

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

В нижней части раздела «Слои карты» вашего скрипта добавьте следующий оператор, который добавляет новый слой карты, отображающий классы перехода:

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

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

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

Классы перехода поверхностных вод
Рисунок 10. Скриншот дельты реки Меконг, демонстрирующий значительные различия в переходах классов поверхностных вод.

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

Ценить Символ Этикетка
0 Не вода
1 Постоянный
2 Новый постоянный
3 Потерянный постоянный
4 Сезонный
5 Новый сезонный
6 Потерянный сезонный
7 От сезонного до постоянного
8 Постоянный или сезонный
9 Эфемерный постоянный
10 Эфемерный сезонный

Подведение итогов по переходному классу

В этом разделе мы снова воспользуемся инструментом «Геометрический многоугольник» для определения интересующей области. Если вы хотите проанализировать новое местоположение, сначала выберите и удалите исходный нарисованный вами многоугольник, чтобы не получить результаты, связанные с объединёнными областями. Подробнее об изменении геометрии см. в разделе «Геометрические инструменты» документации по редактору кода.

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

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

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

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

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

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

Затем мы суммируем переходы классов в интересующей области ( 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. Вывод вкладки консоли, показывающий результаты группового сокращения.

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

Создание сводной диаграммы

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

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

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

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

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

Редактор кода (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 принимает словарь (содержащий площадь и класс перехода воды) и возвращает объект Feature, подходящий для построения диаграммы. Функция 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) .

Приятного анализа!