This notebook extends the code used to create the static images of the NDVI across Africa in 2013 for the MEDS capstone group CropMOSAIKS. This uses Google Earth Engine Python API to create an animated gif of NDVI over Africa from 2000 to 2021.
The following code is adapted from a Google Earth Engine JavaScript tutorial. The code was translated into python and reproduced with several color palettes. The python code is found on the CropMOSAIKS GitHub.
import ee
import geemap
import os
import glob
from PIL import Image
# Initialize google earth engine
ee.Initialize()
# Fetch a MODIS NDVI collection and select NDVI.
= ee.ImageCollection('MODIS/006/MOD13A2').select('NDVI')
img_collection
# Define a mask to clip the NDVI data by.
= ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017').filter(ee.Filter.eq('wld_rgn', 'Africa'))
mask
# Define the regional bounds of animation frames.
= ee.Geometry.Polygon(
region -18.698368046353494, 38.1446395611524],
[[[-18.698368046353494, -36.16300755581617],
[52.229366328646506, -36.16300755581617],
[52.229366328646506, 38.1446395611524]]], None, False
[ )
# Add day-of-year (DOY) property to each image.
def clip_and_get_day_of_year(img):
= img.clip(mask)
img = ee.Date(img.get('system:time_start')).getRelative('day', 'year')
doy return img.set('doy', doy)
# Apply median reduction among matching DOY collections.
def match_day_of_year_and_reduce(img):
= ee.ImageCollection.fromImages(img.get('doy_matches'))
doyCol return doyCol.reduce(ee.Reducer.median())
= [
natural 'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718', '74A901',
'66A000', '529400', '3E8601', '207401', '056201', '004C00', '023B01',
'012E01', '011D01', '011301'
]= [
viridis "#440154FF", "#48186AFF", "#472D7BFF", "#424086FF", "#3B528BFF", "#33638DFF",
"#2C728EFF", "#26828EFF", "#21908CFF", "#1F9F88FF", "#27AD81FF", "#3EBC74FF",
"#5DC863FF", "#82D34DFF", "#AADC32FF", "#D5E21AFF", "#FDE725FF"
]= [
magma "#000004FF", "#0B0724FF", "#210C4AFF", "#3D0965FF", "#56106EFF", "#71196EFF",
"#89226AFF", "#A32C61FF", "#BB3754FF", "#D14545FF", "#E35932FF", "#F1721EFF",
"#F98C0AFF", "#FCAA0FFF", "#F9C932FF", "#F2E865FF", "#FCFFA4FF"
]= [
cividis "#00204DFF", "#002C69FF", "#05366EFF", "#2D426CFF", "#414D6BFF", "#52596CFF",
"#61646FFF", "#6F7073FF", "#7C7B78FF", "#8B8779FF", "#9B9477FF", "#ACA174FF",
"#BCAF6FFF", "#CEBC68FF", "#E0CB5EFF", "#F2DA50FF", "#FFEA46FF"
]
# Define visualization parameters.
= {
vis_params 'region': region,
'dimensions': 600,
'crs': 'EPSG:3857',
'framesPerSecond': 10,
'min': 0.0,
'max': 9000.0,
'palette': magma
}
= img_collection.map(clip_and_get_day_of_year)
img_collection = img_collection.aggregate_array('system:index').getInfo() img_dates
= int(img_dates[0][:4])
start_yr = int(img_dates[-1][:4])
end_yr = range(start_yr, end_yr + 1, 1)
years = [] index_array
for year in years:
print('Downloading: ', year)
= f'{str(year)}-01-01'
date_start = f'{str(year)}-12-31'
date_end # Get a collection of distinct images by 'doy'.
= img_collection.filterDate(date_start, date_end)
distinct_doy
# Define a filter that identifies which images from the complete
# collection match the DOY from the distinct DOY collection.
= ee.Filter.equals(leftField = 'doy', rightField = 'doy')
filtered
# Define a join.
= ee.Join.saveAll('doy_matches')
joined
# Apply the join and convert the resulting FeatureCollection to an ImageCollection.
= ee.ImageCollection(joined.apply(distinct_doy, img_collection, filtered))
join_img_collection
= join_img_collection.map(match_day_of_year_and_reduce)
img_composite = index_array + img_composite.aggregate_array('system:index').getInfo()
index_array
= f"animations/temp_ndvi_{str(year)}.gif"
temp_file_name
geemap.download_ee_video(img_composite, vis_params, temp_file_name)
# filepaths
= "animations/temp_*.gif"
fp_in = "animations/ndvi.gif"
fp_out
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#gif
*imgs = [Image.open(f) for f in sorted(glob.glob(fp_in))]
img, = fp_out,
img.save(fp format = 'GIF',
= imgs,
append_images = True,
save_all = 100,
duration = 1) loop
geemap.add_text_to_gif(= fp_out,
in_gif = fp_out,
out_gif = ('75%', '1%'),
xy = index_array,
text_sequence = 'arial.ttf',
font_type = 20,
font_size ='white',
font_color= True,
add_progress_bar = 'white',
progress_bar_color = 5,
progress_bar_height = 100, # milliseconds per frame so 200 is 5 fps, 100 is 10 fps etc
duration = 0
loop )
For attribution, please cite this work as
Molitor (2022, Jan. 23). Cullen Molitor: CropMOSAIKS Animated NDVI. Retrieved from cullen-molitor.github.io/posts/2022-01-23-cropmosaiks-animated-ndvi/
BibTeX citation
@misc{molitor2022cropmosaiks, author = {Molitor, Cullen}, title = {Cullen Molitor: CropMOSAIKS Animated NDVI}, url = {cullen-molitor.github.io/posts/2022-01-23-cropmosaiks-animated-ndvi/}, year = {2022} }