> For the complete documentation index, see [llms.txt](https://satdocs.hydrosat.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://satdocs.hydrosat.com/stac-api-how-to-guides/using-the-cloud-and-qa-masks.md).

# Using the Cloud and QA Masks

{% hint style="info" %}
You can download this notebook here: <https://github.com/Hydrosat/vz-tutorials>
{% endhint %}

## 1. Explore the cloud mask asset

### 1.1 Access the cloud mask COG

```python
import rioxarray as rxr
from matplotlib import pyplot as plt
import matplotlib.colors as mcolors
import seaborn as sns
import numpy as np

cloud_mask = rxr.open_rasterio(item.assets['CLOUD_MASK'].href)
```

### 1.2 Visualize the cloud mask

Here, we'll define and use a function to plot the mask with a discrete colormap.

```python
def plot_mask(data, set_min=True, title=None, cmap='cubehelix', vmin=None, vmax=None, colorbar_label=None):
    '''
    data: a DataArray containing quality or cloud mask data
    set_min: boolean, if True, sets the color for the minimum value to white
    title: None or string representing the title of the plot
    cmap: colormap to be used with plt.imshow()
    vmin, vmax: min and max values for the colormap
    colorbar_label: None or string
    '''
    data = data.squeeze() # Remove extra dimension if present

    min_val = int(data.min().values)
    max_val = int(data.max().values)
    n_colors = max_val - min_val +1

    cmap = sns.color_palette(cmap, n_colors) # Discrete cmap with breaks at every integer
    if set_min: cmap[0] = 'whitesmoke' # set specific color for min
    cmap = mcolors.ListedColormap(cmap)

    bounds = np.arange(min_val, max_val+2)
    norm = mcolors.BoundaryNorm(bounds, cmap.N)

    plt.figure(figsize=(12,10))
    plot = plt.imshow(data, cmap=cmap, norm=norm)
    if colorbar_label is not None:
        max_ticks = 10
        values = np.arange(min_val, max_val + 1)
        tick_values = values if len(values) <= max_ticks else np.linspace(min_val, max_val, max_ticks, dtype=int)
        cbar = plt.colorbar(
            plot,
            label=colorbar_label,
            ticks=tick_values + 0.5
        )
        cbar.ax.set_yticklabels(tick_values)
        
    plt.xticks([])
    plt.yticks([])
    plt.title(title, weight='bold')
    plt.tight_layout()
    plt.show()
    return
```

Let's plot the cloud mask for the item of interest.

```python
plot_mask(cloud_mask, title=f'Item ID: {item.id}', colorbar_label='Cloud mask value')
```

<figure><img src="/files/MNoYzytpLBXqxhdNfCtv" alt=""><figcaption></figcaption></figure>

The cloud mask includes bit-packed values, so we'll need to unpack them to understand what they represent.

We'll explore this in the next section.

### 1.3 Interpret the cloud mask pixel values

First, we'll need to write a function to convert a given binary value to a 16-bit binary string.

```python
def decimal_to_binary(value, bit_length=16):
    return format(value, f'0{bit_length}b')
```

Next, we'll create a dict with information about each bit's meaning. This information is available on the FAQs page and in our product guide.

```python
# Interpretation when set to True
cloud_mask_bit_meanings = {
    0: "No data",
    1: "Cloud"
}
```

Finally, we'll display a lookup table for each unique pixel value from the cloud mask.

This function takes the cloud mask and bit definitions as input and outputs a table of pixel values mapped to their meanings.

```python
import pandas as pd

def make_lookup_table(mask, meanings):

    unique_decimal_values = np.unique(mask)
    binary_strings = [decimal_to_binary(int(val)) for val in unique_decimal_values]

    df = pd.DataFrame({
        "Decimal": unique_decimal_values,
        "Binary": binary_strings
    })

    # Add Yes/No columns for each bit
    for bit, bit_name in meanings.items():
        df[bit_name] = ["Yes" if binary[-(bit + 1)] == "1" else "No" for binary in binary_strings] # -(bit + 1) ensures we are reading the string from right to left

    return df
```

```python
cloud_LUT = make_lookup_table(cloud_mask, cloud_mask_bit_meanings)
cloud_LUT
```

| Decimal | Binary           | No data | Cloud | Cloud shadow | Snow or ice |
| ------- | ---------------- | ------- | ----- | ------------ | ----------- |
| 0.0     | 0000000000000000 | No      | No    | No           | No          |
| 1.0     | 0000000000000001 | Yes     | No    | No           | No          |
| 2.0     | 0000000000000010 | No      | Yes   | No           | No          |

There we go! Each pixel value represents a specific set of conditions. For example, a value of 2 indicates cloudy conditions; a value of 1 represents no data; and a value of 0 represents clear conditions.

## Access the QA mask asset

The QA mask provides information about radiometric saturation in the VNIR and LWIR bands. Similar to the cloud mask, it contains bit-packed values.

First, we'll use `rioxarray` to access the QA mask data.

```python
qa_mask = rxr.open_rasterio(item.assets['quality_assurance'].href).squeeze()
```

The following dict contains information about each bit's meaning.

```python
# Interpretation of bits 0-8 when set to True
qa_mask_bit_meanings = {
    0: "Blue band saturation",
    1: "Green band saturation",
    2: "Red band saturation",
    3: "Red edge 1 band saturation",
    4: "Red edge 2 band saturation",
    5: "Red edge 3 band saturation",
    6: "NIR band saturation",
    7: "LWIR 1 band saturation",
    8: "LWIR 2 band saturation"
}
```

Let's plot the QA mask to understand the variability in pixel values.

```python
plot_mask(qa_mask, set_min=False, cmap='rainbow', title=f'Item ID: {item.id}', colorbar_label='QA mask value')
```

<figure><img src="/files/kPKglCHP6N2Af2cNxnCC" alt=""><figcaption></figcaption></figure>

## Interpret the QA mask pixel values

Now, we'll use the same functions we wrote previously to interpret the pixel values from the QA mask.

```python
qa_LUT = make_lookup_table(qa_mask, qa_mask_bit_meanings)
qa_LUT
```

| Decimal | Binary           | Blue band saturation | Green band saturation | Red band saturation | Red edge 1 band saturation | Red edge 2 band saturation | Red edge 3 band saturation | NIR band saturation | LWIR 1 band saturation | LWIR 2 band saturation |
| ------- | ---------------- | -------------------- | --------------------- | ------------------- | -------------------------- | -------------------------- | -------------------------- | ------------------- | ---------------------- | ---------------------- |
| 0       | 0000000000000000 | No                   | No                    | No                  | No                         | No                         | No                         | No                  | No                     | No                     |
| 1       | 0000000000000001 | Yes                  | No                    | No                  | No                         | No                         | No                         | No                  | No                     | No                     |
| 2       | 0000000000000010 | No                   | Yes                   | No                  | No                         | No                         | No                         | No                  | No                     | No                     |
| 3       | 0000000000000011 | Yes                  | Yes                   | No                  | No                         | No                         | No                         | No                  | No                     | No                     |
| 4       | 0000000000000100 | No                   | No                    | Yes                 | No                         | No                         | No                         | No                  | No                     | No                     |
| ...     | ...              | ...                  | ...                   | ...                 | ...                        | ...                        | ...                        | ...                 | ...                    | ...                    |
| 67      | 0000000001000011 | Yes                  | Yes                   | No                  | No                         | No                         | No                         | Yes                 | No                     | No                     |
| 68      | 0000000001000100 | No                   | No                    | Yes                 | No                         | No                         | No                         | Yes                 | No                     | No                     |
| 69      | 0000000001000101 | Yes                  | No                    | Yes                 | No                         | No                         | No                         | Yes                 | No                     | No                     |
| 70      | 0000000001000110 | No                   | Yes                   | Yes                 | No                         | No                         | No                         | Yes                 | No                     | No                     |
| 71      | 0000000001000111 | Yes                  | Yes                   | Yes                 | No                         | No                         | No                         | Yes                 | No                     | No                     |

The bottom line is that a pixel value of 0 means that no bands are saturated, while a nonzero pixel value indicates some saturation.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://satdocs.hydrosat.com/stac-api-how-to-guides/using-the-cloud-and-qa-masks.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
