Carga y preparación inicial de los datos

se carga el archivo Excel usando read_excel. Al ejecutar aparecen múltiples warnings indicando que en una columna esperada como numérica se detectan fechas. Es una señal de calidad de datos en una misma variable hay mezclados tipos numéricos y de fecha.

# Cargar los datos de Waze
df = read_excel("C:/Users/User/Downloads/Trama Waze.xlsx")
## Warning: Expecting numeric in O3 / R3C15: got a date
## Warning: Expecting numeric in O6 / R6C15: got a date
## Warning: Expecting numeric in O9 / R9C15: got a date
## Warning: Expecting numeric in O12 / R12C15: got a date
## Warning: Expecting numeric in O15 / R15C15: got a date
## Warning: Expecting numeric in O18 / R18C15: got a date
## Warning: Expecting numeric in O21 / R21C15: got a date
## Warning: Expecting numeric in O24 / R24C15: got a date
## Warning: Expecting numeric in O27 / R27C15: got a date
## Warning: Expecting numeric in O29 / R29C15: got a date
## Warning: Expecting numeric in O31 / R31C15: got a date
## Warning: Expecting numeric in O33 / R33C15: got a date
## Warning: Expecting numeric in O34 / R34C15: got a date
## Warning: Expecting numeric in O35 / R35C15: got a date
## Warning: Expecting numeric in O36 / R36C15: got a date
## Warning: Expecting numeric in O37 / R37C15: got a date
## Warning: Expecting numeric in O38 / R38C15: got a date
## Warning: Expecting numeric in O336 / R336C15: got a date
## Warning: Expecting numeric in O341 / R341C15: got a date
## Warning: Expecting numeric in O346 / R346C15: got a date
## Warning: Expecting numeric in O385 / R385C15: got a date
## Warning: Expecting numeric in O390 / R390C15: got a date
## Warning: Expecting numeric in O395 / R395C15: got a date
## Warning: Expecting numeric in O400 / R400C15: got a date
## Warning: Expecting numeric in O405 / R405C15: got a date
## Warning: Expecting numeric in O672 / R672C15: got a date
## Warning: Expecting numeric in O676 / R676C15: got a date
## Warning: Expecting numeric in O679 / R679C15: got a date
## Warning: Expecting numeric in O682 / R682C15: got a date
## Warning: Expecting numeric in O685 / R685C15: got a date
## Warning: Expecting numeric in O688 / R688C15: got a date
## Warning: Expecting numeric in O691 / R691C15: got a date
## Warning: Expecting numeric in O694 / R694C15: got a date
## Warning: Expecting numeric in O698 / R698C15: got a date
## Warning: Expecting numeric in O702 / R702C15: got a date
## Warning: Expecting numeric in O707 / R707C15: got a date
## Warning: Expecting numeric in O713 / R713C15: got a date
## Warning: Expecting numeric in O719 / R719C15: got a date
## Warning: Expecting numeric in O728 / R728C15: got a date
## Warning: Expecting numeric in O731 / R731C15: got a date
## Warning: Expecting numeric in O737 / R737C15: got a date
## Warning: Expecting numeric in O1271 / R1271C15: got a date
## Warning: Expecting numeric in O1278 / R1278C15: got a date
## Warning: Expecting numeric in O1285 / R1285C15: got a date
## Warning: Expecting numeric in O1292 / R1292C15: got a date
## Warning: Expecting numeric in O1298 / R1298C15: got a date
## Warning: Expecting numeric in O1304 / R1304C15: got a date
## Warning: Expecting numeric in O1309 / R1309C15: got a date
## Warning: Expecting numeric in O1314 / R1314C15: got a date
## Warning: Expecting numeric in O1319 / R1319C15: got a date
## Warning: Expecting numeric in O1324 / R1324C15: got a date
## Warning: Expecting numeric in O1330 / R1330C15: got a date
## Warning: Expecting numeric in O1336 / R1336C15: got a date
## Warning: Expecting numeric in O1342 / R1342C15: got a date
## Warning: Expecting numeric in O1348 / R1348C15: got a date
## Warning: Expecting numeric in O1354 / R1354C15: got a date
## Warning: Expecting numeric in O1361 / R1361C15: got a date
## Warning: Expecting numeric in O3268 / R3268C15: got a date
## Warning: Expecting numeric in O3281 / R3281C15: got a date
## Warning: Expecting numeric in O3296 / R3296C15: got a date
## Warning: Expecting numeric in O3309 / R3309C15: got a date
## Warning: Expecting numeric in O3323 / R3323C15: got a date
## Warning: Expecting numeric in O3337 / R3337C15: got a date
## Warning: Expecting numeric in O3350 / R3350C15: got a date
## Warning: Expecting numeric in O3364 / R3364C15: got a date
## Warning: Expecting numeric in O3374 / R3374C15: got a date
## Warning: Expecting numeric in O3384 / R3384C15: got a date
## Warning: Expecting numeric in O3396 / R3396C15: got a date
## Warning: Expecting numeric in O3409 / R3409C15: got a date
## Warning: Expecting numeric in O3426 / R3426C15: got a date
## Warning: Expecting numeric in O3433 / R3433C15: got a date
## Warning: Expecting numeric in O3444 / R3444C15: got a date
## Warning: Expecting numeric in O3455 / R3455C15: got a date
## Warning: Expecting numeric in O3466 / R3466C15: got a date
#df <- read.csv("C:/Users/User/Downloads/Trama Waze.csv",
       #        header = TRUE,
          #     sep = ";",
            #   encoding = "UTF-8")

# Convertir la columna de fechas a formato adecuado
df$fecha = as.Date(df$creation_Date, format ="%Y-%m-%d %H:%M")


# Cambiar los nombres de los tipos de eventos a español
df$tipo_evento <- recode(df$type,
                                 "ACCIDENT" = "ACCIDENTE",
                                 "HAZARD" = "PELIGRO",
                                 "JAM" = "CONGESTIÓN",
                                 "ROAD_CLOSED" = "VÍA CERRADA")

Posteriormente se convierte el campo creation_Date a objeto fecha (as.Date) y se utiliza ymd_hms de lubridate para obtener fecha y hora separadas. Esto con el fin de asegurar que las marcas de tiempo queden en un formato que permita filtrar por día o extraer la hora.

# Cargar la librería lubridate
library(lubridate)

# Convertir la fecha y extraer la hora y el día
fecha_hora = ymd_hms(df$creation_Date)
hora = hour(fecha_hora)
dia = day(fecha_hora)

# Agregar la columna de hora a los datos
df$hora = hora

# Mostrar la tabla de frecuencia de tipos de eventos
table(df$tipo_evento)
## 
##   ACCIDENTE  CONGESTIÓN     PELIGRO VÍA CERRADA 
##         125        3205         719        1021

Recodificación semántica de los tipos de eventos

Con la creación de df\(tipo_evento a partir de df\)type, se recodifican los tipos de incidentes de Waze a categorías en español: ACCIDENTE, PELIGRO, CONGESTIÓN y VÍA CERRADA. Esto ayuda a que se facilite la interpretación de los resultados.

La tabla de frecuencias muestra claramente que la CONGESTIÓN domina el conjunto con 3205 registros, seguida de VÍA CERRADA con 1021 registros, PELIGRO con 719 registros y ACCIDENTE con 125.

Con este resultado se evidencia que se reportan principalmente problemas de congestión y cierres, mientras que los accidentes y los peligros son relativamente menos frecuentes, aunque no menos relevantes en términos de riesgo.

Desde la perspectiva de patrones puntuales, esta distribución sugiere que la intensidad del proceso puntual variará según el tipo de evento algunos procesos son mucho más densos como “congestión” que otros como “accidentes”.

# Calcular la frecuencia de cada tipo de evento en Trama_Waze
frecuencia_eventos <- df %>%
  group_by(tipo_evento) %>%                 # Agrupar por tipo de evento
  summarise(Frecuencia = n()) %>%     # Contar la frecuencia de cada tipo
  arrange(desc(Frecuencia))           # Ordenar por frecuencia descendente

# Crear un gráfico de barras con ggplot2 usando los datos de Trama_Waze
ggplot(frecuencia_eventos, aes(x = tipo_evento, y = Frecuencia, fill = tipo_evento)) +
  geom_bar(stat = "identity") +
  theme_minimal() +
  labs(title = "Distribución de Tipos de Eventos en Trama Waze", 
       x = "Tipo de Evento", y = "Frecuencia") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +  # Rotar etiquetas para mejor visualización
  scale_fill_brewer(palette = "Set2")  # Utilizar una paleta de colores predefinida

Selección de eventos por tipo y día

Se filtran los eventos de “PELIGRO” ocurridos el día 26 y se almacenan en peligro26. Luego se reconstruyen las coordenadas de latitud y longitud dividiendo las variables location_x y location_y por potencias de 10, en función del número de dígitos. La transformación de estos datos buscan re-escalar las coordenadas para que caigan en el rango geográfico correcto.

Luego se filtra por rango (lat > 4 & lat < 5) actúa como control de calidad espacial, eliminando puntos “falsos” que quedan fuera de la zona de estudio.

Con leaflet se crea un mapa de puntos interactivo, donde cada evento se visualiza como un círculo y se etiqueta con la hora. Este mapa de “Mapa de Riesgos” permite observar si los peligros se concentran en ciertos tramos viales, identificar franjas horarias donde se concentra la ocurrencia de eventos.

# Identificar eventos PELIGRO del día 26
pos <- which(df$tipo_evento == "PELIGRO" & dia == 26)
peligro26 <- df[pos,]
# Cargar librerías necesarias para visualización
library(mapview)
library(leaflet)

# Ajustar las coordenadas de latitud y longitud
peligro26$lat <- peligro26$location_y / 10^(nchar(peligro26$location_y) - 1)
peligro26$long <- peligro26$location_x / 10^(nchar(peligro26$location_x) - 3)

# Filtrar eventos dentro del rango geográfico adecuado
peligro26 <- peligro26[peligro26$lat > 4 & peligro26$lat < 5,]

# Crear un mapa interactivo con leaflet
m26_peligro <- leaflet() %>%
  addTiles() %>%
  addCircleMarkers(lng = peligro26$long, lat = peligro26$lat,
                   clusterOptions = markerClusterOptions(),
                   label = peligro26$hora) %>%
  addControl(html = "<h3>Mapa de Riesgos</h3>", position = "topleft")

# Mostrar el mapa
m26_peligro

Al usar leaflet.extras::addHeatmap sobre peligro26, se genera un mapa de calor kernel donde la intensidad se basa en hora.

Desde la perspectiva de patrones puntuales se están realizando una estimación de densidad de intensidad (λ(x)) de forma no paramétrica.

Las zonas con colores cálidos (amarillo/rojo) corresponden a áreas donde se concentran más reportes de peligro, ponderados por la hora.

Se evidencia que la “nube de puntos” en una superficie continua de riesgo, lo que indica que son “corredores de alta peligrosidad”.

# Cargar las librerías necesarias
library(leaflet)
library(dplyr)
library(leaflet.extras)

# Filtrar datos relevantes de peligro26
peligro26 <- peligro26 %>%
  filter(lat > 4 & lat < 5, long > -75 & long < -73)  # Ajustar las coordenadas de interés

# Crear un mapa interactivo con leaflet y addHeatmap
leaflet(peligro26) %>%
  addProviderTiles("OpenStreetMap") %>%  # Añadir la capa base del mapa
  addHeatmap(
    lng = ~long, lat = ~lat,               # Especificar las columnas de longitud y latitud
    intensity = ~hora,                     # Intensidad opcional basada en la hora (o cualquier otra variable)
    blur = 20,                             # Nivel de desenfoque del mapa de calor
    max = 0.08,                            # Ajustar el valor máximo para la intensidad
    radius = 15                            # Radio de cada punto en el mapa de calor
  ) %>%
  addLegend("bottomright",                 # Añadir leyenda
            title = "Mapa de Calor de Riesgos",
            colors = c("blue", "green", "yellow", "red"),
            labels = c("Bajo", "Moderado", "Alto", "Muy Alto"))

Se repite el mismo flujo para VÍA CERRADA con el día 26 filtrando por tipo y día, se reconstruyen coordenadas, filtrado espacial y mapa de puntos con leaflet.

En el mapa se aprecia que los cierres de vía se concentran en pocos segmentos del corredor, lo cual sugiere heterogeneidad espacial en la ocurrencia de estos eventos.

# Filtrar eventos VÍA CERRADA del día 26
pos <- which(df$tipo_evento == "VÍA CERRADA" & dia == 26)
via_cerrada_26 <- df[pos,]

# Ajustar las coordenadas de latitud y longitud
via_cerrada_26$lat <- via_cerrada_26$location_y / 10^(nchar(via_cerrada_26$location_y) - 1)
via_cerrada_26$long <- via_cerrada_26$location_x / 10^(nchar(via_cerrada_26$location_x) - 3)

# Filtrar eventos dentro del rango geográfico adecuado
via_cerrada_26 <- via_cerrada_26[via_cerrada_26$lat > 4 & via_cerrada_26$lat < 5,]
# Cargar librería leaflet
require(leaflet)

# Crear el mapa interactivo
m26_via_cerrada = leaflet(via_cerrada_26) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~long, lat = ~lat,
                   clusterOptions = markerClusterOptions(),
                   label = ~hora) %>%
  addControl(html = "<h3>Mapa de Cierre de Vías<h3>", position = "topleft")

# Mostrar el mapa interactivo
m26_via_cerrada

Con spatstat, se define una ventana de observación owin y se construye el objeto ppp para via_cerrada_26.

con el test de cuadrantes se muestra una grilla donde solo uno o dos cuadrantes contienen números altos de eventos (21 y 636), mientras el resto están vacíos, lo que evidencia un patrón altamente agrupado.

El warning de “duplicated points” indica que múltiples cierres se reportan exactamente en la misma ubicación, lo que sugiere nodos críticos de la red vial donde recurrentemente se cierran las vías (intersecciones clave, accesos a autopistas, etc.).

# Cargar las librerías necesarias
library(spatstat)

# Definir la zona de interés
zona <- owin(xrange = c(-74.04331, -73.9929), yrange = c(4.885736, 4.948562))

# Crear un patrón de puntos espaciales a partir de los eventos VÍA CERRADA
patron_via_cerrada <- ppp(x = via_cerrada_26$long, y = via_cerrada_26$lat, window = zona)
## Warning: data contain duplicated points
# Graficar el test de cuadrantes
plot(quadratcount(patron_via_cerrada), main = "Patrón de Puntos y Test de Cuadrantes")

# Superponer los puntos sobre los cuadrantes
points(patron_via_cerrada, col = "red")

Al graficar Kest del patron_via_cerrada, se compara la K empírica con la esperada bajo CSR. Si la K empírica se sitúa por encima de la curva de CSR, se interpreta como agrupamiento (clustering) en las escalas espaciales donde se da esa diferencia. Por lo que los cierres no son aleatorios, sino que se concentran en segmentos específicos de la red.

# Gráfico independiente: Función K-Estimación
plot(Kest(patron_via_cerrada), main = "Función K-Estimación")

La estimación de densidad con patron_via_cerrada y su conversión a raster y luego a mapa de calor con leaflet produce una superficie continua donde se destacan los “hotspots” de cierres de vía.

La normalización de la intensidad facilita la interpretación bajo–muy alto, dónde es más probable que ocurra un cierre, dada la realización observada.

# Cargar las librerías necesarias
library(terra)
library(leaflet)
library(spatstat)

# Asegurarse de que el objeto patron_via_cerrada esté correctamente definido
# Crear un patrón de puntos espaciales utilizando los datos correctos (via_cerrada_26)
zona <- owin(xrange = c(-74.04331, -73.9929), yrange = c(4.885736, 4.948562))
patron_via_cerrada <- ppp(x = via_cerrada_26$long, y = via_cerrada_26$lat, window = zona)
## Warning: data contain duplicated points
# Calcular la densidad espacial
im1 <- density(patron_via_cerrada, sigma = 0.01)  # Ajusta sigma según sea necesario

# Convertir la densidad a un objeto raster usando terra
mapa_via_cerrada <- rast(im1)

# Convertir el raster a data.frame para leaflet
df_via_cerrada <- as.data.frame(mapa_via_cerrada, xy = TRUE)
colnames(df_via_cerrada) <- c("long", "lat", "intensity")

# Normalizar los valores de intensidad entre 0 y 1
df_via_cerrada$intensity <- (df_via_cerrada$intensity - min(df_via_cerrada$intensity)) / 
                            (max(df_via_cerrada$intensity) - min(df_via_cerrada$intensity))

# Crear un mapa interactivo usando leaflet
leaflet(df_via_cerrada) %>%
  addProviderTiles("OpenStreetMap") %>%  # Añadir la capa base
  addHeatmap(
    lng = ~long, lat = ~lat,              # Coordenadas de longitud y latitud
    intensity = ~intensity,               # Intensidad normalizada
    blur = 20,                            # Nivel de desenfoque
    max = 1,                              # Valor máximo de la intensidad normalizada
    radius = 15                           # Radio para reflejar la densidad
  ) %>%
  addLegend("bottomright",                # Añadir la leyenda
            title = "Mapa de Calor de Cierres de Vías",
            colors = c("blue", "green", "yellow", "red"),
            labels = c("Bajo", "Moderado", "Alto", "Muy Alto"))

Para ACCIDENTE el día 26 sigues la misma lógica que los anteriores; filtrado, reconstrucción de coordenadas, mapa de puntos, test de cuadrantes, función K y mapa de calor.

Aunque la frecuencia absoluta de ACCIDENTE es baja, el análisis de patrones puntuales es especialmente relevante porque permite identificar tramos de la vía con mayor propensión a accidentes, incluso si el número de casos es pequeño.

El test de cuadrantes y la función K vuelven a indicar agrupamiento, es decir, los accidentes no se distribuyen uniformemente por lo que hay segmentos que concentran el riesgo.

El mapa de calor de accidentes ofrece una representación más suave y comunicable de esos puntos negros de siniestralidad.

# Filtrar eventos de accidentes del día 26
pos <- which(df$tipo_evento == "ACCIDENTE" & dia == 26)
accidente_26 <- df[pos,]

# Ajustar las coordenadas de latitud y longitud
accidente_26$lat <- accidente_26$location_y / 10^(nchar(accidente_26$location_y) - 1)
accidente_26$long <- accidente_26$location_x / 10^(nchar(accidente_26$location_x) - 3)

# Filtrar eventos dentro del rango geográfico adecuado
accidente_26 <- accidente_26[accidente_26$lat > 4 & accidente_26$lat < 5,]
# Cargar librerías necesarias
library(leaflet)

# Crear el mapa interactivo
m26_accidente <- leaflet(accidente_26) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~long, lat = ~lat,
                   clusterOptions = markerClusterOptions(),
                   label = ~hora) %>%
  addControl(html = "<h3>Mapa de Accidentes</h3>", position = "topleft")

# Mostrar el mapa interactivo
m26_accidente
# Cargar las librerías necesarias
require(spatstat)

# Definir la zona de interés
zona = owin(xrange = c(-74.04331, -73.9929), yrange = c(4.885736, 4.948562))

# Crear un patrón de puntos espaciales a partir de los eventos ACCIDENTE
patron_accidente = ppp(x = accidente_26$long, y = accidente_26$lat, window = zona)
## Warning: data contain duplicated points
# Gráfico combinado: Test de Cuadrantes y Patrón de Puntos
par(mfrow = c(1, 1))  # Asegurarse de que solo haya una gráfica

# Graficar el test de cuadrantes
plot(quadratcount(patron_accidente), main = "Patrón de Puntos y Test de Cuadrantes")

# Superponer los puntos sobre los cuadrantes
points(patron_accidente, col = "red" )

# Calcular la función K-estimación
plot(Kest(patron_accidente))

# Cargar las librerías necesarias
library(terra)
library(leaflet)
library(spatstat)

# Asegurarse de que el objeto patron_accidente esté correctamente definido
# Usar las coordenadas correctas de los accidentes
zona <- owin(xrange = c(-74.04331, -73.9929), yrange = c(4.885736, 4.948562))
patron_accidente <- ppp(x = accidente_26$long, y = accidente_26$lat, window = zona)
## Warning: data contain duplicated points
# Calcular la densidad espacial
im1 <- density(patron_accidente)

# Convertir la densidad a un objeto raster usando terra
mapa_accidente <- rast(im1)

# Convertir el raster a data.frame para leaflet
df_accidente <- as.data.frame(mapa_accidente, xy = TRUE)
colnames(df_accidente) <- c("long", "lat", "intensity")

# Normalizar los valores de intensidad entre 0 y 1
df_accidente$intensity <- (df_accidente$intensity - min(df_accidente$intensity)) / 
                          (max(df_accidente$intensity) - min(df_accidente$intensity))

# Crear un mapa interactivo usando leaflet
leaflet(df_accidente) %>%
  addProviderTiles("OpenStreetMap") %>%  # Añadir la capa base
  addHeatmap(
    lng = ~long, lat = ~lat,              # Coordenadas de longitud y latitud
    intensity = ~intensity,               # Intensidad normalizada
    blur = 15,                            # Nivel de desenfoque
    max = 0.5,                              # Valor máximo de la intensidad normalizada
    radius = 10                           # Ajustar el radio de los puntos
  ) %>%
  addLegend("bottomright",                # Añadir la leyenda para interpretar el mapa de calor
            title = "Mapa de Calor de Accidentes",
            colors = c("blue", "green", "yellow", "red"),
            labels = c("Bajo", "Moderado", "Alto", "Muy Alto"))

En el caso de CONGESTIÓN el día 26, el subconjunto congestion_26 es mucho más numeroso, lo que hace al patrón más denso. De nuevo se construye un mapa de puntos en leaflet, donde se ve la saturación de eventos a lo largo del corredor principal.

El patrón de puntos con ppp y test de cuadrantesse muestran cuadrantes con conteos altos lo que indican embotellamientos que se concentran en segmentos específicos, no en toda el área de forma homogénea.

La función K empírica que crece por encima de K bajo CSR refuerza la conclusión de agrupamiento espacial.

El mapa de calor de densidad, con un blur y radio mayores, traduce este patrón denso en una superficie continua donde quedan muy claros los corredores críticos de congestión.

# Filtrar eventos de congestión del día 26
pos <- which(df$tipo_evento == "CONGESTIÓN" & dia == 26)
congestion_26 <- df[pos,]

# Ajustar las coordenadas de latitud y longitud
congestion_26$lat <- congestion_26$location_y / 10^(nchar(congestion_26$location_y) - 1)
congestion_26$long <- congestion_26$location_x / 10^(nchar(congestion_26$location_x) - 3)

# Filtrar eventos dentro del rango geográfico adecuado
congestion_26 <- congestion_26[congestion_26$lat > 4 & congestion_26$lat < 5,]
# Cargar las librerías necesarias
library(leaflet)

# Crear el mapa interactivo
m26_congestion <- leaflet(congestion_26) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~long, lat = ~lat,
                   clusterOptions = markerClusterOptions(),
                   label = ~hora) %>%
  addControl(html = "<h3>Mapa de Congestión</h3>", position = "topleft")

# Mostrar el mapa interactivo
m26_congestion
# Cargar las librerías necesarias
library(spatstat)

# Definir la zona de interés
zona <- owin(xrange = c(-74.04331, -73.9929), yrange = c(4.885736, 4.948562))

# Crear un patrón de puntos espaciales a partir de los eventos CONGESTIÓN
patron_congestion <- ppp(x = congestion_26$long, y = congestion_26$lat, window = zona)
## Warning: data contain duplicated points
# Visualizar el patrón de puntos
par(mfrow = c(1, 1))  # Asegurarse de que solo haya una gráfica

# Graficar el test de cuadrantes
plot(quadratcount(patron_congestion), main = "Patrón de Puntos y Test de Cuadrantes")

# Superponer los puntos sobre los cuadrantes
points(patron_congestion, col = "red")

# Calcular la función K-estimación
plot(Kest(patron_congestion))

# Cargar las librerías necesarias
library(leaflet)
library(terra)

# Definir el patrón de puntos
zona <- owin(xrange = c(-74.04331, -73.9929), yrange = c(4.885736, 4.948562))
patron_congestion <- ppp(x = congestion_26$long, y = congestion_26$lat, window = zona)
## Warning: data contain duplicated points
# Calcular la densidad espacial del patrón de puntos
im1 <- density(patron_congestion)

# Convertir la densidad en un raster utilizando terra
mapa_congestion <- rast(im1)

# Convertir el objeto raster a un data.frame para usarlo en leaflet
df_congestion <- as.data.frame(mapa_congestion, xy = TRUE)
colnames(df_congestion) <- c("long", "lat", "intensity")

# Normalizar los valores de intensidad entre 0 y 1
df_congestion$intensity <- (df_congestion$intensity - min(df_congestion$intensity)) / 
                           (max(df_congestion$intensity) - min(df_congestion$intensity))

# Crear un mapa interactivo usando leaflet
leaflet(df_congestion) %>%
  addProviderTiles("OpenStreetMap") %>%  # Añadir la capa base
  addHeatmap(
    lng = ~long, lat = ~lat,              # Coordenadas de longitud y latitud
    intensity = ~intensity,               # Intensidad normalizada
    blur = 35,                            # Incrementar el desenfoque para suavizar el mapa
    max = max(df_congestion$intensity) * 2,  # Ajustar el valor máximo de intensidad
    radius = 25                           # Aumentar el radio para que se vea más suave
  ) %>%
  addLegend("bottomright",                # Añadir la leyenda para interpretar el mapa de calor
            title = "Mapa de Calor de Congestión",
            colors = c("blue", "green", "yellow", "red"),
            labels = c("Bajo", "Moderado", "Alto", "Muy Alto"))

Finalmente, con leafsync::sync(m26_peligro, m26_accidente, m26_congestion, m26_via_cerrada) se sincronizan los mapas interactivos de los cuatro tipos de evento.

Esto permite comparar visualmente, en el mismo marco espacial, dónde coinciden hotspots de peligro, accidentes, cierres y congestión.

Tambien facilita identificar zonas donde distintas manifestaciones del problema vial se superponen, por ejemplo: segmentos donde hay a la vez alta congestión y alta frecuencia de accidentes o peligros, lo cual podría priorizar intervenciones de infraestructura o gestión del tráfico.

# Sincronizar los mapas interactivos de distintos tipos de eventos
leafsync::sync(m26_peligro, m26_accidente, m26_congestion, m26_via_cerrada)