Many useful climate and weather datasets come as gridded rasters. The techniques for working with them is slightly different than other remote sensing datasets. In this post, I will show how to work with gridded rainfall data in Google Earth Engine. This post also serves an an example of how to use the map/reduce programming style to efficiently work with such large datasets.

We will use CHIRPS (Climate Hazards Group InfraRed Precipitation with Station) Data in this tutorial. This is a high-resolution global gridded rainfall dataset that combines satellite measured precipitation with ground station data in a consistent long time-series dataset. This data is available from 1981 onwards and is extremely useful in computing rainfall deviation and drought monitoring. See this paper for more discussion on applying CHIRPS dataset for calculating trends and variability, including Earth Engine code for pixel-wise trends and statistical significance. (code links under Supplementary Materials)

Earth Engine Data Catalog includes many other gridded precipitation datasets, such as ERA5 and GPM - each with different spatial and temporal resolutions and methodologies. The technique for working with them is identical to the one outlined in this post.

We will take this data and learn how to tackle the following problems

  • Create a map of total rainfall in a given time-period.
  • Calculate the total annual rainfall in an administrative region or a polygon.
  • Create a CSV file of total annual rainfall for any given region for the past 40 years.

Access the Data

The primary computing time step for the CHIRP is the pentad. Pentad represents the grouping of 5 days. There are 6 pentads in a calendar month: Five 5-day pentads and One pentad with the remaining 3 to 6 days of the month. Pentads reset at the beginning of each month. We will use the CHIRPS pentad dataset. Note that CHIRPS is also provided in a daily time-step which is computed by disaggregating the pentad data. Unless you specifically need daily data, you should use the pentad dataset.

var chirps = ee.ImageCollection('UCSB-CHG/CHIRPS/PENTAD')

Creating a Map of Total Rainfall

Let’s create a map of total annual rainfall. The CHIRPS dataset is an ImageCollection with a global image consisting of an Image for every pentad (5 days) with total rainfall in during those 5 days. We can filter the collection to images for a year and then apply a sum() reducer to get a single image where each pixel is the sum of rainfall from all images in the year. The following code computes the total rainfall for the year 2017

var year = 2017
var startDate = ee.Date.fromYMD(year, 1, 1)
var endDate = startDate.advance(1, 'year')
var filtered = chirps
  .filter(ee.Filter.date(startDate, endDate))
var total = filtered.reduce(ee.Reducer.sum())

Remember - the result of applying a reducer to an ImageCollection is an Image. So now that we have an image, we can visualize it to get our map of annual rainfall. The unit of CHIRPS data is millimeters (mm), so 2000 mm/year is a reasonable max value for visualization.

Tip: If you want cartographer-approved color ramps and palettes for your maps, head over to ColorBrewer. The palette from the map below is created using this excellent free resource.

var palette = ['#ffffcc','#a1dab4','#41b6c4','#2c7fb8','#253494']
var visParams = {
  min:0,
  max: 2000,
  palette: palette
}
Map.addLayer(total, visParams, 'Total Precipitation')

Calculating Total Rainfall in a Region

Most hydrological applications will require computing the Areal Mean Rainfall (AMR) - which is the average total rainfall in a region. The region could be anything - a river basin, an administrative area (city/district) or a polygon. Now that we have computed an Image with the total rainfall for each pixel, we can compute average total rainfall in any given geometry using reduceRegion() function. For this example, we will compute the total average rainfall within the city of Bengaluru, India. In the code below, we specify the native resolution of CHIRPS images in meters as the scale. CHIRPS spatial resolution is 0.05° - which is approximately 5566m. The result of a reduceRegion() operation is a dictionary which has the stats for each band of the image. Since CHIRPS data has only 1 band named precipitation, the dictionary will have only a single key named precipitation_sum.

bangalore = ee.FeatureCollection("users/ujavalgandhi/public/bangalore_boundary");
var stats = total.reduceRegion({
  reducer: ee.Reducer.mean(),
  geometry: bangalore,
  scale: 5566,
  })
print(stats.get('precipitation_sum')) // 1336.52 mm

We can clip the image with the region geometry and display the results.

// Clip the image to the city boundary and display
var totalBangalore = total.clip(bangalore)
Map.addLayer(totalBangalore, visParams, 'Total Precipitation (Bangalore)')

Calculating a Time Series of Rainfall

The biggest advantage of the CHIRPS dataset is the long and consistent time series it provides. Combined with the parallel-processing power of Earth Engine, it enables us to get statistics over long periods of time very easily. Here’s where the map() operation comes handy. Data processing in Earth Engine boils down to a) create a list or collection and b) mapping a function over it and optionally c) reducing the results. Here we will write a function that calculates total rainfall for a region for 1 year, and then map() that function over a list of 40 years. map() allows each operation to run in parallel and you will see that a large computation like this finishes in just a few seconds.

Tip: In Earth Engine, if the result of your computation is a number, list or a dictionary - always return them as Features. FeatureCollections allow filters, export and many more operations that other data structures do not allow. It is easy to create a feature with a null geometry as shown below.

// Function to calculate rainfall for 1 year
var yearlyRainfall = function(year) {
  var startDate = ee.Date.fromYMD(year, 1, 1)
  var endDate = startDate.advance(1, 'year')
  var filtered = chirps
    .filter(ee.Filter.date(startDate, endDate))
  var total = filtered.reduce(ee.Reducer.sum())
  var stats = total.reduceRegion({
    reducer: ee.Reducer.mean(),
    geometry: bangalore,
    scale: 5566,
  })
  var f = ee.Feature(null, {
    'year': year,
    'precipitation': stats.get('precipitation_sum')
  })
  return f
}

Now that we have a function that can calculate total rainfall for 1 year, we can map() it over a list of years to get rainfall for ALL years.

var years = ee.List.sequence(1981, 2019)
var rainfallYears = ee.FeatureCollection(
  years.map(yearlyRainfall))

That’s it. rainfallYears is a FeatureCollection with total rainfall for each year. We can export it to get the data as a CSV file.

Export.table.toDrive({
  collection: rainfallYears,
  folder: 'earthengine',
  fileNamePrefix: 'rainfallbyyear',
  fileFormat: 'CSV'})

You can see the full script with comments at https://code.earthengine.google.co.in/e02195614b1ebd403ea701b11217497c

Additional Resources and Scripts

You can also access our full YouTube playlist covering our course material on gridded precipitation data.