The world’s leading publication for data science, AI, and ML professionals.

Spatio-temporal Mapping of Seasonal Droughts

How to create animated gifs showing drought conditions in your region of interest over a period

Spatio-temporal mapping of drought: Image by author
Spatio-temporal mapping of drought: Image by author

Introduction

Increased scale and frequency of droughts are often cited as one of the ill effects of Climate Change. A drought is a climatic phenomenon characterised by deficiency of moisture arising due to shortfall in precipitation. Droughts can be of three types:

  1. Meteorological – Caused by deficiency in precipitation. Indian Meteorological Department (IMD) classifies a meteorological Drought as "moderate" and "severe" based on deficiency of rainfall being more than 25% and 50% respectively.
  2. Hydrological – Prolonged met drought leading to depletion of surface water and underground water causing shortage of water for livestock and human needs.
  3. Agricultural – Situation where rainfall and soil moisture are inadequate for the cropping season.

Monitoring droughts is important as it directly concerns food security. There are many indices which can be used to quantify drought conditions. In this blog, I will demonstrate how spatio-temporal variations in drought can be presented in the form of an animated GIF using open source tools in Python.

2. Workflow

The workflow consists of the following broad steps:

  • Import libraries and authenticate Google Earth Engine (GEE).
  • Define the area of interest (aoi).
  • Create monthly composites from daily precipitation rasters for the period under observation.
  • Create baseline monthly composites for long time – say 10 years.
  • Calculate rainfall deficit for each month.
  • Create a mosaic image with RGB visualisation of computed deficit layers overlayed by vector boundaries by creating a mosaic.
  • Plot the monthly mosaics as frames of an animated GIF.
  • Annotate each frame by the month its corresponding month and display the GIF.

We will begin with the usual Python set up and GEE initialization.

import geemap
import ee
try:
    ee.Initialize()
except:
    ee.Authenticate()
    ee.Initialize()

3. Data Processing

We will use the Climate Hazards Group InfraRed Precipitation with Station (CHIRPS) precipitation data which is assimilated in GEE (https://developers.google.com/earth-engine/datasets/catalog/UCSB-CHG_CHIRPS_DAILY). It is an open dataset with all copyrights waived off. It contains daily precipitation data starting 1981 at 0.05° resolution satellite imagery which is useful to create gridded rainfall time series for trend analysis and seasonal drought monitoring.

As discussed in the work flow, we define the regions for analysis and visualisation. We will use India’s vector shapefile containing GIS information on borders of states. This shapefile is easily available on the internet (IGISmap.com, gadm.org, arcgis.com etc.) and can be uploaded on the GEE as an asset through the code editor. An easier alternative could be to use the FAO-GAUL dataset which is already ingested in GEE but it will require some efforts to include the disputed geographies in Indian territory for accurate mapping.

# We use the country boundaries from Global Administrative Unit Layers (2015) provided by FAO(UN)
india = ee.FeatureCollection("users/skantbksc/states");
# Define the regional bounds for animation frames.
region = india.geometry().bounds();

3.1 Create monthly composites of images for the observation period

We will be analysing the drought conditions for the year 2020. As droughts are mainly observed during the monsoon season when most of the precipitation occurs in India, we will select June to October months for our analysis.

# set start date for analysis
startDate = '2020-06-01';
# set end date for analysis
endDate = '2020-10-31';
#filter the image collection using dates
pptCol = ee.ImageCollection("UCSB-CHG/CHIRPS/DAILY")
        .filterDate(startDate, endDate)
        .select('precipitation');
# Setting year and month as a property of every image which will later be used to form monthly composites
def set_yymm(img):
    return img.set('yymm', img.id().slice(0,6))
pptCol = pptCol.map(set_yymm)
# Define a list of unique observation months from the image collection.
yymmList = ee.List(pptCol.aggregate_array('yymm')).distinct().sort();
# Function to sum up the daily precipitation data of each month 
def groupByMonth(yymm):
    pptMonth = pptCol.filterMetadata('yymm', 'equals', yymm)
                .sum()
                .clip(india)
                .set('yymm', yymm);
    return pptMonth
# Map the function over the list of months to build a collection of monthly image composites.
pptMonthList = yymmList.map(groupByMonth);
pptByYYMM = ee.ImageCollection.fromImages(pptMonthList);

As can be checked using the pptCol.size().getInfo() and pptByYYMM.size().getInfo(), the number of images in the raw daily collection and computed monthly collection are 152 and 5 respectively.

3.2 Calculation of baseline monthly composites

# We use 10 year data to construct a baseline figure 
baseCol = ee.ImageCollection("UCSB-CHG/CHIRPS/DAILY")
        .filterDate('2000-01-01','2010-01-01')
        .select('precipitation');
# Create a list of months
months = ee.List.sequence(1, 12);
# Function to find the average monthly precipitation
def avgByMonth(m):
    img = baseCol.filter(ee.Filter.calendarRange(m,m, 'month')).sum().divide(10).clip(india);
    return img.set('mm',ee.Number(m).format('%02d'));
# Map over the list of months to build a collection of long term average monthly precipitation composites
baseMonthCol = ee.ImageCollection.fromImages(months.map(avgByMonth))

3.3 Calculation of rainfall deficit

# Compare the monthly composites against the corresponding baseline to arrive at the deficit figures
def calc_deficit(img):

    mm = ee.String(img.get('yymm')).slice(4,6); 
    base = baseMonthCol.filterMetadata('mm','equals',mm).first();
    deficit = base.subtract(img).divide(base).set('yymm', img.get('yymm'));
    return deficit;
deficitByMonth = pptByYYMM.map(calc_deficit);

3.4 Remove arid areas for analysis

As many of the arid areas have little precipitation, their deficit percentages may be very high owing to low denominator effect. We will set the deficit percentages for such pixels as zero. We will be classifying areas as arid if annual rainfall over the region is less than 500mm.

# Remove deficit of arid areas from visualisation. We define an area as arid if its annual rainfall is less than 500mm.
baseAnnualCol = baseMonthCol.sum();
notArid = baseAnnualCol.gt(500);
def arid_rem(img):
    return img.multiply(notArid);
deficitByMonth = deficitByMonth.map(arid_rem);

4. Plotting

The next step is to visualise these 5 images which are part of the deficitByMonth image collection. The color palette provided as an argument linearly interpolates the colors between the minimum and maximum deficit values. We set the parameters for visualising the images so that yellow and red pixels represent moderate and severe drought conditions respectively.

# Define RGB visualization parameters.
visParams = {
    'min' : 0,
    'max' : 1,
    'palette': ['e6ffe6','ffffff', 'fff633', 'fc0703']
};

It is important to create an outline of Indian states so that drought pixels can be delineated by state boundaries. We will create RGB visualisation of drought layers and overlay the state boundaries on each layer, thereby creating a mosaic. First, we create the map outline.

# Create an empty image into which to paint the features.
transparent = ee.Image();
empty = ee.Image.cat([transparent, transparent,transparent]);
# Select and (optionally) rename bands.
empty = empty.select(
    ['constant', 'constant_1', 'constant_2'], #old names
    ['vis-red', 'vis-green', 'vis-blue']  #new names
);
# Paint all the polygon edges with the same number and width, display.
outline = empty.paint(
    featureCollection = india,
    color = 1,
    width = 1);

Next, we create a mosaic of RGB image representation of drought followed by map outline, in that order. This order is important as the map outlines won’t be visible otherwise. We create a function rgb_fc_mosaic to do this and map it over the collection of images in defictByMonth.

def rgb_fc_mosaic(img):
    #Create RGB visualization images for use as animation frames.
    imgRGB = img.visualize(**visParams).clip(india)
#   Mosaic the visualization layers and display (or export).
    mosaic = ee.ImageCollection([imgRGB, outline]).mosaic()
    return mosaic
rgbVis = deficitByMonth.map(rgb_fc_mosaic);

The last step in generating the animation is to set the parameters of animation frame and the speed with which it displays the images. We will provide the necessary arguments to getVideoThumbURL function and map it over the rgbVis image collection.

# Define GIF visualization parameters.
gifParams = {
  'region': region,
  'dimensions': 600,
  'crs': 'EPSG:3857',
  'framesPerSecond': 1
};
# Print the GIF URL to the console.
print(rgbVis.getVideoThumbURL(gifParams));

Once the animated GIF (drought_in.gif) is ready, we will add the month information on each frame of the GIF. This can be done easily using add_text_to_gif function of geemap package.

import pandas as pd
text = pd.date_range(startDate, endDate, 
              freq='MS').strftime("%b-%Y").tolist()
# Add the months information on each frame of GIF
in_gif = 'C:UsersADMINDesktopsatellitegeemapimagesdrought_in.gif'
out_gif = 'C:UsersADMINDesktopsatellitegeemapimagesdrought_out.gif'
geemap.add_text_to_gif(in_gif, out_gif, xy = ('40%', '0%'), text_sequence = text, font_size=30, font_color = '#ffffff',duration = 1000)

You can visualize the annotated GIF within Jupyter Notebook using the show_image function.

geemap.show_image(out_gif)

5. Conclusion

Animated gifs are extremely useful tools to represent spatio-temporal changes in weather. These visual tools express information worth a thousand words in a way that appeals to readers and is easy to understand. In this blog, I demonstrated how to create animated gifs to capture the variations in drought conditions over time using open data assimilated in GEE and open source tools in python.

Disclaimer: Views expressed in this blog are personal.

References


Related Articles