Skip to content
monitoreo-ugr edited this page Nov 5, 2026 · 8 revisions

English version

Python

El paquete monitoreo es la base de todas las herramientas: el plugin de QGIS, el servidor MCP y el paquete de R llaman a sus funciones. Todas están en monitoreo.services, devuelven diccionarios y listas de Python (fáciles de pasar a pandas) y pueden escribir CSV, PNG y GeoTIFF.

Instalación y autenticación: Primeros pasos. Las opciones comunes (regiones, periodos, métricas, filtros) se explican en Conceptos.

Los ejemplos de esta página son reales y usan el Parque Nacional de Sierra Nevada (ID de WDPA 389011).

1. Conectar con Earth Engine

from monitoreo import services as mo

mo.init()                     # sin la variable EE_PROJECT: la cuenta compartida de MonitorEO
# mo.init('mi-proyecto-ee')   # o tu cuenta y tu proyecto (o mo.init() con EE_PROJECT)

u = mo.eecu_usage()  # tus EECU de este mes (con tu proyecto: mo.eecu_usage('Contributor') si es su nivel)
print(u['bar'], u['text'])
# [##----------------------------] 8% research EECUs: 8.0% used (2.00 of your 25 EECU-hours of this month with ...

Con la cuenta compartida son tus research EECUs: 25 EECU-h al mes, solo para investigación. Si te queda menos del 10 %, init() da un aviso (EECUWarning) y u['warning'] dice qué hacer; cuando se acaban, init() no conecta con la cuenta compartida hasta el mes siguiente (Conceptos).

2. Elegir la variable

mo.list_variables(sensor='Sentinel-2', text='NDVI')
[{'ref': 27, 'variable': 'NDVI', 'sensor': 'Sentinel-2', 'collection': 'COPERNICUS/S2_SR_HARMONIZED',
  'name': 'Normalized Difference Vegetation Index', 'group': 'Carbon (Primary Production)',
  'resolution_m': 10, 'dates': '28/03/2017-now', 'unit': '[unitless]'}]

mo.list_variables() da las 34 del catálogo (con el NBR). mo.describe_variable('NDSI', 'Sentinel-2') da su fórmula (N(B3, B11)), escala, filtros por píxel que se le aplican y qué filtros quitarían su señal ('measures': ['snow']: con el NDSI hay que dejar la nieve, quality={'snow': False}).

3. Elegir la región

mo.find_protected_areas('Sierra Nevada')
[{'SITE_ID': 555722086, 'NAME': 'Sierra Nevada', 'DESIG': 'Special Areas of Conservation (Habitats Directive)', ...},
 ...
 {'SITE_ID': 389011, 'NAME': 'Sierra Nevada', 'DESIG': 'Parque Nacional', 'IUCN_CAT': 'II', 'ISO3': 'ESP',
  'REP_AREA': 858.83, 'STATUS_YR': 1999, ...},
 {'SITE_ID': 349467, 'NAME': 'Sierra Nevada', 'DESIG': 'Parque Natural', ...}, ...]

La región del parque nacional es {'wdpa_id': 389011}. Otras formas de dar la región (ecorregiones, rectángulos, puntos, GeoJSON, ficheros): Conceptos.

sn = {'wdpa_id': 389011}
zonas = {'file': 'zonas.gpkg', 'name_field': 'nombre'}          # una serie por polígono
punto = {'point': [-3.42, 37.13], 'buffer': 1500}               # círculo de 1,5 km de radio

4. Serie temporal

La media mensual del NDVI de Sentinel-2 en el parque, de 2022 a 2023, con CSV y gráfica:

ts = mo.time_series(sn, 'NDVI', 'Sentinel-2', '2022-01-01', '2023-12-31', step='monthly',
                    metric='mean', stat='mean', csv='ndvi_sierra_nevada.csv', chart='ndvi_sierra_nevada.png')
ts['rows'][:2]
[{'region': 'Sierra Nevada', 'date': '2022-01-01', 'value': 0.3537, 'n_pixels': 346520, 'n_images': 30},
 {'region': 'Sierra Nevada', 'date': '2022-02-01', 'value': 0.4036, 'n_pixels': 298880, 'n_images': 29}]

ts también trae title, unit, scale (el píxel usado: 50 m, porque el parque es grande; ver Conceptos), csv y chart:

Serie mensual del NDVI de Sentinel-2 en el Parque Nacional de Sierra Nevada, 2022-2023

Con pandas:

import pandas as pd
df = pd.DataFrame(ts['rows'])
df['date'] = pd.to_datetime(df['date'])
df.pivot(index='date', columns='region', values='value')      # una columna por región

Variantes:

mo.time_series(sn, 'LST', 'Modis', '2010-01-01', '2023-12-31', step='annual', metric='max')   # máximo anual
mo.time_series(sn, 'NDVI', 'Landsat', '1985-01-01', '2023-12-31', step='annual',
               season=['06-01', '09-30'])                                           # solo los veranos
mo.time_series(sn, 'NDSI', 'Modis', '2022-10-01', '2023-06-30', step='16-day',
               quality={'snow': False})                                             # la nieve no se quita
mo.time_series(punto, 'NDVI', 'Sentinel-2', '2023-01-01', '2023-12-31', step='daily')  # cada imagen

5. Mapa

La mediana del NDVI del verano de 2023, como teselas de Earth Engine y una vista previa en PNG:

capa = mo.map_layer(sn, 'NDVI', 'Sentinel-2', '2023-06-01', '2023-08-31', metric='median',
                    thumbnail='ndvi_verano_2023.png')
capa['url']       # 'https://earthengine.googleapis.com/v1/projects/.../maps/.../tiles/{z}/{x}/{y}'
capa['min'], capa['max'], capa['bounds']   # rampa (percentiles 5 y 95) y extensión [oeste, sur, este, norte]

Mediana del NDVI de Sentinel-2 del verano de 2023 en el Parque Nacional de Sierra Nevada

La rampa de colores es la de MonitorEO para el NDVI: del azul (agua, roca o nieve, NDVI cercano a 0) al verde oscuro (vegetación densa).

La URL vale unas horas en cualquier visor de teselas XYZ. Con folium:

import folium
m = folium.Map(location=[37.07, -3.1], zoom_start=10)
folium.TileLayer(capa['url'], attr='Google Earth Engine', name=capa['name']).add_to(m)
m

Para trabajar con la imagen de Earth Engine (geemap, más cálculos con ee), composite_image la devuelve con la región:

imagen, regiones = mo.composite_image(sn, 'NDVI', 'Sentinel-2', '2023-06-01', '2023-08-31', metric='median')
# imagen es un ee.Image con la banda 'value'

Agregaciones entre años (ver Conceptos):

mo.map_layer(sn, 'LST', 'Modis', '2014-01-01', '2023-12-31', metric='max', temporal='annual', interannual='median')

6. Estadísticas de un mapa por polígono

mo.region_stats(sn, 'LST', 'Modis', '2023-06-01', '2023-08-31', metric='mean')
{'rows': [{'region': 'Sierra Nevada', 'mean': 32.21, 'std': 1.98, 'min': 27.27, 'p5': 29.41, 'p50': 32.09,
           'p95': 34.90, 'max': 40.86, 'n_pixels': 1051}],
 'scale': 1000.0, 'unit': '[ºC]', 'title': 'LST Modis | 2023-06-01 / 2023-08-31 (mean)'}

7. Descargar un mapa en GeoTIFF

El percentil 95 del NDVI de 2023 en un círculo de 1,5 km, a 10 m:

r = mo.download_image(punto, 'NDVI', 'Sentinel-2', '2023-01-01', '2023-12-31', metric='p95', path='ndvi_p95.tif')
{'file': 'ndvi_p95.tif', 'grid': {'crs': 'EPSG:32630', 'resolution': 10.0, 'width': 300, 'height': 301, ...},
 'resolution': 10.0, 'native_resolution': 10.0, 'images': 74, 'unit': '[unitless]', 'min': 0.31, 'max': 1.0, ...}

El GeoTIFF es float32 con NoData −9999, en UTM (crs='auto') u otro sistema (crs='EPSG:25830'), con el píxel de los datos o el que se pida (resolution=20). Earth Engine lo calcula por teselas en paralelo: para regiones de unos millones de píxeles. Con max_pixels=4_000_000, una región mayor se descarga con píxeles 2, 3... veces más grandes; a resolución completa, mejor exportarlo a Google Drive.

8. Tendencias estacionales

¿Ha cambiado el NDVI de cada mes entre 2005 y 2024? Con MODIS (20 años):

t = mo.trend_table(sn, 'NDVI', 'Modis', '2005-01-01', '2024-12-31', step='monthly',
                   csv='tendencias_ndvi.csv', chart='tendencias_ndvi.png')
[(r['label'], round(r['slope'], 4), round(r['p_value'], 3), r['significant']) for r in t['rows']]
[('January', -0.0003, 0.697, False), ('February', 0.0007, 0.65, False), ('March', 0.0015, 0.119, False),
 ('April', 0.0004, 0.795, False), ('May', 0.0009, 0.516, False), ('June', 0.0012, 0.243, False),
 ('July', 0.0013, 0.27, False), ('August', 0.0019, 0.005, True), ('September', 0.002, 0.023, True),
 ('October', 0.001, 0.399, False), ('November', 0.0011, 0.436, False), ('December', 0.0005, 0.516, False)]

El NDVI medio del parque sube en todos los meses salvo enero, pero la subida solo es significativa (p < 0,05) a finales del verano: unas +0,002 unidades por año en agosto y septiembre. Cada fila trae también n (años con datos), tau, z e intercept; t['yearly'] tiene los valores anuales de cada mes. La gráfica muestra esos valores y la recta de Sen de cada mes:

Tendencias mensuales del NDVI de MODIS de 2005 a 2024 en el Parque Nacional de Sierra Nevada

En cada píxel. El mapa de la pendiente de Sen del NDVI anual, solo donde la tendencia es significativa:

mo.trend_map(sn, 'NDVI', 'Modis', '2005-01-01', '2024-12-31', step='annual', output='slope', alpha=0.05,
             thumbnail='tendencia_ndvi_anual.png')

Pendiente de Sen del NDVI anual de MODIS de 2005 a 2024, solo los píxeles con p < 0,05: verde, aumento; rojo, descenso

Verde: aumento; rojo: descenso; en blanco, los píxeles sin tendencia significativa. Con valores, en GeoTIFF:

d = mo.download_trend(sn, 'NDVI', 'Modis', '2005-01-01', '2024-12-31', step='annual', path='tendencia_ndvi_anual.tif')
d['bands']    # ['slope', 'tau', 'z', 'p_value', 'intercept', 'n']
d['unit']     # '[unitless] / year'

Para un mes o un bloque de 16 días: step='monthly', part=8 (agosto) o step='16-day', part=14 (días 209-224). output elige la banda del mapa de teselas: 'slope', 'tau', 'z', 'p_value', 'intercept' o 'n'.

9. Comparar dos periodos

El NDVI de cada mes en 2020-2024 frente a 2005-2009:

c = mo.compare_series(sn, 'NDVI', 'Modis', '2005-01-01', '2009-12-31',     # periodo de referencia
                      '2020-01-01', '2024-12-31',                          # el otro periodo
                      step='monthly', csv='comparacion_ndvi.csv', chart='comparacion_ndvi.png')
c['profile'][:2]
[{'region': 'Sierra Nevada', 'part': 1, 'key': 'm01', 'label': 'January', 'reference': 0.4954, 'other': 0.4953,
  'difference': -0.0002, 'n_reference': 5, 'n_other': 5},
 {'region': 'Sierra Nevada', 'part': 2, 'key': 'm02', 'label': 'February', 'reference': 0.4681, 'other': 0.4829,
  'difference': 0.0149, 'n_reference': 5, 'n_other': 5}]

NDVI mensual medio de MODIS en 2005-2009 y en 2020-2024 en el Parque Nacional de Sierra Nevada

c['rows'] tiene los valores de cada año de los dos periodos. El mapa de la diferencia (la mediana de 2020-2024 menos la de 2005-2009):

mo.compare_map(sn, 'NDVI', 'Modis', '2005-01-01', '2009-12-31', '2020-01-01', '2024-12-31',
               metric='median', thumbnail='diferencia_ndvi.png')
mo.download_comparison(sn, 'NDVI', 'Modis', '2005-01-01', '2009-12-31', '2020-01-01', '2024-12-31',
                       metric='median', path='diferencia_ndvi.tif')     # bandas difference, reference, other

Diferencia de la mediana del NDVI de MODIS entre 2020-2024 y 2005-2009: verde, aumento; rojo, descenso

Severidad de un incendio

El incendio de agosto de 2023 en el Parque Nacional del Teide, con Sentinel-2 (ver Conceptos):

s = mo.burn_severity({'wdpa_id': 389013}, 'Sentinel-2', '2023-05-15', '2023-08-14',   # antes
                     '2023-09-15', '2023-11-30', path='severidad.tif', csv='severidad.csv')   # después
s['burned_ha']                                  # {'El Teide': 956.4}: hectáreas con dNBR >= 0,1
[(r['label'], round(r['ha'])) for r in s['classes'] if r['class'] >= 1]
# [('Low severity', 559), ('Moderate-low severity', 318), ('Moderate-high severity', 79), ('High severity', 0)]
r = mo.burn_severity({'wdpa_id': 389013}, 'Sentinel-2', '2023-05-15', '2023-08-14',
                     '2023-09-15', '2023-11-30', index='RBR', path='rbr.tif')   # el RBR, continuo
r['stats']                                      # [{'region', 'mean', 'median', 'p10', 'p90', 'min', 'max', 'ha'}]

Con index='RBR' da el RBR en lugar del dNBR y sus clases (ver Conceptos).

10. Leer los GeoTIFF

Con GDAL (viene en el entorno de conda) o rasterio:

from osgeo import gdal
ds = gdal.Open('tendencia_ndvi_anual.tif')
nombres = [ds.GetRasterBand(i + 1).GetDescription() for i in range(ds.RasterCount)]   # slope, tau, ...
pendiente = ds.GetRasterBand(1).ReadAsArray()        # NoData: -9999

11. Exportar a Google Drive

Para mapas grandes a resolución completa o series muy largas. Earth Engine los calcula sin límite de tiempo y los deja en una carpeta de tu Google Drive:

tarea = mo.export_image(sn, 'NDVI', 'Sentinel-2', '2023-01-01', '2023-12-31', metric='median',
                        resolution=10, folder='MonitorEO')
mo.task_status(tarea['operation'])     # {'state': 'RUNNING', 'progress': 0.4, ...} hasta 'SUCCEEDED'

mo.time_series_to_drive(sn, 'NDVI', 'Sentinel-2', '2017-04-01', '2023-12-31', step='daily', folder='MonitorEO')

Las tareas también se ven en https://code.earthengine.google.com/tasks.

12. Varias variables para un modelo: recetas

download_variables descarga varias variables y métricas en rejillas alineadas (mismo CRS, mismos bordes, NoData −9999) sobre un área de estudio y, con un píxel más grueso, sobre las ecorregiones que toca. Es lo que usa eHabitat+ para preparar sus variables de entrada. La receta es un YAML (o un diccionario):

crs: auto                                   # UTM del centro del área, o p. ej. EPSG:25830
study: {resolution: 10, buffer: 500}        # área de estudio: rectángulo + 500 m, descarga directa
ecoregion: {resolution: 100, drive: true}   # opcional: ecorregiones que toca, vía Google Drive
period: ['2021-01-01', '2023-12-31']
quality: {clouds: true, water: true, snow: true, aerosols: true, cloud_score: 0.6}
variables:
  - {index: NDVI, sensor: Sentinel-2, metric: [p95, p5]}                       # un fichero por métrica
  - {index: LST, sensor: Modis, temporal: annual, metric: max, interannual: median}
  - {name: pendiente, image: COPERNICUS/DEM/GLO30_2024_1, band: DEM, terrain: slope}  # cualquier imagen de Earth Engine

Primero, un ensayo (dry_run) para ver el tamaño de las rejillas y el número de imágenes:

p = mo.download_variables('receta.yaml', {'wdpa_id': 389011}, 'variables', dry_run=True)
p['grids']['study']       # {'crs': 'EPSG:32630', 'resolution': 10.0, 'width': 7962, 'height': 3102,
                          #  'pixels': 24698124, 'mb_per_variable': 94.22, ...}
[f['file'] for f in p['files']]
# ['1_NDVI_S2_p95.tif', '2_NDVI_S2_p5.tif', '3_LST_MODIS_annual-max_median.tif', '4_pendiente.tif']

Y después la descarga:

mo.download_variables('receta.yaml', 'area_estudio.gpkg', 'variables', log=print)
  • Las variables del área de estudio quedan en variables/ y las de las ecorregiones en variables/ecoregion/, con sus polígonos en ecoregions.gpkg.
  • monitoreo.json guarda con qué se hizo cada fichero: al repetirlo solo se descarga lo que haya cambiado (overwrite=True lo descarga todo).
  • Las rejillas con drive: true se exportan a Google Drive (sin límite de tamaño) y se importan desde Google Drive para escritorio al terminar. Si se interrumpe, al repetirlo se esperan las exportaciones en curso en vez de lanzarlas otra vez.

13. Otros datos de Earth Engine

Todas las funciones aceptan, en lugar de una variable del catálogo, una colección o una imagen de Earth Engine:

mo.time_series(sn, collection='IDAHO_EPSCOR/TERRACLIMATE', band='pr', start='2000-01-01', end='2023-12-31',
               step='annual', metric='sum')                                   # precipitación anual
mo.region_stats(sn, image='COPERNICUS/DEM/GLO30_2024_1', band='DEM', terrain='slope')   # pendiente del terreno

Referencia

Función Qué hace Devuelve
init(project, shared) Conecta con Earth Engine: con tu proyecto o, sin él (o con shared=True), con la cuenta compartida de MonitorEO, que no exporta a Google Drive el proyecto
eecu_usage(limit, refresh, lang) Tus EECU gastados este mes (research EECUs con la cuenta compartida; con tu cuenta, la cuota de tu proyecto) used_percent, left_percent, used_hours, limit_hours, warning, text, bar
about(lang) Descripción, agradecimientos, logotipos y enlaces dict
list_variables(sensor, text) Variables del catálogo lista
describe_variable(variable, sensor) Todo sobre una variable dict
find_protected_areas(text) Áreas protegidas de WDPA por nombre lista
time_series(region, variable, sensor, start, end, step, metric, stat, ...) Serie temporal rows, csv, chart, scale, unit, title
map_layer(region, variable, sensor, start, end, metric, temporal, ...) Teselas del mapa url, min, max, palette, bounds, thumbnail
thumbnail(...) Vista previa del mapa PNG (bytes)
composite_image(...) La imagen de Earth Engine del mapa (ee.Image, ee.FeatureCollection)
region_stats(...) Estadísticas del mapa por polígono rows, scale, unit
download_image(..., path) Mapa en GeoTIFF file, grid, resolution, ...
trend_table(..., step, alpha) Tendencias de la región rows, yearly, csv, chart
trend_map(..., step, part, output, alpha) Teselas del mapa de tendencias como map_layer
download_trend(..., step, part, path) Tendencias por píxel en GeoTIFF file, bands, ...
compare_series(..., other_start, other_end, step) Comparación de periodos profile, rows, csv, chart
compare_map(...) / download_comparison(..., path) Mapa de la diferencia como map_layer / download_image
burn_severity(region, sensor, start, end, other_start, other_end, path, csv, index) Severidad de un incendio (dNBR, clases del USGS; o, con index='RBR', el RBR) classes, burned_ha (o stats del RBR), file
export_image(...) / time_series_to_drive(...) Exportaciones a Google Drive operation, file, folder
task_status(operation) Estado de una exportación state, progress, error
download_variables(recipe, area, folder, ...) Receta de variables en GeoTIFF resumen

Cada función tiene su documentación completa: help(mo.time_series).

Clone this wiki locally