library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.2     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(sf)
## Linking to GEOS 3.11.2, GDAL 3.8.2, PROJ 9.3.1; sf_use_s2() is TRUE
library(osmdata)
## Data (c) OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright
library(leaflet)
library(units)
## udunits database from C:/Users/Asus/AppData/Local/R/win-library/4.3/units/share/udunits/udunits2.xml
library(stringr)
library(ggplot2)
library(tidyr)
library(networkD3)
## 
## Attaching package: 'networkD3'
## 
## The following object is masked from 'package:leaflet':
## 
##     JS
library(dplyr)
library(htmlwidgets)
## 
## Attaching package: 'htmlwidgets'
## 
## The following object is masked from 'package:networkD3':
## 
##     JS
library(htmltools)
library(webshot2)
library(ggspatial)
library(showtext)
## Loading required package: sysfonts
## Loading required package: showtextdb
library(RColorBrewer)
library(hereR)
library(viridis)
## Loading required package: viridisLite
library(lwgeom)
## Linking to liblwgeom 3.0.0beta1 r16016, GEOS 3.11.2, PROJ 9.3.1
## 
## Attaching package: 'lwgeom'
## 
## The following objects are masked from 'package:sf':
## 
##     st_minimum_bounding_circle, st_perimeter
library(grid)
bbox <- getbb("San Salvador de Jujuy, Argentina")

calles <- opq(bbox = bbox, timeout = 300) %>%
  add_osm_feature(key = "highway",
                  value = c("motorway", "primary", "secondary", "tertiary")) %>%
  osmdata_sf()

leaflet() %>%
  addTiles() %>%
  addPolylines(data = calles$osm_lines,
               color = "royalblue",
               weight = 2,
               opacity = 0.8,
               group = "Calles") %>%
  setView(lng = -65.2995, lat = -24.1858, zoom = 13)
municipio <- "San Salvador de Jujuy, Argentina"
q <- getbb(municipio) %>%
  opq() %>%
  add_osm_feature(key = "route", value = "bus")


bus_routes <- osmdata_sf(q)


rutas_limpias <- bus_routes$osm_multilines


head(rutas_limpias[, c("name", "ref")])
## Simple feature collection with 6 features and 2 fields
## Geometry type: MULTILINESTRING
## Dimension:     XY
## Bounding box:  xmin: -65.49014 ymin: -24.25024 xmax: -65.2469 ymax: -24.16131
## Geodetic CRS:  WGS 84
##                                                              name ref
## 9704061-(no role)       Línea 40: Hospital de Niños → La Arbolada  40
## 9704993-(no role)                Línea 38: Alem → Termas de Reyes  38
## 9707820-(no role)   Línea 23: Villa San Martín → Avenida El Éxodo  23
## 9707987-(no role)      Línea 13: Sector B5 → Hospital Pablo Soria  13
## 9709376-(no role) Línea 1: Hospital Snopek → Hospital Pablo Soria   1
## 9717664-(no role)                    Línea 47: Campo Verde → Alem 47A
##                                         geometry
## 9704061-(no role) MULTILINESTRING ((-65.30236...
## 9704993-(no role) MULTILINESTRING ((-65.30042...
## 9707820-(no role) MULTILINESTRING ((-65.29032...
## 9707987-(no role) MULTILINESTRING ((-65.25782...
## 9709376-(no role) MULTILINESTRING ((-65.25586...
## 9717664-(no role) MULTILINESTRING ((-65.29174...
rutas_limpias <- rutas_limpias %>%
  mutate(longitud=st_length(rutas_limpias))
head(rutas_limpias)
## Simple feature collection with 6 features and 12 fields
## Geometry type: MULTILINESTRING
## Dimension:     XY
## Bounding box:  xmin: -65.49014 ymin: -24.25024 xmax: -65.2469 ymax: -24.16131
## Geodetic CRS:  WGS 84
##                    osm_id                                            name
## 9704061-(no role) 9704061       Línea 40: Hospital de Niños → La Arbolada
## 9704993-(no role) 9704993                Línea 38: Alem → Termas de Reyes
## 9707820-(no role) 9707820   Línea 23: Villa San Martín → Avenida El Éxodo
## 9707987-(no role) 9707987      Línea 13: Sector B5 → Hospital Pablo Soria
## 9709376-(no role) 9709376 Línea 1: Hospital Snopek → Hospital Pablo Soria
## 9717664-(no role) 9717664                    Línea 47: Campo Verde → Alem
##                        role              from              operator
## 9704061-(no role) (no role) Hospital de Niños Transporte Xibi- Xibi
## 9704993-(no role) (no role)              Alem             Santa Ana
## 9707820-(no role) (no role)    Punta Diamante             San Jorge
## 9707987-(no role) (no role)         Sector B5             El Urbano
## 9709376-(no role) (no role)   Hospital Snopek             El Urbano
## 9717664-(no role) (no role)       Campo Verde             El Urbano
##                   public_transport:version ref route                   to  type
## 9704061-(no role)                        2  40   bus          La Arbolada route
## 9704993-(no role)                           38   bus      Termas de Reyes route
## 9707820-(no role)                        2  23   bus             Terminal route
## 9707987-(no role)                        2  13   bus Hospital Pablo Soria route
## 9709376-(no role)                        2   1   bus Hospital Pablo Soria route
## 9717664-(no role)                        2 47A   bus                 Alem route
##                   via                       geometry      longitud
## 9704061-(no role)     MULTILINESTRING ((-65.30236...  9736.677 [m]
## 9704993-(no role)     MULTILINESTRING ((-65.30042... 24813.398 [m]
## 9707820-(no role)     MULTILINESTRING ((-65.29032...  6741.800 [m]
## 9707987-(no role)     MULTILINESTRING ((-65.25782... 14835.845 [m]
## 9709376-(no role)     MULTILINESTRING ((-65.25586... 14604.649 [m]
## 9717664-(no role)     MULTILINESTRING ((-65.29174...  7308.490 [m]
rutas_limpias$longitud <- str_remove(rutas_limpias$longitud, fixed(" [m]"))
rutas_limpias$longitud <- as.numeric(rutas_limpias$longitud) / 1000
rutas_limpias <- rutas_limpias %>%
  filter(
    !name %in% c(
      "Línea 36: León → Alem",
      "Línea 39: Alem → León"))
limites <- st_bbox(rutas_limpias)

ggplot() +
  geom_sf(data = calles$osm_lines, color = "grey85", linewidth = 0.2) +
  geom_sf(data = rutas_limpias, aes(color = operator, linewidth = longitud)) +
  scale_linewidth_continuous(range = c(0.25, 1)) + 
  

  coord_sf(xlim = c(limites["xmin"], limites["xmax"]), 
           ylim = c(limites["ymin"], limites["ymax"])) +
  
  labs(
    title = "LINEAS DE BUSES en San Salvador de Jujuy",
    subtitle = "Extension de Recorridos por Lineas",
    caption = "Elaboración:\nJuan José VARGAS\narquitecto & urban manager",
    color = "Empresa Operadora") +
  guides(linewidth = "none") + 
  theme_minimal() +
  theme(
    axis.text = element_blank(), 
    axis.title = element_blank(), 
    axis.ticks = element_blank(), 
    panel.grid = element_blank(),
    plot.caption = element_text(face = "bold", size = 9, hjust = 0.5, color = "grey20", vjust = -1) )

rutas_limpias <- st_transform(rutas_limpias, crs = 4326)
calles$osm_lines <- st_transform(calles$osm_lines, crs = 4326)
velocidad_promedio <- 18 

rutas_limpias <- rutas_limpias %>%
  mutate(

    longitud_km = as.numeric(st_length(geometry)) / 1000,
    tiempo_minutos = (longitud_km / velocidad_promedio) * 60,

    ORIGEN = ifelse("from" %in% names(.), from, "Inicio"),
    DESTINO = ifelse("to" %in% names(.), to, "Final"),
    LINEA = ifelse("ref" %in% names(.), ref, "S/N")
  )

puntos_extremos <- rutas_limpias %>%
  rowwise() %>%
  mutate(

    coords = list(st_coordinates(geometry)),
    lon_ini = coords[1, 1], 
    lat_ini = coords[1, 2],
    lon_fin = coords[nrow(coords), 1], 
    lat_fin = coords[nrow(coords), 2]
  ) %>%
  ungroup()
bounds <- st_bbox(rutas_limpias)
ggplot(data = rutas_limpias, aes(x = reorder(ref, -longitud), y = longitud, fill = operator)) +
  geom_col(show.legend = TRUE) +

  geom_text(aes(label = round(longitud, 1)), vjust = -0.5, size = 3, fontface = "bold") +
  
  labs(
    title = "Extensión de Recorridos por Operador de Línea",
    subtitle = "San Salvador de Jujuy",
    x = "Línea de Bus (REF)",
    y = "Longitud (km)",
    fill = "Empresa Operadora",
    caption = "Elaboración:\nJuan José VARGAS\narquitecto & urban manager" ) +
  
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),

    plot.title = element_text(face = "bold", size = 14),
    plot.caption = element_text(face = "bold", size = 9, hjust = 0.5, color = "grey20", vjust = -1),
    panel.grid.minor = element_blank(),
    panel.grid.major.x = element_blank() )

pal <- colorFactor("Paired", domain = rutas_limpias$ref)


leaflet() %>%
  addProviderTiles(providers$CartoDB.Positron) %>% 
  fitBounds(lng1 = bounds[["xmin"]], lat1 = bounds[["ymin"]], 
            lng2 = bounds[["xmax"]], lat2 = bounds[["ymax"]]) %>% 
  
  addPolylines(data = calles$osm_lines, color = "grey", weight = 1, opacity = 0.2) %>%
  

  addPolylines(data = rutas_limpias,
               color = ~pal(ref), 
               weight = ~longitud * 0.5, 
               opacity = 0.6,
               group = "Recorridos",
               popup = ~paste0("<b>Línea:</b> ", ref, 
                               "<br><b>Longitud:</b> ", round(longitud, 2), " km",
                               "<br><b>Empresa:</b> ", operator)) %>%


  addCircleMarkers(data = puntos_extremos, lng = ~lon_ini, lat = ~lat_ini,
                   color = ~pal(ref), fillColor = ~pal(ref),
                   fillOpacity = 0.2, weight = 1, radius = 10, stroke = FALSE) %>%
  addCircleMarkers(data = puntos_extremos, lng = ~lon_fin, lat = ~lat_fin,
                   color = ~pal(ref), fillColor = ~pal(ref),
                   fillOpacity = 0.2, weight = 1, radius = 10, stroke = FALSE) %>%


  addCircleMarkers(data = puntos_extremos, lng = ~lon_ini, lat = ~lat_ini,
                   color = "white", fillColor = ~pal(ref),
                   fillOpacity = 1, weight = 1.5, radius = 4, stroke = TRUE,
                   label = ~paste("INICIO - Línea", ref)) %>%
  addCircleMarkers(data = puntos_extremos, lng = ~lon_fin, lat = ~lat_fin,
                   color = "white", fillColor = ~pal(ref),
                   fillOpacity = 1, weight = 1.5, radius = 4, stroke = TRUE,
                   label = ~paste("FINAL - Línea", ref)) %>%

  addLegend(pal = pal, values = rutas_limpias$ref, 
            title = "Línea de Colectivo", position = "bottomleft") %>%
  
  addControl(
    html = "<div style='background: rgba(255,255,255,0.8); padding: 10px; border-radius: 8px; 
                 box-shadow: 0 0 15px rgba(0,0,0,0.1); text-align: center;'>
              <span style='font-weight: bold; color: #444; font-size: 14px;'>MOVILIDAD URBANA</span><br>
              <span style='font-size: 12px; color: #666;'>Recorrido por Linea de Operador (km)</span><hr style='margin: 5px 0;'>
              <span style='font-weight: bold; font-size: 13px;'>Juan J. VARGAS</span><br>
              <span style='font-size: 10px; text-transform: uppercase;'>arquitecto & urban manager</span>
            </div>", 
    position = "bottomright")
## Warning in RColorBrewer::brewer.pal(max(3, n), palette): n too large, allowed maximum for palette Paired is 12
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(max(3, n), palette): n too large, allowed maximum for palette Paired is 12
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(max(3, n), palette): n too large, allowed maximum for palette Paired is 12
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(max(3, n), palette): n too large, allowed maximum for palette Paired is 12
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(max(3, n), palette): n too large, allowed maximum for palette Paired is 12
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(max(3, n), palette): n too large, allowed maximum for palette Paired is 12
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(max(3, n), palette): n too large, allowed maximum for palette Paired is 12
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(max(3, n), palette): n too large, allowed maximum for palette Paired is 12
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(max(3, n), palette): n too large, allowed maximum for palette Paired is 12
## Returning the palette you asked for with that many colors
top_rutas <- rutas_limpias %>%
  arrange(desc(longitud)) %>%
  head(20) 


ggplot(data = top_rutas, aes(x = reorder(ref, -longitud), y = longitud, fill = operator)) +

  geom_col(show.legend = TRUE, color = "white", size = 0.2) +


  geom_text(aes(label = paste0(round(longitud, 1), " km")), 
            vjust = -0.7, 
            size = 3.2, 
            fontface = "bold", 
            color = "grey30") +
  

  scale_fill_brewer(palette = "Set1") +
  

  labs(
    title = "EXTENSIÓN DE RECORRIDOS POR LÍNEA",
    subtitle = "San Salvador de Jujuy | Líneas con mayor extensión",
    x = "Número de Línea (REF)",
    y = "Extensión Total (km)",
    fill = "Empresa Operadora",
    caption = "Elaboración:\nJuan J. VARGAS\narquitecto & urban manager"
  ) +
  

  scale_y_continuous(expand = expansion(mult = c(0, 0.15))) +
  

  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1, face = "bold", size = 10),
    axis.title = element_text(face = "bold", color = "grey40"),
    plot.title = element_text(face = "bold", size = 16, color = "#2c3e50"),
    plot.subtitle = element_text(size = 11, color = "#7f8c8d", margin = margin(b = 20)),
    plot.caption = element_text(face = "bold", size = 9, hjust = 0.5, color = "grey20", vjust = -2, lineheight = 1.2),
    panel.grid.minor = element_blank(),
    panel.grid.major.x = element_blank(),
    legend.position = "bottom",
    plot.margin = margin(1, 1, 1.5, 1, "cm")
  )
## Warning in geom_col(show.legend = TRUE, color = "white", size = 0.2): Ignoring
## unknown parameters: `size`

velocidad_promedio <- 18 # km/h

rutas_limpias <- rutas_limpias %>%
  mutate(
    longitud = as.numeric(st_length(.)) / 1000,
    tiempo_minutos = (longitud / velocidad_promedio) * 60
  )

puntos_extremos <- rutas_limpias %>%
  rowwise() %>%
  mutate(
    coords = list(st_coordinates(geometry)),
    lon_ini = coords[1, 1], lat_ini = coords[1, 2],
    lon_fin = coords[nrow(coords), 1], lat_fin = coords[nrow(coords), 2]
  ) %>%
  ungroup()


color_profesional <- "#1B4F72" 


leaflet() %>%
  addProviderTiles(providers$CartoDB.Positron) %>% 
  fitBounds(lng1 = min(puntos_extremos$lon_ini), lat1 = min(puntos_extremos$lat_ini), 
            lng2 = max(puntos_extremos$lon_ini), lat2 = max(puntos_extremos$lat_ini)) %>% 
  

  addPolylines(data = calles$osm_lines, color = "grey", weight = 1, opacity = 0.2) %>%
  

  addPolylines(data = rutas_limpias,
               color = color_profesional, 
               weight = ~tiempo_minutos * 0.2, 
               opacity = 0.6,
               group = "Recorridos",
               popup = ~paste0("<b>Línea:</b> ", ref, 
                               "<br><b>Origen:</b> ", from, 
                               "<br><b>Destino:</b> ", to,
                               "<br><b>Duración Est.:</b> ", round(tiempo_minutos, 0), " min")) %>%


  addCircleMarkers(data = puntos_extremos, lng = ~lon_ini, lat = ~lat_ini,
                   color = color_profesional, fillColor = color_profesional,
                   fillOpacity = 0.2, weight = 1, radius = 12, stroke = FALSE) %>%
  addCircleMarkers(data = puntos_extremos, lng = ~lon_ini, lat = ~lat_ini,
                   color = "white", fillColor = color_profesional,
                   fillOpacity = 1, weight = 1.5, radius = 5, stroke = TRUE,
                   label = ~paste("ORIGEN:", from)) %>% 


  addCircleMarkers(data = puntos_extremos, lng = ~lon_fin, lat = ~lat_fin,
                   color = color_profesional, fillColor = color_profesional,
                   fillOpacity = 0.2, weight = 1, radius = 12, stroke = FALSE) %>%
  addCircleMarkers(data = puntos_extremos, lng = ~lon_fin, lat = ~lat_fin,
                   color = "white", fillColor = color_profesional,
                   fillOpacity = 1, weight = 1.5, radius = 5, stroke = TRUE,
                   label = ~paste("DESTINO:", to)) %>% 


  addControl(
    html = "<div style='background: rgba(255,255,255,0.9); padding: 12px; border-radius: 8px; 
                 box-shadow: 0 0 15px rgba(0,0,0,0.15); text-align: center; border-left: 5px solid #1B4F72;'>
              <span style='font-weight: bold; color: #1B4F72; font-size: 15px;'>MOVILIDAD URBANA</span><br>
              <span style='font-size: 12px; color: #555;'>Jerarquía por Tiempo de Viaje (min)</span><hr style='margin: 6px 0;'>
              <span style='font-weight: bold; font-size: 13px;'>Juan J. VARGAS</span><br>
              <span style='font-size: 10px; text-transform: uppercase; color: #777;'>arquitecto & urban manager</span>
            </div>", 
    position = "bottomright")
links <- rutas_limpias %>%
  st_drop_geometry() %>%
  select(from, to, tiempo_minutos) %>%
  rename(source = from, target = to, value = tiempo_minutos) %>%
  mutate(value = round(value, 0))

nodes <- data.frame(
  name = c(as.character(links$source), as.character(links$target)) %>% unique()
)

links$IDsource <- match(links$source, nodes$name) - 1
links$IDtarget <- match(links$target, nodes$name) - 1

sankey <- sankeyNetwork(
  Links = links, 
  Nodes = nodes,
  Source = "IDsource", 
  Target = "IDtarget", 
  Value = "value", 
  NodeID = "name",
  units = "minutos",
  fontSize = 12, 
  nodeWidth = 30,
  colourScale = JS('d3.scaleOrdinal().range(["#1B4F72"])') 
)


sankey_final <- sankey %>% 
  prependContent(
    tags$h2("SISTEMA DE TRANSPORTE PÚBLICO ACTUAL", 
            style = "font-family: Arial; font-weight: bold; color: #1B4F72; text-align: center; margin-top: 20px;"),
    tags$h4("Sistema de Viajes Radiales", 
            style = "font-family: Arial; color: #666; text-align: center; margin-bottom: 20px;")
  ) %>% 
  appendContent(
    tags$div(
      style = "text-align: center; margin-top: 20px; font-family: Arial; border-top: 1px solid #eee; padding-top: 10px;",
      tags$p(style = "margin: 0; font-weight: bold; color: #333; font-size: 13px;", "Elaboración:"),
      tags$p(style = "margin: 0; font-weight: bold; color: #1B4F72; font-size: 15px;", "Juan José VARGAS"),
      tags$p(style = "margin: 0; color: #777; font-size: 11px; text-transform: uppercase;", "arquitecto & urban manager")
    )
  )

 
sankey_final

SISTEMA DE TRANSPORTE PÚBLICO ACTUAL

Sistema de Viajes Radiales

Elaboración:

Juan José VARGAS

arquitecto & urban manager

#exportar imagen completa

ctm_nombre <- "CENTROS DE TRANSFERENCIA MODAL"

red_alimentadora <- rutas_limpias %>%
  st_drop_geometry() %>%
  select(from, tiempo_minutos) %>%
  rename(source = from) %>%
  mutate(
    target = ctm_nombre,
    value = round(tiempo_minutos * 0.5, 0)
  )

red_troncal <- rutas_limpias %>%
  st_drop_geometry() %>%
  select(to, tiempo_minutos) %>%
  rename(target = to) %>%
  mutate(
    source = ctm_nombre,
    value = round(tiempo_minutos * 0.15, 0)
  ) %>%
  group_by(source, target) %>%
  summarise(value = sum(value), .groups = 'drop')

links_total <- bind_rows(red_alimentadora, red_troncal)
nodes <- data.frame(name = unique(c(links_total$source, links_total$target)))

links_total$IDsource <- match(links_total$source, nodes$name) - 1
links_total$IDtarget <- match(links_total$target, nodes$name) - 1


sankey_jujuy <- sankeyNetwork(
  Links = links_total, 
  Nodes = nodes,
  Source = "IDsource", 
  Target = "IDtarget", 
  Value = "value", 
  NodeID = "name",
  units = "minutos",
  fontSize = 12, 
  nodeWidth = 40,
  nodePadding = 15,
  colourScale = JS('d3.scaleOrdinal().range(["#1B4F72"])') 
)

sankey_final <- sankey_jujuy %>% 
  prependContent(
    tags$h2("SISTEMA DE TRANSPORTE INTEGRADO San Salvador de Jujuy", 
            style = "font-family: sans-serif; font-weight: bold; color: #1B4F72; text-align: center;"),
    tags$h4("Red Alimentadora y Troncal", 
            style = "font-family: sans-serif; color: #666; text-align: center; margin-bottom: 20px;")
  ) %>% 
  appendContent(
    tags$div(
      style = "text-align: center; margin-top: 20px; font-family: sans-serif; border-top: 1px solid #eee; padding-top: 10px;",
      tags$p(style = "margin: 0; font-weight: bold; color: #333; font-size: 13px;", "Elaboración:"),
      tags$p(style = "margin: 0; font-weight: bold; color: #1B4F72; font-size: 15px;", "Juan José VARGAS"),
      tags$p(style = "margin: 0; color: #777; font-size: 11px; text-transform: uppercase;", "arquitecto & urban manager")
    )
  )


sankey_final

SISTEMA DE TRANSPORTE INTEGRADO San Salvador de Jujuy

Red Alimentadora y Troncal

Elaboración:

Juan José VARGAS

arquitecto & urban manager

top_tiempos <- rutas_limpias %>%
  arrange(desc(tiempo_minutos)) %>%
  head(20) 


ggplot(data = top_tiempos, aes(x = reorder(ref, -tiempo_minutos), y = tiempo_minutos, fill = operator)) +
  

  geom_col(show.legend = TRUE, color = "white", size = 0.2) +
  

  geom_text(aes(label = paste0(round(tiempo_minutos, 0), " min")), 
            vjust = -0.7, 
            size = 3.2, 
            fontface = "bold", 
            color = "grey30") +
  

  scale_fill_brewer(palette = "Set1") +
  

  labs(
    title = "TIEMPO DE VIAJE ESTIMADO",
    subtitle = "San Salvador de Jujuy | Basado en la velocidad urbana promedio para transporte público",
    x = "Número de Línea (REF)",
    y = "Duración Estimada (minutos)",
    fill = "Empresa Operadora",
    caption = "Elaboración:\nJuan J. VARGAS\narquitecto & urban manager") +
  

  scale_y_continuous(expand = expansion(mult = c(0, 0.15))) +
  
 
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1, face = "bold", size = 10),
    axis.title = element_text(face = "bold", color = "grey40"),
    plot.title = element_text(face = "bold", size = 16, color = "#2c3e50"),
    plot.subtitle = element_text(size = 11, color = "#7f8c8d", margin = margin(b = 20)),
    plot.caption = element_text(face = "bold", size = 9, hjust = 0.5, color = "grey20", vjust = -2, lineheight = 1.2),
    panel.grid.minor = element_blank(),
    panel.grid.major.x = element_blank(),
    legend.position = "bottom",
    plot.margin = margin(1, 1, 1.5, 1, "cm") )
## Warning in geom_col(show.legend = TRUE, color = "white", size = 0.2): Ignoring
## unknown parameters: `size`

font_add_google("Roboto", "roboto")
showtext_auto()


mapa_estatico <- ggplot() +

  geom_sf(data = calles$osm_lines, color = "grey85", size = 0.2) +
  

  geom_sf(data = rutas_limpias, aes(color = operator), size = 1.2, alpha = 0.8) +
  

  geom_point(data = puntos_extremos, aes(x = lon_ini, y = lat_ini, color = operator), 
             size = 4, alpha = 0.3) +
  geom_point(data = puntos_extremos, aes(x = lon_fin, y = lat_fin, color = operator), 
             size = 4, alpha = 0.3) +
  

  geom_point(data = puntos_extremos, aes(x = lon_ini, y = lat_ini, fill = operator), 
             shape = 21, color = "white", size = 2.5, stroke = 0.8) +
  geom_point(data = puntos_extremos, aes(x = lon_fin, y = lat_fin, fill = operator), 
             shape = 21, color = "white", size = 2.5, stroke = 0.8) +

  scale_color_brewer(palette = "Set1") + 
  scale_fill_brewer(palette = "Set1") +
  

  coord_sf(xlim = c(bounds[["xmin"]], bounds[["xmax"]]), 
           ylim = c(bounds[["ymin"]], bounds[["ymax"]])) +
  

  labs(
    title = "MOVILIDAD URBANA",
    subtitle = "Recorridos y nodos de inicio/fin por empresa operadora",
    caption = "JUAN J. VARGAS\nARQUITECTO & URBAN MANAGER",
    color = "Operador",
    fill = "Operador"
  ) +
  

  theme_minimal() +
  theme(
    text = element_text(family = "roboto", color = "#333333"),
    plot.title = element_text(face = "bold", size = 18, hjust = 0.5),
    plot.subtitle = element_text(size = 11, hjust = 0.5, margin = margin(b = 20)),
    plot.caption = element_text(face = "bold", size = 9, lineheight = 1.1, color = "#555555"),
    panel.grid = element_blank(),
    panel.background = element_rect(fill = "#fcfcfc", color = NA),
    legend.position = "right",
    legend.title = element_text(face = "bold")
  )

print(mapa_estatico)

font_add_google("Roboto", "roboto")
showtext_auto()

n_lineas <- length(unique(rutas_limpias$ref))
colores_expandidos <- colorRampPalette(brewer.pal(12, "Paired"))(n_lineas)


mapa_lineas <- ggplot() +

  geom_sf(data = calles$osm_lines, color = "grey88", size = 0.2) +
  

  geom_sf(data = rutas_limpias, 
          aes(color = ref, size = longitud), 
          alpha = 0.8) +
  
 
  geom_point(data = puntos_extremos, aes(x = lon_ini, y = lat_ini, color = ref), 
             size = 4, alpha = 0.3) +
  geom_point(data = puntos_extremos, aes(x = lon_fin, y = lat_fin, color = ref), 
             size = 4, alpha = 0.3) +
  
  
  geom_point(data = puntos_extremos, aes(x = lon_ini, y = lat_ini, fill = ref), 
             shape = 21, color = "white", size = 2, stroke = 0.7) +
  geom_point(data = puntos_extremos, aes(x = lon_fin, y = lat_fin, fill = ref), 
             shape = 21, color = "white", size = 2, stroke = 0.7) +
  
 
  scale_color_manual(values = colores_expandidos) + 
  scale_fill_manual(values = colores_expandidos) +
  scale_size_continuous(range = c(0.6, 2.2), guide = "none") + 
  
  coord_sf(xlim = c(bounds[["xmin"]], bounds[["xmax"]]), 
           ylim = c(bounds[["ymin"]], bounds[["ymax"]])) +
  
  labs(
    title = "REDES DE TRANSPORTE PÚBLICO - San Salvador de Jujuy",
    subtitle = "Recorrido de Línea por Operador",
    caption = "JUAN J. VARGAS\nARQUITECTO & URBAN MANAGER",
    color = "Línea de Colectivo",
    fill = "Línea de Colectivo"
  ) +
  
  theme_minimal() +
  theme(
    text = element_text(family = "roboto", color = "#333333"),
    plot.title = element_text(face = "bold", size = 18, hjust = 0.5),
    plot.subtitle = element_text(size = 11, hjust = 0.5, margin = margin(b = 15)),
    plot.caption = element_text(face = "bold", size = 8, lineheight = 1.1, color = "#555555"),
    panel.grid = element_blank(),
    panel.background = element_rect(fill = "#fdfdfd", color = NA),
    legend.position = "right",
    legend.text = element_text(size = 7), 
    legend.key.height = unit(0.3, "cm")) +

  guides(color = guide_legend(ncol = 2), fill = guide_legend(ncol = 2))

print(mapa_lineas)