ImageCollection の可視化

ImageCollection を構成する画像は、アニメーションまたは「フィルムストリップ」と呼ばれる一連のサムネイルとして可視化できます。これらの方法は、ImageCollection の内容をすばやく評価し、時空間の変化を検証するための効果的なメディアを提供します(図 1)。

以降のセクションでは、可視化用の ImageCollection を準備する方法、各コレクション可視化方法のコード例、いくつかの高度なアニメーション手法について説明します。


図 1. 2017 年 9 月に大西洋で発生したハリケーンの 3 日間の経過を示すアニメーション。

収集の準備

コレクション内の画像をフィルタ、合成、並べ替え、スタイル設定して、関心のある画像のみを表示したり、現象を強調したりできます。任意の ImageCollection を可視化関数の入力として指定できますが、年内および年間の期間、観測間隔、地域の範囲、品質、表現を考慮してキュレートされたコレクションを使用すると、より良い結果が得られます。

フィルタリング

画像コレクションをフィルタして、ビジュアリゼーションの目的をサポートする関連データのみを含めます。日付、空間範囲、品質、特定のデータセットに固有のプロパティを考慮します。

たとえば、Sentinel-2 の表面反射率コレクションを次のようにフィルタします。

単一の期間

var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
  .filterDate('2018-01-01', '2019-01-01');

連続した日付範囲

var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
  .filter(ee.Filter.calendarRange(171, 242, 'day_of_year'));

関心のある地域

var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
  .filterBounds(ee.Geometry.Point(-122.1, 37.2));

または画像プロパティ。

var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 50));

複数のフィルタをチェーン接続する。

var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
  .filterDate('2018-01-01', '2019-01-01')
  .filterBounds(ee.Geometry.Point(-122.1, 37.2))
  .filter('CLOUDY_PIXEL_PERCENTAGE < 50');

合成

コレクション内の画像数を減らして品質を高めるために、年内と年間の期間を組み合わせます。たとえば、アフリカの年間 NDVI の可視化を作成するとします。1 つの方法は、MODIS の 16 日間の NDVI コレクションをフィルタして、2018 年のすべての観測結果を含めることです。

var ndviCol = ee.ImageCollection('MODIS/006/MOD13A2')
  .filterDate('2018-01-01', '2019-01-01')
  .select('NDVI');

フィルタと集計による年次間複合

上記のコレクションを可視化すると、雲量が多い森林地帯にかなりのノイズがあることがわかります(図 2a)。MODIS コレクションのすべての年で中央値に基づいて連続日付範囲を短縮すると、より正確な表現を実現できます。

// Make a day-of-year sequence from 1 to 365 with a 16-day step.
var doyList = ee.List.sequence(1, 365, 16);

// Import a MODIS NDVI collection.
var ndviCol = ee.ImageCollection('MODIS/006/MOD13A2').select('NDVI');

// Map over the list of days to build a list of image composites.
var ndviCompList = doyList.map(function(startDoy) {
  // Ensure that startDoy is a number.
  startDoy = ee.Number(startDoy);

  // Filter images by date range; starting with the current startDate and
  // ending 15 days later. Reduce the resulting image collection by median.
  return ndviCol
    .filter(ee.Filter.calendarRange(startDoy, startDoy.add(15), 'day_of_year'))
    .reduce(ee.Reducer.median());
});

// Convert the image List to an ImageCollection.
var ndviCompCol = ee.ImageCollection.fromImages(ndviCompList);

このコレクションから生成されるアニメーションはノイズが少なく、各画像は 20 年以上のデータの 16 日間の NDVI 合成の中央値を表しています(図 1b)。このアニメーションについて詳しくは、こちらのチュートリアルをご覧ください。

図 2a年次 NDVI(年次間の合成なし)。 図 2b年次 NDVI と年次間の合成

フィルタと削減による年間内の複合

上記の例では、年次間の合成が適用されています。1 年間の観測データを組み合わせることもできます。たとえば、Landsat データは、センサーごとに特定のシーンの 16 日ごとに収集されますが、画像の一部が雲に隠れていることがよくあります。雲をマスクして、同じシーズンの複数の画像を合成すると、雲のない画像を作成できます。次の例では、1985 年から 2011 年までの各年の中央値を使用して、7 月と 8 月の Landsat 5 画像が合成されています。

// Assemble a collection of Landsat surface reflectance images for a given
// region and day-of-year range.
var lsCol = ee.ImageCollection('LANDSAT/LT05/C02/T1_L2')
  .filterBounds(ee.Geometry.Point(-122.9, 43.6))
  .filter(ee.Filter.dayOfYear(182, 243))
  // Add the observation year as a property to each image.
  .map(function(img) {
    return img.set('year', ee.Image(img).date().get('year'));
  });


// Define a function to scale the data and mask unwanted pixels.
function maskL457sr(image) {
  // Bit 0 - Fill
  // Bit 1 - Dilated Cloud
  // Bit 2 - Unused
  // Bit 3 - Cloud
  // Bit 4 - Cloud Shadow
  var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0);
  var saturationMask = image.select('QA_RADSAT').eq(0);

  // Apply the scaling factors to the appropriate bands.
  var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
  var thermalBand = image.select('ST_B6').multiply(0.00341802).add(149.0);

  // Replace the original bands with the scaled ones and apply the masks.
  return image.addBands(opticalBands, null, true)
      .addBands(thermalBand, null, true)
      .updateMask(qaMask)
      .updateMask(saturationMask);
}

// Define a list of unique observation years from the image collection.
var years = ee.List(lsCol.aggregate_array('year')).distinct().sort();

// Map over the list of years to build a list of annual image composites.
var lsCompList = years.map(function(year) {
  return lsCol
    // Filter image collection by year.
    .filterMetadata('year', 'equals', year)
    // Apply cloud mask.
    .map(maskL457sr)
    // Reduce image collection by median.
    .reduce(ee.Reducer.median())
    // Set composite year as an image property.
    .set('year', year);
});

// Convert the image List to an ImageCollection.
var lsCompCol = ee.ImageCollection.fromImages(lsCompList);

結合と集計による年間内の複合指標

前の 2 つのコンポジット方法は、日数と年の List にマッピングして、フィルタしてコンポジットする新しい日付を段階的に定義します。このオペレーションを実行する別の方法として、結合を適用することもできます。次のスニペットでは、一意の年コレクションを定義し、saveAll 結合を適用して、特定の年に該当するすべての画像を特定します。特定の年に属する画像は、List オブジェクトにグループ化され、個別の年コレクション内の各年の代表者のプロパティとして保存されます。年間コンポジットは、一意の年コレクションにマッピングされた関数で定義された ImageCollections を減算することで、これらのリストから生成されます。

// Assemble a collection of Landsat surface reflectance images for a given
// region and day-of-year range.
var lsCol = ee.ImageCollection('LANDSAT/LT05/C02/T1_L2')
  .filterBounds(ee.Geometry.Point(-122.9, 43.6))
  .filter(ee.Filter.dayOfYear(182, 243))
  // Add the observation year as a property to each image.
  .map(function(img) {
    return img.set('year', ee.Image(img).date().get('year'));
  });

// Make a distinct year collection; one image representative per year.
var distinctYears = lsCol.distinct('year').sort('year');

// Define a join filter; one-to-many join on ‘year’ property.
var filter = ee.Filter.equals({leftField: 'year', rightField: 'year'});

// Define a join.
var join = ee.Join.saveAll('year_match');

// Apply the join; results in 'year_match' property being added to each distinct
// year representative image. The list includes all images in the collection
// belonging to the respective year.
var joinCol = join.apply(distinctYears, lsCol, filter);

// Define a function to scale the data and mask unwanted pixels.
function maskL457sr(image) {
  // Bit 0 - Fill
  // Bit 1 - Dilated Cloud
  // Bit 2 - Unused
  // Bit 3 - Cloud
  // Bit 4 - Cloud Shadow
  var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0);
  var saturationMask = image.select('QA_RADSAT').eq(0);

  // Apply the scaling factors to the appropriate bands.
  var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
  var thermalBand = image.select('ST_B6').multiply(0.00341802).add(149.0);

  // Replace the original bands with the scaled ones and apply the masks.
  return image.addBands(opticalBands, null, true)
      .addBands(thermalBand, null, true)
      .updateMask(qaMask)
      .updateMask(saturationMask);
}

// Map over the distinct years collection to build a list of annual image
// composites.
var lsCompList = joinCol.map(function(img) {
  // Get the list of images belonging to the given year.
  return ee.ImageCollection.fromImages(img.get('year_match'))
    // Apply cloud mask.
    .map(maskL457sr)
    // Reduce image collection by median.
    .reduce(ee.Reducer.median())
    // Set composite year as an image property.
    .copyProperties(img, ['year']);
});

// Convert the image List to an ImageCollection.
var lsCompCol = ee.ImageCollection(lsCompList);

結合と集計による当日コンポジット

合成の別のケースとして、空間的に連続した画像モザイクを作成することもできます。対象領域が同じパス内の 2 つの Landsat 行にまたがっている場合、2017 年と 2018 年の Landsat 8 の各軌道の 2 つの画像の画像モザイクを表示することを目的としています。ここでは、コレクションをパスと行でフィルタリングした後、結合オペレーションを使用して、取得日で定義された同じ軌道から Landsat 画像をモザイク化します。

var lsCol = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
  .filterDate('2017-01-01', '2019-01-01')
  .filter('WRS_PATH == 38 && (WRS_ROW == 28 || WRS_ROW == 29)')
  .map(function(img) {
    var date = img.date().format('YYYY-MM-dd');
    return img.set('date', date);
  });

var distinctDates = lsCol.distinct('date').sort('date');
var filter = ee.Filter.equals({leftField: 'date', rightField: 'date'});
var join = ee.Join.saveAll('date_match');
var joinCol = join.apply(distinctDates, lsCol, filter);

var lsColMos = ee.ImageCollection(joinCol.map(function(col) {
  return ee.ImageCollection.fromImages(col.get('date_match')).mosaic();
}));

並べ替え

コレクションを時刻で並べ替えて、適切な時系列にしたり、任意のプロパティで並べ替えたりできます。デフォルトでは、ビジュアリゼーションのフレーム系列はコレクションの自然な順序で並べ替えられます。シリーズの並べ替えは、sort コレクション メソッドを使用して変更できます。このメソッドでは、昇順または降順で並べ替える Image プロパティを選択します。たとえば、観測時間で並べ替えるには、ユビキタスな system:time_start プロパティを使用します。

var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
  .filterBounds(ee.Geometry.Point(-122.1, 37.2))
  .sort('system:time_start');

または、Sentinel-2 画像の場合のように、雲量の増加順に並べ替えることもできます。

var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
  .filterBounds(ee.Geometry.Point(-122.1, 37.2))
  .sort('CLOUDY_PIXEL_PERCENTAGE');

順序は、地域の平均 NDVI などの派生プロパティで定義することもできます。ここでは、リージョン NDVI がマッピング関数の各画像にプロパティとして追加され、その後、新しいプロパティが並べ替えられます。

// Define an area of interest geometry.
var aoi = ee.Geometry.Point(-122.1, 37.2).buffer(1e4);

// Filter MODIS NDVI image collection by a date range.
var ndviCol = ee.ImageCollection('MODIS/061/MOD13A1')
  .filterDate('2018-01-01', '2019-01-01')
  .select('NDVI')
  // Map over the image collection to calculate regional mean NDVI and add
  // the result to each image as a property.
  .map(function(img) {
    var meanNdvi = img.reduceRegion({
      reducer: ee.Reducer.mean(), geometry: aoi, scale: 500});
    return img.set('meanNdvi', meanNdvi.get('NDVI'));
  })
  // Sort the collection by descending regional mean NDVI.
  .sort('meanNdvi', false);

画像の可視化

画像可視化では、数値が色に変換されます。コレクションの可視化方法で画像データを色として表す方法は 3 つあります。

  1. 可視化引数を getVideoThumbURLgetFilmstripThumbURL に直接指定します。
  2. getVideoThumbURLgetFilmstripThumbURL を適用する前に、visualize イメージ メソッドを画像コレクションにマッピングします。
  3. getVideoThumbURLgetFilmstripThumbURL を適用する前に、sldStyle イメージ メソッドを画像コレクションにマッピングします。詳細については、スタイル設定されたレイヤ記述子をご覧ください。

このガイドの例では、オプション 1 と 2 を使用しています。ここでは、マルチバンド画像の 3 つの画像バンドを赤、緑、青のカラー チャネルにマッピングするか、単一バンドの値をカラーパレットに沿って線形に分類することで、可視化を実現しています。ビジュアリゼーション パラメータには次のようなものがあります。

可視化パラメータ
パラメータ 説明 タイプ
バンド RGB にマッピングする 3 つのバンド名のカンマ区切りリスト list
0 にマッピングする値 3 つの数値のリスト(帯域ごとに 1 つ)
max 255 にマッピングする値 3 つの数値のリスト(帯域ごとに 1 つ)
gain 各ピクセル値に掛ける値 3 つの数値のリスト(帯域ごとに 1 つ)
バイアス 各 DN に追加する値 3 つの数値のリスト(帯域ごとに 1 つ)
gamma ガンマ補正係数 3 つの数値のリスト(帯域ごとに 1 つ)
palette CSS スタイルのカラー文字列のリスト(単一バンド画像のみ) 16 進数文字列のカンマ区切りリスト
opacity レイヤの不透明度(0.0 は完全に透明、1.0 は完全に不透明) 数値

bands 引数を使用して、可視化するバンドを選択します。バンド名を 1 つまたは 3 つ指定します。マルチバンド画像の場合、デフォルトでは最初の 3 つのバンドが選択されます。バンド名の順序によって色が割り当てられます。リスト内の 1 つ目、2 つ目、3 つ目のバンドは、それぞれ赤、緑、青にマッピングされます。

画像を可視化する際には、データ範囲のスケーリングが重要な考慮事項となります。デフォルトでは、0 ~ 1 の浮動小数点データ値は 0 ~ 255 にスケーリングされます。この範囲外の値は、0 未満か 1 より大きいかに応じて、それぞれ 0 または 255 に強制変換されます。整数データの場合、型で定義されたフル容量は 0 ~ 255 の範囲にスケーリングされます(たとえば、符号付き 16 ビットデータの範囲は -32,768 ~ 32,767 で、デフォルトでは [0, 255] にスケーリングされます)。デフォルト設定をそのまま使用すると、画像特徴間のコントラストがほとんどまたはまったくないビジュアリゼーションが作成されることがあります。minmax を使用してコントラストを高め、特定のデータ範囲を強調します。目安として、minmax は、関心領域内のデータの 2 番目と 98 番目のパーセンタイルを示す値に設定します。デジタル標高モデルのこれらの値の計算例を次に示します。

// Import SRTM global elevation model.
var demImg = ee.Image('USGS/SRTMGL1_003');

// Define a rectangular area of interest.
var aoi = ee.Geometry.Polygon(
  [[
    [-103.84153083119054, 49.083004219142886],
    [-103.84153083119054, 25.06838270664608],
    [-85.64817145619054, 25.06838270664608],
    [-85.64817145619054, 49.083004219142886]
  ]],
  null, false);

// Calculate the 2nd and 98th percentile elevation values from rescaled (to
// 500m) pixels intersecting the area of interest. A Dictionary is returned.
var percentClip = demImg.reduceRegion({
  reducer: ee.Reducer.percentile([2, 98]),
  geometry: aoi,
  scale: 500,
  maxPixels: 3e7
});

// Print the regional 2nd and 98th percentile elevation values. Get the
// dictionary keys and use them to get the values for each percentile summary.
var keys = percentClip.keys();
print('Set vis min to:', ee.Number(percentClip.get(keys.get(0))).round());
print('Set vis max to:', ee.Number(percentClip.get(keys.get(1))).round());

palette パラメータは、8 ビットのビジュアリゼーション画像を表す色を定義します。これは単一バンド表現にのみ適用されます。マルチバンド画像で指定するとエラーになります。データが単一バンドの場合、またはマルチバンド画像から単一バンドを可視化する場合、forceRgbOutput パラメータを true に設定します(palette 引数を指定した場合は不要です)。min パラメータと max パラメータを使用して、0 ~ 255 の範囲で直線的にスケーリングする値の範囲を定義します。

単一バンド画像コレクションに可視化関数をマッピングする例を次に示します。MODIS NDVI コレクションがインポートされ、visualization メソッドの可視化引数が設定され、値を RGB 画像表現に変換する関数が NDVI コレクションにマッピングされます。

// Filter MODIS NDVI image collection by a date range.
var ndviCol = ee.ImageCollection('MODIS/061/MOD13A1')
  .filterDate('2018-01-01', '2019-01-01')
  .select('NDVI');

// Define visualization arguments.
var visArgs = {
  min: 0,
  max: 9000,
  palette: [
    'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718', '74A901',
    '66A000', '529400', '3E8601', '207401', '056201', '004C00', '023B01',
    '012E01', '011D01', '011301'
  ]
};

// Define a function to convert an image to an RGB visualization image and copy
// properties from the original image to the RGB image.
var visFun = function(img) {
  return img.visualize(visArgs).copyProperties(img, img.propertyNames());
};

// Map over the image collection to convert each image to an RGB visualization
// using the previously defined visualization function.
var ndviColVis = ndviCol.map(visFun);

マルチバンド画像コレクションに可視化関数をマッピングする例を次に示します。

// Assemble a collection of Sentinel-2 surface reflectance images for a given
// region and date range.
var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
  .filterBounds(ee.Geometry.Point(-96.9037, 48.0395))
  .filterDate('2019-06-01', '2019-10-01');

// Define visualization arguments.
var visArgs = {bands: ['B11', 'B8', 'B3'], min: 300, max: 3500};

// Define a function to convert an image to an RGB visualization image and copy
// properties from the original image to the RGB image.
var visFun = function(img) {
  return img.visualize(visArgs).copyProperties(img, img.propertyNames());
};

// Map over the image collection to convert each image to an RGB visualization
// using the previously defined visualization function.
var s2colVis = s2col.map(visFun);

この場合、3 つのバンドが指定されているため、各 RGB レイヤの強度を定義するパレット引数は指定されません。どちらの例でも、min パラメータと max パラメータを使用して、8 ビット RGB データの限界まで伸ばされる値を制御しています。

動画のサムネイル

getVideoThumbURL() 関数は、ImageCollection 内のすべての画像からアニメーションを生成します。各画像はフレームを表します。アニメーションを制作する一般的なワークフローは次のとおりです。

  1. アニメーションの領域の範囲を決定する境界を持つ Geometry を定義します。
  2. ImageCollection を定義します。
  3. 画像ビジュアリゼーションを検討します。コレクションに画像ビジュアリゼーション関数をマッピングするか、アニメーション引数のセットに画像ビジュアリゼーション引数を追加します。
  4. アニメーション引数を定義し、getVideoThumbURL メソッドを呼び出します。

getVideoThumbURL の結果は URL です。URL をコンソールに出力し、クリックして Earth Engine サーバーを起動します。新しいブラウザタブでアニメーションがオンザフライで生成されます。または、コレクションとその対応するアニメーション引数で ui.Thumbnail 関数を呼び出して、Code Editor コンソールでアニメーションを表示します。レンダリングが完了すると、アニメーションを右クリックしてコンテキスト メニューから適切なオプションを選択することで、アニメーションをダウンロードできます。

次の例は、24 時間の全球温度を示すアニメーションを生成する方法を示しています。この例では、まず ImageCollection に可視化関数をマッピングするのではなく、アニメーション引数とともに可視化引数を含めています。このスクリプトを実行すると、Code Editor コンソールに図 3 のようなアニメーションが表示されます。

// Define an area of interest geometry with a global non-polar extent.
var aoi = ee.Geometry.Polygon(
  [[[-179.0, 78.0], [-179.0, -58.0], [179.0, -58.0], [179.0, 78.0]]], null,
  false);

// Import hourly predicted temperature image collection for northern winter
// solstice. Note that predictions extend for 384 hours; limit the collection
// to the first 24 hours.
var tempCol = ee.ImageCollection('NOAA/GFS0P25')
  .filterDate('2018-12-22', '2018-12-23')
  .limit(24)
  .select('temperature_2m_above_ground');

// Define arguments for animation function parameters.
var videoArgs = {
  dimensions: 768,
  region: aoi,
  framesPerSecond: 7,
  crs: 'EPSG:3857',
  min: -40.0,
  max: 35.0,
  palette: ['blue', 'purple', 'cyan', 'green', 'yellow', 'red']
};

// Print the animation to the console as a ui.Thumbnail using the above defined
// arguments. Note that ui.Thumbnail produces an animation when the first input
// is an ee.ImageCollection instead of an ee.Image.
print(ui.Thumbnail(tempCol, videoArgs));

// Alternatively, print a URL that will produce the animation when accessed.
print(tempCol.getVideoThumbURL(videoArgs));


図 3. 冬至における 1 時間ごとの地表の表面温度をアニメーション GIF として表した画像。

フィルムストリップ

getFilmstripThumbUrl 関数は、ImageCollection 内のすべての画像をノースサウス シリーズに連結した単一の静的画像を生成します。フィルムストリップのフレームの順序は、コレクションの自然な順序に従います。

getFilmstripThumbUrl の結果は URL です。URL をコンソールに出力し、クリックして Earth Engine サーバーを起動します。新しいブラウザタブで、画像がオンザフライで生成されます。レンダリングが完了すると、画像を右クリックしてコンテキスト メニューから適切なオプションを選択することで、画像をダウンロードできます。

次のコード スニペットは、上の動画のサムネイルの例と同じコレクションを使用しています。このスクリプトを実行すると、図 4 のようなフィルムストリップが Code Editor コンソールに表示されます。

// Define an area of interest geometry with a global non-polar extent.
var aoi = ee.Geometry.Polygon(
  [[[-179.0, 78.0], [-179.0, -58.0], [179.0, -58.0], [179.0, 78.0]]], null,
  false);

// Import hourly predicted temperature image collection for northern winter
// solstice. Note that predictions extend for 384 hours; limit the collection
// to the first 24 hours.
var tempCol = ee.ImageCollection('NOAA/GFS0P25')
  .filterDate('2018-12-22', '2018-12-23')
  .limit(24)
  .select('temperature_2m_above_ground');

// Define arguments for the getFilmstripThumbURL function parameters.
var filmArgs = {
  dimensions: 128,
  region: aoi,
  crs: 'EPSG:3857',
  min: -40.0,
  max: 35.0,
  palette: ['blue', 'purple', 'cyan', 'green', 'yellow', 'red']
};

// Print a URL that will produce the filmstrip when accessed.
print(tempCol.getFilmstripThumbURL(filmArgs));


図 4. 冬至における 1 時間ごとの地表の表面温度をフィルムストリップの PNG 画像として表した画像。フィルムストリップは表示のために 4 つのセクションに分割されています。getFilmstripThumbURL の結果は、北と南に結合された単一のコレクション画像のシリーズです。

高度な手法

以降のセクションでは、クリッピング、不透明度、レイヤ コンポジットを使用して、ポリゴン境界の追加、対象領域の強調、コレクション内の画像の比較によって可視化を強化する方法について説明します。

このセクションの次の例はすべて、ここで定義されている同じベース ImageCollection を使用します。

// Import hourly predicted temperature image collection for northern winter
// solstice. Note that predictions extend for 384 hours; limit the collection
// to the first 24 hours.
var tempCol = ee.ImageCollection('NOAA/GFS0P25')
  .filterDate('2018-12-22', '2018-12-23')
  .limit(24)
  .select('temperature_2m_above_ground');

// Define visualization arguments to control the stretch and color gradient
// of the data.
var visArgs = {
  min: -40.0,
  max: 35.0,
  palette: ['blue', 'purple', 'cyan', 'green', 'yellow', 'red']
};

// Convert each image to an RGB visualization image by mapping the visualize
// function over the image collection using the arguments defined previously.
var tempColVis = tempCol.map(function(img) {
  return img.visualize(visArgs);
});

// Import country features and filter to South American countries.
var southAmCol = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017')
  .filterMetadata('wld_rgn', 'equals', 'South America');

// Define animation region (South America with buffer).
var southAmAoi = ee.Geometry.Rectangle({
  coords: [-103.6, -58.8, -18.4, 17.4], geodesic: false});

オーバーレイ

複数の画像を重ねるには、blend Image メソッドを使用します。このメソッドでは、2 つの画像の重複するピクセルが、マスク(不透明度)に基づいてブレンドされます。

ベクター オーバーレイ

行政区域のポリゴンなどのジオメトリを画像に追加すると、有用な空間コンテキストを提供できます。上の図 3 は、世界中の 1 日の地表温度のアニメーションです。陸地と海の境界は多少判別できますが、国境のポリゴン オーバーレイを追加することで明確にできます。

ベクトルデータ(Features)は、paint メソッドを適用して画像に描画されます。特徴は既存の画像にペイントできますが、空白の画像にペイントしてスタイルを設定し、結果を他のスタイル設定された画像レイヤとブレンドすることをおすすめします。ビジュアリゼーション スタックの各レイヤを個別に扱うことで、スタイル設定をより細かく制御できます。

次の例は、南アメリカの国境を空白の Image にペイントし、その結果を世界中の 1 日あたりの温度コレクションの各 Image とブレンドする方法を示しています(図 5)。重ねて表示される国境は、陸地と水域を区別し、気温のパターンのコンテキストを提供します。

// Define an empty image to paint features to.
var empty = ee.Image().byte();

// Paint country feature edges to the empty image.
var southAmOutline = empty
  .paint({featureCollection: southAmCol, color: 1, width: 1})
  // Convert to an RGB visualization image; set line color to black.
  .visualize({palette: '000000'});

// Map a blend operation over the temperature collection to overlay the country
// border outline image on all collection images.
var tempColOutline = tempColVis.map(function(img) {
  return img.blend(southAmOutline);
});

// Define animation arguments.
var videoArgs = {
  dimensions: 768,
  region: southAmAoi,
  framesPerSecond: 7,
  crs: 'EPSG:3857'
};

// Display the animation.
print(ui.Thumbnail(tempColOutline, videoArgs));


図 5. コレクション内の画像にベクター オーバーレイを追加して、空間的なコンテキストを提供します。

画像レイヤー

複数の画像を重ねて、目的のスタイルを実現できます。関心のある領域を強調するとします。画像ビジュアリゼーションのミュート コピーをベースレイヤとして作成し、元のビジュアリゼーションの切り抜きバージョンをオーバーレイできます。前の例を基に、次のスクリプトでは図 6 を生成します。

// Define an empty image to paint features to.
var empty = ee.Image().byte();

// Paint country feature edges to the empty image.
var southAmOutline = empty
  .paint({featureCollection: southAmCol, color: 1, width: 1})
  // Convert to an RGB visualization image; set line color to black.
  .visualize({palette: '000000'});

// Map a blend operation over the temperature collection to overlay the country
// border outline image on all collection images.
var tempColOutline = tempColVis.map(function(img) {
  return img.blend(southAmOutline);
});

// Define a partially opaque grey RGB image to dull the underlying image when
// blended as an overlay.
var dullLayer = ee.Image.constant(175).visualize({
  opacity: 0.6, min: 0, max: 255, forceRgbOutput: true});

// Map a two-part blending operation over the country outline temperature
// collection for final styling.
var finalVisCol = tempColOutline.map(function(img) {
  return img
    // Blend the dulling layer with the given country outline temperature image.
    .blend(dullLayer)
    // Blend a clipped copy of the country outline temperature image with the
    // dulled background image.
    .blend(img.clipToCollection(southAmCol));
});

// Define animation arguments.
var videoArgs = {
  dimensions: 768,
  region: southAmAoi,
  framesPerSecond: 7,
  crs: 'EPSG:3857'
};

// Display the animation.
print(ui.Thumbnail(finalVisCol, videoArgs));


図 6. 画像を切り抜いてミュートされたコピーにオーバーレイすることで、関心のある領域を強調します。

画像データを陰影付きベースレイヤとブレンドして地形を表示し、ビジュアリゼーションに深みを加えることもできます(図 7)。

// Define a hillshade layer from SRTM digital elevation model.
var hillshade = ee.Terrain.hillshade(ee.Image('USGS/SRTMGL1_003')
  // Exaggerate the elevation to increase contrast in hillshade.
  .multiply(100))
  // Clip the DEM by South American boundary to clean boundary between
  // land and ocean.
  .clipToCollection(southAmCol);

// Map a blend operation over the temperature collection to overlay a partially
// opaque temperature layer on the hillshade layer.
var finalVisCol = tempColVis.map(function(img) {
  return hillshade
    .blend(img.clipToCollection(southAmCol).visualize({opacity: 0.6}));
});

// Define animation arguments.
var videoArgs = {
  dimensions: 768,
  region: southAmAoi,
  framesPerSecond: 7,
  crs: 'EPSG:3857'
};

// Display the animation.
print(ui.Thumbnail(finalVisCol, videoArgs));


図 7. 部分的に透明な画像データをヒルシャドウ レイヤに重ねて、地形を表示します。

切り替え効果

画像コレクションをカスタマイズして、フェード、フリッカー、スライドの切り替えを使用して、コレクション内の 2 つの画像の違いを示すアニメーションを作成します。次の例では、次のスクリプトによって生成された同じベース ビジュアリゼーションを使用します。

// Define an area of interest geometry with a global non-polar extent.
var aoi = ee.Geometry.Polygon(
  [[[-179.0, 78.0], [-179.0, -58.0], [179.0, -58.0], [179.0, 78.0]]], null,
  false);

// Import hourly predicted temperature image collection.
var temp = ee.ImageCollection('NOAA/GFS0P25')

// Define a northern summer solstice temperature image.
var summerSolTemp = temp
  .filterDate('2018-06-21', '2018-06-22')
  .filterMetadata('forecast_hours', 'equals', 12)
  .first()
  .select('temperature_2m_above_ground');

// Define a northern winter solstice temperature image.
var winterSolTemp = temp
  .filterDate('2018-12-22', '2018-12-23')
  .filterMetadata('forecast_hours', 'equals', 12)
  .first()
  .select('temperature_2m_above_ground');

// Combine the solstice images into a collection.
var tempCol = ee.ImageCollection([
  summerSolTemp.set('season', 'summer'),
  winterSolTemp.set('season', 'winter')
]);

// Import international boundaries feature collection.
var countries = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');

// Define visualization arguments.
var visArgs = {
  min: -40.0,
  max: 35.0,
  palette: ['blue', 'purple', 'cyan', 'green', 'yellow', 'red']
};

// Convert the image data to RGB visualization images.
// The clip and unmask combination sets ocean pixels to black.
var tempColVis = tempCol.map(function(img) {
  return img
    .visualize(visArgs)
    .clipToCollection(countries)
    .unmask(0)
    .copyProperties(img, img.propertyNames());
});

点滅

コレクションに 2 つの画像しかない場合(この例が該当します)、コレクションのアニメーションではデフォルトでちらつきが発生します。framesPerSecond アニメーション引数を調整して、フリッカーの速度を速くまたは遅くします。上記のコレクションに次の可視化引数を適用すると、図 8 が生成されます。

// Define arguments for animation function parameters.
var videoArgs = {
  dimensions: 768,
  region: aoi,
  framesPerSecond: 2,
  crs: 'EPSG:3857'
};

// Display animation to the Code Editor console.
print(ui.Thumbnail(tempColVis, videoArgs));


図 8. 北半球と冬至の GMT 正午の表面温度がちらつく例。

フェード

2 つのレイヤ間のフェード トランジションは、一方のレイヤの不透明度を同時に減らしながら、もう一方のレイヤの不透明度を 0 近くから 1 まで段階的に増やすことで実現します(図 9)。

// Define a sequence of decreasing opacity increments. Note that opacity cannot
// be 0, so near 1 and 0 are used. Near 1 is needed because a compliment is
// calculated in a following step that can result in 0 if 1 is an element of the
// list.
var opacityList = ee.List.sequence({start: 0.99999, end: 0.00001, count: 20});

// Filter the summer and winter solstice images from the collection and set as
// image objects.
var summerImg = tempColVis.filter(ee.Filter.eq('season', 'summer')).first();
var winterImg = tempColVis.filter(ee.Filter.eq('season', 'winter')).first();

// Map over the list of opacity increments to iteratively adjust the opacity of
// the two solstice images. Returns a list of images.
var imgList = opacityList.map(function(opacity) {
  var opacityCompliment = ee.Number(1).subtract(ee.Number(opacity));
  var winterImgFade = winterImg.visualize({opacity: opacity});
  var summerImgFade = summerImg.visualize({opacity: opacityCompliment});
  return summerImgFade.blend(winterImgFade).set('opacity', opacity);
});

// Convert the image list to an image collection; the forward phase.
var fadeForward = ee.ImageCollection.fromImages(imgList);

// Make a copy of the collection that is sorted by ascending opacity to
// represent the reverse phase.
var fadeBackward = fadeForward.sort({property: 'opacity'});

// Merge the forward and reverse phase frame collections.
var fadeCol = fadeForward.merge(fadeBackward);

// Define animation arguments.
var videoArgs = {
  dimensions: 768,
  region: aoi,
  framesPerSecond: 25,
  crs: 'EPSG:3857'
};

// Display the animation.
print(ui.Thumbnail(fadeCol, videoArgs));


図 9. 夏至と冬至における午後 12 時の GMT 地表温度のフェード例。

スライダー

スライダーの切り替え効果は、基になる画像レイヤを段階的に表示または非表示にします。これは、経度の範囲にわたって重ね合わせた画像の不透明度を反復的に調整することで実現されます(図 10)。

// Define a sequence of longitude increments. Start and end are defined by the
// min and max longitude of the feature to be provided to the region parameter
// of the animation arguments dictionary.
var lonSeq = ee.List.sequence({start: -179, end: 179, count: 20});

// Define a longitude image.
var longitude = ee.Image.pixelLonLat().select('longitude');

// Filter the summer and winter solstice images from the collection and set as
// image objects.
var summerImg = tempColVis.filter(ee.Filter.eq('season', 'summer')).first();
var winterImg = tempColVis.filter(ee.Filter.eq('season', 'winter')).first();

// Map over the list of longitude increments to iteratively adjust the mask
// (opacity) of the overlying image layer. Returns a list of images.
var imgList = lonSeq.map(function(lon) {
  lon = ee.Number(lon);
  var mask = longitude.gt(lon);
  return summerImg.blend(winterImg.updateMask(mask)).set('lon', lon);
});

// Convert the image list to an image collection; concealing phase.
var sliderColForward = ee.ImageCollection.fromImages(imgList);

// Make a copy of the collection that is sorted by descending longitude to
// represent the revealing phase.
var sliderColbackward = sliderColForward
  .sort({property: 'lon', ascending: false});

// Merge the forward and backward phase frame collections.
var sliderCol = sliderColForward.merge(sliderColbackward);

// Define animation arguments.
var videoArgs = {
  dimensions: 768,
  region: aoi,
  framesPerSecond: 25,
  crs: 'EPSG:3857'
};

// Display the animation.
print(ui.Thumbnail(sliderCol, videoArgs));


図 10. 夏至と冬至の午後 0 時の GMT 地表温度間のスライド遷移の例。