Criterion B Details

# Default ecosystem code for template development.
# This line is replaced by build_ecosystem_pages.py for each ecosystem.
ecosystem_code = 'Laguna Glacial'

Import Python modules.

import os
import yaml
from pathlib import Path
from lonboard import Map
from rle.core import Ecosystems, criterion_b_status, rle_category
from rle.core.eoo import make_eoo
from rle.core.aoo import make_aoo_grid

Load the country config file.

project_root = os.environ.get('PIXI_PROJECT_ROOT', str(Path('..').resolve()))
config_path = Path(project_root) / 'config' / 'country_config.yaml'
with open(config_path) as f:
    config = yaml.safe_load(f)

# Ecosystem index (the COG pixel value for this ecosystem), looked up by code
# from the canonical index table. Absent if the index has not been built yet.
import csv
ecosystem_index = None
index_csv = Path(project_root) / 'config' / 'ecosystems' / 'index.csv'
if index_csv.exists():
    with open(index_csv) as f:
        for row in csv.DictReader(f):
            if row['code'] == ecosystem_code:
                ecosystem_index = int(row['index'])
                break

Load & Filter Ecosystem Data

Load data for all the ecosystems.

import sys
# Shared config helpers live in scripts/; make them importable from here.
sys.path.insert(0, str(Path(project_root) / 'scripts'))
from _config import ensure_vector_source

source = config['ecosystem_source']
# ecosystem_code_column is optional: fall back to the name column.
ecosystem_column = source.get('ecosystem_code_column') or source.get('ecosystem_name_column')
# Prefer the ecosystem-sorted `optimized_data` copy when configured, so that
# filtering to one ecosystem uses parquet predicate pushdown instead of loading
# the whole national map into memory. Falls back to `data`.
ecosystems = Ecosystems.from_file(
    ensure_vector_source(source.get('optimized_data') or source['data']),
    ecosystem_column=ecosystem_column,
    ecosystem_name_column=source.get('ecosystem_name_column'),
    functional_group_column=source.get('functional_group_column')
)

Filter by the Laguna Glacial and check the number of features.

ecosystem = ecosystems.filter(ecosystem_code)
has_data = ecosystem.size() > 0
print(f'{ecosystem.size() = }')
if not has_data:
    from IPython.display import Markdown, display
    display(Markdown(
        f'**No spatial data found for {ecosystem_code}.** '
        f'Criterion B calculations are skipped.'
    ))
ecosystem.size() = 50

Extent of Occurrence (EOO) (subcriterion B1)

Extent of occurrence (EOO). The EOO of an ecosystem is the area (km2) of a minimum convex polygon – the smallest polygon in which no internal angle exceeds 180° that encompasses all known current spatial occurrences of the ecosystem type.

The minimum convex polygon (also known as a convex hull) must not exclude any areas, discontinuities or disjunctions, regardless of whether the ecosystem can occur in those areas or not. Regions such as oceans (for terrestrial ecosystems), land (for coastal or marine ecosystems), or areas outside the study area (such as in a different country) must remain included within the minimum convex polygon to ensure that this standardised method is comparable across ecosystem types. In addition, these features contribute to spreading risks across the distribution of the ecosystem by making different parts of its distribution more spatially independent.

Calculate EOO

Start by calculating the convex hull of the ecosystem’s distribution.

import geopandas as gpd

if has_data:
    ecosystem_geometry = ecosystem.geometry.union_all()
    gdf_ecosystem_polygons = gpd.GeoDataFrame(geometry=[ecosystem_geometry], crs=ecosystem.geometry.crs)
    hull = ecosystem_geometry.convex_hull
    gdf_hull = gpd.GeoDataFrame(geometry=[hull], crs=ecosystem.geometry.crs)

Display the ecosystem’s distribution and the convex hull.

from lonboard import Map, PolygonLayer
from rle.core.viz import smart_map

if has_data:
    eoo_hull = make_eoo(ecosystem).compute()
    display(smart_map([eoo_hull, ecosystem]))
/home/runner/work/rle-tyler-colombia/rle-tyler-colombia/.pixi/envs/default/lib/python3.11/site-packages/lonboard/_geoarrow/ops/reproject.py:116: UserWarning: Input being reprojected to EPSG:4326 CRS.
Lonboard is only able to render data in EPSG:4326 projection.
  warnings.warn(
/home/runner/work/rle-tyler-colombia/rle-tyler-colombia/.pixi/envs/default/lib/python3.11/site-packages/lonboard/_geoarrow/ops/reproject.py:116: UserWarning: Input being reprojected to EPSG:4326 CRS.
Lonboard is only able to render data in EPSG:4326 projection.
  warnings.warn(
if has_data:
    hull_ea = gdf_hull.to_crs("ESRI:54034")
    eoo = hull_ea.geometry.iloc[0].area / 1e6
    print(f'EOO is {eoo:.1f} km2')
EOO is 227062.7 km2

Then calculate the area of the convex hull polygon.

Direct calculation of EOO

EOO can also be calculated directly using …

if has_data:
    ecosystem.eoo

Verify that the area returned by calling make_eoo(ecosystem).compute().area_km2 is the same as the area of the convex hull polygon.

if has_data:
    assert ecosystem.eoo == eoo

Area of Occupancy (AOO) (subcriterion B2)

The protocol for this adjustment includes the following steps:

  1. Intersect AOO grid with the ecosystem’s distribution map.
  2. Calculate extent of the ecosystem type in each grid cell (area) and sum these areas to obtain the total ecosystem area (total area).
  3. Arrange grid cells in ascending order based on their area (smaller first). Calculate accumulated sum of area per cell (cumulative area).
  4. Calculate cumulative proportion by dividing cumulative area by total area (cumulative proportion takes values between 0 and 1)
  5. Calculate AOO by counting the number of cells with a cumulative proportion greater than 0.01 (i.e. exclude cells that in combination account for up to 1% of the total mapped extent of the ecosystem type).

AOO Calculation Details

Intersect AOO grid and ecosystem map

  1. Intersect AOO grid with the ecosystem’s distribution map
from pathlib import Path
from rle.core.aoo import make_aoo_grid_cached

if has_data:
    # Prefer a prebuilt grid cache (e.g. a gs:// URI) so the national AOO grid
    # is not recomputed during CI renders — computing it from the full national
    # ecosystem map peaks at many GB of RAM. Fall back to a local cache when no
    # prebuilt cache is configured. Build one with `pixi run build-caches`.
    cache_path = (
        source.get('aoo_grid_cache_url')
        or (Path(project_root) / '.cache' / 'aoo_grid.parquet')
    )
    aoo_grid = make_aoo_grid_cached(ecosystems, cache_path=cache_path)
    aoo_grid_filtered = aoo_grid.filter_by_ecosystem(ecosystem_code)

Visualize variations in the AOO grid.

from matplotlib.colors import LinearSegmentedColormap
from lonboard.colormap import apply_continuous_cmap
from rle.core.aoo import slugify_ecosystem_name

ecosystem_column = slugify_ecosystem_name(ecosystem_code)
if has_data:
    cmap = LinearSegmentedColormap.from_list("white_red", ["white", "red"])
    values = aoo_grid_filtered.grid_cells[ecosystem_column].values
    normalized = (values - values.min()) / (values.max() - values.min())
    colors = apply_continuous_cmap(normalized, cmap)
    display(smart_map([(aoo_grid_filtered, {"get_fill_color": colors}), ecosystem]))
/home/runner/work/rle-tyler-colombia/rle-tyler-colombia/.pixi/envs/default/lib/python3.11/site-packages/lonboard/_geoarrow/ops/reproject.py:116: UserWarning: Input being reprojected to EPSG:4326 CRS.
Lonboard is only able to render data in EPSG:4326 projection.
  warnings.warn(

Calculate grid cell area and total area

  1. Calculate extent of the ecosystem type in each grid cell (area) and sum these areas to obtain the total ecosystem area (total area).
if has_data:
    keep = ['geometry', 'grid_col', 'grid_row', ecosystem_column]
    gdf = aoo_grid_filtered.grid_cells[keep]
    display(gdf)
geometry grid_col grid_row Laguna_Glacial
0 POLYGON ((-77.7941 0.90441, -77.7941 0.99486, ... -867 10 0.023114
1 POLYGON ((-77.70427 0.99486, -77.70427 1.08531... -866 11 0.000277
2 POLYGON ((-77.70427 1.08531, -77.70427 1.17576... -866 12 0.002733
3 POLYGON ((-76.53646 1.80903, -76.53646 1.89951... -853 20 0.003009
4 POLYGON ((-76.3568 2.17099, -76.3568 2.2615, -... -851 24 0.009011
5 POLYGON ((-76.0873 2.80464, -76.0873 2.89518, ... -848 31 0.002767
6 POLYGON ((-76.0873 3.16686, -76.0873 3.25744, ... -848 35 0.002594
7 POLYGON ((-75.99747 2.98573, -75.99747 3.07629... -847 33 0.004202
8 POLYGON ((-75.99747 3.16686, -75.99747 3.25744... -847 35 0.004540
9 POLYGON ((-75.99747 3.25744, -75.99747 3.34802... -847 36 0.010140
10 POLYGON ((-75.99747 3.34802, -75.99747 3.43861... -847 37 0.001732
11 POLYGON ((-75.99747 3.43861, -75.99747 3.52921... -847 38 0.010395
12 POLYGON ((-75.90764 3.07629, -75.90764 3.16686... -846 34 0.002790
13 POLYGON ((-75.81781 3.71044, -75.81781 3.80106... -845 41 0.003060
14 POLYGON ((-75.72798 3.80106, -75.72798 3.8917,... -844 42 0.002559
15 POLYGON ((-75.36865 4.70788, -75.36865 4.79862... -840 52 0.009629
16 POLYGON ((-75.27882 4.79862, -75.27882 4.88937... -839 53 0.003138
17 POLYGON ((-74.29067 3.80106, -74.29067 3.8917,... -828 42 0.002553
18 POLYGON ((-74.20084 4.25434, -74.20084 4.34503... -827 47 0.004358
19 POLYGON ((-74.11101 3.80106, -74.11101 3.8917,... -826 42 0.002879
20 POLYGON ((-74.11101 3.8917, -74.11101 3.98235,... -826 43 0.020303
21 POLYGON ((-74.11101 3.98235, -74.11101 4.073, ... -826 44 0.009757
22 POLYGON ((-74.11101 4.98013, -74.11101 5.07091... -826 55 0.002870
23 POLYGON ((-74.02118 3.8917, -74.02118 3.98235,... -825 43 0.004785
24 POLYGON ((-74.02118 4.98013, -74.02118 5.07091... -825 55 0.003393
25 POLYGON ((-73.75168 4.43572, -73.75168 4.52643... -822 49 0.000970
26 POLYGON ((-73.75168 4.52643, -73.75168 4.61715... -822 50 0.005378
27 POLYGON ((-73.66185 4.43572, -73.66185 4.52643... -821 49 0.002854
28 POLYGON ((-73.66185 4.52643, -73.66185 4.61715... -821 50 0.002956
29 POLYGON ((-73.66185 10.73256, -73.66185 10.824... -821 118 0.041296
30 POLYGON ((-73.57202 10.73256, -73.57202 10.824... -820 118 0.003183
31 POLYGON ((-72.9432 7.61854, -72.9432 7.70977, ... -813 84 0.003843
32 POLYGON ((-72.49404 5.9794, -72.49404 6.07032,... -808 66 0.002519
33 POLYGON ((-72.49404 6.07032, -72.49404 6.16126... -808 67 0.000031
34 POLYGON ((-72.49404 6.79829, -72.49404 6.88936... -808 75 0.004600
35 POLYGON ((-72.49404 6.88936, -72.49404 6.98044... -808 76 0.004686
36 POLYGON ((-72.40421 6.16126, -72.40421 6.25222... -807 68 0.003114
37 POLYGON ((-72.31438 6.52518, -72.31438 6.6162,... -806 72 0.003703
38 POLYGON ((-72.31438 6.6162, -72.31438 6.70724,... -806 73 0.000115
39 POLYGON ((-72.31438 6.70724, -72.31438 6.79829... -806 74 0.003408
40 POLYGON ((-72.22455 6.34319, -72.22455 6.43418... -805 70 0.015378

The column Laguna_Glacial contains the (fractional) area of the ecosystem in each grid cell.

Sum up the areas of each grid cell to get the total area.

if has_data:
    total_area = gdf[ecosystem_column].sum()
    display(total_area)
np.float64(0.24462301832070565)

Calculate cumulative area

  1. Arrange grid cells in ascending order based on their area (smaller first). Calculate accumulated sum of area per cell (cumulative area).
if has_data:
    gdf = gdf.sort_values(by=ecosystem_column)
    gdf["cumulative_area"] = gdf[ecosystem_column].cumsum()
    display(gdf)
geometry grid_col grid_row Laguna_Glacial cumulative_area
33 POLYGON ((-72.49404 6.07032, -72.49404 6.16126... -808 67 0.000031 0.000031
38 POLYGON ((-72.31438 6.6162, -72.31438 6.70724,... -806 73 0.000115 0.000146
1 POLYGON ((-77.70427 0.99486, -77.70427 1.08531... -866 11 0.000277 0.000423
25 POLYGON ((-73.75168 4.43572, -73.75168 4.52643... -822 49 0.000970 0.001393
10 POLYGON ((-75.99747 3.34802, -75.99747 3.43861... -847 37 0.001732 0.003124
32 POLYGON ((-72.49404 5.9794, -72.49404 6.07032,... -808 66 0.002519 0.005644
17 POLYGON ((-74.29067 3.80106, -74.29067 3.8917,... -828 42 0.002553 0.008197
14 POLYGON ((-75.72798 3.80106, -75.72798 3.8917,... -844 42 0.002559 0.010755
6 POLYGON ((-76.0873 3.16686, -76.0873 3.25744, ... -848 35 0.002594 0.013349
2 POLYGON ((-77.70427 1.08531, -77.70427 1.17576... -866 12 0.002733 0.016082
5 POLYGON ((-76.0873 2.80464, -76.0873 2.89518, ... -848 31 0.002767 0.018849
12 POLYGON ((-75.90764 3.07629, -75.90764 3.16686... -846 34 0.002790 0.021639
27 POLYGON ((-73.66185 4.43572, -73.66185 4.52643... -821 49 0.002854 0.024494
22 POLYGON ((-74.11101 4.98013, -74.11101 5.07091... -826 55 0.002870 0.027364
19 POLYGON ((-74.11101 3.80106, -74.11101 3.8917,... -826 42 0.002879 0.030243
28 POLYGON ((-73.66185 4.52643, -73.66185 4.61715... -821 50 0.002956 0.033199
3 POLYGON ((-76.53646 1.80903, -76.53646 1.89951... -853 20 0.003009 0.036208
13 POLYGON ((-75.81781 3.71044, -75.81781 3.80106... -845 41 0.003060 0.039268
36 POLYGON ((-72.40421 6.16126, -72.40421 6.25222... -807 68 0.003114 0.042382
16 POLYGON ((-75.27882 4.79862, -75.27882 4.88937... -839 53 0.003138 0.045520
30 POLYGON ((-73.57202 10.73256, -73.57202 10.824... -820 118 0.003183 0.048703
24 POLYGON ((-74.02118 4.98013, -74.02118 5.07091... -825 55 0.003393 0.052095
39 POLYGON ((-72.31438 6.70724, -72.31438 6.79829... -806 74 0.003408 0.055503
37 POLYGON ((-72.31438 6.52518, -72.31438 6.6162,... -806 72 0.003703 0.059206
31 POLYGON ((-72.9432 7.61854, -72.9432 7.70977, ... -813 84 0.003843 0.063049
7 POLYGON ((-75.99747 2.98573, -75.99747 3.07629... -847 33 0.004202 0.067252
18 POLYGON ((-74.20084 4.25434, -74.20084 4.34503... -827 47 0.004358 0.071610
8 POLYGON ((-75.99747 3.16686, -75.99747 3.25744... -847 35 0.004540 0.076150
34 POLYGON ((-72.49404 6.79829, -72.49404 6.88936... -808 75 0.004600 0.080750
35 POLYGON ((-72.49404 6.88936, -72.49404 6.98044... -808 76 0.004686 0.085436
23 POLYGON ((-74.02118 3.8917, -74.02118 3.98235,... -825 43 0.004785 0.090222
26 POLYGON ((-73.75168 4.52643, -73.75168 4.61715... -822 50 0.005378 0.095600
4 POLYGON ((-76.3568 2.17099, -76.3568 2.2615, -... -851 24 0.009011 0.104611
15 POLYGON ((-75.36865 4.70788, -75.36865 4.79862... -840 52 0.009629 0.114239
21 POLYGON ((-74.11101 3.98235, -74.11101 4.073, ... -826 44 0.009757 0.123996
9 POLYGON ((-75.99747 3.25744, -75.99747 3.34802... -847 36 0.010140 0.134136
11 POLYGON ((-75.99747 3.43861, -75.99747 3.52921... -847 38 0.010395 0.144532
40 POLYGON ((-72.22455 6.34319, -72.22455 6.43418... -805 70 0.015378 0.159910
20 POLYGON ((-74.11101 3.8917, -74.11101 3.98235,... -826 43 0.020303 0.180213
0 POLYGON ((-77.7941 0.90441, -77.7941 0.99486, ... -867 10 0.023114 0.203327
29 POLYGON ((-73.66185 10.73256, -73.66185 10.824... -821 118 0.041296 0.244623

Calculate cumulative proportion

  1. Calculate cumulative proportion by dividing cumulative area by total area (cumulative proportion takes values between 0 and 1)
if has_data:
    gdf["cumulative_proportion"] = gdf["cumulative_area"] / total_area
    display(gdf)
geometry grid_col grid_row Laguna_Glacial cumulative_area cumulative_proportion
33 POLYGON ((-72.49404 6.07032, -72.49404 6.16126... -808 67 0.000031 0.000031 0.000128
38 POLYGON ((-72.31438 6.6162, -72.31438 6.70724,... -806 73 0.000115 0.000146 0.000597
1 POLYGON ((-77.70427 0.99486, -77.70427 1.08531... -866 11 0.000277 0.000423 0.001729
25 POLYGON ((-73.75168 4.43572, -73.75168 4.52643... -822 49 0.000970 0.001393 0.005693
10 POLYGON ((-75.99747 3.34802, -75.99747 3.43861... -847 37 0.001732 0.003124 0.012772
32 POLYGON ((-72.49404 5.9794, -72.49404 6.07032,... -808 66 0.002519 0.005644 0.023070
17 POLYGON ((-74.29067 3.80106, -74.29067 3.8917,... -828 42 0.002553 0.008197 0.033507
14 POLYGON ((-75.72798 3.80106, -75.72798 3.8917,... -844 42 0.002559 0.010755 0.043966
6 POLYGON ((-76.0873 3.16686, -76.0873 3.25744, ... -848 35 0.002594 0.013349 0.054570
2 POLYGON ((-77.70427 1.08531, -77.70427 1.17576... -866 12 0.002733 0.016082 0.065742
5 POLYGON ((-76.0873 2.80464, -76.0873 2.89518, ... -848 31 0.002767 0.018849 0.077053
12 POLYGON ((-75.90764 3.07629, -75.90764 3.16686... -846 34 0.002790 0.021639 0.088460
27 POLYGON ((-73.66185 4.43572, -73.66185 4.52643... -821 49 0.002854 0.024494 0.100129
22 POLYGON ((-74.11101 4.98013, -74.11101 5.07091... -826 55 0.002870 0.027364 0.111862
19 POLYGON ((-74.11101 3.80106, -74.11101 3.8917,... -826 42 0.002879 0.030243 0.123630
28 POLYGON ((-73.66185 4.52643, -73.66185 4.61715... -821 50 0.002956 0.033199 0.135714
3 POLYGON ((-76.53646 1.80903, -76.53646 1.89951... -853 20 0.003009 0.036208 0.148014
13 POLYGON ((-75.81781 3.71044, -75.81781 3.80106... -845 41 0.003060 0.039268 0.160525
36 POLYGON ((-72.40421 6.16126, -72.40421 6.25222... -807 68 0.003114 0.042382 0.173253
16 POLYGON ((-75.27882 4.79862, -75.27882 4.88937... -839 53 0.003138 0.045520 0.186081
30 POLYGON ((-73.57202 10.73256, -73.57202 10.824... -820 118 0.003183 0.048703 0.199093
24 POLYGON ((-74.02118 4.98013, -74.02118 5.07091... -825 55 0.003393 0.052095 0.212961
39 POLYGON ((-72.31438 6.70724, -72.31438 6.79829... -806 74 0.003408 0.055503 0.226894
37 POLYGON ((-72.31438 6.52518, -72.31438 6.6162,... -806 72 0.003703 0.059206 0.242030
31 POLYGON ((-72.9432 7.61854, -72.9432 7.70977, ... -813 84 0.003843 0.063049 0.257741
7 POLYGON ((-75.99747 2.98573, -75.99747 3.07629... -847 33 0.004202 0.067252 0.274920
18 POLYGON ((-74.20084 4.25434, -74.20084 4.34503... -827 47 0.004358 0.071610 0.292735
8 POLYGON ((-75.99747 3.16686, -75.99747 3.25744... -847 35 0.004540 0.076150 0.311296
34 POLYGON ((-72.49404 6.79829, -72.49404 6.88936... -808 75 0.004600 0.080750 0.330100
35 POLYGON ((-72.49404 6.88936, -72.49404 6.98044... -808 76 0.004686 0.085436 0.349257
23 POLYGON ((-74.02118 3.8917, -74.02118 3.98235,... -825 43 0.004785 0.090222 0.368819
26 POLYGON ((-73.75168 4.52643, -73.75168 4.61715... -822 50 0.005378 0.095600 0.390806
4 POLYGON ((-76.3568 2.17099, -76.3568 2.2615, -... -851 24 0.009011 0.104611 0.427640
15 POLYGON ((-75.36865 4.70788, -75.36865 4.79862... -840 52 0.009629 0.114239 0.467001
21 POLYGON ((-74.11101 3.98235, -74.11101 4.073, ... -826 44 0.009757 0.123996 0.506885
9 POLYGON ((-75.99747 3.25744, -75.99747 3.34802... -847 36 0.010140 0.134136 0.548339
11 POLYGON ((-75.99747 3.43861, -75.99747 3.52921... -847 38 0.010395 0.144532 0.590834
40 POLYGON ((-72.22455 6.34319, -72.22455 6.43418... -805 70 0.015378 0.159910 0.653699
20 POLYGON ((-74.11101 3.8917, -74.11101 3.98235,... -826 43 0.020303 0.180213 0.736697
0 POLYGON ((-77.7941 0.90441, -77.7941 0.99486, ... -867 10 0.023114 0.203327 0.831184
29 POLYGON ((-73.66185 10.73256, -73.66185 10.824... -821 118 0.041296 0.244623 1.000000

Count AOO cells

  1. Calculate AOO by counting the number of cells with a cumulative proportion greater than 0.01 (i.e. exclude cells that in combination account for up to 1% of the total mapped extent of the ecosystem type).
if has_data:
    aoo = len(gdf[gdf["cumulative_proportion"] > 0.01])
    print(f'AOO is {aoo} cells')
AOO is 37 cells

AOO Calculation (direct call)

if has_data:
    aoo_count = ecosystem.aoo
    print(f'AOO: {aoo_count} grid cells')
AOO: 37 grid cells

Criterion B Summary

Criterion B status (spatial)
Vulnerable (VU) — Laguna Glacial (Laguna Glacial), index 59

Status reflects the spatial thresholds for EOO (B1) and AOO (B2) only. A final listing under B1/B2 additionally requires at least one of: (a) an observed or inferred continuing decline; (b) threatening processes likely to cause continuing decline within 20 years; or (c) few threat-defined locations — none of which are derived from the spatial metrics.

Sub-criterion Metric Value Category
B1 EOO 227063 km² Least Concern (LC)
B2 AOO 37 cells Vulnerable (VU)
Overall B — — Vulnerable (VU)