Repository navigation
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).
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).
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}).
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 radioLa 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:

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ónVariantes:
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 imagenLa 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]
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)
mPara 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')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)'}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.
¿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:

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')
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'.
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}]
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
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).
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: -9999Para 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.
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 EnginePrimero, 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 envariables/ecoregion/, con sus polígonos enecoregions.gpkg. -
monitoreo.jsonguarda con qué se hizo cada fichero: al repetirlo solo se descarga lo que haya cambiado (overwrite=Truelo descarga todo). - Las rejillas con
drive: truese 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.
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| 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).
MonitorEO es un resultado del Proyecto EVEREST (PID2023-151939OB-I00), financiado por MICIU/AEI 10.13039/501100011033 y cofinanciado por FEDER – Unión Europea, y del Proyecto NevadEO (C-EXP-074-UGR23), cofinanciado por el Programa FEDER 2014-2020 y la Consejería de Economía, Conocimiento, Empresa y Universidades de la Junta de Andalucía. Es también un resultado del Proyecto PRESINMED (Preserving the singularity of Mediterranean high-mountain biodiversity hotspots: a NBS approach, PCI 2025-163175) de la convocatoria Biodiversa+ NBS 2023.
MonitorEO is an output of the EVEREST Project (PID2023-151939OB-I00), funded by MICIU/AEI 10.13039/501100011033, and co-funded by ERDF – European Union, as well as of the NevadEO Project (C-EXP-074-UGR23), co-funded by the ERDF 2014-2020 Program and the Regional Ministry of Economy, Knowledge, Enterprise, and Universities of the Regional Government of Andalusia. It is also an output of the PRESINMED Project (Preserving the singularity of Mediterranean high-mountain biodiversity hotspots: a NBS approach, PCI 2025-163175) of the Biodiversa+ NBS 2023 call.
OBSNEV, Universidad de Granada · GPL-3.0-or-later



Empezar
Herramientas
Ayuda
Start
Tools
- Earth Engine app
- QGIS plugin
- MCP: Claude and other assistants
- Python package
- R package
- Results viewer
Help