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).
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
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
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
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
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
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