autobuses de lleida en tiempo real: cómo funciona moventis lleida

moventis lleida es una web para seguir los autobuses de lleida en tiempo real: las 14 líneas urbanas, cuánto falta para el próximo bus en cada parada y dónde está, más o menos, cada bus. la primera versión es de octubre de 2025.
la web oficial de moventis ya da los tiempos de llegada, pero va muy lenta, y la razón es un fichero. cada vez que la abres, el móvil descarga todas las líneas que lleva moventis, de pamplona a girona. un año después sigue igual, y el 8 de octubre de 2026 lo volví a descargar para tener las cifras exactas.
el fichero de líneas de moventis
cuando abres la página de tiempo real de moventis.es, su javascript pide /es/moventis/es/lines entero y luego busca en él las líneas del día con this.where({ID_GRUPO: id_grupo[i], ID_ZONA: zona, DIAS_QUE_CIRCULA: data_aux}). el filtro lo hace tu móvil.
el 8 de octubre ese json tenía 10.556 filas y pesaba 3,3 mb. formateado, son 190.010 líneas de texto. de las 10.556 filas, 282 son de las 14 líneas urbanas de lleida, un 2,7%. el resto son pamplona, el vallès, el maresme, la costa brava, el llobregat y las interurbanas.
por la red viaja comprimido y ocupa 55 kb. que gzip lo pueda reducir 60 veces ya dice qué hay dentro, el mismo texto repetido miles de veces. el móvil, sin embargo, tiene que descomprimirlo y leerlo entero para encontrar sus 282 filas.
una fila por día
la api no te dice que la línea 7 funciona hoy. lo que hace es repetir la línea 7 una vez por cada día que circulará. esta es una de sus 25 filas:
{
"nid": "86618",
"ID_LINEA": "135",
"COD_LINEA": "7",
"DESC_LINEA": "COSTA MANGRANERS-AV.SANT PERE",
"ID_ZONA": "2",
"ID_SUBZONA": "1",
"TREAL": "S",
"FORMA": "cuadrado",
"COLOR": "#099496",
"TEXT_COLOR": null,
"ID_EXPLOTADORA": "5",
"ID_CONCESION": "1",
"ID_GRUPO": "1",
"ADAPTADA": "S",
"MARCA": "86862",
"DIAS_QUE_CIRCULA": "20261006"
}
entre las 25 filas solo cambia el último campo, la fecha, que va del 6 al 30 de octubre. en todo el fichero pasa lo mismo, con fechas hasta dos meses vista. y las 45 líneas que pertenecen a más de una zona o subzona aparecen repetidas para cada una.
lo que no viene repetido viene sucio. los nombres usan guiones bajos como espacios y acentos sueltos como apóstrofos, y les faltan acentos. el código corrige 12 palabras a mano, entre ellas "atlestisme", una errata de la propia api. en la línea 70 hay un trayecto que se llama GARRIGUESBLOCSLACAIXA-ANTONIVILAPLANA, y hay una parada que se llama 20133 RICARD VIÑES/SANITAT, con un número pegado delante.
cómo se sincronizan las líneas y las paradas
la primera versión la hice en menos de 48 horas, en octubre de 2025. extraje las 11 líneas y las 184 paradas de lleida en un json, las cargué en postgres e hice la interfaz con next.js y tailwind. los tiempos de llegada no se guardan nunca. cada vez que abres una parada, el servidor se los pide a moventis en directo.
en marzo añadí las líneas que faltaban, la n1, la 16 y la 70, y un flujo de n8n que refrescaba la base de datos cada 48 horas. desde junio ese trabajo vive en el mismo repositorio, en un servicio en typescript que se ejecuta cada noche. hace esto:
- descarga el fichero y se queda con las filas de la zona de lleida, y también con las de cualquier línea que ya tenga guardada, por si moventis le cambia la zona.
- descarta las interurbanas y el bus turístico, que son la 120, la 121, la 122, la 401 y la bt.
- agrupa las filas por línea. las fechas repetidas dejan de ser filas y pasan a ser el calendario de la línea.
- elige un día laborable, un sábado y un domingo de cada calendario y pide sus trayectos a
GetTrayectos. para el 8 de octubre salen 32 peticiones. - junta las variantes de los tres días y guarda cada parada con el id de moventis como clave.

el paso 5 es el que hace que todo aguante. de las 184 paradas de la primera versión, 183 siguen existiendo un año después con el mismo id. 10 han cambiado de nombre. seis que llevaban el nombre de la estación de autobuses ahora llevan el de plaça espanya, y "magraners" ha pasado a ser "mangraners". la coordenada del hospital de santa maria se ha movido 135 metros. si la clave fuera el nombre, cada cambio sería una parada nueva, y los enlaces compartidos como ?stop=10242 dejarían de funcionar.
la lista de paradas de una línea solo se sustituye cuando la línea se ha sincronizado entera, con todas las peticiones respondidas. si falla una, el servicio añade las paradas que ha visto y no quita ninguna, porque una parada que falta por un error de red no es una parada retirada.
cuando la sincronización borró la red
el 2 de agosto de 2026, moventis quitó la zona de lleida del fichero de líneas. los trayectos y los tiempos de cada línea se seguían sirviendo como siempre. solo habían desaparecido del listado.
la sincronización encontró cero líneas, y eso destapó un error. al terminar, el código marca como borrado todo lo que la pasada no ha visto, con un notIn de prisma. con una lista vacía, prisma no entiende ninguna fila, entiende todas. tres noches seguidas el registro dijo que todo había ido bien mientras la red entera quedaba borrada. la purga de 28 días habría eliminado todas las paradas para siempre el 30 de agosto.
lo arreglé el 4 de agosto. la primera pieza es una comprobación antes de borrar nada, que aquí acorto:
// MIN_SEEN_STOP_RATIO = 0.5
if (discoveredLines === 0) return { safe: false, reason: "no lines were discovered" };
if (incompleteLines > 0) return { safe: false, reason: `${incompleteLines} line(s) synced incompletely` };
if (seenStopCount === 0) return { safe: false, reason: "the run upserted no stops" };
if (seenStopCount < knownStopCount * MIN_SEEN_STOP_RATIO) return { safe: false, reason: "under the 50% floor" };
return { safe: true };
la segunda es un plan b para cuando el fichero no trae lleida. la sincronización parte de las líneas que ya tiene guardadas y reconstruye el calendario día a día con GetTrayectos, que para un día sin servicio solo responde [{ numLinea }]. también separa las líneas paradas de las retiradas. la 10 no circula en todo agosto y vuelve en septiembre, y sin esa distinción cada verano la daría por muerta.
el plan b no funcionó a la primera. el cliente de prisma del proyecto añade deletedAt: null a todas las consultas de rutas, así que la consulta que tenía que encontrar las rutas borradas para recuperarlas solo veía las vivas, que eran cero. la comprobación nueva hizo su trabajo y no tocó nada, pero la red seguía caída. media hora después lo arreglé con un deletedAt: undefined explícito.
el 4 de octubre lleida volvía a salir en el fichero, y el 8 seguía ahí, con 18 líneas. el código ya no se cree que una línea ha dejado de existir solo porque no aparece.
tiempos de llegada y posición de los autobuses
la web tiene las 14 líneas, los tiempos en directo de cada parada y la posición aproximada de los autobuses. moventis no publica gps ni identificadores de vehículo, solo la lista de llegadas de cada parada. para situar un bus, el código lo busca entre dos paradas. si sale en la lista de la parada b y no en la de la parada anterior, a, es que está entre a y b. le basta con una pasada de unas ocho paradas por trayecto y hasta ocho consultas más para estrechar los tramos donde hay autobuses.
el primer localizador proyectaba hacia atrás los tiempos de la parada final a una velocidad fija. en una línea circular, sin embargo, la parada final también es el origen, y los tiempos que salen ahí son salidas futuras. acabó dibujando tres autobuses en fila en el passeig de ronda mientras la parada decía que el primero llegaba en 5 minutos y el siguiente en 26.
todas las peticiones a moventis pasan por una cola de 5 por segundo que comparten todos los usuarios. si alguien activara la predicción en las 14 líneas a la vez, serían entre 250 y 500 peticiones cada 25 segundos, y la cola no se vaciaría nunca. por eso solo se predicen las tres últimas líneas seleccionadas, y las peticiones de fondo, como el próximo bus que muestra cada parada del mapa, van por un carril de baja prioridad. con la cola llena, abrir una parada tarda 3,1 segundos de mediana. con la cola vacía, 2,4.

líneas y paradas del autobús de lleida
con los trayectos del 8 de octubre, la red urbana de lleida tiene 249 paradas. la línea 7 es la más larga, con 71 paradas distintas entre la ida y la vuelta. la n1, la nocturna, tiene 66, y la 1, la más corta, 13.
129 paradas son de una sola línea. en el otro extremo, plaça espanya/saracibar y plaça espanya/pont universitat tienen siete cada una.
cada noche, la sincronización se queda con 282 filas del fichero y tira el resto. la web oficial sigue pidiendo las 10.556 cada vez que alguien la abre.