Skip to content

Instantly share code, notes, and snippets.

@jdbcode
Last active January 6, 2021 22:26
Show Gist options
  • Save jdbcode/369c16d781c39385852f50cdc25f2873 to your computer and use it in GitHub Desktop.
Save jdbcode/369c16d781c39385852f50cdc25f2873 to your computer and use it in GitHub Desktop.
Earth Engine GOES event tracking animation.

Code Editor script

/**
 * @license
 * Copyright 2020 Google LLC.
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Makes a GOES MCMIPC animation that tracks an event. Set a region of interest
 * and two points that define the path to track along the duration of the
 * animation.
 */



// #############################################################################
// ### INPUTS ###
// #############################################################################

var COLLECTION = 'NOAA/GOES/16/MCMIPC';  // or 'NOAA/GOES/17/MCMIPC'

// Observation start and end time of interest.
var START_TIME = '2020-12-23T11:00:00';
var END_TIME = '2020-12-23T15:30:00';
var TIME_ZONE = 'America/North_Dakota/Center';

// Video parameters.
var VID_PARAMS = {
  dimensions: 512, // Max dim.
  region: window,  // Edit geometry import using Code Editor drawing tools.
  framesPerSecond: 18,
  crs: 'EPSG:5070',
};

// Visualization params.
var GOES_RGB_VIS = {
  min: 0,
  max: 0.3,
  gamma: 1.3,
};

/* GEOMETRY IMPORTS
var window = 
    ee.Geometry.Polygon(
        [[[-106.21025959225202, 39.76736868283722],
          [-106.21025959225202, 35.578020596544334],
          [-98.16826740475202, 35.578020596544334],
          [-98.16826740475202, 39.76736868283722]]], null, false),
    endpoints = ee.Geometry.MultiPoint(
        [[-102.03545490475202, 37.24015600167216],
        [-100.76104084225202, 33.898903754557274]]);
*/

// #############################################################################



// Band names.
var BLUE = 'CMI_C01';
var RED = 'CMI_C02';
var VEGGIE = 'CMI_C03';
var GREEN = 'GREEN';

GOES_RGB_VIS['bands'] = [RED, GREEN, BLUE];

// Create a states outline layer.
var states = ee.FeatureCollection('FAO/GAUL/2015/level1');
var statesOutline = ee.Image().byte()
  .paint({featureCollection: states, color: 1, width: 1})
  .visualize({palette: '000000', opacity: 0.6});

/**
 * Properly scales an MCMIPM image.
 *
 * @param {ee.Image} image An unaltered MCMIPM image.
 * @return {ee.Image}
 */
var applyScaleAndOffset = function(image) {
  var names = image.select('CMI_C..').bandNames();

  // Scale the radiance bands using the image's metadata.
  var scales = names.map(function(name) {
    return image.getNumber(ee.String(name).cat('_scale'));
  });
  var offsets = names.map(function(name) {
    return image.getNumber(ee.String(name).cat('_offset'));
  });
  var scaled = image.select('CMI_C..')
                   .multiply(ee.Image.constant(scales))
                   .add(ee.Image.constant(offsets));

  return image.addBands({srcImg: scaled, overwrite: true});
};

/**
 * Computes and adds a green radiance band to a MCMIPM image.
 *
 * The image must already have been properly scaled via applyScaleAndOffset.
 *
 * For more information on computing the green band, see:
 *   https://doi.org/10.1029/2018EA000379
 *
 * @param {ee.Image} image An image to add a green radiance band to. It
 *     must be the result of the applyScaleAndOffset function.
 * @return {ee.Image}
 */
var addGreenBand = function(image) {
  function toBandExpression(bandName) { return 'b(\'' + bandName + '\')'; }

  var B_BLUE = toBandExpression(BLUE);
  var B_RED = toBandExpression(RED);
  var B_VEGGIE = toBandExpression(VEGGIE);

  // Green = 0.45 * Red + 0.10 * NIR + 0.45 * Blue
  var GREEN_EXPR = GREEN + ' = 0.45 * ' + B_RED + ' + 0.10 * ' + B_VEGGIE +
      ' + 0.45 * ' + B_BLUE;

  var green = image.expression(GREEN_EXPR).select(GREEN);
  return image.addBands(green);
};

/**
 * Creates GOES visualization layer.
 *
 * The image must already have been properly scaled via applyScaleAndOffset
 * and had the green band added via addGreenBand.
 *
 * @param {ee.Image} image An image to visualize.
 * @return {ee.Image}
 */
var visualizeGoes = function(image) {
  return image.visualize(GOES_RGB_VIS)
    .resample('bicubic')
    .reproject({crs: VID_PARAMS.crs, scale: 1500})
    .blend(statesOutline)
    .set('system:time_start', image.get('system:time_start'));
};

// Build the base collection.
var start = ee.Date(START_TIME, TIME_ZONE);
var end = ee.Date(END_TIME, TIME_ZONE);
var col = ee.ImageCollection(COLLECTION)
  .filterDate(start, end)
  .map(applyScaleAndOffset)
  .map(addGreenBand)
  .map(visualizeGoes);

// Figure out the transform distance interval x, y
var coords = ee.List(endpoints.coordinates());
var startLoc = ee.List(coords.get(0));
var endLoc = ee.List(coords.get(1));
var xDelta = ee.Number(endLoc.get(0)).subtract(ee.Number(startLoc.get(0)));
var yDelta = ee.Number(endLoc.get(1)).subtract(ee.Number(startLoc.get(1)));
var xMove = ee.Number(xDelta.divide(col.size()).multiply(-111000));  // Degrees to meters
var yMove = ee.Number(yDelta.divide(col.size()).multiply(111000));  // Degrees to meters

// Shift each image in the collection.
var seq = ee.List.sequence(0, ee.Number(col.size()).subtract(1));
var colList = col.toList(col.size());
var colTracking = ee.ImageCollection.fromImages(seq.map(function(i) {
  var img = ee.Image(colList.get(i));
  return img.translate({
    x: xMove.multiply(ee.Number(i)),
    y: yMove.multiply(ee.Number(i)),
    units: 'meters'
  }).set('system:time_start', img.get('system:time_start'));
}));

// Figure out where to place the start and end locations
Map.addLayer(col.first());  // First frame
Map.addLayer(col.sort('system:time_start', false).first());  // Last frame

// Uncomment this line after the start and end points are placed.
print(ui.Thumbnail(colTracking, VID_PARAMS));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment