Skip to content

GEOG 336 Demo follow-along guide

Open OnDemand | GlobalProtect VPN

Part 1: Logging in to Open OnDemand

For your convenience, the cluster can be accessed through a graphical user interface (GUI) called Open OnDemand. This interface will allow you to access all of the features you will need for GEOG 336ds.

  1. Navigate to https://ondemand.hpc.uwec.edu (Requires on-campus or VPN connection).
  2. Enter your UWEC login and authenticate with Okta.
  3. Complete the first time sign-in process if you have never used the cluster before. * Follow the instructions on screen exactly. Make sure you accept all the terms of service.

Trouble with setup?

If you're struggling with first time sign-in or accepting the terms of service, try following our detailed guide at https://docs.hpc.uwec.edu/ood/first-time/

Part 2 - Copying Class Files

Before you are able to run the demo in this guide, you'll need to copy files on the cluster to your own personal directory.

  1. Click on the "Home Directory" tile on the main dashboard page.
    Home Directory Tile
  2. Note that you should see a my_geog336 directory in your files. This is important for the demo.
    my_geog336 in the file list
  3. On the left side click on "Class Files: GEOG336"
    Sidebar showing Class Files bookmark
  4. Click the checkmark box for EC_rasters and NDVI_NDWI_Demo.ipynb to indicate you want to copy them.
  5. In the top right click on Copy/Move
    • This will show a popup on the left side. This is just telling you that you are in "Copy/Move" mode.
  6. Navigate to "Your Files: GEOG336" in the left sidebar to go to your own directory.
  7. Click the "Copy" button on the left to copy the files to your directory.
    Click copy in the top left

Part 3: Jupyter Notebook

You will be using a program called Jupyter Notebook for this demo. Jupyter allows you to interactively run code on the cluster with an easy-to-use interface.

Starting a Jupyter session

To use Jupyter, you must request resources from the cluster. First, click on the "Jupyter Notebook" tile on the OnDemand home screen. Once you click it, request the following resources on the next screen:

  • Accounting Group: 2255.geog.336.001
  • Slurm Partition: Week (7 days) - Default
  • CPU Cores: 1
  • Memory: 15G
  • #GPU Cards: No GPUs - Default
  • Number of Hours: 2
  • Working Directory: (Leave this blank)
  • Email Notifications: None - No Email

Double-check that all of your values are right, then click the blue Launch button.

Accessing your session

Once you launch the session, it will need to start. This can take some time, but once your session changes from "Starting" to "Running", click the blue Connect to Jupyter button.

Opening the demo notebook

Once you open Jupyter, you will be in your "Home Directory". You should see a folder called "my_geog336". Open this folder, then click on the "NDVI_NDWI_Demo.ipynb" file inside. Once opened, you will be on the Jupyter Notebook screen, and you can now begin entering code.

Part 4 - Notebook Cells

Each "Chunk" of code in a notebook is called a Cell. Below you will find each cell used in the demo, which you can copy for convenience. Make sure to run each cell after you copy it. If you don't run them in order, it will not work.

Initial Set Up

Cell 1:

This cell is already in the notebook for you!

# By default, Python can't really do a whole lot, but it really shines by importing code
# that *other* people make.
from tifffile import imread
import matplotlib.pyplot as plt 
import numpy as np 
import rasterio

# ---- DO THIS ---- #

# Click on this cell and either hit "Run" at the top or press "shift+enter" on your keyboard.
# - If the left side says "In [#]" then you've successfully completed this step.
# - Do this for every new cell you type.

# --- READ ABOVE ---- #
Output

Nothing will show if this is successful.

Cell 2:

#Read the raster tiff files and find out how many channels are there

EC_april_23 = imread('EC_rasters/EC_april23.tif')
print(EC_april_23.shape)

EC_aug_23 = imread('EC_rasters/EC_aug23.tif')
print(EC_aug_23.shape)

Output
(2625, 2212, 4)
(2625, 2212, 4)

Cell 3:

#Find the min and max in the dataset. Then normalize to have all pixels between 0 and 1
print(f"Original April Image Min: {EC_april_23.min()}")
print(f"Original April Image Max: {EC_april_23.max()}")

# Normalize image using NumPy
april_normalized = (EC_april_23 - EC_april_23.min()) / (EC_april_23.max() - EC_april_23.min())

#Check for consistency: 
print(f"Normalized April Image Min: {april_normalized.min()}")
print(f"Normalized April Image Max: {april_normalized.max()}")
Output
Original April Image Min: 0
Original April Image Max: 11853
Normalized April Image Min: 0.0
Normalized April Image Max: 1.0

Cell 4:

#Find the min and max in the dataset. Then normalize to have all pixels between 0 and 1
print(f"Original August Image Min: {EC_aug_23.min()}")
print(f"Original August Image Max: {EC_aug_23.max()}")

# Normalize image using NumPy
aug_normalized = (EC_aug_23 - EC_aug_23.min()) / (EC_aug_23.max() - EC_aug_23.min())

#Check for consistency: 
print(f"Normalized Aug Image Min: {aug_normalized.min()}")
print(f"Normalized Aug Image Max: {aug_normalized.max()}")
Output
Original August Image Min: 0
Original August Image Max: 10193
Normalized Aug Image Min: 0.0
Normalized Aug Image Max: 1.0

Cell 5:

#Print using first channel (Red) for April and Aug image side by side: 

# Create subplots with 1 row, 2 columns
fig, axes = plt.subplots(1, 2, figsize=(12, 6))

# First Image (April Red Channel with Viridis Colormap)
img1 = axes[0].imshow(april_normalized[:, :, 0], cmap='viridis')
axes[0].set_title("April Red Channel Image")
axes[0].axis("off")  # Hide axis

# Second Image (Aug Red Channel with Viridis Colormap)
img2= axes[1].imshow(aug_normalized[:, :, 0], cmap='viridis')  # Display as RGB
axes[1].set_title("August Red Channel Image")
axes[1].axis("off")  # Hide axis
Output
(-0.5, 2211.5, 2624.5, -0.5)

Then afterwards there should be two images showing April and August Red Channels.

NDVI

Cell 6:

#Perform NDVI for April
#Note that the image has 4 channels.
#The channels are Red, Green, Blue, and Infrared
# NDVI will be done on the first channel (Red) and the fourth channel (Infrared)

# Extract Red and NIR channels
red_apr = april_normalized[:, :, 0]  # First channel (Red)
nir_apr = april_normalized[:, :, 3]  # Fourth channel (Infrared)

# Compute NDVI with handling for division by zero
denominator_apr = nir_apr + red_apr
ndvi_apr = np.where(denominator_apr == 0, 0, (nir_apr - red_apr) / denominator_apr) 


#Do the same for August
# Extract Red and NIR channels
red_aug = aug_normalized[:, :, 0]  # First channel (Red)
nir_aug = aug_normalized[:, :, 3]  # Fourth channel (Infrared)

# Compute NDVI with handling for division by zero
denominator_aug = nir_aug + red_aug
ndvi_aug = np.where(denominator_aug == 0, 0, (nir_aug - red_aug) / denominator_aug) 
Output

You should see a red note about "RuntimeWarning: Invalid value encountered in true_device". This is perfectly ok! It's just letting you know it wasn't able to do some math on parts of the drone images.

Cell 7:

#Check to see that the NDVI's are between -1 and 1

#Find the min and max in the NDVI. 
print(f"April NDVI Min: {ndvi_apr.min()}")
print(f"April NDVI  Max: {ndvi_apr.max()}")

#Check for consistency: 
print(f"Aug NDVI  Min: {ndvi_aug.min()}")
print(f"Aug NDVI  Max: {ndvi_aug.max()}")
Output
April NDVI Min: -0.4425087108013938
April NDVI  Max: 0.8810361368724017
Aug NDVI  Min: -0.10714285714285715
Aug NDVI  Max: 0.9235737351991388

Cell 8:

#Print NDVI for April and August with legend


# Define NDVI colormap range
vmin, vmax = -1, 1  # NDVI ranges from -1 to 1

# Create subplots with 1 row, 2 columns
fig, axes = plt.subplots(1, 2, figsize=(12, 6))

# First Image (April NDVI)
img1 = axes[0].imshow(ndvi_apr, cmap='viridis', vmin=vmin, vmax=vmax)
axes[0].set_title("April NDVI Image")
axes[0].axis("off")  # Hide axis

# Second Image (Aug NDVI)
img2 = axes[1].imshow(ndvi_aug, cmap='viridis', vmin=vmin, vmax=vmax)  # Display as RGB
axes[1].set_title("August NDVI Image")
axes[1].axis("off")  # Hide axis

plt.colorbar(img1, ax=axes.ravel().tolist()) 

# Show the plot
plt.show()
Output

Once generated, you should see two images showing the NDVI for April and August.

NDWI

Cell 9:

#Perform NDWI for April
#Note that the image has 4 channels.
#The channels are Red, Green, Blue, and Infrared
# NDWI will be done on the second channel (Green) and the fourth channel (Infrared)

# Extract Green and NIR channels
green_apr = april_normalized[:, :, 1]  # Second channel (Green)
nir_apr = april_normalized[:, :, 3]  # Fourth channel (Infrared)

# Compute NDWI with handling for division by zero
denominator_apr = nir_apr + green_apr
ndwi_apr = np.where(denominator_apr == 0, 0, (green_apr - nir_apr) / denominator_apr) 


#Do the same for August
# Extract Green and NIR channels
green_aug = aug_normalized[:, :, 1]  # Second channel (Green)
nir_aug = aug_normalized[:, :, 3]  # Fourth channel (Infrared)

# Compute NDWI with handling for division by zero
denominator_aug = nir_aug + green_aug
ndwi_aug = np.where(denominator_aug == 0, 0, (green_aug - nir_aug) / denominator_aug) 
Output

You should see a red note about "RuntimeWarning: Invalid value encountered in true_device". Just like with previous code for NDVI, it's just letting you know it wasn't able to do some math on parts of the drone images.

Cell 10:

#Check to see that the NDWI's are between -1 and 1

#Find the min and max in the NDVI. 
print(f"April NDWI Min: {ndwi_apr.min()}")
print(f"April NDWI  Max: {ndwi_apr.max()}")

#Check for consistency: 
print(f"Aug NDWI  Min: {ndwi_aug.min()}")
print(f"Aug NDWI  Max: {ndwi_aug.max()}")
Output
April NDWI Min: -0.5324675324675325
April NDWI  Max: 0.7660332541567697
Aug NDWI  Min: -0.14764316322555512
Aug NDWI  Max: 0.8497772119669001

Cell 11:

#Print NDWI for April and August with legend

# Define NDWI colormap range
vmin, vmax = -1, 1  # NDWI ranges from -1 to 1

# Create subplots with 1 row, 2 columns
fig, axes = plt.subplots(1, 2, figsize=(12, 6))

# First Image (April NDWI)
img1 = axes[0].imshow(ndwi_apr, cmap='viridis', vmin=vmin, vmax=vmax)
axes[0].set_title("April NDWI Image")
axes[0].axis("off")  # Hide axis

# Second Image (Aug NDWI)
img2 = axes[1].imshow(ndwi_aug, cmap='viridis', vmin=vmin, vmax=vmax)  # Display as RGB
axes[1].set_title("August NDWI Image")
axes[1].axis("off")  # Hide axis

plt.colorbar(img1, ax=axes.ravel().tolist()) 

# Show the plot
plt.show()
Output

Once generated, you should see two images showing the NDWI for April and August.