# Paquetes a utilizar
library(readr)
library(dplyr)
library(purrr)
library(sf)
library(mapview)
library(osrm)
library(ggplot2)
library(patchwork)

Objetivo

Estimar tiempos de viaje a servicios esenciales, como insumo para estadísticas que fundamenten programas de urbanización de asentamientos precarios en la ciudad de Montevideo.

Fuentes de datos

Carga y preparación de datos

asentamientos <- read_sf("~/Downloads/MVD/v_ai_asentamientos_publicables/v_ai_asentamientos_publicables.shp") |> group_by(CODIGOINE, NOMBRE, ESTADODESC) |> 
    summarise() |> 
    st_transform(4326) |> 
    st_make_valid()

educacion <- read_sf("~/Downloads/MVD/ceip/CEIP.shp") |> 
    filter(Departamen == "MONTEVIDEO")|> 
    rename(id = OBJECTID) |> 
    st_transform(4326)

salud <- mutate(read_sf("~/Downloads/MVD/Centros_de_salud_UY/Centros_de_salud_UY.shp"),
                 tipo = "Centro de salud") |> 
    rbind(mutate(read_sf("~/Downloads/MVD/Policlinicas/Policlinicas.shp"),
                 tipo = "Policlínica")) |> 
    rbind(mutate(read_sf("~/Downloads/MVD/Hospitales_UY/Hospitales_UY.shp"),
                 tipo = "Hospital"))|> 
    filter(departamen == "MONTEVIDEO") |> 
    st_transform(4326)

Universo de análisis

Asentamientos Precarios

Los datos oficiales consideran la existencia de 348 asentamientos irregulares en la ciudad, con cinco categorías de intervención:

mapview(summarise(group_by(asentamientos, ESTADODESC)),
        layer.name = "Status")

Todos los asentamientos serán considerados para el análisis.

Salud

En Uruguay, la diferencia principal entre los tipos de establecimiento de salud pública es el nivel de complejidad y los servicios ofrecidos: una policlínica brinda atención ambulatoria y primaria, un centro de salud integra policlínicas y servicios de prevención con atención primaria, y un hospital es un centro de mayor complejidad con internación, urgencias, cirugías y tratamientos especializados.

mapview(list(st_union(asentamientos), select(salud, tipo)),
        layer.name = c("Asentamientos precarios", "Establecimientos de Salud Pública"))

Para el análisis consideraremos Hospitales y Centros de Salud, ya que son los establecimientos que brindan servicios de salud preventiva.

salud <- salud |> 
    filter(tipo %in% c("Hospital", "Centro de salud"))

Educación

mapview(list(st_union(asentamientos), select(educacion, Tipo_de_Ed)),
        layer.name = c("Asentamientos precarios", "Establecimientos de Educación Pública"))

Para el análisis consideraremos establecimientos de educación primaria común.

educacion <- educacion |> 
    filter(Tipo_de_Ed == "ESCUELAS COMUNES")

Estimación de distancias

Estimaremos la distancia ente asentamientos y puntos de servicio con los siguientes parámetros:

Para la estimación de tiempo y distancia de viajes se utilizará OSRM (Open Source Routing Machine), un sistema de ruteo de código abierto que calcula rutas óptimas utilizando como referencia las calles georreferenciadas en la base de datos libre OpenStreetMap (OSM).

## Preparativos

# El ruteo requiere de una instancia local de OSRM
# véase:
# https://rpubs.com/HAVB/osrm
# IMPORTANTE configurar osrm para rutas a pie ("foot"),
# en la sección "Preprocesar los datos de ruteo" del tutorial
options(osrm.server = "http://127.0.0.1:5000/")

# Función auxiliar para encontrar el establecimiento más cercano a cada origen
obtener_df_viajes <- function(origins, destinations) {
    
    origins <- sf::st_centroid(origins)
    destinations <- sf::st_centroid(destinations)
    
    id_closest <- unlist(nngeo::st_nn(origins, destinations))
    
    # Return routing ready dataframe
    data.frame(
        orig_id = origins[["CODIGOINE"]],
        orig_X = st_coordinates(origins)[, 1],
        orig_Y = st_coordinates(origins)[, 2],
        dest_id = destinations[id_closest, ][["id"]], 
        dest_X = st_coordinates(destinations[id_closest, ])[, 1],
        dest_Y = st_coordinates(destinations[id_closest, ])[, 2],
        stringsAsFactors = FALSE
    )
    
} 

viajes_educacion <- obtener_df_viajes(asentamientos, educacion)
viajes_salud <- obtener_df_viajes(asentamientos, salud)

Como ejemplo, calculamos y mostramos uno de los viajes desde asentamiento hasta hospital o centro de salud más cercano:

viaje <- osrmRoute(src = c(viajes_salud[1,]$orig_X, viajes_salud[1,]$orig_Y),
          dst = c(viajes_salud[1,]$dest_X, viajes_salud[1,]$dest_Y),
          overview = "full")

mapview(viaje)

Resolvemos todos los viajes

get_routes <- function(trips_df) {
    
    get_route <- purrr::possibly(
        function(orig_id, orig_X, orig_Y, dest_id, dest_X, dest_Y) {
            
            route <- osrm::osrmRoute(src = c(orig_X, orig_Y),
                                     dst = c(dest_X, dest_Y),
                                     overview = FALSE)
            data.frame(orig_id, 
                       dest_id, 
                       duration = route["duration"], 
                       distance = route["distance"],
                       stringsAsFactors = FALSE) 
        },
        otherwise = NULL)
    
    purrr::pmap_df(trips_df, get_route)
}


rutas_educacion <- get_routes(trips_df = viajes_educacion) 

rutas_salud <- get_routes(trips_df = viajes_salud) 

Análisis básico de resultados

Distribución de tiempo y distancia de viajes

# Distancia recorrida

# Educación
p1 <- ggplot(rutas_educacion, aes(x = distance)) +
    geom_histogram(color = "white", fill = "salmon",
                   bins = 30,
                   alpha = 0.7) +
    scale_x_continuous(breaks = seq(0, ceiling(max(rutas_educacion$distance)), 1)) +
    theme_minimal(base_size = 14) +
    labs(title = "Distribución de distancias de viaje\n", subtitle = "a educación pública (primaria)", x = "km", y = "casos")

# Salud
p2 <- ggplot(rutas_salud, aes(x = distance)) +
    geom_histogram(color = "white", fill = "lightblue",
                   bins = 30,
                   alpha = 0.7) +
    scale_x_continuous(breaks = seq(0, ceiling(max(rutas_salud$distance)), 1)) +
    theme_minimal(base_size = 14) +
    labs(subtitle = "a salud pública (hospitales y centros de salud)", x = "km", y = "casos")

# Combinar
p1 / p2

# Tiempo de viaje

# Educación
p1 <- ggplot(rutas_educacion, aes(x = duration)) +
    geom_histogram(color = "white", fill = "salmon",
                   bins = 30,
                   alpha = 0.7) +
    scale_x_continuous(breaks = seq(0, ceiling(max(rutas_educacion$duration)), 10)) +
    theme_minimal(base_size = 14) +
    labs(title = "Distribución de tiempos de viaje\n", subtitle = "a educación pública (primaria)", 
         x = "minutos", y = "casos")

# Salud
p2 <- ggplot(rutas_salud, aes(x = duration)) +
    geom_histogram(color = "white", fill = "lightblue",
                   bins = 30,
                   alpha = 0.7) +
    scale_x_continuous(breaks = seq(0, ceiling(max(rutas_salud$duration)), 10)) +
    theme_minimal(base_size = 14) +
    labs(subtitle = "a salud pública (hospitales y centros de salud)", x = "minutos", y = "casos")

# Combinar
p1 / p2

Métricas

mean(rutas_educacion$distance > 2)
## [1] 0.08045977
mean(rutas_salud$duration > 60)
## [1] 0.1293103