Análisis Exploratorio de Incendios Forestales Usando Datos de Focos de Calor del Sensor MODIS¶
Observatorio Provincial de Gestión de Riesgos - Secretaría de Emergencias y Gestión de Riesgos - Gobierno de la provincia de Neuquén
¿CUÁL ES LA FUNCIÓN DEL OBSERVATORIO PROVINCIAL DE GESTIÓN DE RIESGOS?¶
El Observatorio Provincial de Gestión de Riesgos tiene como objetivo contribuir al diseño, implementación y monitoreo de políticas públicas tendientes a optimizar la gestión integral del riesgo en la provincia del Neuquén, a través del desarrollo de un sistema de información que permita recopilar, analizar y consolidar datos relevantes para la toma de decisiones y la mejora continua de las estrategias de reducción de riesgo y abordaje de situaciones de emergencias. Sus funciones se enmarcan en la ley provincial 2713: “Enfoque de riesgo en políticas de planificación y desarrollo territorial de la Provincia del Neuquén”.
FUENTES DE INFORMACIÓN:
CIAT – Centro de Información de Alerta Temprana
SIGISVI - Sistema Integral de Gestión de la Información de Seguridad Vial
Vialidad Provincial (Estado de las rutas)
RAPH (SIEN) Registro de Atención Pre Hospitalaria
Medios gráficos de comunicación
OpenWeatherMap
Servicio meteorológico Nacional
Policía de la provincia de Neuquén
Dirección Bomberos de la Policía
Sistema de Manejo del Fuego
FICHA TÉCNICA¶
Período de Análisis: Noviembre de 2000 a Mayo de 2024
Cantidad de Registros: 5,596 focos de calor detectados
Fuentes de Datos:
Focos de Calor: University of Maryland. (n.d.). Fire Information for Resource Management System (FIRMS). NASA. Recuperado de https://firms.modaps.eosdis.nasa.gov/
Modelos Digitales de Elevación (MDE): Instituto Geográfico Nacional (IGN). (2019). Nuevo Modelo Digital de Elevaciones de la República Argentina. Recuperado de https://www.ign.gob.ar/content/nuevo-modelo-digital-de-elevaciones-para-la-rep%C3%BAblica-argentina
Vegetación: Movia, C. (1982). Mapa de Vegetación de la República Argentina. Instituto Nacional de Tecnología Agropecuaria (INTA) Y Centro de Investigación y Extensión Forestal Andino Patagónico (CIEFAP). Cartografía de
Centros Urbanos y Redes Viales: OpenStreetMap. (n.d.). Datos Geoespaciales Abiertos. Recuperado de https://www.openstreetmap.org/ Y Consejo de Planificación y Acción para el Desarrollo (COPADE).
Este trabajo tiene como objetivo realizar un análisis estadístico descriptivo que sirva como base para la toma de decisiones estratégicas en el marco del "Operativo Fuego" de la Provincia del Neuquén. Este operativo se estructura en torno a tres pilares fundamentales: comunicación preventiva, coordinación interinstitucional y articulación con gobiernos locales, con el propósito de prevenir, mitigar y responder de manera eficiente a los incendios forestales. Los datos generados a partir de este análisis tienen como finalidad priorizar áreas de riesgo, optimizar la asignación de recursos y diseñar estrategias específicas de respuesta ante emergencias. Además, permiten mejorar las campañas de comunicación preventiva, coordinar esfuerzos entre instituciones y apoyar la planificación local con información técnica y geoespacial precisa.
El análisis se basa en datos recopilados por el sensor MODIS (Moderate Resolution Imaging Spectroradiometer), instalado en los satélites Terra y Aqua de la NASA. Este sensor detecta focos de calor en tiempo real, proporcionando información crítica sobre áreas con temperaturas significativamente más altas que su entorno. Estas anomalías térmicas pueden estar asociadas a incendios forestales, quemas de biomasa o actividades industriales. La información obtenida a través de MODIS permite identificar patrones temporales y espaciales de los focos de calor, lo que es esencial para comprender su dinámica y distribución. Para evitar confusiones con infocos de calor derivados de actividades propias de la industria hidrocarburífera, se aplicó una máscara que excluye zonas petroleras, asegurando así un análisis más preciso de los focos de calor relacionados con incendios forestales y de interfase. Para maximizar la utilidad de estos datos, el análisis se complementa con el cruce de variables clave como:
- Vegetación
- Relieve
- Pendientes
- Alturas
- Proximidad a Centros Urbanos y Redes Viales
LIBRERÍAS¶
# Instalar Librerías
#%pip install pandas matplotlib seaborn geopandas folium plotly nltk scipy wordcloud nbconvert
# Importar de Librerías
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import geopandas as gpd
import folium
import plotly.express as px
import nltk
from scipy.interpolate import make_interp_spline
from scipy.interpolate import make_interp_spline
from nltk.corpus import stopwords
from wordcloud import WordCloud, STOPWORDS
from wordcloud import WordCloud
from nltk.corpus import stopwords
from folium.plugins import MarkerCluster
from folium.plugins import HeatMap, MeasureControl, MarkerCluster
DATAFRAME Y VISUALIZACIÓN DE ARCHIVOS¶
# Lectura Archivo
df_incendios = pd.read_csv('modis_incendios.csv')
pd.set_option('display.max_rows', None)
df_incendios.head()
| fid | LATITUDE | LONGITUDE | BRIGHTNESS | SCAN | TRACK | ACQ_DATE | ACQ_TIME | SATELLITE | INSTRUMENT | ... | DAYNIGHT | TYPE | dem_nqn_re | comun_veg | dist_aurb | dist_rut | nombre | region | Aspecto | dem_nqn_pe | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 5 | -38.3537 | -70.2693 | 319.9 | 1.0 | 1.0 | 2000/11/19 | 1459 | Terra | MODIS | ... | D | 0.0 | 1157.56909 | E18:: Colliguaya integerrima,Trevoa patagonica... | NaN | NaN | Picunches | Centro Oeste | 12.78170 | 14.38076 |
| 1 | 11 | -38.6914 | -70.1088 | 328.8 | 2.2 | 1.4 | 2000/12/08 | 1529 | Terra | MODIS | ... | D | 0.0 | 1019.21796 | E10: Larrea divaricata y Atriplex lampa, con H... | NaN | NaN | Picunches | Centro Oeste | 117.08196 | 9.02464 |
| 2 | 12 | -38.6882 | -70.1160 | 336.0 | 2.2 | 1.4 | 2000/12/08 | 1529 | Terra | MODIS | ... | D | 0.0 | 1108.71448 | E16: Colliguaya integerrima, Verbena glauca y ... | NaN | NaN | Picunches | Centro Oeste | 85.72124 | 4.19148 |
| 3 | 13 | -37.8495 | -70.3602 | 325.5 | 1.6 | 1.3 | 2000/12/09 | 1434 | Terra | MODIS | ... | D | 0.0 | 1364.26404 | E18:: Colliguaya integerrima,Trevoa patagonica... | NaN | NaN | Loncopué | Norte | 38.99630 | 20.01369 |
| 4 | 14 | -37.8530 | -70.3427 | 325.8 | 1.6 | 1.3 | 2000/12/09 | 1434 | Terra | MODIS | ... | D | 0.0 | 1266.30603 | E18:: Colliguaya integerrima,Trevoa patagonica... | NaN | NaN | Loncopué | Norte | 176.16109 | 7.55501 |
5 rows × 24 columns
VALORES¶
# Reemplazo de valores nulos por NA
# Lista de valores considerados como nulos o vacíos
valores_nulos = ['NaN', 'null', 'S/D', '', ' ', 'N/A', 'None', None]
# Reemplazar todos los valores especificados en 'valores_nulos' por np.nan
df_incendios.replace(valores_nulos, np.nan, inplace=True)
# Verificar si hay cambios mostrando, por ejemplo, la suma de valores nulos por columna
print(df_incendios.isna().sum())
fid 0 LATITUDE 0 LONGITUDE 0 BRIGHTNESS 0 SCAN 0 TRACK 0 ACQ_DATE 0 ACQ_TIME 0 SATELLITE 0 INSTRUMENT 0 CONFIDENCE 0 VERSION 0 BRIGHT_T31 0 FRP 0 DAYNIGHT 0 TYPE 0 dem_nqn_re 2 comun_veg 0 dist_aurb 5489 dist_rut 3875 nombre 0 region 0 Aspecto 5 dem_nqn_pe 0 dtype: int64
GRÁFICOS¶
ANÁLISIS TEMPORAL¶
# Convertir la columna ACQ_DATE de df_incendios a formato datetime y crear df_date
df_incendios['ACQ_DATE'] = pd.to_datetime(df_incendios['ACQ_DATE'], errors='coerce')
# Crear un nuevo DataFrame df_date basado en df_incendios
df_date = df_incendios.copy()
FOCOS DE CALOR POR AÑO¶
# Extraer el año de la columna ACQ_DATE
df_date['year'] = df_date['ACQ_DATE'].dt.year
# Contar los casos por año
cases_per_year = df_date['year'].value_counts().sort_index()
# Crear el gráfico de barras
plt.figure(figsize=(12, 8))
cases_per_year.plot(kind='bar', color='skyblue')
# Agregar etiquetas y título
plt.title('Número de Casos por Año (2000-2024)', fontsize=16)
plt.xlabel('Año', fontsize=14)
plt.ylabel('Número de Casos', fontsize=14)
# Ajustar los límites del eje Y para agregar espacio extra en la parte superior
plt.ylim(0, cases_per_year.max() * 1.2) # Aumentar un 10% el límite superior del eje Y
# Agregar etiquetas de los valores en las barras
for i, value in enumerate(cases_per_year):
plt.text(i, value + 1, str(value), ha='center', va='bottom')
# Mostrar el gráfico
plt.tight_layout()
plt.show()
# Filtrar los casos de incendios para los años 2023 y 2024
df_filtered = df_date[(df_date['year'] == 2023) | (df_date['year'] == 2024)]
# Contar los casos por año en el DataFrame filtrado
cases_per_year = df_filtered['year'].value_counts().sort_index()
# Crear el gráfico de barras
plt.figure(figsize=(12, 8))
cases_per_year.plot(kind='bar', color='skyblue')
# Agregar etiquetas y título
plt.title('Número de Casos por Año (2023 y 2024)', fontsize=16)
plt.xlabel('Año', fontsize=14)
plt.ylabel('Número de Casos', fontsize=14)
# Ajustar los límites del eje Y para agregar espacio extra en la parte superior
plt.ylim(0, cases_per_year.max() * 1.2) # Aumentar un 20% el límite superior del eje Y
# Agregar etiquetas de los valores en las barras
for i, value in enumerate(cases_per_year):
plt.text(i, value + 1, str(value), ha='center', va='bottom')
# Mostrar el gráfico
plt.tight_layout()
plt.show()
FOCOS DE CALOR POR MES¶
# Extraer el mes de la columna ACQ_DATE
df_date['month'] = df_date['ACQ_DATE'].dt.month
# Contar los casos por mes
cases_per_month = df_date['month'].value_counts().sort_index()
# Diccionario para mapear los números de los meses a sus nombres
meses = {1: 'Enero', 2: 'Febrero', 3: 'Marzo', 4: 'Abril', 5: 'Mayo', 6: 'Junio',
7: 'Julio', 8: 'Agosto', 9: 'Septiembre', 10: 'Octubre', 11: 'Noviembre', 12: 'Diciembre'}
# Crear el gráfico de barras
plt.figure(figsize=(12, 8))
cases_per_month.plot(kind='bar', color='skyblue')
# Reemplazar los números del eje X con los nombres de los meses
plt.xticks(ticks=range(len(cases_per_month)), labels=[meses[m] for m in cases_per_month.index])
# Agregar etiquetas y título
plt.title('Número de Casos por Mes', fontsize=16)
plt.xlabel('Mes', fontsize=14)
plt.ylabel('Número de Casos', fontsize=14)
# Ajustar los límites del eje Y para agregar espacio extra en la parte superior
plt.ylim(0, cases_per_month.max() * 1.2)
# Agregar etiquetas de los valores en las barras
for i, value in enumerate(cases_per_month):
plt.text(i, value + 1, str(value), ha='center', va='bottom')
# Mostrar el gráfico
plt.tight_layout()
plt.show()
FOCOS DE CALOR POR DÍA DE LA SEMANA¶
# Extraer el día de la semana de la columna ACQ_DATE (0=Lunes, 6=Domingo)
df_date['weekday'] = df_date['ACQ_DATE'].dt.weekday
# Diccionario para mapear los números de los días de la semana a sus nombres
dias_semana = {0: 'Lunes', 1: 'Martes', 2: 'Miércoles', 3: 'Jueves', 4: 'Viernes', 5: 'Sábado', 6: 'Domingo'}
# Contar los casos por día de la semana
cases_per_weekday = df_date['weekday'].value_counts().sort_index()
# Crear el gráfico de barras
plt.figure(figsize=(12, 8))
cases_per_weekday.plot(kind='bar', color='skyblue')
# Reemplazar los números del eje X con los nombres de los días de la semana
plt.xticks(ticks=range(len(cases_per_weekday)), labels=[dias_semana[d] for d in cases_per_weekday.index])
# Agregar etiquetas y título
plt.title('Número de Casos por Día de la Semana', fontsize=16)
plt.xlabel('Día de la Semana', fontsize=14)
plt.ylabel('Número de Casos', fontsize=14)
# Ajustar los límites del eje Y para agregar espacio extra en la parte superior
plt.ylim(0, cases_per_weekday.max() * 1.2)
# Agregar etiquetas de los valores en las barras
for i, value in enumerate(cases_per_weekday):
plt.text(i, value + 1, str(value), ha='center', va='bottom')
# Mostrar el gráfico
plt.tight_layout()
plt.show()
CRUCES DE VARIABLES TEMPORALES¶
# Crear una tabla cruzada (crosstab) entre el día de la semana y el mes
heatmap_data = pd.crosstab(df_date['month'], df_date['weekday'])
# Mapeo de los días de la semana y meses para las etiquetas
dias_semana = ['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo']
meses = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre']
# Crear el mapa de calor sin etiquetas en las celdas y con una gama de colores "Reds"
plt.figure(figsize=(12, 8))
sns.heatmap(heatmap_data, annot=False, fmt='d', cmap='Reds', xticklabels=dias_semana, yticklabels=meses)
# Rotar las etiquetas del eje X y ajustar las del eje Y
plt.xticks(rotation=45, ha='right', fontsize=12)
plt.yticks(rotation=0, fontsize=12)
# Título y etiquetas
plt.title('Mapa de Calor: Número de Casos por Día de la Semana y Mes', fontsize=16)
plt.xlabel('Día de la Semana', fontsize=14)
plt.ylabel('Mes', fontsize=14)
# Mostrar el mapa de calor
plt.tight_layout()
plt.show()
FOCOS DE CALOR POR DEPARTAMENTO¶
# Contar los casos por año
cases_per_year = df_date['nombre'].value_counts().sort_index()
# Crear el gráfico de barras
plt.figure(figsize=(12, 8))
cases_per_year.plot(kind='bar', color='skyblue')
# Agregar etiquetas y título
plt.title('Número de Casos por Departamento', fontsize=16)
plt.xlabel('Departamento', fontsize=14)
plt.ylabel('Número de Casos', fontsize=14)
# Ajustar los límites del eje Y para agregar espacio extra en la parte superior
plt.ylim(0, cases_per_year.max() * 1.2) # Aumentar un 10% el límite superior del eje Y
# Agregar etiquetas de los valores en las barras
for i, value in enumerate(cases_per_year):
plt.text(i, value + 1, str(value), ha='center', va='bottom')
# Mostrar el gráfico
plt.tight_layout()
plt.show()
# Crear una tabla cruzada (crosstab) entre el departamento ('nombre') y el mes
heatmap_data_dep_mes = pd.crosstab(df_date['nombre'], df_date['month'])
# Mapeo de los meses para las etiquetas
meses = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre']
# Crear el mapa de calor
plt.figure(figsize=(15, 10))
sns.heatmap(heatmap_data_dep_mes, annot=False, fmt='d', cmap='Reds', xticklabels=meses, yticklabels=heatmap_data_dep_mes.index)
# Rotar las etiquetas del eje X para que no se superpongan
plt.xticks(rotation=45, ha='right', fontsize=10)
plt.yticks(fontsize=10)
# Título y etiquetas
plt.title('Mapa de Calor: Número de Casos por Departamento y Mes', fontsize=16)
plt.xlabel('Mes', fontsize=14)
plt.ylabel('Departamento', fontsize=14)
# Mostrar el mapa de calor
plt.tight_layout()
plt.show()
# Crear una tabla cruzada (crosstab) entre el departamento ('nombre') y el día de la semana ('weekday')
heatmap_data_dep_weekday = pd.crosstab(df_date['nombre'], df_date['weekday'])
# Mapeo de los días de la semana para las etiquetas
dias_semana = ['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo']
# Crear el mapa de calor
plt.figure(figsize=(15, 10))
sns.heatmap(heatmap_data_dep_weekday, annot=False, fmt='d', cmap='Reds', xticklabels=dias_semana, yticklabels=heatmap_data_dep_weekday.index)
# Rotar las etiquetas del eje X para que no se superpongan
plt.xticks(rotation=45, ha='right', fontsize=10)
plt.yticks(fontsize=10)
# Título y etiquetas
plt.title('Mapa de Calor: Número de Casos por Departamento y Día de la Semana', fontsize=16)
plt.xlabel('Día de la Semana', fontsize=14)
plt.ylabel('Departamento', fontsize=14)
# Mostrar el mapa de calor
plt.tight_layout()
plt.show()
FOCOS DE CALOR POR REGIÓN¶
# Contar los casos por año
cases_per_year = df_date['region'].value_counts().sort_index()
# Crear el gráfico de barras
plt.figure(figsize=(12, 8))
cases_per_year.plot(kind='bar', color='skyblue')
# Agregar etiquetas y título
plt.title('Número de Casos por Región', fontsize=16)
plt.xlabel('Región', fontsize=14)
plt.ylabel('Número de Casos', fontsize=14)
# Ajustar los límites del eje Y para agregar espacio extra en la parte superior
plt.ylim(0, cases_per_year.max() * 1.2) # Aumentar un 10% el límite superior del eje Y
# Agregar etiquetas de los valores en las barras
for i, value in enumerate(cases_per_year):
plt.text(i, value + 1, str(value), ha='center', va='bottom')
# Mostrar el gráfico
plt.tight_layout()
plt.show()
FOCOS DE CALOR Y ALTURA¶
# Crear el gráfico de dispersión
plt.figure(figsize=(10, 6))
plt.scatter(df_date['ACQ_DATE'], df_date['dem_nqn_re'], color='blue', alpha=0.5)
# Agregar etiquetas y título
plt.title('Gráfico de Dispersión de Alturas Por Año', fontsize=16)
plt.xlabel('Año', fontsize=14)
plt.ylabel('Altura', fontsize=14)
# Mostrar el gráfico
plt.tight_layout()
plt.show()
FOCOS DE CALOR POR Y PENDIENTE¶
# Crear el gráfico de dispersión
plt.figure(figsize=(10, 6))
plt.scatter(df_date['ACQ_DATE'], df_date['dem_nqn_pe'], color='blue', alpha=0.5)
# Agregar etiquetas y título
plt.title('Gráfico de Dispersión de Pendientes', fontsize=16)
plt.xlabel('Fecha', fontsize=14)
plt.ylabel('Pendiente', fontsize=14)
# Mostrar el gráfico
plt.tight_layout()
plt.show()
FOCOS DE CALOR POR Y ORIENTACIÓN DE LA PENDIENTE¶
# Crear una nueva columna que agrupa los valores de 'Aspecto' según las categorías definidas
def categorizar_aspecto(valor):
if 0 <= valor <= 45 or 315 <= valor <= 360:
return 'Ladera Norte'
elif 45 < valor <= 135:
return 'Ladera Este'
elif 135 < valor <= 225:
return 'Ladera Sur'
elif 225 < valor <= 315:
return 'Ladera Oeste'
# Aplicar la función para categorizar los valores de 'Aspecto'
df_date['ladera'] = df_date['Aspecto'].apply(categorizar_aspecto)
# Contar el número de casos por categoría de ladera
cases_per_ladera = df_date['ladera'].value_counts().sort_index()
# Crear el gráfico de barras
plt.figure(figsize=(12, 8))
bars = cases_per_ladera.plot(kind='bar', color='lightblue')
# Agregar etiquetas y título
plt.title('Número de Casos por Ladera', fontsize=16)
plt.xlabel('Ladera', fontsize=14)
plt.ylabel('Número de Casos', fontsize=14)
# Agregar etiquetas a las barras
for bar in bars.patches:
plt.text(bar.get_x() + bar.get_width() / 2,
bar.get_height(),
int(bar.get_height()),
ha='center',
va='bottom',
fontsize=12)
# Ajustar los límites del eje Y para agregar espacio extra en la parte superior
plt.ylim(0, cases_per_ladera.max() * 1.5) # Aumentar un 50% el límite superior del eje Y
# Mostrar el gráfico
plt.tight_layout()
plt.show()
FOCOS DE CALOR POR Y VEGETACIÓN¶
# Obtener los valores únicos y su conteo en la columna 'comun_veg'
comun_veg_counts = df_date['comun_veg'].value_counts()
# Mostrar el resultado
print(comun_veg_counts)
comun_veg Herbaceo Subarbustivo 2019 Arbustal Nativo 1202 E18:: Colliguaya integerrima,Trevoa patagonica y Nassauvia axillaris 443 F5: Stipa spp. con Mulinum spinosum y Colliguaya integerrima 202 Ñire 194 Erial 176 Exotico-Artificial 169 Remanente Disturbio Reciente 141 Humedales 118 F2: Mulinum spinosum, Stipa speciosa, colliguaya integerrima y Adesmia campestris 88 F6: Mulinum spinosum, Senecio filaginoides y diversas especies de Stipa y Festuca 82 Lenga 80 F4: Diversas especies de Stipa con Mulinum spinosum, Trevoa patagonica y Colliguaya integerrima 76 E2: Larrea divaricata y Atriplex lampa 75 F2: Mulinum spinosum, Stipa speciosa, Colliguaya integerrima y Adesmia campestris 65 E21: Grindelia chiloensis, Senecio filaginoides y Stipa spp. 51 E16: Colliguaya integerrima, Verbena glauca y Larrea nitida 50 Araucaria 47 E13: Atriplex lampa dominante 37 SR: Sin relevar 27 D1: Semidesierto de altura 24 E19: Colliguaya integerrima, Mulinum spinosum y Nassauvia axillaris 22 E15: Senecio filaginoides con Larrea nitida y Stipa spp. 18 Matorral Mixto 18 H2: Cortaderales 17 Bosque Mixto 16 E4: Larrea divaricata con Prosopis globosum 16 E23: Stipa spp, Senecio filaginoides y Colliguaya integerrima 16 E9: Larrea divaricata y Atriplex lampa con Suaeda divaricata 12 E20: Fabiana imbricata y Mulinum spinosum 12 X1: Vegetacion de llano aluvial de grandes rios y arroyos 10 E17: Colliguaya integerrima, Verbena glauca y Larrea nitida 9 Dy: Peladal sobre yeso 9 G4: Diversas especies de Stipa, Poa y Festuca, con Mulinum spinosum 8 F1: Mulinum spinosum, Stipa spp., Nassauvia glomerulosa y Festuca pallescens 8 Db: Peladal sobre basaltos 5 E10: Larrea divaricata y Atriplex lampa, con Haploppapus pectinatus y Verbena seriphioides 5 Coihue 5 X1: Vegatacion de llano aluvial de grandes rios y arroyos 3 F3: Diversas especies de Stipa, con Glindelia chiloensis y Cassia kurtzii 3 U4: Urbano, densidad dispersa 3 G5: Diversas especies de Stipa con grupos aislados de Adesmia pinifolia 3 G3: Diversas especies de Stipa y Trevoa patagonica con Haplopappus pectinatus y Mulinum spinosum 2 Cipres 2 Hielo-Nieve 2 E6: Larrea divaricata , con Larrea nitida y Verbena glauca 2 E22: Mulinum spinosum y Stipa spp. con Verbena connatibracteata 1 Rauli 1 Chacay 1 U3: Urbano, densidad baja 1 Name: count, dtype: int64
def agrupar_vegetacion(valor):
if valor in [
'Herbaceo Subarbustivo',
'E18:: Colliguaya integerrima,Trevoa patagonica y Nassauvia axillaris',
'F5: Stipa spp. con Mulinum spinosum y Colliguaya integérrima',
'F2: Mulinum spinosum, Stipa speciosa, colliguaya integerrima y Adesmia campestris',
'F6: Mulinum spinosum, Senecio filaginoides y diversas especies de Stipa y Festuca',
'F4: Diversas especies de Stipa con Mulinum spinosum, Trevoa patagonica y Colliguaya integerrima',
'E19: Colliguaya integerrima, Mulinum spinosum y Nassauvia axillaris',
'E23: Stipa spp, Senecio filaginoides y Colliguaya integerrima',
'E20: Fabiana imbricata y Mulinum spinosum',
'G4: Diversas especies de Stipa, Poa y Festuca, con Mulinum spinosum',
'F1: Mulinum spinosum, Stipa spp., Nassauvia glomerulosa y Festuca pallescens',
'F3: Diversas especies de Stipa, con Glindelia chiloensis y Cassia kurtzii',
'G5: Diversas especies de Stipa con grupos aislados de Adesmia pinifolia',
'G3: Diversas especies de Stipa y Trevoa patagonica con Haplopappus pectinatus y Mulinum spinosum',
'E6: Larrea divaricata , con Larrea nitida y Verbena glauca',
'E22: Mulinum spinosum y Stipa spp. con Verbena connatibracteata',
]:
return 'Estepa'
elif valor in [
'Arbustal Nativo',
'E2: Larrea divaricata y Atriplex lampa',
'E21: Grindelia chiloensis, Senecio filaginoides y Stipa spp',
'E16: Colliguaya integerrima, Verbena glauca y Larrea nítida',
'E13: Atriplex lampa dominante',
'E15: Senecio filaginoides con Larrea nitida y Stipa spp.',
'Matorral Mixto',
'H2: Cortaderales',
'E4: Larrea divaricata con Prosopis globosum',
'E9: Larrea divaricata y Atriplex lampa con Suaeda divaricata',
'X1: Vegetacion de llano aluvial de grandes rios y arroyos',
'E17: Colliguaya integerrima, Verbena glauca y Larrea nítida',
'E10: Larrea divaricata y Atriplex lampa, con Haploppapus pectinatus y Verbena seriphioides',
]:
return 'Desierto de Monte'
elif valor in [
'Lenga',
'Araucaria',
'Ñire',
'Bosque Mixto',
'Coihue',
'Cipres',
'Rauli',
'Chacay',
]:
return 'Bosque'
elif valor == 'SR: Sin relevar':
return 'NA'
elif valor in [
'Erial',
'Dy: Peladal sobre yeso',
'Remanente Disturbio Reciente',
]:
return 'Erial'
elif valor in [
'D1: Semidesierto de altura',
'Hielo-Nieve',
]:
return 'Semi-desierto de altura'
elif valor in [
'U4: Urbano, densidad dispersa',
'U3: Urbano, densidad baja',
]:
return 'Urbano'
elif valor == 'Exotico-Artificial':
return 'Exotico-Artificial'
elif valor == 'Humedales':
return 'Humedales'
else:
return 'Otro' # Para valores que no encajen en las categorías anteriores
# Aplicar la función a la columna 'comun_veg' y crear una nueva columna
df_incendios['vegetacion'] = df_incendios['comun_veg'].apply(agrupar_vegetacion)
# Contar el número de casos por categoría de vegetación
cases_per_vegetacion = df_incendios['vegetacion'].value_counts().sort_index()
# Crear el gráfico de barras
plt.figure(figsize=(12, 8))
bars = cases_per_vegetacion.plot(kind='bar', color='lightblue')
# Agregar etiquetas y título
plt.title('Número de Casos por Tipo de Vegetación', fontsize=16)
plt.xlabel('Tipo de Vegetación', fontsize=14)
plt.ylabel('Número de Casos', fontsize=14)
# Agregar etiquetas a las barras
for bar in bars.patches:
plt.text(bar.get_x() + bar.get_width() / 2,
bar.get_height(),
int(bar.get_height()),
ha='center',
va='bottom',
fontsize=12)
# Ajustar los límites del eje Y para agregar espacio extra en la parte superior
plt.ylim(0, cases_per_vegetacion.max() * 1.2) # Aumentar un 20% el límite superior del eje Y
# Mostrar el gráfico
plt.tight_layout()
plt.show()
FOCOS DE CALOR POR Y DISTANCIAS A CENTROS URBANOS¶
# Contar el número de casos por distancia a centros urbanos
menos_3km = df_incendios['dist_aurb'].notna().sum() # Contar todos los valores que no son NA
mas_3km = df_incendios['dist_aurb'].isna().sum() # Contar los valores NA
# Crear una serie con los resultados
cases_summary = pd.Series([menos_3km, mas_3km], index=['A menos de 3 km de Distancia', 'A más de 3 km de Distancia'])
# Crear el gráfico de barras
plt.figure(figsize=(12, 8))
bars = cases_summary.plot(kind='bar', color='lightblue')
# Agregar etiquetas y título
plt.title('Número de Casos por Distancia a Centros Urbanos', fontsize=16)
plt.xlabel('Categoría de Distancia', fontsize=14)
plt.ylabel('Número de Casos', fontsize=14)
# Agregar etiquetas a las barras
for bar in bars.patches:
plt.text(bar.get_x() + bar.get_width() / 2,
bar.get_height(),
int(bar.get_height()),
ha='center',
va='bottom',
fontsize=12)
# Ajustar los límites del eje Y para agregar espacio extra en la parte superior
plt.ylim(0, cases_summary.max() * 1.2) # Aumentar un 20% el límite superior del eje Y
# Mostrar el gráfico
plt.tight_layout()
plt.show()
FOCOS DE CALOR POR Y DISTANCIAS A RUTAS¶
# Contar el número de casos por distancia a rutas
menos_3km = df_incendios['dist_rut'].notna().sum() # Contar todos los valores que no son NA
mas_3km = df_incendios['dist_rut'].isna().sum() # Contar los valores NA
# Crear una serie con los resultados
cases_summary = pd.Series([menos_3km, mas_3km], index=['A menos de 3 km de Distancia', 'A más de 3 km de Distancia'])
# Crear el gráfico de barras
plt.figure(figsize=(12, 8))
bars = cases_summary.plot(kind='bar', color='lightblue')
# Agregar etiquetas y título
plt.title('Número de Casos por Distancia a Rutas', fontsize=16)
plt.xlabel('Categoría de Distancia', fontsize=14)
plt.ylabel('Número de Casos', fontsize=14)
# Agregar etiquetas a las barras
for bar in bars.patches:
plt.text(bar.get_x() + bar.get_width() / 2,
bar.get_height(),
int(bar.get_height()),
ha='center',
va='bottom',
fontsize=12)
# Ajustar los límites del eje Y para agregar espacio extra en la parte superior
plt.ylim(0, cases_summary.max() * 1.2) # Aumentar un 20% el límite superior del eje Y
# Mostrar el gráfico
plt.tight_layout()
plt.show()
MAPA INCENDIOS¶
df_loc = df_incendios.loc[(~df_incendios.LATITUDE.isna()) & (~df_incendios.LONGITUDE.isna())]
df_loc[['LATITUDE', 'LONGITUDE']].describe()
| LATITUDE | LONGITUDE | |
|---|---|---|
| count | 5596.000000 | 5596.000000 |
| mean | -38.537225 | -70.681941 |
| std | 0.876694 | 0.349069 |
| min | -41.056900 | -71.943400 |
| 25% | -38.896425 | -70.829325 |
| 50% | -38.336950 | -70.730000 |
| 75% | -37.926025 | -70.505125 |
| max | -36.256800 | -69.211700 |
def create_map(df_loc, latitude, longitude, zoom, tiles='OpenStreetMap'):
"""
Generate a Folium Map with clustered markers of accident locations.
"""
world_map = folium.Map(location=[latitude, longitude], zoom_start=zoom, tiles=tiles)
marker_cluster = MarkerCluster().add_to(world_map)
# Iterate over the DataFrame rows and add each marker to the cluster
for idx, row in df_loc.iterrows():
folium.Marker(
location=[row['LATITUDE'], row['LONGITUDE']],
# You can add more attributes to your marker here, such as a popup
popup=f"Lat, Lng: {row['LATITUDE']}, {row['LONGITUDE']}"
).add_to(marker_cluster)
return world_map
MAPA CLUSTERS¶
#Create and display a Folium map with clustered markers for accident locations
# Assuming df_loc is a pandas DataFrame containing accident locations
# Ensure that the column 'Start_Lat' exists in the DataFrame 'df_loc' and is spelled correctly
# For example, if the column is named 'Start_Lat', ensure it is spelled exactly the same way in the DataFrame
map_nqn = create_map(df_loc, -38.50, -68.9, 6)
MAPA COMPLETO¶
def create_map_with_layers(df_loc, latitude, longitude, zoom, output_file='map_neuquen_layers.html'):
"""
Generate a Folium Map with various layers, with tooltips showing only aliases and values.
"""
# Create the base map
world_map = folium.Map(location=[latitude, longitude], zoom_start=zoom, tiles='OpenStreetMap')
# Add the Esri satellite image layer (hidden by default)
folium.TileLayer(
tiles='https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
name='Esri Satellite',
attr='Tiles © Esri',
overlay=True,
control=True,
show=False
).add_to(world_map)
# Add clustered markers (visible by default)
marker_cluster = MarkerCluster(name='Markers', show=True).add_to(world_map)
for idx, row in df_loc.iterrows():
popup_text = (f"Tipo: {row['comun_veg']}, Fecha: {row['ACQ_DATE']}<br>"
f"Altura: {row['dem_nqn_re']}, Departamento: {row['nombre']}<br>"
f"DistCU: {row['dist_aurb']}, DistRutas: {row['dist_rut']}")
folium.Marker(
location=[row['LATITUDE'], row['LONGITUDE']],
popup=folium.Popup(popup_text, max_width=300)
).add_to(marker_cluster)
# Add heatmap layer (visible by default)
heat_data = [[row['LATITUDE'], row['LONGITUDE']] for index, row in df_loc.iterrows()]
HeatMap(heat_data, min_opacity=0.5, max_zoom=18, radius=10, blur=15, name='Heatmap', show=True).add_to(world_map)
# Load and add GeoJSON layers (hidden by default)
folium.GeoJson(
'D:/Documents/GitHub/OPGR/Incendios/geojson/huellas_arreo.geojson',
name='Huellas de Arreo',
style_function=lambda x: {'color': 'blue', 'weight': 2},
show=True
).add_to(world_map)
folium.GeoJson(
'D:/Documents/GitHub/OPGR/Incendios/geojson/rutas_nqn.geojson',
name='Rutas de Neuquén',
style_function=lambda x: {'color': 'red', 'weight': 2},
show=True
).add_to(world_map)
# Polygons with tooltips
gdf_urbanos = gpd.read_file('D:/Documents/GitHub/OPGR/Incendios/geojson/centros_urbanos_nqn.geojson')
folium.GeoJson(
gdf_urbanos,
name='Centros Urbanos',
style_function=lambda x: {'color': 'green', 'fillColor': 'green', 'fillOpacity': 0.5},
tooltip=folium.GeoJsonTooltip(fields=['fna'], aliases=['Localidad']),
show=True
).add_to(world_map)
# Points with tooltips
gdf_brigadas = gpd.read_file('D:/Documents/GitHub/OPGR/Incendios/geojson/brigadas_forestales.geojson')
folium.GeoJson(
gdf_brigadas,
name='Brigadas Forestales',
style_function=lambda x: {'color': 'orange', 'fillOpacity': 0.7},
tooltip=folium.GeoJsonTooltip(
fields=['Localidad', 'Recur_huma', 'recur_oper', 'referente1', 'Email'],
aliases=['Localidad:', 'Recursos Humanos:', 'Recursos Operativos:', 'Referente:', 'Email:']
),
show=True
).add_to(world_map)
# Add measure control and layer control
MeasureControl(position='bottomleft', primary_length_unit='meters').add_to(world_map)
folium.LayerControl().add_to(world_map)
# Add title to the map
title_html = '''
<div style="position: fixed; top: 10px; left: 50%; transform: translate(-50%, 0);
z-index: 1000; background-color: white; padding: 10px; border: 2px solid grey;
border-radius: 5px; box-shadow: 2px 2px 5px grey; font-size: 18px;">
<b>Mapa de Focos de Calor - MODIS</b>
</div>
'''
world_map.get_root().html.add_child(folium.Element(title_html))
# Export the map to an HTML file
world_map.save(output_file)
print(f"Mapa exportado como {output_file}")
return world_map
# Center the map on Neuquén province (approximate coordinates)
neuquen_latitude = -38.9516
neuquen_longitude = -68.0591
# Create and export the map
map_neuquen_layers = create_map_with_layers(
df_loc,
neuquen_latitude,
neuquen_longitude,
7, # Adjust the zoom level to fit the province
output_file='map_neuquen_layers.html'
)
map_neuquen_layers
Mapa exportado como map_neuquen_layers.html