# Import modules
import ee
import requests
import numpy as np
import pandas as pd
import xarray as xr
import matplotlib.pyplot as plt
from matplotlib import colors, colormaps
from pprint import pprint
import json
from datetime import datetime
import io
6 Image for an area
When dealing with geospatial data, analyzing single images can provide detailed insights into specific temporal snapshots of a region. Unlike animations that track changes over time, working with individual images allows for a focused examination of spatial characteristics of a region at a given moment. Google Earth Engine offers robust tools for accessing and processing these images, enabling researchers to extract valuable information from diverse datasets. This tutorial will guide you through the process of retrieving and analyzing single images from Google Earth Engine.
This tutorial focused on static properties that do not change substantially over time, like elevation above sea level and some soil properties.
# Authenticate
# ee. Authernticate()
# Initialize the library.
ee.Initialize()
Define helper functions
# Define function to save images to the local drive
def save_geotiff(ee_image, filename, crs, scale, geom, bands=[]):
"""
Function to save images from Google Earth Engine into local hard drive.
"""
= ee_image.getDownloadUrl({'region': geom,'scale':scale,
image_url 'bands': bands,
'crs': f'EPSG:{crs}',
'format': 'GEO_TIFF'})
# Request data using URL and save data as a new GeoTiff file
= requests.get(image_url)
response with open(filename, 'wb') as f:
f.write(response.content)return print('Saved image')
# Define function to retrieve colormaps
def get_hex_cmap(name,n=10):
"""
Function to get list of HEX colors from a Matplotlib colormap.
"""
= colormaps.get_cmap(name)
rgb_cmap = np.linspace(0, rgb_cmap.N-1, n).astype(int)
rgb_index = [colors.rgb2hex(rgb_cmap(k)) for k in rgb_index]
hex_cmap return hex_cmap
Example 1: State level elevation
# Read US states
= ee.FeatureCollection("TIGER/2018/States")
US_states
# Select Kansas
= US_states.filter(ee.Filter.eq('NAME','Kansas'))
region
# Create mask
= ee.Image.constant(1).clip(region).mask() mask
# Get image with elevation data from the Shuttle Radar Topography Mission (SRTM)
= ee.Image("USGS/SRTMGL1_003")
srtm = srtm.clip(region).mask(mask) elev_img
# Get colormap for both geotiff raster and folium raster amp
= get_hex_cmap('Spectral', 12) cmap
# Save geotiff
= '../outputs/kansas_elevation_250m.tif'
elev_filename =4326, scale=250, geom=region.geometry()) save_geotiff(elev_img, elev_filename, crs
Saved image
# Read GeoTiff file using Xarray (and remove extra dimension)
= xr.open_dataarray(elev_filename).squeeze() elev_raster
# Create figure
=(6,3), cmap='Spectral', add_colorbar=True,
elev_raster.plot.imshow(figsize={'label':'Elevation m'});
cbar_kwargs'Kansas Elevation Map')
plt.title('Longitude')
plt.xlabel('Latitude')
plt.ylabel(#plt.axis('equal')
plt.tight_layout() plt.show()
Example 2: State level soil textural class
In this example we will plot the 12 soil textural classes for the state of Kansas. We will also learn how to use Matplotlib’s object-based syntax to define a colorbar with custom labels. The source for this example is from the 800-meter spatial resolution gridded soil product created by Walkinshaw et al. (2020) using the USDA-NRCS Soil Survey Geodatabase. Check out the link below for some cool maps:
- Walkinshaw, Mike, A.T. O’Geen, D.E. Beaudette. “Soil Properties.” California Soil Resource Lab, 1 Oct. 2020, casoilresource.lawr.ucdavis.edu/soil-properties/.
# Read US states
= ee.FeatureCollection("TIGER/2018/States")
US_states
# Select Kansas
= US_states.filter(ee.Filter.eq('NAME','Kansas'))
region
# Create mask
= ee.Image.constant(1).clip(region).mask() mask
= 'projects/earthengine-legacy/assets/projects/sat-io/open-datasets/CSRL_soil_properties/physical/soil_texture_profile/texture_025'
url_link = ee.Image(url_link).clip(region).mask(mask)
texture_img
# Palette
= ['#BEBEBE', #Sand
palette '#FDFD9E', #Loamy Sand
'#ebd834', #Sandy Loam
'#307431', #Loam
'#CD94EA', #Silt Loam
'#546BC3', #Silt
'#92C158', #Sandy Clay Loam
'#EA6996', #Clay Loam
'#6D94E5', #Silty Clay Loam
'#4C5323', #Sandy Clay
'#E93F4A', #Silty Clay
'#AF4732', #Clay
]
# Visualization parameters
= colors.ListedColormap(palette)
texture_cmap
# Save geotiff
= '../outputs/kansas_texture_800m.tif'
texture_filename =4326, scale=800, geom=region.geometry()) save_geotiff(texture_img, texture_filename, crs
Saved image
# Read saved geotiff image
= xr.open_dataarray(texture_filename).squeeze() texture_raster
= plt.subplots(figsize=(6,3))
fig, ax
= texture_raster.plot.imshow(ax=ax, cmap=texture_cmap,
raster =False, vmin=1, vmax=13)
add_colorbar
'Textural Class 0-25 cm')
ax.set_title('Latitude', fontsize=12)
ax.set_xlabel('Longitude', fontsize=12)
ax.set_xlabel(='major', color='lightgrey', linestyle=':')
ax.grid(which
# Add colorbar
= fig.colorbar(raster, ax=ax)
cbar
# Customize the colorbar
=np.linspace(1.5, 12.5, 12),
cbar.set_ticks(ticks=['Sand','Loamy sand','Sandy loam',
labels'Loam','Silt loam','Silt',
'Sandy clay loam','Clay loam',
'Silty clay loam','Sandy clay',
'Silty clay','Clay'])
plt.tight_layout()#plt.savefig('flint_hills.jpg', dpi=300)
plt.show()
Example 3: Regional soil properties
# Ecoregions map
# https://developers.google.com/earth-engine/datasets/catalog/RESOLVE_ECOREGIONS_2017#description
= ee.FeatureCollection("RESOLVE/ECOREGIONS/2017")
eco_regions
# Select flint hills region
= eco_regions.filter(ee.Filter.inList('ECO_ID',[392])) # Find ecoregion ID using their website: https://ecoregions.appspot.com
region
# Define approximate region bounding box [E,S,W,N]
= ee.Geometry.Rectangle([-95.6, 36, -97.4, 40])
bbox
# Create mask for the region
= ee.Image.constant(1).clip(region).mask() mask
Load maps of soil physical properties
# SAND
# Select layer, clip to region, and then mask
= ee.Image("projects/soilgrids-isric/sand_mean").clip(region).mask(mask)
sand = sand.select('sand_0-5cm_mean').multiply(0.1) # From g/kg to %
sand_img
# Save geotiff
= '../outputs/flint_hills_sand_250m.tif'
sand_filename =4326, scale=250, geom=region.geometry()) save_geotiff(sand_img, sand_filename, crs
Saved image
# SOIL ORGANIC CARBON
# Select layer, clip to region, and then mask
= ee.Image("projects/soilgrids-isric/soc_mean").clip(region).mask(mask)
soc
# Select surface sand layer
= soc.select('soc_0-5cm_mean').multiply(0.01) # From dg/kg to %
soc_img
# Save geotiff
= '../outputs/flint_hills_soc_250m.tif'
soc_filename =4326, scale=250, geom=region.geometry()) save_geotiff(soc_img, soc_filename, crs
Saved image
# Read GeoTiff images saved in our local drive
= xr.open_dataarray(sand_filename).squeeze()
sand_raster = xr.open_dataarray(soc_filename).squeeze() soc_raster
# Create figure
= 12 # Define font size variable
fs
=(6,4))
plt.figure(figsize
1,2,1)
plt.subplot(='OrRd', add_colorbar=True, vmin=0, vmax=50,
sand_raster.plot.imshow(cmap={'label':'Sand (%)', 'shrink':0.5});
cbar_kwargs'Sand 0-25 cm')
plt.title('Longitude', fontsize=fs)
plt.xlabel('Latitude', fontsize=fs)
plt.ylabel(=fs)
plt.xticks(fontsize=fs)
plt.yticks(fontsize='major', color='lightgrey', linestyle=':')
plt.grid(which-95.75, 36.5, 0, 0.2, head_width=0.1, head_length=0.1, fc='k', ec='k')
plt.arrow(-95.75, 36.25, 'N', fontsize=15, ha='center')
plt.text(-97.5, -95.5])
plt.xlim([36, 40])
plt.ylim([
1,2,2)
plt.subplot(='copper_r', add_colorbar=True, vmin=0, vmax=6,
soc_raster.plot.imshow(cmap={'label':'SOC (%)', 'shrink':0.5});
cbar_kwargs'Organic carbon 0-25 cm')
plt.title('Longitude', fontsize=fs)
plt.xlabel('Latitude', fontsize=fs)
plt.ylabel(=fs)
plt.xticks(fontsize=fs)
plt.yticks(fontsize='major', color='lightgrey', linestyle=':')
plt.grid(which-97.5, -95.5])
plt.xlim([36,40])
plt.ylim([
plt.tight_layout() plt.show()
Example 4: Regional drought
In this example we will explore how to retrieve available dates for an Image Collection and then we will use that information to select a specific Image. More specifically, to illsutrate these concepts we will use the collection from the U.S. Drought Monitor, that releases weekly maps of drought conditions for the United States. The selected region for the map is the area covering the Ogallala aquifer, which is one of the largest aquifers in the world spanning multiple states in the U.S. Great Plains.
Read boundary
# Import boundary for Ogallala Aquifer from local drive
= '../datasets/ogallala_aquifer_bnd.geojson'
filename_bnd
# Read the file
with open(filename_bnd) as file:
= json.load(file)
roi_json
# Get coordinates for plot
= zip(*roi_json['features'][0]['geometry']['coordinates'][0])
lon,lat
# Define the ee.Geometry so that GEE can use it
= ee.Geometry(roi_json['features'][0]['geometry'])
roi_geom
# Create mask
= ee.Image.constant(1).clip(roi_geom).mask() mask
Load USDM image collection
# Load U.S. Drought monitor Image Collection
= ee.ImageCollection("projects/sat-io/open-datasets/us-drought-monitor") usdm_collection
To access a specific number of items from a collection, we can use the .toList()
method. For instance, to return the first three images of the U.S. Drought Monitor collection defined above we can run the following command: usdm_collection.toList(3).getInfo()
# Define function to get dates from each ee.Image object
= lambda image: ee.Image(image).date().format('YYYY-MM-dd')
get_date
# Get the size of the image collection
= usdm_collection.size()
N
# Apply function to collection
= usdm_collection.toList(N).map(get_date).getInfo()
usdm_dates
# Iterate over all the dates and select images for August 2011
for k,date in enumerate(usdm_dates):
= datetime.strptime(date, '%Y-%m-%d')
date if date.year == 2012 and date.month == 10:
print(k, date)
666 2012-10-02 00:00:00
667 2012-10-09 00:00:00
668 2012-10-16 00:00:00
669 2012-10-23 00:00:00
670 2012-10-30 00:00:00
Note that the following code will not work:
= lambda image: image.date().format('YYYY-MM-dd')
get_date = usdm_collection.map(get_date).getInfo() usdm_dates
The reason it does not work is because a mapping algorithm on a collection must return a Feature or Image, and the above code returns a date string.
# Read specific image
= 670
image_number = ee.Image(usdm_collection.toList(N).get(image_number))
usdm_img
# Clip and add 1 to the layer, so that we represent "None" as 0
= usdm_img.clip(roi_geom).add(1).mask(mask)
usdm_img
# Get latest image of collection
# usdm_img = ee.Image(usdm_collection.toList(N).get(-1)).mask(mask)
# Get first image of collection
# usdm_img = ee.Image(usdm_collection.toList(N).get(0)).mask(mask) # or
# usdm_img = ee.Image(usdm_collection.toList(1)).mask(mask)
# Define colormap
= ["#FFFFFF", "#FFFF00", "#FCD37F", "#FFAA00", "#E60000", "#730000"]
usdm_hex_palette
# Visualization parameters
= colors.ListedColormap(usdm_hex_palette)
usdm_cmap usdm_cmap
# Get map from url (it may take several seconds)
= usdm_img.getDownloadUrl({
image_url 'region': roi_geom,
'scale':1_000,
'crs': 'EPSG:4326',
'format': 'GEO_TIFF'})
# Request data using URL and save data as a new GeoTiff file
= requests.get(image_url)
response
# Check if the request was successful
if response.status_code == 200:
# Read image data into a BytesIO object
= io.BytesIO(response.content)
image_data
= plt.subplots(figsize=(4,6))
fig,ax
# Use Xarray to open the raster image directly from memory
= xr.open_dataarray(image_data, engine='rasterio').squeeze()
raster
# Create raster map showing drought conditions
= raster.plot.imshow(ax=ax, cmap=usdm_cmap,
usdm_map =-0.5, vmax=5.5, add_colorbar=False)
vmin
# Add aquifer boundaries
='k', linewidth=1)
ax.plot(lon, lat, color
# Add colorbar
= fig.colorbar(usdm_map, ax=ax, shrink=0.5, label='Drought Intensity')
cbar
# Add labels in the center of each segment
=[0,1,2,3,4,5],
cbar.set_ticks(ticks=['None','D0','D1','D2','D3','D4'])
labels
# Add labels
f'Ogallala Aquifer {usdm_dates[image_number]}')
ax.set_title('Longitude')
ax.set_xlabel('Latitude')
ax.set_ylabel(=':')
plt.grid(linestyle
plt.show()