Criterion B Details

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

Import Python modules.

import os
import yaml
from pathlib import Path
from lonboard import Map
from rle_python_gee.ecosystems import Ecosystems
from rle_python_gee.eoo import make_eoo
from rle_python_gee.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)

Load & Filter Ecosystem Data

Load data for all the ecosystems.

source = config['ecosystem_source']
ecosystems = Ecosystems.from_file(
    source['data'],
    ecosystem_column=source['ecosystem_code_column'],
    ecosystem_name_column=source['ecosystem_name_column'],
    functional_group_column=source['functional_group_column']
)

Filter by the T1.2.28 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() = 26

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_python_gee.viz import smart_map

if has_data:
    eoo_hull = make_eoo(ecosystem).compute()
    display(smart_map([eoo_hull, ecosystem]))
/home/runner/work/rle-tyler-20/rle-tyler-20/.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-20/rle-tyler-20/.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 8580.5 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_python_gee.aoo import make_aoo_grid_cached

if has_data:
    cache_path = 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_python_gee.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-20/rle-tyler-20/.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 T1_2_28
0 POLYGON ((-74.29067 4.43572, -74.29067 4.52643... -828 49 0.003897
1 POLYGON ((-74.29067 4.52643, -74.29067 4.61715... -828 50 0.034975
2 POLYGON ((-74.29067 4.61715, -74.29067 4.70788... -828 51 0.164345
3 POLYGON ((-74.29067 4.70788, -74.29067 4.79862... -828 52 0.051827
4 POLYGON ((-74.20084 4.43572, -74.20084 4.52643... -827 49 0.149319
5 POLYGON ((-74.20084 4.52643, -74.20084 4.61715... -827 50 0.359999
6 POLYGON ((-74.20084 4.61715, -74.20084 4.70788... -827 51 0.298449
7 POLYGON ((-74.11101 4.43572, -74.11101 4.52643... -826 49 0.166300
8 POLYGON ((-74.11101 4.52643, -74.11101 4.61715... -826 50 0.274655
9 POLYGON ((-73.84152 4.79862, -73.84152 4.88937... -823 53 0.005312
10 POLYGON ((-73.84152 4.88937, -73.84152 4.98013... -823 54 0.121441
11 POLYGON ((-73.84152 4.98013, -73.84152 5.07091... -823 55 0.002572
12 POLYGON ((-73.84152 5.07091, -73.84152 5.1617,... -823 56 0.376609
13 POLYGON ((-73.84152 5.1617, -73.84152 5.2525, ... -823 57 0.017755
14 POLYGON ((-73.75168 4.88937, -73.75168 4.98013... -822 54 0.050689
15 POLYGON ((-73.75168 5.07091, -73.75168 5.1617,... -822 56 0.228110
16 POLYGON ((-73.75168 5.1617, -73.75168 5.2525, ... -822 57 0.028374
17 POLYGON ((-73.75168 5.2525, -73.75168 5.34332,... -822 58 0.028796
18 POLYGON ((-73.75168 5.34332, -73.75168 5.43414... -822 59 0.007267
19 POLYGON ((-73.66185 5.2525, -73.66185 5.34332,... -821 58 0.014719
20 POLYGON ((-73.66185 5.34332, -73.66185 5.43414... -821 59 0.083539
21 POLYGON ((-73.66185 5.52498, -73.66185 5.61584... -821 61 0.045647
22 POLYGON ((-73.57202 5.43414, -73.57202 5.52498... -820 60 0.053498
23 POLYGON ((-73.57202 5.52498, -73.57202 5.61584... -820 61 0.808054
24 POLYGON ((-73.57202 5.61584, -73.57202 5.70671... -820 62 0.203478
25 POLYGON ((-73.57202 5.70671, -73.57202 5.79759... -820 63 0.023380
26 POLYGON ((-73.48219 5.43414, -73.48219 5.52498... -819 60 0.273289
27 POLYGON ((-73.48219 5.52498, -73.48219 5.61584... -819 61 0.711038
28 POLYGON ((-73.48219 5.61584, -73.48219 5.70671... -819 62 0.414585
29 POLYGON ((-73.48219 5.70671, -73.48219 5.79759... -819 63 0.021208
30 POLYGON ((-73.39236 5.43414, -73.39236 5.52498... -818 60 0.119411
31 POLYGON ((-73.39236 5.52498, -73.39236 5.61584... -818 61 0.365912
32 POLYGON ((-73.39236 5.61584, -73.39236 5.70671... -818 62 0.050400
33 POLYGON ((-73.03303 5.52498, -73.03303 5.61584... -814 61 0.002981
34 POLYGON ((-72.9432 5.52498, -72.9432 5.61584, ... -813 61 0.278828
35 POLYGON ((-72.9432 5.61584, -72.9432 5.70671, ... -813 62 0.124436
36 POLYGON ((-72.85337 5.52498, -72.85337 5.61584... -812 61 0.002133

The column T1_2_28 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(5.967229079404574)

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 T1_2_28 cumulative_area
36 POLYGON ((-72.85337 5.52498, -72.85337 5.61584... -812 61 0.002133 0.002133
11 POLYGON ((-73.84152 4.98013, -73.84152 5.07091... -823 55 0.002572 0.004705
33 POLYGON ((-73.03303 5.52498, -73.03303 5.61584... -814 61 0.002981 0.007686
0 POLYGON ((-74.29067 4.43572, -74.29067 4.52643... -828 49 0.003897 0.011583
9 POLYGON ((-73.84152 4.79862, -73.84152 4.88937... -823 53 0.005312 0.016896
18 POLYGON ((-73.75168 5.34332, -73.75168 5.43414... -822 59 0.007267 0.024163
19 POLYGON ((-73.66185 5.2525, -73.66185 5.34332,... -821 58 0.014719 0.038882
13 POLYGON ((-73.84152 5.1617, -73.84152 5.2525, ... -823 57 0.017755 0.056637
29 POLYGON ((-73.48219 5.70671, -73.48219 5.79759... -819 63 0.021208 0.077845
25 POLYGON ((-73.57202 5.70671, -73.57202 5.79759... -820 63 0.023380 0.101225
16 POLYGON ((-73.75168 5.1617, -73.75168 5.2525, ... -822 57 0.028374 0.129599
17 POLYGON ((-73.75168 5.2525, -73.75168 5.34332,... -822 58 0.028796 0.158394
1 POLYGON ((-74.29067 4.52643, -74.29067 4.61715... -828 50 0.034975 0.193369
21 POLYGON ((-73.66185 5.52498, -73.66185 5.61584... -821 61 0.045647 0.239016
32 POLYGON ((-73.39236 5.61584, -73.39236 5.70671... -818 62 0.050400 0.289416
14 POLYGON ((-73.75168 4.88937, -73.75168 4.98013... -822 54 0.050689 0.340105
3 POLYGON ((-74.29067 4.70788, -74.29067 4.79862... -828 52 0.051827 0.391933
22 POLYGON ((-73.57202 5.43414, -73.57202 5.52498... -820 60 0.053498 0.445431
20 POLYGON ((-73.66185 5.34332, -73.66185 5.43414... -821 59 0.083539 0.528970
30 POLYGON ((-73.39236 5.43414, -73.39236 5.52498... -818 60 0.119411 0.648381
10 POLYGON ((-73.84152 4.88937, -73.84152 4.98013... -823 54 0.121441 0.769821
35 POLYGON ((-72.9432 5.61584, -72.9432 5.70671, ... -813 62 0.124436 0.894258
4 POLYGON ((-74.20084 4.43572, -74.20084 4.52643... -827 49 0.149319 1.043577
2 POLYGON ((-74.29067 4.61715, -74.29067 4.70788... -828 51 0.164345 1.207922
7 POLYGON ((-74.11101 4.43572, -74.11101 4.52643... -826 49 0.166300 1.374222
24 POLYGON ((-73.57202 5.61584, -73.57202 5.70671... -820 62 0.203478 1.577700
15 POLYGON ((-73.75168 5.07091, -73.75168 5.1617,... -822 56 0.228110 1.805810
26 POLYGON ((-73.48219 5.43414, -73.48219 5.52498... -819 60 0.273289 2.079099
8 POLYGON ((-74.11101 4.52643, -74.11101 4.61715... -826 50 0.274655 2.353753
34 POLYGON ((-72.9432 5.52498, -72.9432 5.61584, ... -813 61 0.278828 2.632582
6 POLYGON ((-74.20084 4.61715, -74.20084 4.70788... -827 51 0.298449 2.931031
5 POLYGON ((-74.20084 4.52643, -74.20084 4.61715... -827 50 0.359999 3.291030
31 POLYGON ((-73.39236 5.52498, -73.39236 5.61584... -818 61 0.365912 3.656942
12 POLYGON ((-73.84152 5.07091, -73.84152 5.1617,... -823 56 0.376609 4.033551
28 POLYGON ((-73.48219 5.61584, -73.48219 5.70671... -819 62 0.414585 4.448137
27 POLYGON ((-73.48219 5.52498, -73.48219 5.61584... -819 61 0.711038 5.159175
23 POLYGON ((-73.57202 5.52498, -73.57202 5.61584... -820 61 0.808054 5.967229

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 T1_2_28 cumulative_area cumulative_proportion
36 POLYGON ((-72.85337 5.52498, -72.85337 5.61584... -812 61 0.002133 0.002133 0.000357
11 POLYGON ((-73.84152 4.98013, -73.84152 5.07091... -823 55 0.002572 0.004705 0.000789
33 POLYGON ((-73.03303 5.52498, -73.03303 5.61584... -814 61 0.002981 0.007686 0.001288
0 POLYGON ((-74.29067 4.43572, -74.29067 4.52643... -828 49 0.003897 0.011583 0.001941
9 POLYGON ((-73.84152 4.79862, -73.84152 4.88937... -823 53 0.005312 0.016896 0.002831
18 POLYGON ((-73.75168 5.34332, -73.75168 5.43414... -822 59 0.007267 0.024163 0.004049
19 POLYGON ((-73.66185 5.2525, -73.66185 5.34332,... -821 58 0.014719 0.038882 0.006516
13 POLYGON ((-73.84152 5.1617, -73.84152 5.2525, ... -823 57 0.017755 0.056637 0.009491
29 POLYGON ((-73.48219 5.70671, -73.48219 5.79759... -819 63 0.021208 0.077845 0.013045
25 POLYGON ((-73.57202 5.70671, -73.57202 5.79759... -820 63 0.023380 0.101225 0.016963
16 POLYGON ((-73.75168 5.1617, -73.75168 5.2525, ... -822 57 0.028374 0.129599 0.021718
17 POLYGON ((-73.75168 5.2525, -73.75168 5.34332,... -822 58 0.028796 0.158394 0.026544
1 POLYGON ((-74.29067 4.52643, -74.29067 4.61715... -828 50 0.034975 0.193369 0.032405
21 POLYGON ((-73.66185 5.52498, -73.66185 5.61584... -821 61 0.045647 0.239016 0.040055
32 POLYGON ((-73.39236 5.61584, -73.39236 5.70671... -818 62 0.050400 0.289416 0.048501
14 POLYGON ((-73.75168 4.88937, -73.75168 4.98013... -822 54 0.050689 0.340105 0.056996
3 POLYGON ((-74.29067 4.70788, -74.29067 4.79862... -828 52 0.051827 0.391933 0.065681
22 POLYGON ((-73.57202 5.43414, -73.57202 5.52498... -820 60 0.053498 0.445431 0.074646
20 POLYGON ((-73.66185 5.34332, -73.66185 5.43414... -821 59 0.083539 0.528970 0.088646
30 POLYGON ((-73.39236 5.43414, -73.39236 5.52498... -818 60 0.119411 0.648381 0.108657
10 POLYGON ((-73.84152 4.88937, -73.84152 4.98013... -823 54 0.121441 0.769821 0.129008
35 POLYGON ((-72.9432 5.61584, -72.9432 5.70671, ... -813 62 0.124436 0.894258 0.149861
4 POLYGON ((-74.20084 4.43572, -74.20084 4.52643... -827 49 0.149319 1.043577 0.174885
2 POLYGON ((-74.29067 4.61715, -74.29067 4.70788... -828 51 0.164345 1.207922 0.202426
7 POLYGON ((-74.11101 4.43572, -74.11101 4.52643... -826 49 0.166300 1.374222 0.230295
24 POLYGON ((-73.57202 5.61584, -73.57202 5.70671... -820 62 0.203478 1.577700 0.264394
15 POLYGON ((-73.75168 5.07091, -73.75168 5.1617,... -822 56 0.228110 1.805810 0.302621
26 POLYGON ((-73.48219 5.43414, -73.48219 5.52498... -819 60 0.273289 2.079099 0.348419
8 POLYGON ((-74.11101 4.52643, -74.11101 4.61715... -826 50 0.274655 2.353753 0.394447
34 POLYGON ((-72.9432 5.52498, -72.9432 5.61584, ... -813 61 0.278828 2.632582 0.441173
6 POLYGON ((-74.20084 4.61715, -74.20084 4.70788... -827 51 0.298449 2.931031 0.491188
5 POLYGON ((-74.20084 4.52643, -74.20084 4.61715... -827 50 0.359999 3.291030 0.551517
31 POLYGON ((-73.39236 5.52498, -73.39236 5.61584... -818 61 0.365912 3.656942 0.612838
12 POLYGON ((-73.84152 5.07091, -73.84152 5.1617,... -823 56 0.376609 4.033551 0.675950
28 POLYGON ((-73.48219 5.61584, -73.48219 5.70671... -819 62 0.414585 4.448137 0.745428
27 POLYGON ((-73.48219 5.52498, -73.48219 5.61584... -819 61 0.711038 5.159175 0.864585
23 POLYGON ((-73.57202 5.52498, -73.57202 5.61584... -820 61 0.808054 5.967229 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 29 cells

AOO Calculation (direct call)

if has_data:
    aoo_count = ecosystem.aoo
    print(f'AOO: {aoo_count} grid cells')
AOO: 29 grid cells
if has_data:
    display(smart_map([aoo_grid_filtered, ecosystem]))
/home/runner/work/rle-tyler-20/rle-tyler-20/.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(

Criterion B Summary

Ecosystem Code Ecosystem Name EOO AOO
T1.2.28 Magdalena Valley montane forests 8580 km² 29 cells