Skip to content

PATTERNS

filter collections

Image and feature collections often include more data than you need. As a result, both tend to follow this pattern:

graph LR

  step01("Access collection from cloud") ;
  step02("Filter collection") ;

  step01 --> step02


  classDef task fill:#C9C3E6,stroke-width:0px,color:#000000 ;

  class step01 task; 
  class step02 task;

A filter is a method of asking a true or false question about the content of a collection and then only keeping the records where the answer is true. In other GIS, these are called selections or queries which are often written in SQL, which follows a similar logic but employs a different syntax.

In Earth Engine, you can filter collections in three ways:

  • by time,
  • by location,
  • by attribute.

filter by time

An image collection represents a time series when it contains images that capture the same region of space at different moments in time. The date associated with each image will be a property of the image. Earth Engine provides several different methods to filter the collection by defining a temporal interval, or a window of time defined by a start and end date.

filter by calendar range

This method allows you to choose a calendar unit for a temporal interval.

var select_by_calendar = ic.filter(
  ee.Filter.calendarRange(start, end, "unit")
  )
;

filter by date range

This method defines a temporal interval with a start and end date. The start_date and end_date must be strings in the format: "YYYY-MM-DD".

var select_by_date = ic.filter(
  ee.Filter.date("start_date," "end_date")
  )
;

filter by location

This involves making spatial comparisons between two layers. One layer is a collection of things (features, images) that has more things than you need. The other layer is a geometry, feature, or feature collection that defines your place of interest. These filters work by comparing the location of things in the first collection with the location of the place of interest in the second layer.

by bounds

One of the most common spatial filters tests for overlap between items in the collection and the place of interest. Any item in the collection that overlaps the place of interest will pass through the filter and the rest of the things will be filtered out. Even things that only overlap the place of interest on the very edge will still pass through the filter and remain in the collection. The diagram below shows this by passing items 7, 13, 19 through the filter. You can think of the items in this collection as either individual images (image collection) or individual features (feature collection).

filter-bounds-overlap


It is sometimes helpful to think of this as a fork filter, because this filter does not alter the shape of the items in the collection. The filter does not cut them, as a knife would, and the collective shape of the items that pass through the filter does not match the shape of the place of interest.

The syntax for this method in Earth Engine looks a little clunky because you first call .filter() on the collection that you want to filter (called c for collection in the code snippet), which then takes ee.Filter.bounds() as an argument, which itself takes the place of interest object as an argument.

graph LR

  input["collection"] ;
  method("<b>.filter()</b>") ;
  output[/"collection_filter_bounds"/]  ;

  input --> method --> output

  arg["ee.Filter.bounds()"] ;

  arg --o method

  arg2["place_of_interest"] ;

  arg2 --o arg

  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; 
  class arg2 arg;
var collection_filter_bounds = collection.filter(ee.Filter.bounds(place_of_interest));

Because this method is very commonly used to filter both image and feature collections but has such wonky syntax, Earth Engine provides an alternative expression to call the method that is a bit simpler to write. In the snippet below, c is the collection to filter and place_of_interest is the object that is being used to test for overlap it.

var collection_filter_bounds = collection.filterBounds(place_of_interest);

The main advantage of learning to write the first, clunky syntax is that you can use it to include the filter in AND methods (see below).


check results

After applying a filter, it is good practice to quickly check to see if the filter reduced the collection and by how much.

print(
  "collection before:",
  collection.size(),
  "collection after:"
  collection_filter_bounds.size()
);

full pattern

Here is the full pattern for filtering by overlap.

// Filter feature collection for overlap with fork. 

var collection_filter_bounds = collection.filterBounds(fork);

// Check filtered result.  

print(
  "collection before:",
  collection.size(),
  "collection after:"
  collection_filter_bounds.size()
);

🌎 other spatial filters

The .filterBounds() method is a generally fast method to filter by location, but sometimes you may not want to keep features that only overlap the boundary of the place of interest. To help in these cases, I wrote a custom method that allows you to filter features in one collection based on their spatial relationship to features in another collection (place of interest).

var fc_spatial_filter = geo.fcFilter.spatial('spatialRelationship', collection, place_of_interest);

You may choose one of three spatial relationships.

SPATIAL RELATIONSHIP DESCRIPTION
“containedIn” Returns features in collection that are contained by place of interest.
“disjoint” Returns features in collection that do not touch place of interest.
“intersects” Returns features in collection that touch a place of interest.

filter by attribute

These methods allow you to filter a collection based on property values of items in the collection. They work for both image collections and feature collections. The filter essentially asks a true/falsw question about the properties of the collection and returns the elements of the collection where the answer is true.

filter-by-attribute

filter by attribute

var collection_filtered = collection.filter(
  ee.Filter.eq('property', 'value')
);

check filter results

print(
  "collection before:",
  collection.size(),
  "collection after:"
  collection_filtered.size()
);

full pattern

// Filter by attribute  

var collection_filtered = collection.filter(
  ee.Filter.eq('property', 'value')
);

// Check filtered result.  

print(
  "collection before:",
  c.size(),
  "collection after:"
  c_filtered.size()
);

other criteria

There are a number of filters that follow the same pattern as above and take property and attribute value as arguments. The ee.Filter.eq() and ee.Filter.neq() can take either a string or number as the value argument. The other filters listed below typically take a number.

ee.Filter.eq('property', 'value')        // Equal to
ee.Filter.neq('property', 'value')       // Not equal to
ee.Filter.gt('property', 0)              // greater than
ee.Filter.gte('property', 0)            // greater than or equal to
ee.Filter.lt('property', 0)             // less than
ee.Filter.lte('property', 0)            // less than or equal to

filter FC by logical criteria

These patterns are similar to logical comparisons in raster but work with vector data. They can be particularly helpful to filter a feature collection by location using features in more than one other collection or to filter a feature collection by both location and by attribute.


AND by location

filter-bounds-overlap

var fc_filter_and = fc.filter(
  ee.Filter.and(
    ee.Filter.bounds(A),
    ee.Filter.bounds(B)
  )
);

print(
  "collection before:",
  fc.size(),
  "collection after:",
  fc_filter_and.size()
);

AND by location and by attribute

filter-bounds-overlap

var fc_filter_amd = fc.filter(
  ee.Filter.and(
    ee.Filter.bounds(A),
    ee.Filter.eq("class", 1)
  )
);

print(
  "collection before:",
  fc.size(),
  "collection after:",
  fc_filter_and.size()
);

OR by location

filter-bounds-overlap

var fc_filter_or = fc.filter(
  ee.Filter.or(
    ee.Filter.bounds(A),
    ee.Filter.bounds(B)
  )
);

print(
  "collection before:",
  fc.size(),
  "collection after:",
  fc_filter_and.size()
);

OR by location or by attribute

filter-bounds-overlap

var fc_filter_or = fc.filter(
  ee.Filter.or(
    ee.Filter.bounds(A),
    ee.Filter.eq("class", 2)
  )
);

print(
  "collection before:",
  fc.size(),
  "collection after:",
  fc_filter_and.size()
);

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