Import datasets
Vegetation index
Google Earth Engine has a range data products that provide time series of vegetation indices. Here, we use the MODIS Terra Vegetation Indices for 16-days Global 250m product (also available at 500m and 1km resolution). After importing, we select the 'EVI' band.
var
dataset
=
ee
.
ImageCollection
(
'MODIS/006/MOD13Q1'
)
.
select
(
'EVI'
);
// or 'NDVI'
Region of interest
A FeatureCollection (or Geometry) is needed to define regions to summarize vegetation index data over. For example, you can use the Global Administrative Unit Layers (GAUL) dataset , to extract zonal statistics for administrative regions. The GAUL provides administrative unit boundaries for all countries in the world at three levels. Here, we'll use the districts of Maharashtra, India; we'll get zonal statistics for each district (35 districts).
var
regions
=
ee
.
FeatureCollection
(
'FAO/GAUL/2015/level2'
)
.
filter
(
ee
.
Filter
.
inList
(
'ADM1_NAME'
,
[
'Maharashtra'
]));
Alternatively, you can select a different FeatureCollection from the Earth Engine Data Catalog or upload your own ShapeFile by following the instructions in the Importing Table Data section of the Earth Engine Developer's Guide.
Defining spatiotemporal reduction parameters
The statistics we're after include spatial and temporal components. Above, we've defined the bounds of the spatial component, now we define the temporal component, i.e. the series of time windows to generate representative composite vegetation index images for. The following variables set the length a time window and the duration of the series.
The variables are set to generate zonal statistics ( spatialReducers
) for
image composites constructed from three time periods ( intervalCount
) that
start on 2018-01-01 ( startDate
) with each time window being 1 ( interval
)
year ( intervalUnit
) reduced by mean ( temporalReducer
). In other words, we'll
be generating annual mean vegetation index zonal statistics for years 2018,
2019, and 2020.
var
startDate
=
'2018-01-01'
;
// time period of interest beginning date
var
interval
=
1
;
// time window length
var
intervalUnit
=
'year'
;
// unit of time e.g. 'year', 'month', 'day'
var
intervalCount
=
3
;
// number of time windows in the series
var
temporalReducer
=
ee
.
Reducer
.
mean
();
// how to reduce images in time window
// Defines mean, standard deviation, and variance as the zonal statistics.
var
spatialReducers
=
ee
.
Reducer
.
mean
().
combine
({
reducer2
:
ee
.
Reducer
.
stdDev
(),
sharedInputs
:
true
}).
combine
({
reducer2
:
ee
.
Reducer
.
variance
(),
sharedInputs
:
true
});
Calculating spatiotemporal statistics
Now, we'll calculate spatiotemporal vegetation index statistics. Two ways to
arrange the statistics table are provided: a long table and a wide table.
Depending on your application, one arrangement might be more suitable than the
other. The resulting tables are exported to Google Drive
as a CSV file, but there are multiple other ways to export
,
including downloading the CSV file directly using getDownloadURL
.
Long table
The long table format will have one row per unique combination of region and
time window. So in this example, there will be 105 rows
(35 districts * 3 time windows). First, a list of 0-based time window
indices is constructed, then we map over the time window index list to generate
composite images for each time window, and then apply reduceRegions
to
calculate the zonal statistics per region. Finally, the start date of the
time window is added as a feature property so the statistics can be tied to
a given image composite. The result is a FeatureCollection of
FeatureCollections, which must be flattened. The flattened FeatureCollection is
then exported to Google Drive as a CSV file.
// Get time window index sequence.
var
intervals
=
ee
.
List
.
sequence
(
0
,
intervalCount
-
1
,
interval
);
// Map reductions over index sequence to calculate statistics for each interval.
var
zonalStatsL
=
intervals
.
map
(
function
(
i
)
{
// Calculate temporal composite.
var
startRangeL
=
ee
.
Date
(
startDate
).
advance
(
i
,
intervalUnit
);
var
endRangeL
=
startRangeL
.
advance
(
interval
,
intervalUnit
);
var
temporalStat
=
dataset
.
filterDate
(
startRangeL
,
endRangeL
)
.
reduce
(
temporalReducer
);
// Calculate zonal statistics.
var
statsL
=
temporalStat
.
reduceRegions
({
collection
:
regions
,
reducer
:
spatialReducers
,
scale
:
dataset
.
first
().
projection
().
nominalScale
(),
crs
:
dataset
.
first
().
projection
()
});
// Set start date as a feature property.
return
statsL
.
map
(
function
(
feature
)
{
return
feature
.
set
({
composite_start
:
startRangeL
.
format
(
'YYYY'
),
// or 'YYYY-MM-dd'
});
});
});
zonalStatsL
=
ee
.
FeatureCollection
(
zonalStatsL
).
flatten
();
print
(
'Spatiotemporal statistics (long)'
,
zonalStatsL
);
Export
.
table
.
toDrive
({
collection
:
zonalStatsL
,
description
:
'zonal_stats_long'
,
});
Wide table
The wide table will have one row for each region (35 rows in this case) with
a column per unique combination of statistic and time window. The new column
names are the concatenation of the statistic and the time window separated
by an underscore. The process uses a client-side for loop on the number
of time windows, and for each one, zonal statistics are calculated and appended
as new properties to the FeatureCollection. An alternative approach is to use
Earth Engine's iterate
function, but a comparison of the approaches'
performance was equal, so we've chosen the for loop for improved readability.
var
temporalStatW
;
// defined dynamically for each temporal image composite
var
startRangeW
;
// defined dynamically for each temporal image composite
var
reduce
=
function
(
feature
)
{
// Calculate zonal statistics.
var
statsW
=
temporalStatW
.
reduceRegion
({
reducer
:
spatialReducers
,
geometry
:
feature
.
geometry
(),
scale
:
dataset
.
first
().
projection
().
nominalScale
(),
crs
:
dataset
.
first
().
projection
()
});
// Append date to the statistic label.
var
keys
=
ee
.
Dictionary
(
statsW
).
keys
();
var
newKeys
=
keys
.
map
(
function
(
key
)
{
return
ee
.
String
(
key
).
cat
(
'_'
)
.
cat
(
startRangeW
.
format
(
'YYYY'
));
// or 'YYYY-MM-dd'
});
// Add the statistic properties to the feature.
return
feature
.
set
(
statsW
.
rename
(
keys
,
newKeys
));
};
var
zonalStatsW
=
regions
;
// make a copy of the regions FeatureCollection
// Loop through sequence of intervals to calculate statistics for each.
for
(
var
i
=
0
;
i
<
intervalCount
;
i
++
)
{
var
startRangeW
=
ee
.
Date
(
startDate
).
advance
(
i
,
intervalUnit
);
var
endRangeW
=
startRangeW
.
advance
(
interval
,
intervalUnit
);
temporalStatW
=
dataset
.
filterDate
(
startRangeW
,
endRangeW
).
mean
()
.
reduce
(
temporalReducer
);
zonalStatsW
=
zonalStatsW
.
map
(
reduce
);
}
print
(
'Spatiotemporal statistics (wide)'
,
zonalStatsW
);
Export
.
table
.
toDrive
({
collection
:
zonalStatsW
,
description
:
'zonal_stats_wide'
,
});