1. Introducción
  2. Descarga y descomprensión de dato
  3. Reclasificación del Índice de Condición Estructural
  4. Vectorización de raster clasificado
  5. Adición de campos
  6. Generación de estadísticas zonales
  7. Generación y adición de campo binario de áreas protegidas
  8. Disolución de geometrías (Dissolve)
  9. Cálculo de área

1. Introducción

El siguiente documento tiene como fin describir y explicar las rutinas de código que permiten descargar el producto de Índice de Condición Estructural (SCI por su siglas en inglés) (Hansen et al. 2019) y posterior edición para su incorporación en el flujo de trabajo de la plataforma BioTablero. En primer lugar se detallan las funciones que descargan y descomprimen el producto a trabajar, posteriormente se toman como base las rutinas realizadas por Marcerlo Villa y Jaime Burbano relacionado con la edición de la capa de Índice espacial de la Huella Humana. El código base completo puede ser encontrado en la dirección de la plataforma Github: https://github.com/PEM-Humboldt/huella-humana-indicadores/tree/python. Igualmente, todos los argumentos creados para que las funciones se ejecuten de manera correcta se pueden encontrar en: https://github.com/PEM-Humboldt/huella-humana-indicadores/blob/python/src/utils/constants.py.

2. Descarga y descomprensión de datos

En primer lugar se debe instalar las librería knitr. Knirt se requiere para la generación de informes dinámicos en R.
#install.packages("knitr")
#require("knitr")
La descarga de datos se realiza con la función download.file, esta función necesita mínimo dos argumentos para ser ejecutada. El primer argumento, url, es la dirección de descarga del producto. El segundo argumento, desfile, es la ruta (dirección y carpeta) dentro del sistema de archivos computador donde el usuario desea descargar el archivo .zip proveniente de GeoNetwork. Tanto la dirección url como en la ruta de salida desfile son argumentos tipo caracter. A continuación definimos estos parámetros, los cuales deben ser especificados por cada usuario.
#url = "https://springernature.figshare.com/ndownloader/files/15667565"
#destfile = "D:/SCI.zip"
Esta rutina se ha diseñado para Índice de Condición Estructural (SCI)(Hansen et al. 2019) que evalúa la estatura del dosel, la cobertura y el historial de perturbaciones en los trópicos húmedos reflejando su calidad.
#download.file(url = url, destfile = destfile) #Recuerde cambiar en este espacio con la ruta donde desea guardar el archivo descargado en su ordenador
Una vez descargado el archivo, se realiza su descomprensión SCI.zip. La descompresión se realiza con la función unzip de la librería utils instalada al principio de esta rutina. La función requiere de dos argumentos mínimos: zipfile, en donde se señala la ruta y el nombre en donde se encuentra el archivo .zip a descomprimir, y exdir, que va a hacer referencia a la dirección del ordenador en la cual queremos descomprimir el archivo .zip.
#unzip(zipfile = destfile, exdir = "D:/") #Recuerde verificar tanto la ruta del archivo, #como la dirección donde desea descomprimir la información.  

3. Reclasificación de la huella humana

Dentro del archivo descargado y descomprimido en el paso anterior, se encuentra el Índice de Condición Estructural y trae clasificado de 1 a 18, donde 0 es un bajo y 18 un alto valor de integridad de los bosques. De acuerdo con el flujo de trabajo definido por el equipo de BioTablero, el SCI se reclasifica en dos categorías de la siguiente manera: 1-13 (Baja) y 14-18(Moderada).En primer lugar, la rutina lee el raster desde su ruta de ubicación con la función open de la librería rasterio de esta manera: rasterio.open(raster_sci), donde raster_sci corresponde al raster del índice ya descargado en formato .tif. Posteriormente, la reclasificación se hace con la función reclassify que tiene las siguientes características:

- Objetivo:Reclasificar el raster del Índice de Condición Estructural.

- Operación: La función toma los valores antiguos del raster y los reemplaza por los rangos de valores nuevos que el usuario requiera.

- Argumentos: Requiere de arr, un arreglo tipo numpy (matriz) en el que se almacenan los valores de cada uno de los archivos raster del SCI, y el argumento value_map, en el que se lista en un tipo tupla el límite inferior y superior de cada rango a reclasificar, y el nuevo valor que toma cada rango.

- Resultado: Genera un nuevo raster clasificado con los nuevos valores de categoría.

- Ejemplo: En el siguiente ejemplo se muestra el argumento SCI que indica el raster de entrada con los valores antiguos, y en el argumento valupe_map que contiene los rangos y nuevos valores del raster:

#    >>>SCI = np.array([[12, 3, 8, 10], [15, 13, 18, 1], 
#                      [1, 3,  4, 16], [0, 12, 3, 2]])
#    >>> SCI
#    array([[12, 3, 8, 10],
#           [15, 13, 18, 1],
#           [1, 3,  4, 16],
#           [0, 12, 3, 2]])
#    >>> value_map = [((0, 13), 0), ((14, 18), 1)]
#    >>> SCI_cat=reclassify(arr, value_map)
#    >>> SCI_cat
#    array([[0, 0, 0, 0],
#           [1, 0, 1, 0],
#           [0, 0, 0, 1],
#           [0, 0, 0, 0]])

4. Vectorización de raster clasificado

Una vez se encuentra reclasificado el SCI, se vectoriza en dos pasos. En primer lugar se convierte en un objeto espacial tipo shapes con la función shapes de la librería rasterio módulo features (rasterio.features.shapes). Posteriormente el reusltado se convierte en un objeto geodataframe con la función shapes_to_geodataframe que tiene las siguientes características:

- Objetivo: Convertir el raster de SCI reclasificado y convertilo a un geodataframe.

- Operación: Toma el SCI reclasificado en el paso anterior y convierte todas las características (o features) en un geoDataFrame.

- Argumentos: shapes que es de tipo shapefile y es el producto de la función rasterio.features.shapes, crs que será el sistema de referencia de la nueva capa a transformar en tipo texto, y field_name que es el nombre que llevará el campo dentro del objeto geodataframe y donde se almacenará el valor reclasificado del SCI en tipo texto.

- Resultado: Genera un nuevo objeto tipo geoDataFrame.

- Ejemplo: En el siguiente ejemplo se muestra en primer lugar la transformación del raster clasificado con la función rasterio.features.shapes y su posterior transformación a un geodataframe:

#>>> SCI_cat
#    array([[0, 0, 0, 0],
#           [1, 0, 1, 0],
#           [0, 0, 0, 1],
#           [0, 0, 0, 0]]), dtype=uint8)

#    >>> SCI_cat_shapes = rasterio.features.shapes(SCI_cat)
#    >>> SCI_cat_shapes
#    <generator object shapes at 0x7f9ace463820>
#
#    >>> SCI_cat_shape=shapes_to_geodataframe(SCI_cat_shapes, src.crs.to_string())
#        value_field = features.columns[0]
#       value                                           geometry
#    0    1.0  POLYGON ((0.00000 0.00000, 0.00000 1.00000, 1....
#    1    1.0  POLYGON ((4.00000 0.00000, 4.00000 2.00000, 5....
#    2    0.0  POLYGON ((0.00000 1.00000, 0.00000 2.00000, 1....
#    3    1.0  POLYGON ((0.00000 2.00000, 0.00000 3.00000, 1....
#    4    1.0  POLYGON ((3.00000 2.00000, 3.00000 5.00000, 4....
#    5    0.0  POLYGON ((4.00000 2.00000, 4.00000 5.00000, 5....
#    6    0.0  POLYGON ((3.00000 0.00000, 3.00000 1.00000, 2....
#    7    1.0  POLYGON ((1.00000 4.00000, 1.00000 5.00000, 2....

Una vez se haya creado el geodataframe del producto reclasificado del Índice de Condición Estructural, se intersecta con el producto de Huella Espacial Humana que se extrae de la rutina de Github de este mismo producto (la rutina del índice de huella se entrega junto con esta rutina con el nombre Rutina_IHEH_github.Rmd). El producto del Índice de Huella Humana además trae dentro de sus campos las geocercas y tipos de áreas protegidas por las cuales el usuario filtra la información dentro del Biotablero, de esta manera y con esta intersección, el Índice de Condición Estructural queda con estos campos agregados sin necesidad de ser intersectada con las geocercas que establece el instituto. La función que realiza esta intersección es overlay de la librería geopandas y tiene las siguientes características:

- Objetivo: Hacer una intersección entre dos capas tipo geodataframe.

- Operación: Toma el geoDataFrame generado en el anterior paso y la capa de la Huella Humana que es igualmente un geoDataFrame y las opera en una intersección.

- Argumentos: Los dos primeros argumentos serán las capas a superponer de tipo shape, en este caso SCI_final que es la capa final y procesada de Huella Humana y SCI_cat_shape. El tercer argumento es how, la forma en que las capas se quieren superponer (intersection, union, symetrical difference, difference) en tipo texto.

- Resultado: Genera un nuevo objeto tipo geodataframe.

- Ejemplo:

#     SCI_cat_inters = intersection = geopandas.overlay(SCI_cat_shape, IHEH_final, how="intersection") 
#intersection
#   SCI_cat_shape  IHEH_final                                           geometry
#0    1    1  POLYGON ((1.000000000 2.000000000, 2.000000000...
#1    2    1  POLYGON ((2.000000000 2.000000000, 2.000000000...
#2    2    2  POLYGON ((3.000000000 4.000000000, 4.000000000...

5. Adición de campos

Al objeto geodataframe producto de la intersección entre las capas del IHEH y el SCI categorizado en el anterior paso, se le agregan tres campos de acuerdo con el flujo de trabajo definido por el equipo de BioTablero: categoría (sci_cat), año (sci_year) y promedio (sci_avg). Para lo anterior los nombres y abreviaciones se introducen en un vector que contiene los nombres de los campos de la siguiente manera:

# Para el caso de agregar el campo de categoría según la reclasificación:
#         categories = intersection[value_field].map(category_map) #Se extraen los valores de la categoría de la capa intersectada
#         SCI_cat_inters[sci_field_names.get("category")] = categories

#En donde category_map= {0: "baja", 1: "moderada"}

#         SCI_cat_inters[sci_field_names.get("category")] = categories


# Para el caso del campo del año:
#         year = re.findall(r"(?:19|20)\d{2}", raster_sci)[0] #Se extre el año del nombre de la capa raster original del SCI
#         intersection[sci_field_names.get("period")] = year

#En donde sci_field_names = {
#    "area": "area_ha",
#    "average": "sci_avg",
#    "category": "sci_cat",
#    "protection": "binary_protected",
#    "year": "sci_year"

6. Generación de estadísticas zonales

El campo del promedio del valor del índice para cada año por cada polígono dentro del geodataframe SCI_cat_inters y cada geocerca intersectada, se agrega con la función zonal_stats que tiene las siguientes características:

- Objetivo: Genera el cáculo de estadísticas zonales.

- Operación: Toma el geoDataFrame generado en el anterior paso de la intersección y calcula el valor promedio del SCI para cada geocerca.

- Argumentos: Hace uso de una capa geodataframe producto de la intersección anterior (SCI_cat_inters) y el archivo raster original del SCI.

- Resultado: Genera el cálculo del promedio del SCI a cada una de las geocercas.

- Ejemplo:

#means = [item.get("mean") for item in zonal_stats(intersection, raster_sci)]
#        intersection[sci_field_names.get("average")] = means

7.Generación y adición de campo binario de áreas protegidas

En el paso 4.en el que se intersectan la Huella Humana (IHEH_final) y el SCI categorizado y vectorizado (SCI_cat_inters), se generan 15 campos (“anu”, dcs, dnmi, drmi, pnn, pnr, rfpn, rfpr, rn, rnsc, sfa, sff, sfl, vp, ar) correspondientes a cada tipo de áreas protegidas existentes en el Registro Único Nacional de Áreas Protegidas (RUNAP). Para simplificar estos campos es necesario genear uno único campo que resume la presencia cada tipo de área protegida de acuerdo con el flujo de trabajo definido por el equipo de BioTablero, lo cual se hace uso de la función compute_protection_sequence. La función compute_protection_sequence tiene las siguientes características:

- Objetivo: Genera campo binario que da cuenta de la ausencia o presencia de área protegida.

- Operación: Asigna un valor de 0 (ausencia) o 1 (presencia) cuando el polígono correspondiente incluye o no área protegida. De acuerdo con el flujo definido por el equipo de BioTablero, cada uno de los campos de tipo de área protegida almacena el identificador único (ID) asignado para cada área protegida desde la capa original del RUNAP. La función convierte este campo a un valor de presencia (1) cuando el ID del área protegida es mayor a cero. Por defecto, la capa de geocercas codifica la ausencia de un área protegida en cada polígono con un valor de cero (0).

- Argumentos:df que se refiere a un dataframe (tabla) que almacena la secuencia de valores tomados de los 15 campos de áreas protegidas, y fields que es un vector que contiene el nombre de los campos a concatenar.

- Resultado: Genera un campo de tipo string, y corresponde a una cadena de 15 caracteres de acuerdo con la presencia o ausencia de cada uno de los tipos de áreas protegidas.

- Ejemplo:
#>>> values = [[0, 0, 0, 27100002], [2060001, 0, 0, 0], [0, 0, 0,0]]
#   >>> fields = ["anu","dcs","dnmi","drmi"]
#    >>> df = pd.DataFrame(values, columns=fields)
#    >>> df
#           anu  dcs  dnmi      drmi
#    0        0    0     0  27100002
#    1  2060001    0     0         0
#    2        0    0     0         0
#    >>> compute_protection_sequence(df, fields=["anu","dnmi","drmi"])
#   0    001
#    1    100
#    2    000
#    dtype: object

8.Disolución de geometrías (Dissolve)

Al producto anterior se le aplica un dossolve a cada uno de los productos obtenidos con la función SCI_final.dissolve que tiene las siguientes características:

- Objetivo: Disolver geometrías.

- Operación: Toma el geoDataFrame generado en el anterior con los campos agregados y estadístiacas generadas.

- Argumentos: Hace uso del argumento by que se refiere a los campos por los cuales se quiere hacer el dissolve, en este caso será el año, la categoría del índice y el promedio del índice, en este caso un tipo lista. El segundo argumento es aggfunc que se refiere a la característica por la cual se requiere aregar el dissolve en este caso el promedio “mean” en tipo texto. En este caso se utiliza el promedio como función de agregación porque se quiere obtener el valor promedio del SCI para cada geocerca intersectada.

- Resultado: Genera un nuevo geodataframe disuelto por los argumentos especificados.

- Ejemplo:

# Create empty GeoDataFrame to store the result.
#    SCI_final = geopandas.GeoDataFrame()
#  SCI_final = SCI_final.append(intersection, ignore_index=True)
#dissolve_fields = list(geofences.columns) + sci_dissolve_fields
#    dissolve_fields.remove("geometry")
#    if None in dissolve_fields:
#       dissolve_fields.remove(None)
#    SCI_final = SCI_final.dissolve(by=dissolve_fields, aggfunc="mean")
#    SCI_final = SCI_final.reset_index()

#Donde sci_dissolve_fields = [
# SCI_FIELD_NAMES.get("category"),
#    SCI_FIELD_NAMES.get("year"),
#    SCI_FIELD_NAMES.get("protection")
#]

9.Cálculo de área

Finalmente, la rutina calcula el área en hecareas para cada uno de los polígonos que de la capa SCI_final utilizando la función SCI_final.geometry.get para obtener el área que se guarda implcitamente en algún lado del objeto geoDataFrame SCI_final y con la función SCI_final.geometry.arealo agrega al campo area anteriormente agregado; finalmente, el área es multiplicado por un factor definido como 0.0001.

# Calcula el área solo si la capa está proyectada
#    if SCI_final.crs.is_projected:
#        SCI_final[hf_field_names.get("area")] = SCI_final.geometry.area * area_factor
#    else:
#        warnings.warn("Area was not computed because input data is not projected.")

# Donde
#area_factor = 0.0001

Referencias: Hansen, Andrew, Kevin Barnett, Patrick Jantz, Linda Phillips, Scott J. Goetz, Matt Hansen, Oscar Venter, James E. M. Watson, Patrick Burns, Scott Atkinson, Susana Rodríguez-Buritica, Jamison Ervin, Anne Virnig, Christina Supples, and Rafael De Camargo. 2019. “Global Humid Tropics Forest Structural Condition and Forest Structural Integrity Maps.” Scientific Data 6(1):232.