Leonardo Uieda

Visualizing the on-going massive floods in Rio Grande do Sul, Brazil

The state of Rio Grande do Sul in southern Brazil is currently experiencing massive floods due to intense rain fall, affecting hundreds of thousands of people. The scale of these events is difficult to comprehend both at the human and at the physical level. To help visualize how large the floods are, I thought it would be good to look at the Landsat scenes from before and during the floods around the city of Porto Alegre (the state capital).

I’ll use xlandsat and other scientific Python libraries to create visualizations of Landsat data and an animated gif to flip back and forth between the before and after images.

import xlandsat as xls
import matplotlib.pyplot as plt  # for plotting
from matplotlib_scalebar.scalebar import ScaleBar  # to add a scale bar
import imageio  # to make a gif

I downloaded two Landsat scenes from USGS EarthExplorer. Both were Landsat 8 Level 1 Collection 2 scenes with WRS path/row = 221/81, one from 2024/04/06 and the other from 2024/05/08. I downloaded the full scene and places the .tar files in the same folder as this Jupyter notebook.

Don’t know how to use EarthExplorer? Watch this YouTube tutorial I made a while ago going through the entire process.

Loading the data

Now I can load the before scene, cropped to show mostly the region of Porto Alegre.

region = (425000, 530000, -3350000, -3270000)  # UTM meters
bands = ("red", "green", "blue")
before = xls.load_scene("LC08_L1TP_221081_20240406_20240412_02_T1.tar", bands=bands, region=region)

And we can make a quick RGB composite and plot it for a quick check of our scene.

rgb = xls.composite(before)
# Need to adjust the colors to remove the top-of-the-atmosphere
# glare from the L1 product
rgb = xls.adjust_l1_colors(rgb)

plt.figure(figsize=(12, 8), layout="constrained")
rgb.plot.imshow()
plt.axis("scaled")
plt.show()

png

Now I can load the panchromatic band from the same file and cropped to the same region. The pan band has a higher resolution of 15m and we’ll use it to sharpen our image.

before_pan = xls.load_panchromatic("LC08_L1TP_221081_20240406_20240412_02_T1.tar", region=region)

Now pansharpen the scene to get the RGB data at 15m instead of 30m.

before_sharp = xls.pansharpen(before, before_pan)

And plot the pansharpened RGB composite of the before scene.

rgb = xls.composite(before_sharp)
rgb = xls.adjust_l1_colors(rgb, percentile=1)

plt.figure(figsize=(12, 8), layout="constrained")
rgb.plot.imshow()
plt.axis("scaled")
plt.show()

png

Now I can do the same thing for the after scene.

after = xls.load_scene("LC08_L1TP_221081_20240508_20240508_02_RT.tar", bands=bands, region=region)
after_pan = xls.load_panchromatic("LC08_L1TP_221081_20240508_20240508_02_RT.tar", region=region)
after_sharp = xls.pansharpen(after, after_pan)

Creating before and after images

Now I make two images of the RGB composites for the before and after scenes with added annotations. I had to adjust the percentile range for the color correction in the after scene because of the higher amount of clouds in that image.

for scene, percentile in [(before_sharp, 1), (after_sharp, 5)]:
    rgb = xls.composite(scene)
    rgb = xls.adjust_l1_colors(rgb, percentile=percentile)
    
    plt.figure(figsize=(14, 12), layout="tight", facecolor="black")
    ax = plt.subplot(111)
    rgb.plot.imshow(ax=ax)
    # Make sure pixels are square
    ax.set_aspect("equal")
    # Remove so that it looks fancier
    ax.axis("off")
    # Add a caption at the bottom
    text = (
        "Floods in Rio Grande do Sul, Brazil. "
        "Pansharpened Landsat 8 scenes from "
        f"{before.attrs['date_acquired']} and "
        f"{after.attrs['date_acquired']}. "
        "Visualization by Leonardo Uieda (CC0 license)."
    )
    ax.text(
        0.005, 0.01, 
        text, 
        transform=ax.transAxes,
        color="white",
        fontsize=9,
        bbox=dict(
            facecolor="#000000dd",
            boxstyle="square",
            pad=0.75,
        ),
    )
    # Add the date to the top
    ax.text(
        0.995, 0.975, 
        f"{scene.attrs['date_acquired'].replace('-', '/')}", 
        transform=ax.transAxes,
        horizontalalignment="right",
        color="white",
        fontsize=16,
        bbox=dict(
            facecolor="#000000dd",
            boxstyle="square",
            pad=0.5,
        ),
    )
    # Add a scale bar
    ax.add_artist(
        ScaleBar(
            dx=1, 
            units="m", 
            location="upper left", 
            box_alpha=0,
            color="white", 
            scale_loc="bottom", 
            width_fraction=0.005, 
            length_fraction=0.06,
            pad=0.4,
        )
    )
    # Save the figure at 600 and 100 DPI
    for dpi, prefix in [(600, ""), (100, "-lowres")]:
        plt.savefig(
            f"rio-grande-do-sul-floods-{scene.attrs['date_acquired']}{prefix}.jpg", 
            dpi=dpi, 
            bbox_inches="tight",
        )
plt.show()

png

png

The massive scale of the floods are quite visible from the two images above. The brown water dominates the center of the image and there is also a darker, almost black, flooded river cutting through the city in the center right of the image.

Creating an animated gif

To help visualize this further, I’ll use the imageio module to load the two images and make a looping gif animation. I’ll use the lower resolution images to avoid making the gif file too large. Setting loop=0 makes the gif loop infinitely.

images = [
    imageio.v3.imread(f"rio-grande-do-sul-floods-{scene.attrs['date_acquired']}-lowres.jpg")
    for scene in [before, after]
]
imageio.mimsave("rio-grande-do-sul-floods-2024.gif", images, duration=1000, loop=0)

Animation of before and after the floods, with the after image showing a large amount of brown water at the center of the image where there were city and farms.

Flipping back and forth makes it easier to see all of the affected areas. It’s devastating to see such destruction. Unfortunately, it’s also a sign of things to come as the climate continues to change due to human action during late-stage capitalism.

Downloading the data and images

You can download the Landsat data, both images in high and low resolution, the animated gif, and this source code from figshare at doi:10.6084/m9.figshare.25800298. All are made available under the CC-0 license.

How to help: You can find out more about how to help the people affected by the floods from this report on Terra (in Portuguese).


Back to the blog