bus de lleida en temps real: com funciona moventis lleida per dins

moventis lleida és una web per seguir el bus de lleida en temps real: les 14 línies urbanes, quant falta per al pròxim bus a cada parada i on és, més o menys, cada bus. la primera versió és de l'octubre de 2025.
la web oficial de moventis ja dona els temps d'arribada, però va molt lenta, i la raó és un fitxer. cada cop que l'obres, el mòbil carrega totes les línies que porta moventis, de pamplona a girona. un any després continua igual, i el 8 d'octubre de 2026 el vaig tornar a baixar per tenir-ne els números exactes.
el fitxer de línies de moventis
quan obres la pàgina de temps real de moventis.es, el seu javascript demana /es/moventis/es/lines sencer i després hi busca les línies del dia amb this.where({ID_GRUPO: id_grupo[i], ID_ZONA: zona, DIAS_QUE_CIRCULA: data_aux}). el filtre el fa el teu mòbil.
el 8 d'octubre aquell json tenia 10.556 files i pesava 3,3 mb. formatat, són 190.010 línies de text. de les 10.556 files, 282 són de les 14 línies urbanes de lleida, un 2,7%. la resta són pamplona, el vallès, el maresme, la costa brava, el llobregat i les interurbanes.
per la xarxa viatja comprimit i ocupa 55 kb. que el gzip el pugui reduir 60 vegades ja diu què hi ha a dins, el mateix text repetit milers de vegades. el mòbil, però, l'ha de descomprimir i llegir sencer per trobar-hi les seves 282 files.
una fila per dia
l'api no et diu que la línia 7 funciona avui. el que fa és repetir la línia 7 un cop per cada dia que circularà. aquesta és una de les seves 25 files:
{
"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 les 25 files només canvia l'últim camp, la data, que va del 6 al 30 d'octubre. a tot el fitxer passa el mateix, amb dates fins a dos mesos endavant. i les 45 línies que pertanyen a més d'una zona o subzona hi surten repetides per a cadascuna.
el que no ve repetit ve brut. els noms fan servir guions baixos com a espais i accents solts com a apòstrofs, i hi falten accents. el codi corregeix 12 paraules a mà, entre elles "atlestisme", una errada de la mateixa api. a la línia 70 hi ha un trajecte que es diu GARRIGUESBLOCSLACAIXA-ANTONIVILAPLANA, i hi ha una parada que es diu 20133 RICARD VIÑES/SANITAT, amb un número enganxat al davant.
com se sincronitzen les línies i les parades
la primera versió la vaig fer en menys de 48 hores, l'octubre de 2025. vaig extreure les 11 línies i les 184 parades de lleida en un json, les vaig carregar a postgres i vaig fer la interfície amb next.js i tailwind. els temps d'arribada no es guarden mai. cada cop que obres una parada, el servidor els demana a moventis en directe.
al març hi vaig afegir les línies que faltaven, la n1, la 16 i la 70, i un flux d'n8n que refrescava la base de dades cada 48 hores. des del juny aquesta feina viu dins del mateix repositori, en un servei en typescript que s'executa cada nit. fa això:
- baixa el fitxer i es queda les files de la zona de lleida, i també les de qualsevol línia que ja tingui desada, per si moventis li canvia la zona.
- descarta les interurbanes i el bus turístic, que són la 120, la 121, la 122, la 401 i la bt.
- agrupa les files per línia. les dates repetides deixen de ser files i passen a ser el calendari de la línia.
- tria un dia feiner, un dissabte i un diumenge de cada calendari i en demana els trajectes a
GetTrayectos. per al 8 d'octubre surten 32 peticions. - ajunta les variants dels tres dies i desa cada parada amb l'id de moventis com a clau.

el pas 5 és el que fa que tot plegat aguanti. de les 184 parades de la primera versió, 183 encara existeixen un any després amb el mateix id. 10 han canviat de nom. sis que portaven el nom de l'estació d'autobusos ara porten el de plaça espanya, i "magraners" ha passat a ser "mangraners". la coordenada de l'hospital de santa maria s'ha mogut 135 metres. si la clau fos el nom, cada canvi seria una parada nova, i els enllaços compartits com ?stop=10242 deixarien de funcionar.
la llista de parades d'una línia només se substitueix quan la línia s'ha sincronitzat sencera, amb totes les peticions contestades. si en falla una, el servei afegeix les parades que ha vist i no en treu cap, perquè una parada que falta per un error de xarxa no és una parada retirada.
quan la sincronització va esborrar la xarxa
el 2 d'agost de 2026, moventis va treure la zona de lleida del fitxer de línies. els trajectes i els temps de cada línia es continuaven servint com sempre. només havien desaparegut del llistat.
la sincronització va trobar zero línies, i això va destapar un error. en acabar, el codi marca com a esborrat tot el que la passada no ha vist, amb un notIn de prisma. amb una llista buida, prisma no entén cap fila, entén totes. tres nits seguides el registre va dir que tot havia anat bé mentre la xarxa sencera quedava esborrada. la purga de 28 dies hauria eliminat totes les parades per sempre el 30 d'agost.
ho vaig arreglar el 4 d'agost. la primera peça és una comprovació abans d'esborrar res, que aquí escurço:
// 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 segona és un pla b per quan el fitxer no porta lleida. la sincronització parteix de les línies que ja té desades i reconstrueix el calendari dia a dia amb GetTrayectos, que per a un dia sense servei només respon [{ numLinea }]. també separa les línies aturades de les retirades. la 10 no circula en tot l'agost i torna al setembre, i sense aquesta distinció cada estiu la donaria per morta.
el pla b no va funcionar a la primera. el client de prisma del projecte afegeix deletedAt: null a totes les consultes de rutes, així que la consulta que havia de trobar les rutes esborrades per recuperar-les només veia les vives, que eren zero. la comprovació nova va fer la seva feina i no va tocar res, però la xarxa continuava caiguda. mitja hora després ho vaig arreglar amb un deletedAt: undefined explícit.
el 4 d'octubre lleida tornava a sortir al fitxer, i el 8 encara hi era, amb 18 línies. el codi ja no es creu que una línia ha deixat d'existir només perquè no hi surt.
temps d'arribada i posició dels busos
la web té les 14 línies, els temps en directe de cada parada i la posició aproximada dels busos. moventis no publica gps ni identificadors de vehicle, només la llista d'arribades de cada parada. per situar un bus, el codi el busca entre dues parades. si surt a la llista de la parada b i no a la de la parada anterior, a, és que és entre a i b. en té prou amb una passada d'unes vuit parades per trajecte i fins a vuit consultes més per estrènyer els trams on hi ha busos.
el primer localitzador projectava cap enrere els temps de la parada final a una velocitat fixa. en una línia circular, però, la parada final també és l'origen, i els temps que hi surten són sortides futures. va acabar dibuixant tres busos en filera al passeig de ronda mentre la parada deia que el primer arribava en 5 minuts i el següent en 26.
totes les peticions a moventis passen per una cua de 5 per segon que comparteixen tots els usuaris. si algú activés la predicció a les 14 línies alhora, serien entre 250 i 500 peticions cada 25 segons, i la cua no es buidaria mai. per això només es prediuen les tres últimes línies seleccionades, i les peticions de fons, com el proper bus que ensenya cada parada del mapa, van per un carril de prioritat baixa. amb la cua plena, obrir una parada triga 3,1 segons de mediana. amb la cua buida, 2,4.

les línies i les parades del bus de lleida
amb els trajectes del 8 d'octubre, la xarxa urbana de lleida té 249 parades. la línia 7 és la més llarga, amb 71 parades diferents entre l'anada i la tornada. la n1, la nocturna, en té 66, i la 1, la més curta, 13.
129 parades són d'una sola línia. a l'altre extrem, plaça espanya/saracibar i plaça espanya/pont universitat en tenen set cadascuna.
cada nit, la sincronització es queda 282 files del fitxer i llença la resta. la web oficial continua demanant les 10.556 cada cop que algú l'obre.