Working with Gridded Rainfall Data in Google Earth Engine

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 5000m as the scale. CHIRPS spatial resolution is 0.05° – which is approximately 6km. 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: 5000,
  })
print(stats.get('precipitation_sum')) // 1336.52 mm

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: 5000,
  })
  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/54d6d2c14290bd4ee1cadeb05dc90aed

If you are curious, this is what the exported CSV looks like. Past 39 years of total rainfall in Bangalore city as calculated from the CHIRPS dataset.

If you are new to Earth Engine and want to master it, check out my course End-to-End Google Earth Engine.

YearRainfall (mm)
1981992
1982570
1983905
1984758
1985717
1986981
1987794
19881114
1989833
1990756
19911030
1992739
1993841
1994763
19951052
19961108
1997739
19981378
19991084
20001085
20011133
2002727
2003751
20041108
20051414
2006818
20071280
20081144
20091034
20101378
20111093
2012873
2013998
20141030
20151100
2016663
20171337
2018923
20191036

2 Comments

Leave a Comment

  1. Very good job your investigation. Have you tried the ERA5 Monthly aggregates – Latest climate reanalysis produced by ECMWF / Copernicus Climate Change Service catalog?

    • I haven’t worked with ERA5 data. ERA5 is 0.25-degree resolution which is much coarser than CHIRPS. There is also GPM available in Earth Engine. Seems like I need another post comparing all precipitation data sources 🙂

Leave a Reply