library(tidyverse)
library(sf)
library(leaflet)
library(osrm)
library(tidygeocoder)

Clase 05: geolocalización y georreferenciación

Geolocalización simple con geo() de tidygeocoder()

Los dos parámetrso que necesita para levantar una dirección exacta son adress() que es la dirección exacta y method() que es el servicio que vamos a usar para levantarla. En este caso se usa nominatim que es de OSM

FLACSO_arg <- geo(address = "Tucumán 1966, CABA, Buenos Aires, Argentina",
                  method = "osm")

Vemos qué resultado se obtuvo

FLACSO_arg
## # A tibble: 1 × 3
##   address                                       lat  long
##   <chr>                                       <dbl> <dbl>
## 1 Tucumán 1966, CABA, Buenos Aires, Argentina -34.6 -58.4

Ahora vamos a usar la función leaflet() para poder generar un mapa interactivo de la sede de FLACSO

leaflet(FLACSO_arg) %>%
  addTiles() %>% 
  addMarkers(~long, ~lat)

Si ponemos addtiles nos da por defecto el mapa de OSM pero con la función addprovidertiles podríamos cambiar ese mapa por otro, como en el siguiente caso:

leaflet(FLACSO_arg) %>%
  addProviderTiles(providers$CartoDB.Positron) %>% 
  addMarkers(~long, ~lat)

Ahora, agreguemos un pop-up del punto que mapeamos

leaflet(FLACSO_arg) %>%
  addProviderTiles(providers$CartoDB.Positron) %>% 
  addMarkers(~long, ~lat,
             popup = "FLACSO")

Ahora queremos poder agregar varias universidades y para eso vamos a usar la función geocode() también del paquete tidygeocoder y un dataset que contenga la lista de direcciones de las universidades, extraído del portal de datos abiertos de BA.

universidades <- read.csv("data/universidades_caba.csv",
                          encoding = "UTF-8",
                          stringsAsFactors = TRUE)
dim(universidades)
## [1] 141   5
head(universidades)
##   regimen
## 1 Privado
## 2 Privado
## 3 Privado
## 4 Privado
## 5 Privado
## 6 Privado
##                                                                                    nombre
## 1                                  FLACSO (Facultad Latinoamericana de Ciencias Sociales)
## 2                                                   Instituto Tecnológico de Buenos Aires
## 3                                                           Instituto Universitario CEMIC
## 4                                                           Instituto Universitario CEMIC
## 5                 Instituto Universitario de Ciencias de la Salud de la Fundación Barceló
## 6 Instituto Universitario de Salud Mental de la Asociación Psicoanalítica de Buenos Aires
##           direccion_norm        barrio    comuna
## 1           Ayacucho 555     Balvanera  Comuna 3
## 2 Av. Eduardo Madero 399 Puerto Madero  Comuna 1
## 3            Galván 4102      Saavedra Comuna 12
## 4        Valdenegro 4337      Saavedra Comuna 12
## 5     Av. Las Heras 2191      Recoleta  Comuna 2
## 6             Maure 1850       Palermo Comuna 14
summary(universidades)
##     regimen  
##  Privado:86  
##  Público:55  
##              
##              
##              
##              
##              
##                                                                        nombre  
##  Universidad de Buenos Aires                                              :20  
##  Universidad de Morón                                                     :12  
##  Universidad del Salvador                                                 :11  
##  Universidad Nacional de las Artes                                        : 9  
##  Universidad de Ciencias Empresariales y Sociales                         : 6  
##  Pontificia Universidad Católica Argentina Santa María de los Buenos Aires: 5  
##  (Other)                                                                  :78  
##                                   direccion_norm         barrio  
##  Av. Luis María Campos 480               :  3    Balvanera  :24  
##  Paraguay 1457                           :  2    Recoleta   :17  
##  Viamonte 427                            :  2    San Nicolas:16  
##  25 de Mayo 457                          :  1    Palermo    :12  
##  25 de Mayo 586                          :  1    Retiro     :10  
##  Almirante Juan Pablo Sáenz Valiente 1010:  1    Almagro    : 9  
##  (Other)                                 :131    (Other)    :53  
##        comuna  
##  Comuna 1 :44  
##  Comuna 3 :24  
##  Comuna 2 :17  
##  Comuna 14:12  
##  Comuna 5 : 9  
##  Comuna 13: 7  
##  (Other)  :28

El campo que nos ayudará a georreferenciar todos los registros será el que se llama direccion_norm y su contenido debería estar escrito lo más claro posible y con el mayor nivel de detalle (Dirección, Ciudad, Provincia, País) para garantizar el éxito de la geolocalización.

Como en nuestros datos solo tenemos la calle y altura, utilizaremos paste() para sumarle a la dirección la ciudad y el país (Ciudad de Buenos Aires, Argentina) donde queremos ubicar la información.

universidades <- universidades %>%
  mutate(direccion = paste(direccion_norm, ", Ciudad de Buenos Aires, Argentina", sep=""))
head(universidades)
##   regimen
## 1 Privado
## 2 Privado
## 3 Privado
## 4 Privado
## 5 Privado
## 6 Privado
##                                                                                    nombre
## 1                                  FLACSO (Facultad Latinoamericana de Ciencias Sociales)
## 2                                                   Instituto Tecnológico de Buenos Aires
## 3                                                           Instituto Universitario CEMIC
## 4                                                           Instituto Universitario CEMIC
## 5                 Instituto Universitario de Ciencias de la Salud de la Fundación Barceló
## 6 Instituto Universitario de Salud Mental de la Asociación Psicoanalítica de Buenos Aires
##           direccion_norm        barrio    comuna
## 1           Ayacucho 555     Balvanera  Comuna 3
## 2 Av. Eduardo Madero 399 Puerto Madero  Comuna 1
## 3            Galván 4102      Saavedra Comuna 12
## 4        Valdenegro 4337      Saavedra Comuna 12
## 5     Av. Las Heras 2191      Recoleta  Comuna 2
## 6             Maure 1850       Palermo Comuna 14
##                                                   direccion
## 1           Ayacucho 555, Ciudad de Buenos Aires, Argentina
## 2 Av. Eduardo Madero 399, Ciudad de Buenos Aires, Argentina
## 3            Galván 4102, Ciudad de Buenos Aires, Argentina
## 4        Valdenegro 4337, Ciudad de Buenos Aires, Argentina
## 5     Av. Las Heras 2191, Ciudad de Buenos Aires, Argentina
## 6             Maure 1850, Ciudad de Buenos Aires, Argentina

Las funciones de geolocalización suelen ser costosas computacionalmente y pueden tardar varios minutos en ejecutarse ya que se calcula una demora aproximada de 1 segundo por registro. Por lo tanto, si tenemos 141 registros, tardaríamos 141 segundos o 2,35 minutos.

A su vez, cada registro que intentemos geolocalizar será una nueva “consulta” que le hacemos a la base de datos de OpenStreetMap. Por lo tanto, si llegamos a tener algún “micro corte” de internet mientras estamos ejecutando el chunk, podría llegar a arrojar algún error de conexión y no georreferenciar algun/os registros. Para evitar esto es necesario que antes de seguir activemos Sys.sleep() que suspende la ejecución de funciones durante un intervalo de tiempo específico. En este caso, asignaremos 1 segundo.

Sys.sleep(1)
universidades <- universidades %>%
  geocode(address = direccion, method = "osm")
head(universidades)
## # A tibble: 6 × 8
##   regimen nombre              direccion_norm barrio comuna direccion   lat  long
##   <fct>   <fct>               <fct>          <fct>  <fct>  <chr>     <dbl> <dbl>
## 1 Privado FLACSO (Facultad L… Ayacucho 555   Balva… Comun… Ayacucho… -34.6 -58.4
## 2 Privado Instituto Tecnológ… Av. Eduardo M… Puert… Comun… Av. Edua… -34.6 -58.4
## 3 Privado Instituto Universi… Galván 4102    Saave… Comun… Galván 4… -34.6 -58.5
## 4 Privado Instituto Universi… Valdenegro 43… Saave… Comun… Valdeneg… -34.6 -58.5
## 5 Privado Instituto Universi… Av. Las Heras… Recol… Comun… Av. Las … -34.6 -58.4
## 6 Privado Instituto Universi… Maure 1850     Paler… Comun… Maure 18… -34.6 -58.4

Visualizamos las direcciones de recién

leaflet(universidades) %>%
  addProviderTiles(providers$CartoDB.Positron) %>% 
  addMarkers(~long, ~lat,
             popup = ~nombre)

Geolozalización inversa

La tercera y última función de geolocalización que veremos hoy se llama reverse_geocode() y se utiliza cuando necesitamos hacer lo inverso a lo que hicimos hasta ahora. Es decir, obtener las direcciones a partir de las coordenadas. Para eso vamos a levantar primero el data

estaciones <- read.csv("data/estaciones_bicicletas_publicas.csv",
                            encoding = "UTF-8",
                            stringsAsFactors = TRUE)
dim(estaciones)
## [1] 407   5
head(estaciones)
##    id numero_de_estacion                 nombre   latitud  longitud
## 1 240                239                  VIDAL -34.55814 -58.46726
## 2  95                 95              ESMERALDA -34.59565 -58.36916
## 3  30                 30                   PEÑA -34.59082 -58.39737
## 4 121                121                  YATAY -34.60117 -58.42851
## 5 473                216         EMILIO LAMARCA -34.61340 -58.49336
## 6 166                166 CEMENTERIO DE RECOLETA -34.58835 -58.39415

Vemos las estaciones en un mapa

leaflet(estaciones) %>%
  addProviderTiles(providers$CartoDB.Positron) %>% 
  addMarkers(~longitud, ~latitud,
             popup = ~nombre)

Ahora vamos a usar la función para obtener la dirección exacta a partir de introducir las coordenadas de latitud y longitud. En vez de tener un campo de adress() vamos a tener latitud y longitud

estaciones <- estaciones %>%
  reverse_geocode(lat=latitud, long=longitud, method = "osm")

Ahopra podemos agregar la dirección al mapa interactivo

leaflet(estaciones) %>%
  addProviderTiles(providers$CartoDB.Positron) %>% 
  addMarkers(~longitud, ~latitud,
             popup = ~address)

Análisis de distancias isócronas

Las isócronas muestran todo el área al que se puede llegar desde determinado punto en el mismo tiempo y a una velocidad específica. Son una buena alternativa al clásico cálculo de las áreas de influencia (o buffer).

El paquete osrm que utilizaremos a continuación se compone de las siguientes 6 funciones:

osrmIsochrone(): polígonos isócronos
osrmIsodistance(): polígonos isodistantes
osrmNearest():puntos más cercanos
osrmRoute(): camino más corto entre 2 puntos
osrmTable(): matriz de tiempos de viaje entre puntos
osrmTrip(): viaje entre múltiples puntos

Hoy trabajaremos con osrmIsochrone() y con osrmRoute().

Primero, ajustemos todos los parámetros o argumentos necesarios para responder la pregunta: ¿Cuántas estaciones de Ecobici (bicicletas públicas) se encuentran a menos de 20 minutos caminando desde la sede de FLACSO Argentina? ¿Y cuál es la dirección exacta de cada una?

loc: Coordenadas sobre las cuales calcular las isócronas. En este caso “long” y “lat” del dataset FLACSO_arg.
breaks: Rango de minutos a analizar. En nuestro caso de 0 a 20 min cada 5 min.
res: Cantidad de puntos que se utilizan para calcular la isócrona. Por defecto el parámetro viene en 30 pero para mejorar la precisión utilizaremos 40.
osrm.profile: Modo de transporte en el que queremos realizar el ruteo: car, bike o foot. Utilizaremos “foot”.
isocrona <- osrmIsochrone(loc = c(FLACSO_arg$long, FLACSO_arg$lat),
                     breaks = seq(from=0,to=20,by=5),
                     res = 35,
                     osrm.profile = "foot")

Visualizamos los polígonos en un ggplot

ggplot()+
  geom_sf(data = isocrona, aes(fill= isomax), color= NA)+
  scale_fill_viridis_c(direction = -1)                                   #No se ve nada, genera el casi gráfico pero no los polígonos pintados

Vemos los datos en un mapa interactivo

leaflet(isocrona) %>%
  addProviderTiles(providers$CartoDB.Positron) %>% 
  addPolygons(fillColor = ~colorBin("YlOrRd", domain = isocrona$isomax)(isomax),
  color = NA,
  fillOpacity = 0.5)%>%
  addMiniMap(tiles=providers$CartoDB.Positron)

Ahora le garego la ubicación de FLACSO

leaflet(isocrona) %>%
  addProviderTiles(providers$CartoDB.Positron) %>% 
  addPolygons(fillColor = ~colorBin("YlOrRd", domain = isocrona$isomax)(isomax),
  color = NA,
  fillOpacity = 0.5) %>%
  addMarkers(data=FLACSO_arg)%>%
  addMiniMap(tiles=providers$CartoDB.Positron)

Listo, ya tenemos nuestro mapa de isócronas y pudimos conocer que zonas están a menos de 20 minutos a pié desde la FLACSO. Ahora utilicemos estos polígonos para calcular cuántas estaciones de Ecobici se encuentran dentro de las isócronas calculadas.

Tal como vimos en las clases anteriores, para cruzar 2 datasets en el espacio es necesario que ambos sean geográficos y tengan el mismo CRS. Veamos cuál es el de las isócronas.

st_crs(isocrona)
## Coordinate Reference System:
##   User input: EPSG:4326 
##   wkt:
## GEOGCRS["WGS 84",
##     ENSEMBLE["World Geodetic System 1984 ensemble",
##         MEMBER["World Geodetic System 1984 (Transit)"],
##         MEMBER["World Geodetic System 1984 (G730)"],
##         MEMBER["World Geodetic System 1984 (G873)"],
##         MEMBER["World Geodetic System 1984 (G1150)"],
##         MEMBER["World Geodetic System 1984 (G1674)"],
##         MEMBER["World Geodetic System 1984 (G1762)"],
##         MEMBER["World Geodetic System 1984 (G2139)"],
##         ELLIPSOID["WGS 84",6378137,298.257223563,
##             LENGTHUNIT["metre",1]],
##         ENSEMBLEACCURACY[2.0]],
##     PRIMEM["Greenwich",0,
##         ANGLEUNIT["degree",0.0174532925199433]],
##     CS[ellipsoidal,2],
##         AXIS["geodetic latitude (Lat)",north,
##             ORDER[1],
##             ANGLEUNIT["degree",0.0174532925199433]],
##         AXIS["geodetic longitude (Lon)",east,
##             ORDER[2],
##             ANGLEUNIT["degree",0.0174532925199433]],
##     USAGE[
##         SCOPE["Horizontal component of 3D system."],
##         AREA["World."],
##         BBOX[-90,-180,90,180]],
##     ID["EPSG",4326]]

Como ahora sé que es 4326 voy a pasar el dataset de las estaciones a uno geográfico con 4326

estaciones_geo <- estaciones %>%
  st_as_sf(coords=c("longitud", "latitud"), crs=4326)
sf_use_s2(FALSE)    #Desactivo para que no me de error geográfico

Finalmente, utilicemos st_intersection() para realizar la intersección entre ambos datasets y conocer cuántas estaciones se solapan con la isócrona calculada.

estaciones_geo <- st_intersection(estaciones_geo, isocrona)
estaciones_geo
## Simple feature collection with 19 features and 7 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: -58.40609 ymin: -34.60942 xmax: -58.38236 ymax: -34.59082
## Geodetic CRS:  WGS 84
## # A tibble: 19 × 8
##       id numero_de_estacion nombre                   address  id.1 isomin isomax
##  * <int>              <int> <fct>                    <chr>   <int>  <dbl>  <dbl>
##  1   589                 16 MUSEO DEL AGUA           1992, …     1      0      5
##  2    83                 83 PARANÁ                   485, M…     2      5     10
##  3    76                 76 AYACUCHO                 176, A…     2      5     10
##  4   171                171 PASTEUR                  2309, …     2      5     10
##  5   468                 84 TEATRO PICADERO          Havann…     2      5     10
##  6   174                174 MINISTERIO DE EDUCACION  1699, …     3     10     15
##  7   144                144 PUEYRREDÓN               694, B…     3     10     15
##  8    27                 27 MONTEVIDEO               Univer…     3     10     15
##  9    33                 33 FACULTAD DE MEDICINA     2202, …     3     10     15
## 10    45                 45 URUGUAY                  585, U…     3     10     15
## 11   131                131 HOSPITAL DE CLÍNICAS     897, P…     3     10     15
## 12    30                 30 PEÑA                     2290, …     4     15     20
## 13    69                 69 ECUADOR                  1199, …     4     15     20
## 14   544                 78 TEATRO COLÓN             682, C…     4     15     20
## 15   567                228 UNIVERSIDAD DEL SALVADOR 1307, …     4     15     20
## 16   162                162 LARREA Y BARTOLOMÉ MITRE 162 - …     4     15     20
## 17    71                 71 CERRITO                  1219, …     4     15     20
## 18     8                  8 CONGRESO                 Virrey…     4     15     20
## 19    64                 64 RIOBAMBA                 1879, …     4     15     20
## # ℹ 1 more variable: geometry <POINT [°]>

En el resultado se observa que a menos de 20 minutos caminando desde la FLACSO podemos encontrar 19 estaciones de bicicletas públicas.

Veamos un gráfico de barras que nos muestre en qué rango de distancia se ubica cada estación.

ggplot()+
  geom_bar(data=estaciones_geo, aes(x=as.factor(isomax)), fill="turquoise4")+
  labs(title="Estaciones de Ecobici cercanas a FLACSO",
       x="Distancia máxima (minutos)",
       y="Cantidad de estaciones")+
  theme_light()

Vemos todo en un mapa interactivo

leaflet(isocrona) %>%
  addProviderTiles(providers$CartoDB.Positron) %>% 
  addPolygons(fillColor = ~colorBin("YlOrRd", domain = isocrona$isomax)(isomax),
  color = NA,
  fillOpacity = 0.5) %>%
  addMarkers(data=FLACSO_arg,
             popup = "FLACSO") %>%
  addCircles(data=estaciones_geo,
             popup = ~nombre,
             color = "black")%>%
  addMiniMap(tiles=providers$CartoDB.Positron)

Ruteo simple

Para poder ver las rutas vamos a quedarnos solo con la estación que está a menos de 5’ caminando desde FLACSO

estacion1 <- estaciones_geo %>%
  filter(isomax==5)

La estación más cercana se llama “MUSEO DEL AGUA” y está ubicada en Viamonte 1992, Balvanera. Hagamos un ruteo con osrmRoute() para poder calcular la distancia y el tiempo de viaje exacto entre la sede de FLACSO y la estación en cuestión.

ruteo1 <- osrmRoute(src = c(FLACSO_arg$long, FLACSO_arg$lat),
                    dst = estacion1,
                          overview = "full",
                          osrm.profile = "foot")
ruteo1
## Simple feature collection with 1 feature and 4 fields
## Geometry type: LINESTRING
## Dimension:     XY
## Bounding box:  xmin: -58.39568 ymin: -34.60215 xmax: -58.39501 ymax: -34.60092
## Geodetic CRS:  WGS 84
##       src dst duration distance                       geometry
## src_1 src   1 2.791667   0.2044 LINESTRING (-58.39501 -34.6...

Y ahora agregamos el trayecto a un mapa interactivo

leaflet(ruteo1) %>% 
    addProviderTiles(providers$CartoDB.Positron) %>% 
    addPolylines(color = "red",
                 label = paste("Distancia:", ruteo1$distance, "|", 
                               "Duración:", ruteo1$duration))%>%
  addMiniMap()

Le agregamos todos los datos previos

leaflet(ruteo1) %>% 
    addProviderTiles(providers$CartoDB.Positron) %>% 
    addPolylines(color = "red",
                 label = paste("Distancia:", ruteo1$distance, "|", 
                               "Duración:", ruteo1$duration)) %>%
  addCircleMarkers(data=FLACSO_arg, ~long, ~lat,
                   color="black",
                   popup = "FLACSO") %>%
  addCircleMarkers(data=estacion1,
                   color="black",
                   popup = "Estación 071 - CERRITO")%>%
  addMiniMap(tiles=providers$CartoDB.Positron)

Ruteo múltiple

Hasta acá ya logramos tener nuestro primer ruteo. Pero, ¿Qué ocurre si en vez de hacerlo con una sola estación, necesitamos hacerlo para todas las estaciones que se encuentran a menos de 20 minutos a pié de la FLACSO?

La función osrmRoute() fue diseñada para que solamente se aplique sobre un registro, es decir que no existe un parámetro que nos permita ejecutarla sobre un dataset de 2 o más registros. Para lograr esto con lo que sabemos hasta ahora podríamos georreferenciar todos los registros por separado y luego unirlos con rbind(). Sin embargo, existen diferentes métodos que son muy útiles si queremos evitar tantos pasos: hoy optaremos por una función de “mapeo” llamada pmap(), que se utiliza para aplicar una función creada previamente con function() a múltiples argumentos de manera paralela. Toma como entrada una lista de argumentos (en este caso serán las coordenadas de origen y destino) y una función (que tendremos que diseñar a partir de osrmRoute()).

Por lo tanto, para obtener los resultados deseados vamos a tener que crear lo que en R le llamamos función y luce así:

ruteo_funcion <- function(nombre_origen, lon_origen, lat_origen,
                          nombre_destino, lon_destino, lat_destino)
  {
  ruta <- osrmRoute(src = c(lon_origen, lat_origen),
                    dst = c(lon_destino, lat_destino),
                    overview = "full",
                    osrm.profile = "foot")
  
  ruta %>%
  mutate(src=nombre_origen,
         dst=nombre_destino)
}

Como los argumentos relacionados a coordenadas indican que vamos a utilizar las columnas “longitud” y “latitud” para todos los datos, vamos a tener que transformar nuestro dataset geográfico “estaciones_geo” a tradicional con st_set_geometry() y agregarle ambas columnas con mutate().

estaciones_geo <- estaciones_geo %>%
  mutate(long = st_coordinates(.)[,1],
         lat  = st_coordinates(.)[,2]) %>%
  st_drop_geometry()

Tal como se mencionó más arriba, otro punto importante a destacar es que la función pmap() necesita si o si que le asignemos un vector para que lo tome como input y repita sobre cada elemento la función elegida, que en este caso será ruteo_funcion. Por lo tanto es necesario que transformemos los 6 argumentos elegidos a un vector con la función list():

ruteo2 <- list("FLACSO", FLACSO_arg$long, FLACSO_arg$lat,
               estaciones_geo$nombre, estaciones_geo$long, estaciones_geo$lat)
ruteo2 <- pmap(ruteo2, ruteo_funcion)
summary(ruteo2)
##       Length Class Mode
##  [1,] 5      sf    list
##  [2,] 5      sf    list
##  [3,] 5      sf    list
##  [4,] 5      sf    list
##  [5,] 5      sf    list
##  [6,] 5      sf    list
##  [7,] 5      sf    list
##  [8,] 5      sf    list
##  [9,] 5      sf    list
## [10,] 5      sf    list
## [11,] 5      sf    list
## [12,] 5      sf    list
## [13,] 5      sf    list
## [14,] 5      sf    list
## [15,] 5      sf    list
## [16,] 5      sf    list
## [17,] 5      sf    list
## [18,] 5      sf    list
## [19,] 5      sf    list
ruteo2 <- ruteo2 %>% 
  reduce(rbind)
summary(ruteo2)
##      src                              dst        duration         distance     
##  Length:19          AYACUCHO            : 1   Min.   : 2.792   Min.   :0.2044  
##  Class :character   CERRITO             : 1   1st Qu.: 9.373   1st Qu.:0.6917  
##  Mode  :character   CONGRESO            : 1   Median :12.010   Median :0.8883  
##                     ECUADOR             : 1   Mean   :12.766   Mean   :0.9413  
##                     FACULTAD DE MEDICINA: 1   3rd Qu.:17.620   3rd Qu.:1.2989  
##                     HOSPITAL DE CLÍNICAS: 1   Max.   :19.493   Max.   :1.4368  
##                     (Other)             :13                                    
##           geometry 
##  LINESTRING   :19  
##  epsg:4326    : 0  
##  +proj=long...: 0  
##                    
##                    
##                    
## 

Ahora hacemos un mapa con todos los recorridos

leaflet(ruteo2) %>% 
    addProviderTiles(providers$CartoDB.DarkMatter) %>% 
    addPolylines(color = "red",
                 label = paste("Distancia:", ruteo1$distance, "|", 
                               "Duración:", ruteo1$duration))%>%
  addCircleMarkers(data=FLACSO_arg, ~long, ~lat,
                   popup = "FLACSO",
                   color="white") %>%
  addCircleMarkers(data=estaciones_geo, ~long, ~lat,
                   popup = ~nombre,
                   color="gray") %>%
  addMiniMap(tiles=providers$CartoDB.DarkMatter)

Y ajustemos un poco la estética como por ejemplo:

Paleta de colores de los recorridos con su respectiva referencia.
Parámetros de los marcadores/círculos: tamaño, transparencia, etc.
Parámetros en las etiquetas: agregar mayor detalle en el texto y redondear decimales para distancia y duración.
Parámetros en los pop-up: agregar mayor detalle en el texto.
paleta <- c(low="gold", high= "deeppink4")
leaflet(ruteo2) %>% 
    addProviderTiles(providers$CartoDB.DarkMatter) %>% 
    addPolylines(color = ~colorNumeric(paleta, ruteo2$distance)(distance),
                 label = paste("Desde", ruteo2$src, "hasta", ruteo2$dst, "|", "Distancia:", round(ruteo2$distance, 2), "km", "|", "Duración:", round(ruteo2$duration, 2), "min")) %>%
  addLegend("bottomright", pal = colorNumeric(paleta, ruteo2$distance), values = ~distance,
            title = "Distancia",
            labFormat = labelFormat(suffix = "km")) %>%
  addCircleMarkers(data=FLACSO_arg, ~long, ~lat, 
                   popup = paste("ORIGEN:FLACSO"),
                   color="white",
                   fillOpacity = 1,
                   radius=8) %>%
  addCircleMarkers(data=estaciones_geo, ~long, ~lat,
                   popup = paste("DESTINO:", estaciones$nombre),
                   color="gray",
                   fillOpacity = 1,
                   radius=6)%>%
  addMiniMap(tiles=providers$CartoDB.DarkMatter)

Por último, probemos cambiando el mapa de fondo por uno más claro:

leaflet(ruteo2) %>% 
    addProviderTiles(providers$CartoDB.Positron) %>% 
    addPolylines(color = ~colorNumeric(paleta, ruteo2$distance)(distance),
                 label = paste("Desde", ruteo2$src, "hasta", ruteo2$dst, "|", "Distancia:", round(ruteo2$distance, 2), "km", "|", "Duración:", round(ruteo2$duration, 2), "min")) %>%
  addLegend("bottomright", pal = colorNumeric(paleta, ruteo2$distance), values = ~distance,
            title = "Distancia",
            labFormat = labelFormat(suffix = "km")) %>%
  addCircleMarkers(data=FLACSO_arg, ~long, ~lat, 
                   popup = paste("ORIGEN:FLACSO"),
                   color="black",
                   fillOpacity = 1,
                   radius=8) %>%
  addCircleMarkers(data=estaciones_geo, ~long, ~lat,
                   popup = paste("DESTINO:", estaciones$nombre),
                   color="gray",
                   fillOpacity = 1,
                   radius=6)%>%
  addMiniMap(tiles=providers$CartoDB.Positron)