mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-12 14:22:21 +00:00
Merge branch 'main' into fix/keyless-unicode-search
This commit is contained in:
12 files changed
+698
-5
No files matched your search
@@ -0,0 +1,505 @@
|
||||
/**
|
||||
* Rio de Janeiro route ids to the line a rider would say.
|
||||
*
|
||||
* SMTR's realtime feeds send internal keys (`O0731AAN0A` is SN731,
|
||||
* `20000501110` is BRT line 50). They look decodable but are not:
|
||||
* `O0014AAA0A` is LECD135, not 14. The table is copied from `routes.txt` in
|
||||
* SMTR's static GTFS (https://dados.mobilidade.rio/gtfs/schedule, CC BY 4.0),
|
||||
* valid to 2026-12-31. An id missing here renders as its raw key.
|
||||
*/
|
||||
export const RIO_ROUTE_NAMES = Object.freeze({
|
||||
20000101130: '10',
|
||||
20000111110: '11',
|
||||
20000121130: '12',
|
||||
20000131130: '13',
|
||||
20000141130: '14',
|
||||
20000151130: '15',
|
||||
20000161130: '16',
|
||||
20000171110: '17',
|
||||
20000181130: '18',
|
||||
20000191130: '19',
|
||||
20000201130: '20',
|
||||
20000221110: '22',
|
||||
20000251130: '25',
|
||||
20000281130: '28',
|
||||
20000311140: '31',
|
||||
20000351110: '35',
|
||||
20000401130: '40',
|
||||
20000411130: '41',
|
||||
20000421110: '42',
|
||||
20000431130: '43',
|
||||
20000461130: '46',
|
||||
20000501110: '50',
|
||||
20000511110: '51',
|
||||
20000521110: '52',
|
||||
20000531130: '53',
|
||||
20000601110: '60',
|
||||
20000611130: '61',
|
||||
20000671130: '67',
|
||||
20000681130: '68',
|
||||
20000701110: '70',
|
||||
20000731130: '73',
|
||||
20000801110: '80',
|
||||
20000901110: '90',
|
||||
'20000EXEC1110': 'ESP01',
|
||||
E2110AAA0A: '2110',
|
||||
E2111AAA0A: '2111',
|
||||
E2112AAA0A: '2112',
|
||||
E2114AAA0A: '2114',
|
||||
E2303AAA0A: '2303',
|
||||
E2305AAA0A: '2305',
|
||||
E2307AAA0A: '2307',
|
||||
E2308AAA0A: '2308',
|
||||
E2309AAA0A: '2309',
|
||||
E2310AAA0A: '2310',
|
||||
E2334AAA0A: '2334',
|
||||
E2335AAA0A: '2335',
|
||||
E2336AAA0A: '2336',
|
||||
E2337AAA0A: '2337',
|
||||
E2338AAA0A: '2338',
|
||||
E2339AAA0A: '2339',
|
||||
E2342AAA0A: '2342',
|
||||
E2343AAA0A: '2343',
|
||||
E2344AAA0A: '2344',
|
||||
E2345AAA0A: '2345',
|
||||
E2381AAA0A: '2381',
|
||||
E2383AAA0A: '2383',
|
||||
E2613AAA0A: '2613',
|
||||
E2614AAA0A: '2614',
|
||||
E2802AAA0A: '2802',
|
||||
E2803AAA0A: '2803',
|
||||
E2804AAA0A: '2804',
|
||||
O0006AAA0A: '006',
|
||||
O0007AAA0A: '007',
|
||||
O0007AAN0A: 'SN007',
|
||||
O0010AAA0A: '010',
|
||||
O0014AAA0A: 'LECD135',
|
||||
O0100AAA0A: '100',
|
||||
O0104AAA0A: '104',
|
||||
O0104AAN0A: 'SN104',
|
||||
O0107AAA0A: '107',
|
||||
O0109AAA0A: 'LECD131',
|
||||
O0111AAA0A: 'LECD134',
|
||||
O0117AAA0A: '117',
|
||||
O0133AAA0A: '133',
|
||||
O0133AAN0A: 'SN133',
|
||||
O0157AAA0A: 'LECD127',
|
||||
O0160AAA0A: '160',
|
||||
O0161AAA0A: '161',
|
||||
O0162AAA0A: '162',
|
||||
O0164AAA0A: 'LECD136',
|
||||
O0165AAA0A: '165',
|
||||
O0167AAA0A: '167',
|
||||
O0169AAA0A: '169',
|
||||
O0201AAA0A: '201',
|
||||
O0202AAA0A: '202',
|
||||
O0209AAA0A: '209',
|
||||
O0209AAN0A: 'SN209',
|
||||
O0210AAA0A: '210',
|
||||
O0217AAA0A: '217',
|
||||
O0220AAA0A: '220',
|
||||
O0229AAA0A: '229',
|
||||
O0232AAA0A: '232',
|
||||
O0232AAN0A: 'SN232',
|
||||
O0238AAA0A: '238',
|
||||
O0238AAN0A: 'SN238',
|
||||
O0239AAA0A: '239',
|
||||
O0247AAA0A: '247',
|
||||
O0249AAA0A: '249',
|
||||
O0254AAA0A: '254',
|
||||
O0265AAA0A: '265',
|
||||
O0265AAN0A: 'SN265',
|
||||
O0265AAP0A: 'SP265',
|
||||
O0275AAA0A: '275',
|
||||
O0277AAA0A: '277',
|
||||
O0292AAA0A: '292',
|
||||
O0292AAN0A: 'SN292',
|
||||
O0296AAA0A: '296',
|
||||
O0298AAA0A: '298',
|
||||
O0298AAN0A: 'SN298',
|
||||
O0300AAA0A: '300',
|
||||
O0300AAR0A: 'SR300',
|
||||
O0301AAA0A: '301',
|
||||
O0302AAA0A: '302',
|
||||
O0302AAN0A: 'SN302',
|
||||
O0306AAA0A: '306',
|
||||
O0311AAA0A: '311',
|
||||
O0312AAA0A: '312',
|
||||
O0312AAN0A: 'SN312',
|
||||
O0313AAA0A: '313',
|
||||
O0315AAA0A: '315',
|
||||
O0319AAA0A: '319',
|
||||
O0319AAN0A: 'SN319',
|
||||
O0319AAV0A: 'SV319',
|
||||
O0321AAA0A: '321',
|
||||
O0323AAA0A: '323',
|
||||
O0324AAA0A: '324',
|
||||
O0324AAN0A: 'SN324',
|
||||
O0324AAP0A: 'SP324',
|
||||
O0325AAA0A: '325',
|
||||
O0326AAA0A: '326',
|
||||
O0326AAPBA: 'SPB326',
|
||||
O0327AAA0A: '327',
|
||||
O0328AAA0A: '328',
|
||||
O0328AAN0A: 'SN328',
|
||||
O0328AAP0A: 'SP328',
|
||||
O0328AAR0A: 'SR328',
|
||||
O0329AAA0A: '329',
|
||||
O0329AAP0A: 'SP329',
|
||||
O0335AAA0A: '335',
|
||||
O0338AAA0A: '338',
|
||||
O0341AAA0A: '341',
|
||||
O0342AAA0A: '342',
|
||||
O0342AAR0A: 'SR342',
|
||||
O0343AAA0A: '343',
|
||||
O0343AAP0A: 'SP343',
|
||||
O0348AAA0A: '348',
|
||||
O0349AAA0A: '349',
|
||||
O0350AAA0A: '350',
|
||||
O0352AAA0A: '352',
|
||||
O0353AAA0A: '353',
|
||||
O0353AAN0A: 'SN353',
|
||||
O0355AAA0A: '355',
|
||||
O0355AAN0A: 'SN355',
|
||||
O0355AAR0A: 'SR355',
|
||||
O0361AAA0A: '361',
|
||||
O0362AAA0A: '362',
|
||||
O0362AAR0A: 'SR362',
|
||||
O0363AAA0A: '363',
|
||||
O0363AAN0A: 'SN363',
|
||||
O0363AAV0A: 'SV363',
|
||||
O0368AAA0A: '368',
|
||||
O0368AAN0A: 'SN368',
|
||||
O0369AAA0A: '369',
|
||||
O0371AAA0A: '371',
|
||||
O0378AAA0A: '378',
|
||||
O0378AAR0A: 'SR378',
|
||||
O0380AAA0A: '380',
|
||||
O0383AAA0A: '383',
|
||||
O0383AAN0A: 'SN383',
|
||||
O0384AAA0A: '384',
|
||||
O0384AAR0A: 'SR384',
|
||||
O0385AAA0A: '385',
|
||||
O0386AAA0A: '386',
|
||||
O0386AAR0A: 'SR386',
|
||||
O0388AAA0A: '388',
|
||||
O0388AAN0A: 'SN388',
|
||||
O0388AAR0A: 'SR388',
|
||||
O0389AAP0A: 'SP389',
|
||||
O0390AAA0A: '390',
|
||||
O0391AAV0A: 'SV391',
|
||||
O0393AAA0A: '393',
|
||||
O0393AAN0A: 'SN393',
|
||||
O0393AAR0A: 'SR393',
|
||||
O0397AAA0A: '397',
|
||||
O0397AAN0A: 'SN397',
|
||||
O0397AAR0A: 'SR397',
|
||||
O0399AAA0A: '399',
|
||||
O0399AAN0A: 'SN399',
|
||||
O0399AAR0A: 'SR399',
|
||||
O0404AAP0A: 'SP404',
|
||||
O0409AAA0A: '409',
|
||||
O0410AAA0A: '410',
|
||||
O0410AAN0A: 'LECD139',
|
||||
O0415AAA0A: '415',
|
||||
O0415AAN0A: 'SN415',
|
||||
O0416AAA0A: '416',
|
||||
O0422AAA0A: '422',
|
||||
O0422AAN0A: 'SN422',
|
||||
O0426AAA0A: '426',
|
||||
O0433AAA0A: 'LECD150',
|
||||
O0435AAA0A: 'LECD151',
|
||||
O0448AAA0A: '448',
|
||||
O0455AAA0A: '455',
|
||||
O0456AAA0A: 'LECD132',
|
||||
O0457AAA0A: '457',
|
||||
O0457AAN0A: 'SN457',
|
||||
O0461AAA0A: '461',
|
||||
O0472AAA0A: '472',
|
||||
O0473AAA0A: '473',
|
||||
O0474AAA0A: '474',
|
||||
O0474AAN0A: 'SN474',
|
||||
O0474AAV0A: 'SV474',
|
||||
O0475AAA0A: 'LECD133',
|
||||
O0476AAA0A: '476',
|
||||
O0483AAA0A: '483',
|
||||
O0483AAN0A: 'SN483',
|
||||
O0483AAPAA: 'SPA483',
|
||||
O0483AAPBA: 'SPB483',
|
||||
O0483AAV0A: 'SV483',
|
||||
O0484AAA0A: '484',
|
||||
O0485AAA0A: '485',
|
||||
O0485AAP0A: 'SP485',
|
||||
O0486AAA0A: '486',
|
||||
O0492AAA0A: '492',
|
||||
O0497AAA0A: '497',
|
||||
O0497AAN0A: 'SN497',
|
||||
O0498AAA0A: '498',
|
||||
O0507AAA0A: '507',
|
||||
O0513AAA0A: '513',
|
||||
O0518AAA0A: '518',
|
||||
O0519AAA0A: '519',
|
||||
O0536AAA0A: 'LECD138',
|
||||
O0539AAA0A: '539',
|
||||
O0550AAA0A: '550',
|
||||
O0550AAN0A: 'SN550',
|
||||
O0550AAPAA: 'SPA550',
|
||||
O0550AAPBA: 'SPB550',
|
||||
O0552AAA0A: '552',
|
||||
O0552AAN0A: 'LECD149',
|
||||
O0553AAA0A: '553',
|
||||
O0554AAA0A: '554',
|
||||
O0554AAN0A: 'SN554',
|
||||
O0555AAA0A: '555',
|
||||
O0557AAA0A: '557',
|
||||
O0565AAA0A: '565',
|
||||
O0565AAN0A: 'SN565',
|
||||
O0583AAA0A: '583',
|
||||
O0584AAA0A: '584',
|
||||
O0585AAA0A: '585',
|
||||
O0600AAA0A: '600',
|
||||
O0600AAN0A: 'SN600',
|
||||
O0601AAA0A: '601',
|
||||
O0603AAA0A: '603',
|
||||
O0606AAA0A: '606',
|
||||
O0606AAV0A: 'SV606',
|
||||
O0607AAA0A: '607',
|
||||
O0608AAA0A: '608',
|
||||
O0610AAA0A: '610',
|
||||
O0611AAA0A: '611',
|
||||
O0613AAA0A: '613',
|
||||
O0613AAPAA: 'SPA613',
|
||||
O0614AAA0A: '614',
|
||||
O0616AAA0A: '616',
|
||||
O0621AAA0A: '621',
|
||||
O0622AAA0A: '622',
|
||||
O0623AAA0A: '623',
|
||||
O0624AAA0A: '624',
|
||||
O0624AAN0A: 'SN624',
|
||||
O0624AAV0A: 'SV624',
|
||||
O0625AAA0A: '625',
|
||||
O0626AAA0A: '626',
|
||||
O0627AAA0A: '627',
|
||||
O0628AAA0A: '628',
|
||||
O0629AAA0A: '629',
|
||||
O0629AAN0A: 'SN629',
|
||||
O0630AAA0A: '630',
|
||||
O0634AAA0A: '634',
|
||||
O0635AAA0A: '635',
|
||||
O0635AAP0A: 'SP635',
|
||||
O0636AAA0A: '636',
|
||||
O0636AAN0A: 'SN636',
|
||||
O0638AAA0A: '638',
|
||||
O0639AAA0A: '639',
|
||||
O0639AAN0A: 'SN639',
|
||||
O0645AAA0A: '645',
|
||||
O0649AAA0A: 'LECD126',
|
||||
O0650AAA0A: '650',
|
||||
O0651AAA0A: '651',
|
||||
O0652AAA0A: '652',
|
||||
O0653AAA0A: '653',
|
||||
O0663AAA0A: '663',
|
||||
O0665AAA0A: '665',
|
||||
O0665AAVAA: 'LECD147',
|
||||
O0665AAVBA: 'SVB665',
|
||||
O0669AAA0A: '669',
|
||||
O0669AAV0A: 'SV669',
|
||||
O0678AAA0A: '678',
|
||||
O0679AAA0A: '679',
|
||||
O0685AAA0A: 'LECD140',
|
||||
O0685AAVAA: 'LECD148',
|
||||
O0685AAVBA: 'LECD141',
|
||||
O0687AAA0A: '687',
|
||||
O0688AAA0A: '688',
|
||||
O0691AAA0A: '691',
|
||||
O0692AAA0A: '692',
|
||||
O0692AAV0A: 'SV692',
|
||||
O0693AAA0A: '693',
|
||||
O0696AAA0A: '696',
|
||||
O0696AAN0A: 'SN696',
|
||||
O0711AAA0A: '711',
|
||||
O0712AAA0A: '712',
|
||||
O0721AAA0A: '721',
|
||||
O0731AAA0A: '731',
|
||||
O0731AAN0A: 'SN731',
|
||||
O0737AAA0A: '737',
|
||||
O0741AAA0A: '741',
|
||||
O0743AAA0A: '743',
|
||||
O0745AAA0A: '745',
|
||||
O0746AAA0A: '746',
|
||||
O0753AAA0A: '753',
|
||||
O0754AAA0A: '754',
|
||||
O0756AAA0A: '756',
|
||||
O0757AAA0A: '757',
|
||||
O0759AAA0A: '759',
|
||||
O0759AAP0A: 'SP759',
|
||||
O0764AAA0A: '764',
|
||||
O0765AAA0A: '765',
|
||||
O0766AAA0A: '766',
|
||||
O0766AAP0A: 'SP766',
|
||||
O0770AAA0A: '770',
|
||||
O0771AAA0A: '771',
|
||||
O0774AAA0A: '774',
|
||||
O0774AAV0A: 'LECD142',
|
||||
O0775AAA0A: '775',
|
||||
O0777AAV0A: 'SV777',
|
||||
O0778AAA0A: '778',
|
||||
O0779AAA0A: '779',
|
||||
O0779AAN0A: 'SN779',
|
||||
O0780AAA0A: '780',
|
||||
O0783AAA0A: '783',
|
||||
O0785AAA0A: '785',
|
||||
O0786AAA0A: '786',
|
||||
O0788AAA0A: '788',
|
||||
O0790AAA0A: '790',
|
||||
O0790AAN0A: 'SN790',
|
||||
O0790AAV0A: 'SV790',
|
||||
O0791AAA0A: '791',
|
||||
O0793AAA0A: '793',
|
||||
O0794AAA0A: '794',
|
||||
O0794AAN0A: 'SN794',
|
||||
O0795AAP0A: 'SP795',
|
||||
O0796AAA0A: '796',
|
||||
O0798AAA0A: '798',
|
||||
O0799AAA0A: '799',
|
||||
O0801AAA0A: '801',
|
||||
O0801AAP0A: 'SP801',
|
||||
O0802AAA0A: '802',
|
||||
O0803AAA0A: '803',
|
||||
O0803AAN0A: 'SN803',
|
||||
O0803AAP0A: 'SP803',
|
||||
O0804AAA0A: '804',
|
||||
O0805AAA0A: '805',
|
||||
O0805AAN0A: 'SN805',
|
||||
O0805AAP0A: 'SP805',
|
||||
O0807AAA0A: '807',
|
||||
O0808AAA0A: '808',
|
||||
O0809AAA0A: '809',
|
||||
O0810AAA0A: '810',
|
||||
O0810AAN0A: 'SN810',
|
||||
O0810AAP0A: 'SP810',
|
||||
O0812AAA0A: '812',
|
||||
O0817AAA0A: '817',
|
||||
O0819AAA0A: '819',
|
||||
O0820AAA0A: '820',
|
||||
O0821AAA0A: '821',
|
||||
O0821AAN0A: 'SN821',
|
||||
O0822AAA0A: '822',
|
||||
O0825AAA0A: '825',
|
||||
O0825AAN0A: 'SN825',
|
||||
O0826AAA0A: '826',
|
||||
O0829AAA0A: '829',
|
||||
O0830AAA0A: '830',
|
||||
O0831AAA0A: '831',
|
||||
O0831AAV0A: 'SV831',
|
||||
O0833AAA0A: '833',
|
||||
O0834AAA0A: '834',
|
||||
O0835AAA0A: '835',
|
||||
O0836AAA0A: '836',
|
||||
O0837AAA0A: '837',
|
||||
O0838AAA0A: '838',
|
||||
O0840AAA0A: '840',
|
||||
O0840AAN0A: 'SN840',
|
||||
O0841AAA0A: '841',
|
||||
O0842AAA0A: '842',
|
||||
O0843AAA0A: '843',
|
||||
O0844AAA0A: '844',
|
||||
O0845AAA0A: '845',
|
||||
O0846AAA0A: '846',
|
||||
O0846AAN0A: 'SN846',
|
||||
O0847AAA0A: '847',
|
||||
O0848AAA0A: '848',
|
||||
O0848AAN0A: 'SN848',
|
||||
O0849AAA0A: '849',
|
||||
O0850AAA0A: '850',
|
||||
O0851AAA0A: '851',
|
||||
O0852AAA0A: '852',
|
||||
O0853AAA0A: '853',
|
||||
O0853AAN0A: 'SN853',
|
||||
O0853AAV0A: 'SV853',
|
||||
O0854AAAAA: '854',
|
||||
O0855AAA0A: '855',
|
||||
O0856AAA0A: '856',
|
||||
O0857AAA0A: '857',
|
||||
O0857AAN0A: 'SN857',
|
||||
O0859AAA0A: '859',
|
||||
O0861AAA0A: '861',
|
||||
O0861AAN0A: 'SN861',
|
||||
O0862AAA0A: '862',
|
||||
O0863AAA0A: '863',
|
||||
O0863AAV0A: 'SV863',
|
||||
O0864AAA0A: '864',
|
||||
O0864AAP0A: 'SP864',
|
||||
O0865AAA0A: '865',
|
||||
O0866AAA0A: '866',
|
||||
O0866AAV0A: 'SV866',
|
||||
O0867AAA0A: '867',
|
||||
O0867AAN0A: 'SN867',
|
||||
O0868AAA0A: '868',
|
||||
O0869AAA0A: '869',
|
||||
O0870AAA0A: '870',
|
||||
O0871AAA0A: '871',
|
||||
O0872AAA0A: '872',
|
||||
O0878AAA0A: '878',
|
||||
O0878AAN0A: 'SN878',
|
||||
O0878AAV0A: 'SV878',
|
||||
O0880AAA0A: '880',
|
||||
O0880AAN0A: 'SN880',
|
||||
O0881AAA0A: '881',
|
||||
O0883AAA0A: '883',
|
||||
O0884AAA0A: '884',
|
||||
O0884AAN0A: 'SN884',
|
||||
O0885AAA0A: '885',
|
||||
O0885AAN0A: 'SN885',
|
||||
O0892AAA0A: '892',
|
||||
O0893AAA0A: '893',
|
||||
O0893AAN0A: 'SN893',
|
||||
O0894AAA0A: '894',
|
||||
O0895AAA0A: '895',
|
||||
O0895AAN0A: 'SN895',
|
||||
O0897AAA0A: '897',
|
||||
O0897AAN0A: 'SN897',
|
||||
O0898AAA0A: '898',
|
||||
O0898AAN0A: 'SN898',
|
||||
O0899AAA0A: '899',
|
||||
O0899AAV0A: 'SV899',
|
||||
O0900AAA0A: '900',
|
||||
O0901AAA0A: '901',
|
||||
O0901AAVBA: 'SVB901',
|
||||
O0905AAA0A: '905',
|
||||
O0906AAA0A: '906',
|
||||
O0908AAA0A: '908',
|
||||
O0908AAN0A: 'SN908',
|
||||
O0908AAV0A: 'SV908',
|
||||
O0910AAA0A: '910',
|
||||
O0913AAA0A: '913',
|
||||
O0915AAA0A: '915',
|
||||
O0917AAA0A: '917',
|
||||
O0917AAV0A: 'SV917',
|
||||
O0918AAA0A: '918',
|
||||
O0918AAP0A: 'SP918',
|
||||
O0919AAA0A: '919',
|
||||
O0919AAN0A: 'SN919',
|
||||
O0920AAA0A: '920',
|
||||
O0921AAA0A: '921',
|
||||
O0921AAV0A: 'SV921',
|
||||
O0922AAA0A: '922',
|
||||
O0922AAV0A: 'SV922',
|
||||
O0925AAA0A: '925',
|
||||
O0926AAA0A: '926',
|
||||
O0928AAA0A: '928',
|
||||
O0932AAA0A: '932',
|
||||
O0940AAA0A: '940',
|
||||
O0944AAA0A: 'LECD143',
|
||||
O0946AAA0A: '946',
|
||||
O0950AAA0A: 'LECD144',
|
||||
O0951AAA0A: 'LECD145',
|
||||
O0954AAA0A: '954',
|
||||
O0954AAN0A: 'SN954',
|
||||
O0959AAA0A: 'LECD146',
|
||||
O0961AAA0A: '961',
|
||||
O0975AAA0A: '975',
|
||||
O0979AAA0A: '979',
|
||||
O0987AAA0A: '987',
|
||||
O0990AAA0A: '990',
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import { greatCircleKm } from '../geoDistance.js';
|
||||
import { RIO_ROUTE_NAMES } from './rioRouteNames.js';
|
||||
|
||||
/**
|
||||
* @module transitFeeds
|
||||
@@ -376,6 +377,18 @@ function burlingtonRouteLabel(routeId) {
|
||||
return BURLINGTON_ROUTE_NAMES[routeId] || routeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rio route key to the line a rider would say. One SMTR `routes.txt` covers
|
||||
* both feeds (see rioRouteNames.js).
|
||||
* @param {string|null} routeId
|
||||
* @returns {string|null}
|
||||
*/
|
||||
function rioRouteLabel(routeId) {
|
||||
return Object.hasOwn(RIO_ROUTE_NAMES, routeId)
|
||||
? RIO_ROUTE_NAMES[routeId]
|
||||
: routeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registry of feeds. Order is presentation order in the stats/credit text.
|
||||
* `loadRadiusKm` is the distance from `center` inside which the feed is polled.
|
||||
@@ -729,6 +742,52 @@ export const TRANSIT_FEED_REGISTRY = Object.freeze([
|
||||
defaultMode: 'bus',
|
||||
routeLabel: burlingtonRouteLabel,
|
||||
}),
|
||||
Object.freeze({
|
||||
id: 'smtr-rio-onibus',
|
||||
name: 'Rio buses (SPPO)',
|
||||
operator: 'Secretaria Municipal de Transportes (SMTR), Rio de Janeiro',
|
||||
region: 'Rio de Janeiro, RJ',
|
||||
// Every vehicle in a 2026-10-29 snapshot, from Santa Cruz in the west to
|
||||
// the Zona Sul, sat within 31 km of this point.
|
||||
center: Object.freeze({ lat: -22.92, lon: -43.42 }),
|
||||
loadRadiusKm: 45,
|
||||
url: 'https://dados.mobilidade.rio/gtfs/realtime/onibus/vehicle-positions',
|
||||
license: 'CC BY 4.0',
|
||||
licenseUrl:
|
||||
'https://www.data.rio/documents/997bc16cc4344a5cb0e48ab06cbdfd1f',
|
||||
attribution: 'SMTR / Prefeitura da Cidade do Rio de Janeiro — buses (SPPO)',
|
||||
defaultEnabled: true,
|
||||
terms: Object.freeze({
|
||||
quote:
|
||||
'This work is licensed under a Creative Commons Attribution 4.0 International License',
|
||||
note: 'The data.rio catalog page labels the feed [beta] and states a gateway limit of 5 requests a second and 60 a minute behind a 10-second cache, which the 15-second proxy TTL stays inside. SMTR relays the concessionaires’ ITS data and repairs trips with its own matching; the page places accuracy with the suppliers. Speeds are m/s, as the spec requires: checked against vehicle movement on 2026-11-05, though a 2026-10-29 snapshot read as km/h. Line names come from SMTR’s GTFS `routes.txt`, under the same CC BY 4.0 terms.',
|
||||
}),
|
||||
defaultMode: 'bus',
|
||||
routeLabel: rioRouteLabel,
|
||||
}),
|
||||
Object.freeze({
|
||||
id: 'smtr-rio-brt',
|
||||
name: 'Mobi-Rio BRT',
|
||||
operator:
|
||||
'Secretaria Municipal de Transportes (SMTR), Rio de Janeiro, for the Mobi-Rio BRT fleet',
|
||||
region: 'Rio de Janeiro, RJ',
|
||||
center: Object.freeze({ lat: -22.92, lon: -43.42 }),
|
||||
loadRadiusKm: 45,
|
||||
url: 'https://dados.mobilidade.rio/gtfs/realtime/brt/vehicle-positions',
|
||||
license: 'CC BY 4.0',
|
||||
licenseUrl:
|
||||
'https://www.data.rio/documents/8bb196cc02174d25b77437bb2f6443d4',
|
||||
attribution:
|
||||
'SMTR / Prefeitura da Cidade do Rio de Janeiro — BRT (Mobi-Rio)',
|
||||
defaultEnabled: true,
|
||||
terms: Object.freeze({
|
||||
quote:
|
||||
'This work is licensed under a Creative Commons Attribution 4.0 International License',
|
||||
note: 'Same catalog terms, [beta] label and gateway limits as the bus feed. The fleet is operated by Mobi-Rio and tracked by SONDA, whose data SMTR relays. Speeds are m/s too. Line names come from the same `routes.txt`.',
|
||||
}),
|
||||
defaultMode: 'bus',
|
||||
routeLabel: rioRouteLabel,
|
||||
}),
|
||||
]);
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
transitModeResolved,
|
||||
transitRouteLabel,
|
||||
} from './transitFeeds.js';
|
||||
import { RIO_ROUTE_NAMES } from './rioRouteNames.js';
|
||||
|
||||
test('every registered feed is keyless, https, licensed, and uniquely identified', () => {
|
||||
const ids = new Set();
|
||||
@@ -482,3 +483,72 @@ test('Burlington is live and carries its terms link, which its licence requires'
|
||||
assert.match(bur.licenseUrl, /Open%20Data%20Terms%20of%20Use\.pdf$/);
|
||||
assert.match(bur.url, /^https:\/\//, 'port 80 has no listener at all');
|
||||
});
|
||||
|
||||
test('Rio buses and BRT cover the city from Centro to Santa Cruz, and not São Paulo', () => {
|
||||
const ids = (lat, lon) =>
|
||||
transitFeedsInRange(lat, lon).map((feed) => feed.id);
|
||||
for (const [place, lat, lon] of [
|
||||
['Centro', -22.9068, -43.1729],
|
||||
['Barra da Tijuca', -23.0004, -43.3659],
|
||||
['Santa Cruz', -22.9194, -43.6847],
|
||||
]) {
|
||||
assert.ok(
|
||||
ids(lat, lon).includes('smtr-rio-onibus'),
|
||||
`buses reach ${place}`,
|
||||
);
|
||||
assert.ok(ids(lat, lon).includes('smtr-rio-brt'), `BRT reaches ${place}`);
|
||||
}
|
||||
assert.deepEqual(
|
||||
ids(-23.5505, -46.6333).filter((id) => id.startsWith('smtr-rio')),
|
||||
[],
|
||||
'São Paulo, 360 km away, never polls Rio',
|
||||
);
|
||||
});
|
||||
|
||||
test('Rio route keys read as the line on the front of the bus', () => {
|
||||
// Some keys look parseable and are not (`O0014AAA0A` is LECD135).
|
||||
const buses = getTransitFeed('smtr-rio-onibus');
|
||||
assert.equal(transitRouteLabel(buses, 'O0868AAA0A'), '868');
|
||||
assert.equal(transitRouteLabel(buses, 'O0731AAN0A'), 'SN731');
|
||||
assert.equal(transitRouteLabel(buses, 'O0759AAP0A'), 'SP759');
|
||||
assert.equal(transitRouteLabel(buses, 'O0014AAA0A'), 'LECD135');
|
||||
assert.equal(transitRouteLabel(buses, 'E2303AAA0A'), '2303');
|
||||
const brt = getTransitFeed('smtr-rio-brt');
|
||||
assert.equal(transitRouteLabel(brt, '20000501110'), '50');
|
||||
assert.equal(transitRouteLabel(brt, '20000EXEC1110'), 'ESP01');
|
||||
// Unknown ids, prototype names included, keep their raw key.
|
||||
assert.equal(transitRouteLabel(buses, 'O9999AAA0A'), 'O9999AAA0A');
|
||||
assert.equal(transitRouteLabel(buses, 'constructor'), 'constructor');
|
||||
assert.equal(transitRouteLabel(brt, null), null);
|
||||
});
|
||||
|
||||
test('the Rio route table is a clean join of published keys to names', () => {
|
||||
const entries = Object.entries(RIO_ROUTE_NAMES);
|
||||
assert.ok(entries.length > 400, 'the whole published table is carried');
|
||||
for (const [id, name] of entries) {
|
||||
assert.match(id, /^(?:[OE]\d{4}[A-Z0-9]{5}|2\d{4}[A-Z0-9]{4,8})$/, id);
|
||||
assert.ok(name && name === name.trim(), `${id} has a name`);
|
||||
assert.notEqual(name, id, `${id} is translated`);
|
||||
}
|
||||
});
|
||||
|
||||
test('Rio feeds are CC BY 4.0 and live', () => {
|
||||
for (const id of ['smtr-rio-onibus', 'smtr-rio-brt']) {
|
||||
const feed = getTransitFeed(id);
|
||||
assert.equal(feed.defaultEnabled, true, `${id} is live`);
|
||||
assert.equal(feed.license, 'CC BY 4.0');
|
||||
assert.match(feed.licenseUrl, /^https:\/\/www\.data\.rio\/documents\//);
|
||||
assert.match(feed.attribution, /Prefeitura da Cidade do Rio de Janeiro/);
|
||||
assert.equal(transitModeFor(feed, null), 'bus');
|
||||
}
|
||||
const sources = readFileSync(
|
||||
new URL('../../DATA_SOURCES.md', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
assert.ok(
|
||||
sources
|
||||
.split('\n')
|
||||
.some((line) => line.startsWith('| **SMTR Rio de Janeiro')),
|
||||
'Rio has a DATA_SOURCES row',
|
||||
);
|
||||
});
|
||||
@@ -137,6 +137,7 @@ function encodeFeed({
|
||||
const position = new PbfWriter();
|
||||
position.writeFloatField(1, entity.lat);
|
||||
position.writeFloatField(2, entity.lon);
|
||||
if (entity.speed != null) position.writeFloatField(5, entity.speed);
|
||||
vehicle.writeBytesField(2, position.finish());
|
||||
if (entity.timestamp) vehicle.writeVarintField(5, entity.timestamp);
|
||||
const descriptor = new PbfWriter();
|
||||
@@ -284,6 +285,15 @@ test('the snapshot builder fills timestamps from the header it decoded', () => {
|
||||
assert.equal(snapshot.feedTimestamp, 1_700_000_000);
|
||||
});
|
||||
|
||||
test('Rio speeds pass through as the m/s they are published in', () => {
|
||||
// Checked against vehicle movement on 2026-11-05.
|
||||
const bytes = encodeFeed({
|
||||
entities: [{ id: 'moving', lat: -22.9, lon: -43.2, speed: 10 }],
|
||||
});
|
||||
const rio = buildTransitSnapshot(getTransitFeed('smtr-rio-onibus'), bytes, 1);
|
||||
assert.equal(rio.vehicles[0].speedMps, 10);
|
||||
});
|
||||
|
||||
test('conditional-request validators and gzip ride along with feed identification', () => {
|
||||
const entur = getTransitFeed('entur-norway');
|
||||
const plain = transitUpstreamHeaders(entur);
|
||||
|
||||
@@ -12,6 +12,11 @@ import {
|
||||
} from '../../data/transitFeeds.js';
|
||||
import { transitModeTier } from '../../data/transitPresetStyle.js';
|
||||
|
||||
/** Regions, not feeds: one region can carry several feeds (Rio has two). */
|
||||
const TRANSIT_REGION_COUNT = new Set(
|
||||
TRANSIT_ENABLED_FEEDS.map((feed) => feed.region),
|
||||
).size;
|
||||
|
||||
/**
|
||||
* The registry entry behind a vehicle. Live feeds answer first so a feed the
|
||||
* session disabled stops supplying vocabulary; the registry is the fallback,
|
||||
@@ -249,7 +254,7 @@ export function createQueries({ state, parts }) {
|
||||
// English data (the analyst's empty note to the model); the voice
|
||||
// card shows it through NOTE_PATTERNS (voice/resultDisplay.js).
|
||||
coverage: state._altitudeGateOpen
|
||||
? `No feed here yet · ${TRANSIT_ENABLED_FEEDS.length} regions available`
|
||||
? `No feed here yet · ${TRANSIT_REGION_COUNT} regions available`
|
||||
: `Fly below ${Math.round(ACTIVATION_ALTITUDE_M / 1000).toLocaleString('en-US')} km to a covered region`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { createQueries } from './queries.js';
|
||||
import { TRANSIT_ENABLED_FEEDS } from '../../data/transitFeeds.js';
|
||||
|
||||
test('the empty-coverage note counts regions, not feeds', () => {
|
||||
const rio = TRANSIT_ENABLED_FEEDS.filter(
|
||||
(feed) => feed.region === 'Rio de Janeiro, RJ',
|
||||
);
|
||||
assert.equal(rio.length, 2, 'Rio is one region with two feeds');
|
||||
const regions = new Set(TRANSIT_ENABLED_FEEDS.map((feed) => feed.region));
|
||||
assert.ok(regions.size < TRANSIT_ENABLED_FEEDS.length);
|
||||
|
||||
const stats = createQueries({
|
||||
state: {
|
||||
_activeFeeds: new Map(),
|
||||
_vehicles: new Map(),
|
||||
_enabled: true,
|
||||
_altitudeGateOpen: true,
|
||||
},
|
||||
parts: {},
|
||||
}).methods.getStats();
|
||||
assert.equal(stats.status, 'zoom-in');
|
||||
assert.equal(
|
||||
stats.coverage,
|
||||
`No feed here yet · ${regions.size} regions available`,
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user