Source code for geefcc.stat.stat_fcc_loss
"""Compute statistics per class for a TMF or GFC loss forest cover change raster."""
from pathlib import Path
import dask.array as da_
import pandas as pd
import rioxarray
from osgeo import gdal
[docs]
def stat_fcc_loss(
input_file,
years,
epsg,
output_file="fcc_statistics.csv"):
"""Compute statistics per class for a TMF or GFC loss forest cover change raster.
Reprojects the input raster to a metric CRS, then uses
:func:`dask.array.bincount` to count pixels per class and converts
counts to hectares. The input raster must be a single-band raster
produced by :func:`sum_raster_bands` applied to the output of
:func:`get_fcc_loss`.
Pixel values encode the following classes:
- 0 = non-forest at ``years[0]``
- 1 = deforestation during period 1 (``years[0]``--``years[1]``)
- 2 = deforestation during period 2 (``years[1]``--``years[2]``)
- ... (one class per intermediate period)
- n = forest at ``years[-1]``
Parameters
----------
input_file : str or Path
Path to the single-band GeoTIFF raster file (in ``EPSG:4326``)
produced by :func:`sum_raster_bands`.
years : list of int
List of years, e.g. ``[2000, 2010, 2020]``.
epsg : int
EPSG code of a metric CRS (e.g. ``32740`` for UTM zone 40S).
output_file : str or Path, optional
Path to the output CSV file. Default is ``"fcc_statistics.csv"``.
Returns
-------
pandas.DataFrame
DataFrame with columns ``category``, ``label``, ``count``,
``area_ha``. Also written to ``output_file``.
Examples
--------
>>> import geefcc
>>> res_df = geefcc.stat_fcc_loss(
... input_file="out_tmf/fcc_tmf.tif",
... years=[2000, 2010, 2020],
... epsg=32740,
... output_file="fcc_statistics.csv",
... )
"""
input_file = Path(input_file)
output_file = Path(output_file)
# Build labels from years
n_periods = len(years) - 1
n_classes = len(years) + 1
labels = {0: f"non-forest in {years[0]}"}
for i in range(n_periods):
labels[i + 1] = f"deforestation {years[i]}-{years[i + 1]}"
labels[n_periods + 1] = f"forest in {years[-1]}"
# Reproject to metric CRS at 30 m resolution
proj_file = input_file.with_stem(input_file.stem + f"_epsg{epsg}")
if not proj_file.is_file():
ds = gdal.Warp(
str(proj_file), str(input_file),
xRes=30, yRes=30,
dstSRS=f"EPSG:{epsg}",
resampleAlg="near",
targetAlignedPixels=True,
creationOptions=["COMPRESS=DEFLATE"],
)
ds = None
# Count pixels per class with dask bincount
fcc = rioxarray.open_rasterio(str(proj_file),
chunks={"x": 512, "y": 512})
x_res, y_res = fcc.rio.resolution()
pixel_area_m2 = abs(x_res) * abs(y_res)
fcc_flat = fcc.data.ravel()
counts = da_.bincount(fcc_flat, minlength=n_classes).compute()
counts = counts[:n_classes]
# Build DataFrame
categories = list(range(n_classes))
res_df = pd.DataFrame({
"category": categories,
"label": [labels[i] for i in categories],
"count": counts,
"area_ha": (
(counts * pixel_area_m2 / 10_000).round().astype(int)
),
})
res_df.to_csv(str(output_file), index=False)
return res_df
# End