Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Contributors: Steven Pestana

Download the sample datasets for this tutorial

For ease of access during the hackweek, sample files are available for download for running the command in the cell below.

Import the packages we’ll need for this tutorial


Part 1: Comparing airborne IR imagery with ground-truth observations

Airborne IR imagery

The Naval Postgraduate School Twin Otter aircraft carried the UW APL thermal infrared imager and SWESARR instrument over Grand Mesa for SnowEx 2020. (Photo by Chris Chickadel)

The Naval Postgraduate School Twin Otter aircraft carried the UW APL thermal infrared imager and SWESARR instrument over Grand Mesa for SnowEx 2020. (Photo by Chris Chickadel)

Load IR image mosaic geotiff file

Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.hdr: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.HDR: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.IMD: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.imd: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.RPB: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.rpb: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.PVL: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.pvl: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915_rpc.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915_RPC.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915_metadata.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915_METADATA.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915_MTL.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915_MTL.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/METADATA.DIM: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/metadata.dim: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915_metadata.xml: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915_METADATA.XML: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.RPC: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.rpc: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/summary.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SUMMARY.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/HDR2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/HDR2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/HDRWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/HDRWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/RPC2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/RPC2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/RPCWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/RPCWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.TXT: 403

Inspect the contents of the file we just opened

Loading...
CRS.from_wkt('PROJCS["WGS 84 / UTM zone 12N",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-111],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","32612"]]')

Under the dataarray’s attributes we can see that we have a coordinate reference system already defined (crs) as EPSG:32612. We can also find this through da.rio.crs.

However, we would like to reproject this into the common projection used by datasets on the SnowEx SQL database: EPSG:26912. We can do that using rioxarray’s reproject method (see an example here).

NOTE: Reprojections typically require a lot of processing time, so expect this cell to run for up to 1 minute.

Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.tif.msk: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_IR_PLANE_2020Feb08_mosaicked_2020-02-08T181915.tif.MSK: 403
CRS.from_wkt('PROJCS["NAD83 / UTM zone 12N",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-111],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","26912"]]')

Next, the filename shows us when this imagery was taken in UTC time, “2020-02-08T181915”

We can create a pandas timestamp variable in local time for comparison with other datasets:

/tmp/ipykernel_4105/1783445607.py:2: DeprecationWarning: The 'generic' unit for NumPy timedelta is deprecated, and will raise an error in the future. This includes implicit conversion of bare integers (e.g. `+ 1`). Please use a specific unit instead.
  airborne_ir_timestamp = pd.Timestamp(2020,2,8,18,19,15) - pd.Timedelta(hours=7)

Plot the airborne TIR image.

  • What does our chosen colorscale tell us about temperatures here?

  • What can we see?

<Figure size 2000x1000 with 2 Axes>

Bonus activity:

To help with our image interpretation, we can load visible imagery taken concurrently from the UW-APL airborne instrument. (Note: this example image is a single band black and white image, though we also have full RGB images available through NSIDC)

Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.hdr: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.HDR: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.IMD: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.imd: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.RPB: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.rpb: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.PVL: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.pvl: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915_rpc.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915_RPC.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915_metadata.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915_METADATA.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915_MTL.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915_MTL.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915_metadata.xml: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915_METADATA.XML: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.RPC: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.rpc: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/HDR2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/HDR2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/HDRWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/HDRWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/RPC2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/RPC2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/RPCWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/RPCWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.tif.msk: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/SNOWEX2020_EO_PLANE_2020Feb08_mosaicked_2020-02-08T181915.tif.MSK: 403
Loading...

Plot the visible and infrared images side by side. This time, we will change the x and y axes limits (set_xlim, and set_ylim) to zoom in closer.

<Figure size 2000x1000 with 4 Axes>

Ground-based temperature observations

To provide a source of “ground truth” for the airborne and satellite thermal infrared images during the SnowEx 2020 Grand Mesa campaign, we can use ground-based snow surface temperature measurements. On February 5, 2020, we installed a thermal infrared radiometer pointing at the snow surface at snow pit #2S10 (left), and buried temperature sensors beneath the snow surface (right). These logged observations at 5-minute intervals until we removed the instrumentation a week later on February 12.

Snow temperature sensor setup at snow pit 2S10: (left) tripod-mounted thermal ifrared radiometer to measure snow surface, (right) temperature probes to be buried beneath the snow surface. (Photos by Steven Pestana)

Snow temperature sensor setup at snow pit 2S10: (left) tripod-mounted thermal ifrared radiometer to measure snow surface, (right) temperature probes to be buried beneath the snow surface. (Photos by Steven Pestana)

Where is snow pit 2S10?

We can find this information through a query to the SnowEx SQL database. First, set up the connection:

🔍 Testing Lambda connection...
✅ Connected: True
📊 Database: PostgreSQL 16.10 on x86_64-conda-linux-gnu, compiled by x86_64-conda-linux-gnu-cc (conda-forge gcc 14.3.0-4) 14.3.0, 64-bit

Then, set up a query to find the entry with the site ID that we want (2S10). Preview the resulting geodataframe.

Found 1 sites containing '2S10':

Inspect of the geodatframe’s metadata

<Geographic 2D CRS: EPSG:4326> Name: WGS 84 Axis Info [ellipsoidal]: - Lat[north]: Geodetic latitude (degree) - Lon[east]: Geodetic longitude (degree) Area of Use: - name: World. - bounds: (-180.0, -90.0, 180.0, 90.0) Datum: World Geodetic System 1984 ensemble - Ellipsoid: WGS 84 - Prime Meridian: Greenwich
0 POINT (-108.19232 39.01947) Name: geometry, dtype: geometry

We can now plot our snow pit site from this geodataframe on top of the airborne IR image. Note we will first have to reproject the snow pit location to UTM12N since it is currently in geographic coordinates. (See more tips about plotting geodataframes here)

<Figure size 2000x500 with 2 Axes>

Change the x and y axes limits (set_xlim, and set_ylim) to zoom in to our point of interest. In this case we can use df.geometry.total_bounds to get the x and y values that define the area our geometry takes up. (In this case we have a point so it will return just the point’s location, but this would work if we had a polygon as well)

<Figure size 1000x1000 with 2 Axes>

Import the snow temperature timeseries dataset

This data is available through NSIDC, but we have already downloaded a local copy for this tutorial.

The raw data file doesn’t include the column names, so we need to set the column headers following the dataset’s README file.

Description:
	This folder contains data files from two Campbell Scientific CR10X dataloggers which were installed at two sites on Grand Mesa to measure snow temperature profiles as part of the SnowEx 2020 field campaign.
	
	"Grand Mesa 1" (GM1) measured snow surface temperature with an Apogee SI-111 thermal infrared radiometer mounted on a tripod at a height of about 127 cm above the snow surface. The radiometer pointed 45 degrees off-nadir at the snow surface just to the west of the tripod. Five Campbell Scientific temperature probes were mounted vertically along a wooden stake with zip ties, at depths of 5, 10, 15, 20, and 30 cm below the snow surface. (CR10X_GM1_final_storage_1.dat) 
	
	"Grand Mesa 2" (GM2) measured snow temperatures with five temperature probes mounted vertically on a wooden stake with zip ties at depths of 5, 10, 15, 20, and 30 cm below the snow surface. This location was just outside the fence perimeter of the Mesa West met station (northwest of the met station tower). Snow surface temperature is measured at the Mesa West station by its own set of thermal infrared radiometers, which can provide snow surface temperature adjacent to this vertical profile of snow temperature. The Mesa West station should also be able to provide similar snow temperatures below the surface with its own string of temperature sensors.

Date/times: 
	GM1: 2-5-2020 11:12 AM MST (UTC−07:00) - 2-12-2020 3:36 PM MST (UTC−07:00)
	GM2: 2-5-2020 12:35 PM MST (UTC−07:00) - 2-12-2020 2:36 PM MST (UTC−07:00)

Locations: 
	GM1 was located at Pit ID 2S10
	GM2 was located at the Mesa West met station 


File formats:
*.dat - comma-separated values from the Campbell Scientific CR10X Dataloggers
*.xlsx - MS Excel worksheet

Columns:
	GM1:
		'table', 'year', 'doy', 'time',
		'rad_avg', 'rad_max', 'rad_min', 'rad_std',
		'sb_avg', 'sb_max', 'sb_min', 'sb_std',
		'temp1_avg', 'temp1_max', 'temp1_min', 'temp1_std',
		'temp2_avg', 'temp2_max', 'temp2_min', 'temp2_std',
		'temp3_avg', 'temp3_max', 'temp3_min', 'temp3_std',
		'temp4_avg', 'temp4_max', 'temp4_min', 'temp4_std',
		'temp5_avg', 'temp5_max', 'temp5_min', 'temp5_std',
		'batt_a','batt_b'
	GM2:
		'table', 'year', 'doy', 'time',
		'temp1_avg', 'temp1_max', 'temp1_min', 'temp1_std',
		'temp2_avg', 'temp2_max', 'temp2_min', 'temp2_std',
		'temp3_avg', 'temp3_max', 'temp3_min', 'temp3_std',
		'temp4_avg', 'temp4_max', 'temp4_min', 'temp4_std',
		'temp5_avg', 'temp5_max', 'temp5_min', 'temp5_std',
		'batt_a','batt_b'



Resources:
	An example jupyter notebook for reading and plotting the data is available on github:
		https://github.com/spestana/snowex2020-snow-temp

Create a list of column headers according to the readme above (for “GM1” which we can read was the datalogger at snowpit 2S10)

Open the file as a pandas data frame with read_csv

We need to do some formatting of the data fields, but we can preview what we just loaded fist

Loading...

Data cleanup and formatting

This function lets us convert year and day of year (the format that the datalogger uses) to a pandas datetime index:

/tmp/ipykernel_4105/3645742961.py:12: DeprecationWarning: The 'generic' unit for NumPy timedelta is deprecated, and will raise an error in the future. This includes implicit conversion of bare integers (e.g. `+ 1`). Please use a specific unit instead.
  return sum(np.asarray(v, dtype=t) for t, v in zip(types, vals)

Inspect the contents

Loading...

Make a simple plot of the data. We are interested in the variable rad_avg which is the average temperature measured by the radiometer over each 5 minute period.

<Figure size 1000x400 with 1 Axes>

But then we want to focus on the date/time when our IR image was from, so zoom in on Feb 8th by changing our plot’s xlim (using pandas Timestamps for the x axis values).

<Figure size 1000x400 with 1 Axes>

Compare Airborne IR against the “ground truth” snow surface temperature

What is the temperature at this point in the airborne IR image?

Use rioxarray’s clip function to extract the raster values that intersect with the point’s geometry. Because we have a point, this will return a single value for the pixel that overlaps this point.

Loading...

Our result is a DataArray with a single data value at one set of x and y coordinates.

Add a 100 m radius buffer around this point and get the temperature from the airborne imagery for a larger area around the snow pit.

0 POLYGON ((743176.188 4322688.736, 743175.706 4... dtype: geometry

What does this polygon look like when we plot it on top of the airborne IR image now?

<Figure size 1000x1000 with 2 Axes>

Clip the airborne IR raster again, now with our 200 m diameter polygon around the snow pit site.

Loading...

The result of clipping is again a DataArray, this time though it is 40x40. We can plot this to see what it looks like, and to see the distribution of temperatures in the area.

<Figure size 1000x500 with 3 Axes>

Plot the airborne IR temperature data on top of the ground-based timeseries

/tmp/ipykernel_4105/1336368688.py:7: UserWarning: This axis already has a converter set and is updating to a potentially incompatible converter
  plt.plot(airborne_ir_timestamp, airborne_ir_area_temperature.mean(),
<Figure size 1000x400 with 1 Axes>

Part 2: Satellite IR remote sensing obsevations

Satellite IR imagery with ASTER

Advantages of satellite IR images: We don’t always have airplanes with IR cameras flying around. Satellites can provide images at more regular intervals for long-term studies, and can see areas that are difficult to access on the ground or by air.

For this tutorial, we will look at an image from NASA’s Advanced Spaceborne Thermal Emission and Reflection Radiometer (ASTER) imager, which is onboard the Terra satellite along with a MODIS imager. We can compare an ASTER IR image of Grand Mesa that was taken at roughly the same time as the airborne IR image. The ASTER image we will be working with is from ASTER’s band 14 which is sensitive to radiance in the 10.95-11.65 µm wavelength range.

Load an ASTER geotiff that we’ve downloaded for this tutorial, and inspect its contents.

Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14.hdr: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14.HDR: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14.IMD: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14.imd: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14.RPB: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14.rpb: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14.PVL: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14.pvl: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14_rpc.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14_RPC.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14_metadata.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14_METADATA.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14_MTL.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14_MTL.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14_metadata.xml: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14_METADATA.XML: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14.RPC: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14.rpc: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/HDRT_00302082020180748_20200209065849_17218_ImageData14.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/HDRT_00302082020180748_20200209065849_17218_ImageData14.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/HDR_L1T_00302082020180748_20200209065849_17218_ImageData14.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/HDR_L1T_00302082020180748_20200209065849_17218_ImageData14.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/RPCT_00302082020180748_20200209065849_17218_ImageData14.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/RPCT_00302082020180748_20200209065849_17218_ImageData14.TXT: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/RPC_L1T_00302082020180748_20200209065849_17218_ImageData14.txt: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/RPC_L1T_00302082020180748_20200209065849_17218_ImageData14.TXT: 403

Inspect the ASTER file we just opened

Loading...

What is its CRS?

CRS.from_wkt('PROJCS["WGS 84 / UTM zone 12N",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-111],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","32612"]]')
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14.tif.msk: 403
Warning 1: HTTP response code on https://snowex-tutorials.s3.us-west-2.amazonaws.com/thermal-ir/AST_L1T_00302082020180748_20200209065849_17218_ImageData14.tif.MSK: 403

When was this image taken?

/tmp/ipykernel_4105/2077060720.py:1: DeprecationWarning: The 'generic' unit for NumPy timedelta is deprecated, and will raise an error in the future. This includes implicit conversion of bare integers (e.g. `+ 1`). Please use a specific unit instead.
  aster_ir_timestamp = pd.Timestamp(2020,2,8,18,7,48) - pd.Timedelta(hours=7)

Plot the image. What are the units of the values on the colorbar?

<Figure size 640x480 with 2 Axes>

It’s necessary to read the product documentation to understand what we are looking at here.

We are using the ASTER Level 1 Precision Terrain Corrected Registered At-Sensor Radiance (AST_L1T) product. Product documentation is available here. Also helpful is the ** (AST_L1B) product documentation here from which AST_L1T is derived.

The values here are stored as scaled “digital number” (DN) values rather than the actual radiance values. The product documentation also provides information about how to unscale these values back into radiance units, and from radiance to brightness temperature, using laboratory calibrated constants.

I’ve written two functions here to do this unit conversion for the five ASTER TIR bands in two steps (DN to radiance, radiance to brightness temperature). The function takes as its arguments the DN or radiance values respectively, and the band number (in our case band number 14, not the band wavelengths).

Use the above functions to convert from DN to radiance, radiance to brightness temperature (assume and emissivity of 1 for all surfaces, note that the airborne imagery also assumed emissivity of 1).

Then convert from degeees K to degrees C by subtracting 273.15.

/home/runner/micromamba/envs/snow-observations-cookbook/lib/python3.14/site-packages/xarray/computation/apply_ufunc.py:820: RuntimeWarning: invalid value encountered in log
  result_data = func(*input_data)

During this unit conversion, xarray dropped the coordinate reference system attributes, so here we add the original crs to the new ASTER degrees celsius dataarray.

/tmp/ipykernel_4105/3871339668.py:1: FutureWarning: It is recommended to use 'rio.write_crs()' instead. 'rio.set_crs()' will likelybe removed in a future release.
  aster_band14_tb_c.rio.set_crs(aster_ir.rio.crs, inplace=True);

Plot our image again, this time setting our colorscale and colorbar values. We should see “realistic” surface temperature values in degrees C now.

<Figure size 1500x1000 with 2 Axes>

Plot ASTER next to Airborne IR to see spatial resolution differences

<Figure size 1200x600 with 4 Axes>

Make another plot to zoom in on snow pit 2S10:

<Figure size 1200x400 with 4 Axes>

Get the temperature of the ASTER pixel at the snow pit point using rioxarray clip.

Loading...

How many ASTER pixels in the area did we select?

Plot the clipped area:

<>:5: SyntaxWarning: "\d" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\d"? A raw string is also an option.
<>:5: SyntaxWarning: "\d" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\d"? A raw string is also an option.
/tmp/ipykernel_4105/3974720511.py:5: SyntaxWarning: "\d" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\d"? A raw string is also an option.
  cbar_kwargs={'label': 'Temperature $\degree C$'})
<Figure size 1000x500 with 3 Axes>

Plot the ASTER IR temperature data on top of the ground-based timeseries and airborne IR temperature

<>:37: SyntaxWarning: "\d" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\d"? A raw string is also an option.
<>:37: SyntaxWarning: "\d" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\d"? A raw string is also an option.
/tmp/ipykernel_4105/4068806060.py:37: SyntaxWarning: "\d" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\d"? A raw string is also an option.
  plt.ylabel('Temperature [$C\degree$]')
/tmp/ipykernel_4105/4068806060.py:7: UserWarning: This axis already has a converter set and is updating to a potentially incompatible converter
  plt.plot(airborne_ir_timestamp, airborne_ir_area_temperature.mean(),
<Figure size 1000x400 with 1 Axes>

Bonus activity: comparing two thermal IR rasters

How does the finer spatial resolution airborne IR image compare with the coarser resolution ASTER IR image?

One way to compare these two images is to “upscale” the finer resolution airborne IR image to the same spatial resolution as ASTER.

We can use the rioxarray reproject_match function to do this. (also see this example)

Note that this function has multiple options for how we want to resample the image data to the new spatial resolution. We will use resampling=5 which corresponds to taking the mean value.

Preview the result.

  • What are the image dimensions of the reprojected airborne IR image compared with the original?

Loading...

Plot the ASTER image, original airborne IR image, and resampled airborne IR image next to each other to visualize these spatial resolution differences.

<Figure size 1500x400 with 6 Axes>

Finally, compute the difference between the ASTER IR image and resampled airborne IR image, then plot the result:

<>:7: SyntaxWarning: "\d" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\d"? A raw string is also an option.
<>:7: SyntaxWarning: "\d" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\d"? A raw string is also an option.
/tmp/ipykernel_4105/1907299178.py:7: SyntaxWarning: "\d" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\d"? A raw string is also an option.
  cbar_kwargs={'label': 'Temperature $\degree C$'})
<Figure size 600x500 with 2 Axes>

Next steps:

Project ideas with these data:

  • Compare more snow pit temperature data (using snowexsql queries) against airborne and satellite IR imagery

  • Investigate spatial patterns of snow temperature from open areas to forested areas on the mesa, differences between ASTER and airborne IR imagery

  • Improved data visualization using hvplot or something similar to create interactive plots of IR and/or visible imagery

  • Compare thermal infrared and SAR imagery, or snow temperature observations and snow model outputs

Additional learning resources:

Open an airborne TIR mosaic NetCDF file

Inspect the dataset and its dimensions

Loading...
FrozenMappingWarningOnValuesAccess({'pass': 17, 'easting, x': 4171, 'northing, y': 3581, 'na': 1})

There is an extra dimension (“na”) that we will want to drop, we will want to rename some of the dims, assign coordinates to those dims, and add a coordinate reference system for plotting.

Rename the dimensions to some easier to use names

This NetCDF file was generated in MATLAB, and the dates/times are in an epoch format. Use utcfromtimestamp() and isoformat() to convert and reformat into a more convenient format.

/tmp/ipykernel_4105/3153572546.py:2: DeprecationWarning: datetime.datetime.utcfromtimestamp() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.fromtimestamp(timestamp, datetime.UTC).
  utctime = [datetime.utcfromtimestamp(this_time).isoformat() for this_time in ds.time.values]

Assign and then transpose coordinates in our dataset

Set spatial dimensions then define which coordinate reference system the spatial dimensions are in

Loading...
References
  1. Lundquist, J. D., Chickadel, C., Cristea, N., Currier, W. R., Henn, B., Keenan, E., & Dozier, J. (2018). Separating snow and forest temperatures with thermal infrared remote sensing. Remote Sensing of Environment, 209, 764–779. 10.1016/j.rse.2018.03.001