Skip to content

PATTERNS

local operations

Local overlay methods compare values at pixels in one or more rasters.

local-overlay-operations


masks

Masks act like masking tape when you paint. When you mask pixels in a raster before displaying the raster as a map layer, all the pixels with the mask will remain transparent (they will not be displayed with a color). Similarly, when you mask pixels of an input raster in an operation, the masked pixels will be excluded from the computation.

Typically, workflows with masks involve three steps.

graph LR
  step01("Make mask")
  step02("Apply mask") ;
  step03("Paint (display as layer)") ;
  step04("Transform (input in operation)") ;


  step01 --> step02
  step02 --> step03
  step02 --> step04


  classDef steps fill:#C9C3E6,stroke-width:1px,stroke: #00000000, color:#000000; 



  class step01 steps; 
  class step02 steps;
  class step03 steps;
  class step04 steps; 

mask pixels

This pattern uses another raster, typically a Boolean raster as a mask on another raster.

Any pixel with the value 0 in the mask acts like masking tape and prevents numbers in the output raster from being painted at that location. Masked values will not be displayed with colors when you place the raster layer on a Map. Masked values in an input raster will also be ignored in any subsequent operation.

mask


graph LR
  input[image]
  method(".updateMask()") ;
  output[/"image_with_mask"/]  ;
  arg01["mask<br>boolean_raster"] ;

  input --> method
  method --> output
  arg01 --o method


  classDef in-out fill:#FFFFFF,stroke-width:1px,stroke: #000000, color:#000000; 
  classDef op fill:#000000,stroke-width:0px,color:#FFFFFF;
  classDef arg fill:#CCCCCC,stroke-width:0px,color:#000000;


  class input in-out; 
  class method op;
  class output in-out;
  class arg01 arg; 


var image_with_mask = image.updateMask(boolean_image);

self mask pixels

If you want to ignore pixels that store the value 0 in an raster, you can self-mask. This is not technically a local operation because it only involves one raster, but I wanted to keep the mask operations together.

self mask


graph LR
  input[image]
  method(".selfMask()") ;
  output[/"image_with_mask"/]  ;

  input --> method
  method --> output


  classDef in-out fill:#FFFFFF,stroke-width:1px,stroke: #000000, color:#000000; 
  classDef op fill:#000000,stroke-width:0px,color:#FFFFFF;
  classDef arg fill:#CCCCCC,stroke-width:0px,color:#000000;


  class input in-out; 
  class method op;
  class output in-out;


var image_with_mask = image.selfMask();

unmask

If you are working with a masked image, you can remove the mask and populate all the masked locations with a constant. Again, this is not technically a local operation because it only involves one raster, but I wanted to keep the mask operations together.

self mask



graph LR
  input["image_with_mask"]
  method(".unmask()") ;
  output[/"image_without_mask"/]  ;
  arg["constant"]

  input --> method
  method --> output
  arg --o method  

  classDef in-out fill:#FFFFFF,stroke-width:1px,stroke: #000000, color:#000000; 
  classDef op fill:#000000,stroke-width:0px,color:#FFFFFF;
  classDef arg fill:#CCCCCC,stroke-width:0px,color:#000000;

  class input in-out; 
  class method op;
  class output in-out;
  class arg arg


var image_without_mask = image_with_mask.unmask();

In Earth Engine, unmask() will replace masked values with 0 by default (This makes the method the inverse of selfMask(). You can include an argument (a number in the parantheses) to specify a different constant.


logical comparisons

The diagram below illustrates three common logical comparisons between two regions: A, B. Going from left to right, the first picture shows the two regions. The second picture shows where either region A or region B are present (or true), called the union of the two. The third picture shows where both region A and region B are present, called the intersection of the two. The fourth and final picture shows where region A is present but not region B. In this last case, region B acts like an eraser or knife that cuts out the portion of region A that it touches. Sometimes this last case is called the difference or subtraction of two sets.

Logical comparisons


The patterns below describe how each logical comparison shown above can be implemented with raster data models.


union

The .or() method takes two rasters as inputs and kicks out a boolean raster that represents their union: pixels in the output raster are true if they are true (not 0) in either raster A or raster B.

OR union


The inputs are commonly boolean rasters, as illustrated in the above diagram, but the method will work with nominal (class) data, returning a boolean raster.

OR union nominal case


The order of the inputs (which raster is image_A versus image_B) does not really matter. The main thing to remember here is that any masked pixels will be excluded from this operation. So it is good practice to triple-check your inputs to see if you are using a mask on pixels that should be zeros so that you do not inadvertently erase locations that are true in one layer but masked in another.

var image_union = image_A.or(image_B);

intersection

The and() method takes two rasters as inputs and kicks out a boolean raster that represents their intersection: pixels in the output raster are true (not 0) if they are true (not zero) in both raster A and raster B.

AND intersection


Like the or operation, the inputs are commonly boolean rasters, but the method will work with nominal (class) data, returning a boolean raster as shown below.

AND nominal case


The order of inputs again does not really matter here. And because this operation is like a knife that cuts and alters the shapes of inputs, this method is less sensitive to masks, unlike the or() method.

var image_intersection = image_A.and(image_B);

not

In Earth Engine, finding locations that are in raster A but not in raster B is a little tricky. The workflow involves inverting the binary of raster_B and then multiplying it against raster_A.

NOT


var image_A_not_B = image_A.multiply(image_B_inverted_binary);

map arithmetic

As the diagram at the top of this page illustrates, a common type of local overlay operation performs arithmetic operations (addition, subtraction, multiplication, and division) with two rasters.


addition

The .add() method performs addition between values in corresponding pixels of two rasters. The order (which raster is A versus B) does not matter. The main thing to remember is that any pixel that is masked will be excluded from the operation (so that output pixel will remain masked).

local-overlay-operations


var image_A_add_B = image_A.add(image_B);

subtraction

The .subtract() method performs subtraction between values in corresponding pixels of two rasters. The order matters here: you subtract image_B from image_A. Masked pixels in either raster will remain masked in the output.

local-overlay-operations

var image_A_subtract_B = image_A.subtract(image_B);

multiplication

The .multiply() method performs multiplication between values in corresponding pixels of two rasters. The order does not matter here. Masked pixels do matter and will remain masked.

Multiplication is often used with a boolean raster as a method to erase values in another image, because 0 will convert to 0 and 1 will retain the original value.

local-overlay-operations

var image_A_subtract_B = image_A.multiply(image_B);

division

The .divide() method performs division between values in corresponding pixels of the two rasters. The order does matter here because you divide the values in image_A by the values in image_B. Masked pixels in either image again remain masked in the output.

local-overlay-operations

var image_A_divide_B = image_A.divide(image_B);

spectral indices

Spectral indices are derived from local operations that compare values in two or more spectral bands. There are many, many different indices. Below I describe two indices that we will use in tutorial 9 and problem 9.

NDVI

The normalized difference vegetation index (NDVI) is a measure of the health of vegetation. It is calculated by comparing reflectance of near infrared (NIR) light and red light.


ndvi


This takes advantage of the difference in the spectral signatures of healthy versus stressed or dead vegetation; specifically, healthy vegetation will have higher reflectance in the NIR band due to cell structure and lower reflectance in red bands due to absorption by chlorophyll.


ndvi-signature

NDVI values will range from -1 to 1. Although results will vary by region, year, and instrument, here are some ballpark numbers to help interpret NDVI values:

  • 0 or less: water
  • 0 - 0.15: impervious surfaces, rock, sand, or snow
  • 0.15 - 0.33: sparse and/or stressed vegetation
  • 0.33 - 0.66: moderatly healthy vegetation
  • 0.66 - 1: dense and/or healthy vegetation

NBR

The normalized burn ratio (NBR) is a measure of the severity of a fire. It is calculated by comparing reflectance of near infrared (NIR) and shortwave infrared (SWIR) light.

nbr

source


This takes advantage of differences in the spectral signatures of healthy and burned vegetation.


nbr signatures


awesome spectral indices module

Although you can compute most spectral indices from scratch in Earth Engine, I appreciate using a module developed by Dave Montero Loaiza that is… pretty… awesome.

Here are the steps for using his module.


load module

// ------------------------------------------------------------------------
//  Load spectral indices module.  
// ------------------------------------------------------------------------

var spectral = require("users/dmlmont/spectral:spectral");

print(
  "SPECTRAL INDICES", 
  spectral.indices
);

define parameters

This will depend on which data product you are using from the Earth Engine Data catalog and based on this table from Dave’s github docs.

Please note: this pattern is set up to work directly from the image starter scripts for Landsat and Sentinel. That is why the input image is called output. If you are trying to compare indices at two different snapshots in time, you may have changed the name of your image and will need to insert a line that temporarily renames your image. Otherwise EE will likely throw a “Line ###: output is not defined” error message.

// Temporarily rename the input image to work with pattern.

var output = target_input;  // Change "target_input" to the name of image you want to use as the input. 

L5 & L7

For Landsat 5 or 7 Surface Reflectance, use this pattern:

// ------------------------------------------------------------------------
//  Define parameters for Landsat 5 or 7 Surface Reflectance. 
// ------------------------------------------------------------------------

var parameters = {
    "B": output.select("SR_B1"),
    "G": output.select("SR_B2"),
    "R": output.select("SR_B3"),
    "N": output.select("SR_B4"),
    "S1": output.select("SR_B5"),
    "S2": output.select("SR_B7"),
    "T": output.select("SR_B6")
};

L8 & L9

For Landsat 8 or 9, use this pattern:


// ------------------------------------------------------------------------
//  Define parameters for Landsat 8 or 9 Surface Reflectance. 
// ------------------------------------------------------------------------

var parameters = {
    "A": output.select("SR_B1"),
    "B": output.select("SR_B2"),
    "G": output.select("SR_B3"),
    "R": output.select("SR_B4"),
    "N": output.select("SR_B5"),
    "S1": output.select("SR_B6"),
    "S2": output.select("SR_B7"),
    "T1": output.select("SR_B10"),
    "T2": output.select("SR_B11")
};

S2

For Sentinel 2, use this pattern:


// ------------------------------------------------------------------------
//  Define parameters for Sentinel 2 Surface Reflectance. 
// ------------------------------------------------------------------------

var parameters = {
    "A": output.select("B1"),
    "B": output.select("B2"),
    "G": output.select("B3"),
    "R": output.select("B4"),
    "RE1": output.select("B5"),
    "RE2": output.select("B6"),
    "RE3": output.select("B7"),
    "N": output.select("B8"),
    "N2": output.select("B8A"),
    "WV": output.select("B9"),
    "S1": output.select("B11"),
    "S2": output.select("B12")
};

compute spectral indices

You can compute one or more indices with this pattern. This example computes NDVI, NBR, and MNDWI. Each index will be stored as a new band in the output image.

// ------------------------------------------------------------------------
//  Compute spectral index or indices.
// ------------------------------------------------------------------------

var output_si = spectral.computeIndex(output,["NDVI", "NBR"], parameters);

print(
  "IMAGE WITH SPECTRAL INDICES",
  output_si);


This work is licensed under CC BY-NC-SA 4.0