diff --git a/assets/audio/R_RAIL_REPLACEMENT_SERVICE_001.mp3 b/assets/audio/R_RAIL_REPLACEMENT_SERVICE_001.mp3 new file mode 100644 index 0000000..8790aaf Binary files /dev/null and b/assets/audio/R_RAIL_REPLACEMENT_SERVICE_001.mp3 differ diff --git a/assets/audio/rail_replacement/BAKERLOO.mp3 b/assets/audio/rail_replacement/BAKERLOO.mp3 new file mode 100644 index 0000000..7a227cc Binary files /dev/null and b/assets/audio/rail_replacement/BAKERLOO.mp3 differ diff --git a/assets/audio/rail_replacement/CENTRAL.mp3 b/assets/audio/rail_replacement/CENTRAL.mp3 new file mode 100644 index 0000000..bac3ec1 Binary files /dev/null and b/assets/audio/rail_replacement/CENTRAL.mp3 differ diff --git a/assets/audio/rail_replacement/CIRCLE.mp3 b/assets/audio/rail_replacement/CIRCLE.mp3 new file mode 100644 index 0000000..ea077e6 Binary files /dev/null and b/assets/audio/rail_replacement/CIRCLE.mp3 differ diff --git a/assets/audio/rail_replacement/DISTRICT.mp3 b/assets/audio/rail_replacement/DISTRICT.mp3 new file mode 100644 index 0000000..50beff4 Binary files /dev/null and b/assets/audio/rail_replacement/DISTRICT.mp3 differ diff --git a/assets/audio/rail_replacement/DLR.mp3 b/assets/audio/rail_replacement/DLR.mp3 new file mode 100644 index 0000000..1508710 Binary files /dev/null and b/assets/audio/rail_replacement/DLR.mp3 differ diff --git a/assets/audio/rail_replacement/HAMMERSMITH_N_CITY.mp3 b/assets/audio/rail_replacement/HAMMERSMITH_N_CITY.mp3 new file mode 100644 index 0000000..f00862b Binary files /dev/null and b/assets/audio/rail_replacement/HAMMERSMITH_N_CITY.mp3 differ diff --git a/assets/audio/rail_replacement/JUBILEE.mp3 b/assets/audio/rail_replacement/JUBILEE.mp3 new file mode 100644 index 0000000..617677a Binary files /dev/null and b/assets/audio/rail_replacement/JUBILEE.mp3 differ diff --git a/assets/audio/rail_replacement/LONDON_OVERGROUND.mp3 b/assets/audio/rail_replacement/LONDON_OVERGROUND.mp3 new file mode 100644 index 0000000..6fc346c Binary files /dev/null and b/assets/audio/rail_replacement/LONDON_OVERGROUND.mp3 differ diff --git a/assets/audio/rail_replacement/METROPOLITAN.mp3 b/assets/audio/rail_replacement/METROPOLITAN.mp3 new file mode 100644 index 0000000..123b74d Binary files /dev/null and b/assets/audio/rail_replacement/METROPOLITAN.mp3 differ diff --git a/assets/audio/rail_replacement/NORTHERN.mp3 b/assets/audio/rail_replacement/NORTHERN.mp3 new file mode 100644 index 0000000..43ffdc6 Binary files /dev/null and b/assets/audio/rail_replacement/NORTHERN.mp3 differ diff --git a/assets/audio/rail_replacement/PICCADILLY.mp3 b/assets/audio/rail_replacement/PICCADILLY.mp3 new file mode 100644 index 0000000..a005734 Binary files /dev/null and b/assets/audio/rail_replacement/PICCADILLY.mp3 differ diff --git a/assets/audio/rail_replacement/TFL_RAIL.mp3 b/assets/audio/rail_replacement/TFL_RAIL.mp3 new file mode 100644 index 0000000..2a72c3c Binary files /dev/null and b/assets/audio/rail_replacement/TFL_RAIL.mp3 differ diff --git a/assets/audio/rail_replacement/VICTORIA.mp3 b/assets/audio/rail_replacement/VICTORIA.mp3 new file mode 100644 index 0000000..bc11b28 Binary files /dev/null and b/assets/audio/rail_replacement/VICTORIA.mp3 differ diff --git a/assets/datasets/tube_stations.json b/assets/datasets/tube_stations.json new file mode 100644 index 0000000..05a1a92 --- /dev/null +++ b/assets/datasets/tube_stations.json @@ -0,0 +1,3929 @@ +{ + "Acton Town": { + "lines": [ + "District", + "Piccadilly" + ], + "location": { + "lat": 51.50837199999999, + "lng": -0.27444 + } + }, + "Aldgate": { + "lines": [ + "Metropolitan", + "Circle" + ], + "location": { + "lat": 51.5134365, + "lng": -0.0772463 + } + }, + "Aldgate East": { + "lines": [ + "Hammersmith and City", + "District" + ], + "location": { + "lat": 51.5152843, + "lng": -0.0722437 + } + }, + "Alperton": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.539601, + "lng": -0.2988365 + } + }, + "Amersham": { + "lines": [ + "Metropolitan" + ], + "location": { + "lat": 51.6106434, + "lng": -0.0799942 + } + }, + "Angel": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.5346761, + "lng": -0.1042905 + } + }, + "Arnos Grove": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.619799, + "lng": -0.1296871 + } + }, + "Arsenal": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.5584175, + "lng": -0.1056624 + } + }, + "Baker Street": { + "lines": [ + "Metropolitan", + "Bakerloo", + "Circle", + "Jubilee", + "Hammersmith and City" + ], + "location": { + "lat": 51.5205651, + "lng": -0.1567973 + } + }, + "Balham": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.4439889, + "lng": -0.1494105 + } + }, + "Bank": { + "lines": [ + "Waterloo and City", + "Northern", + "Central", + "DLR" + ], + "location": { + "lat": 51.5072178, + "lng": -0.1275862 + } + }, + "Barbican": { + "lines": [ + "Metropolitan", + "Circle", + "Hammersmith and City" + ], + "location": { + "lat": 51.51891149999999, + "lng": -0.0954145 + } + }, + "Barking": { + "lines": [ + "District", + "Hammersmith and City", + "London Overground" + ], + "location": { + "lat": 51.5377369, + "lng": 0.076127 + } + }, + "Barkingside": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5899246, + "lng": 0.08387420000000001 + } + }, + "Barons Court": { + "lines": [ + "District", + "Piccadilly" + ], + "location": { + "lat": 51.5792759, + "lng": -0.2625196 + } + }, + "Bayswater": { + "lines": [ + "District", + "Circle" + ], + "location": { + "lat": 51.5135456, + "lng": -0.1865659 + } + }, + "Becontree": { + "lines": [ + "District" + ], + "location": { + "lat": 51.5625977, + "lng": 0.1224296 + } + }, + "Belsize Park": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.5475617, + "lng": -0.1671077 + } + }, + "Bermondsey": { + "lines": [ + "Jubilee" + ], + "location": { + "lat": 51.49226549999999, + "lng": -0.0652005 + } + }, + "Bethnal Green": { + "lines": [ + "Central", + "London Overground" + ], + "location": { + "lat": 51.531103, + "lng": -0.0480805 + } + }, + "Blackfriars": { + "lines": [ + "District", + "Circle" + ], + "location": { + "lat": 51.51199200000001, + "lng": -0.1038617 + } + }, + "Blackhorse Road": { + "lines": [ + "Victoria", + "London Overground" + ], + "location": { + "lat": 51.5843796, + "lng": -0.0375063 + } + }, + "Bond Street": { + "lines": [ + "Central", + "Jubilee", + "Elizabeth Line" + ], + "location": { + "lat": 51.5112678, + "lng": -0.1433897 + } + }, + "Borough": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.4881095, + "lng": -0.0762725 + } + }, + "Boston Manor": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.4918967, + "lng": -0.3182689 + } + }, + "Bounds Green": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.6086813, + "lng": -0.1274673 + } + }, + "Bow Road": { + "lines": [ + "District", + "Hammersmith and City" + ], + "location": { + "lat": 51.5275138, + "lng": -0.0235044 + } + }, + "Brent Cross": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.5763589, + "lng": -0.2236956 + } + }, + "Brixton": { + "lines": [ + "Victoria" + ], + "location": { + "lat": 51.4612794, + "lng": -0.1156148 + } + }, + "Bromley-by-Bow": { + "lines": [ + "District", + "Hammersmith and City" + ], + "location": { + "lat": 51.5278208, + "lng": -0.0128373 + } + }, + "Buckhurst Hill": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.627572, + "lng": 0.034513 + } + }, + "Burnt Oak": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.602809, + "lng": -0.266965 + } + }, + "Caledonian Road": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.543659, + "lng": -0.1175446 + } + }, + "Camden Town": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.5390261, + "lng": -0.1425516 + } + }, + "Canada Water": { + "lines": [ + "Jubilee", + "London Overground" + ], + "location": { + "lat": 51.4962989, + "lng": -0.0482742 + } + }, + "Canary Wharf": { + "lines": [ + "Jubilee", + "Elizabeth Line", + "DLR" + ], + "location": { + "lat": 51.5054306, + "lng": -0.0235333 + } + }, + "Canning Town": { + "lines": [ + "Jubilee", + "DLR" + ], + "location": { + "lat": 51.5189494, + "lng": 0.0132 + } + }, + "Cannon Street": { + "lines": [ + "District", + "Circle" + ], + "location": { + "lat": 51.5119949, + "lng": -0.091962 + } + }, + "Canons Park": { + "lines": [ + "Jubilee" + ], + "location": { + "lat": 51.6076736, + "lng": -0.2962361 + } + }, + "Chalfont and Latimer": { + "lines": [ + "Metropolitan" + ], + "location": { + "lat": 51.5072178, + "lng": -0.1275862 + } + }, + "Chalk Farm": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.5444834, + "lng": -0.152158 + } + }, + "Chancery Lane": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.516154, + "lng": -0.1120214 + } + }, + "Charing Cross": { + "lines": [ + "Bakerloo", + "Northern" + ], + "location": { + "lat": 51.5090275, + "lng": -0.1255016 + } + }, + "Chesham": { + "lines": [ + "Metropolitan" + ], + "location": { + "lat": 51.4082476, + "lng": -0.0550584 + } + }, + "Chigwell": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.62628100000001, + "lng": 0.080647 + } + }, + "Chiswick Park": { + "lines": [ + "District" + ], + "location": { + "lat": 51.4943221, + "lng": -0.2763004 + } + }, + "Chorleywood": { + "lines": [ + "Metropolitan" + ], + "location": { + "lat": 51.655632, + "lng": -0.5161829 + } + }, + "Clapham Common": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.4589252, + "lng": -0.1493071 + } + }, + "Clapham North": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.4653972, + "lng": -0.1290355 + } + }, + "Clapham South": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.4658813, + "lng": -0.1413263 + } + }, + "Cockfosters": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.647851, + "lng": -0.151255 + } + }, + "Colindale": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.5896908, + "lng": -0.2491029 + } + }, + "Colliers Wood": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.41704439999999, + "lng": -0.1778555 + } + }, + "Covent Garden": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.5116571, + "lng": -0.1240436 + } + }, + "Croxley": { + "lines": [ + "Metropolitan" + ], + "location": { + "lat": 51.5296492, + "lng": -0.2004515 + } + }, + "Dagenham East": { + "lines": [ + "District" + ], + "location": { + "lat": 51.5402631, + "lng": 0.1482103 + } + }, + "Dagenham Heathway": { + "lines": [ + "District" + ], + "location": { + "lat": 51.5443315, + "lng": 0.1479599 + } + }, + "Debden": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.6475081, + "lng": 0.0822029 + } + }, + "Dollis Hill": { + "lines": [ + "Jubilee" + ], + "location": { + "lat": 51.5525583, + "lng": -0.2380731 + } + }, + "Ealing Broadway": { + "lines": [ + "District", + "Central", + "Elizabeth Line" + ], + "location": { + "lat": 51.5132537, + "lng": -0.3043136 + } + }, + "Ealing Common": { + "lines": [ + "District", + "Piccadilly" + ], + "location": { + "lat": 51.51027149999999, + "lng": -0.2877267 + } + }, + "Earl's Court": { + "lines": [ + "District", + "Piccadilly" + ], + "location": { + "lat": 51.490331, + "lng": -0.1958417 + } + }, + "East Acton": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5127758, + "lng": -0.2476803 + } + }, + "East Finchley": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.592468, + "lng": -0.1664342 + } + }, + "East Ham": { + "lines": [ + "District", + "Hammersmith and City" + ], + "location": { + "lat": 51.5333972, + "lng": 0.04991139999999999 + } + }, + "East Putney": { + "lines": [ + "District" + ], + "location": { + "lat": 51.4592, + "lng": -0.21098 + } + }, + "Eastcote": { + "lines": [ + "Metropolitan", + "Piccadilly" + ], + "location": { + "lat": 51.57726719999999, + "lng": -0.3981245 + } + }, + "Edgware": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.61548819999999, + "lng": -0.2609598 + } + }, + "Edgware Road": { + "lines": [ + "Bakerloo", + "Hammersmith and City", + "District", + "Circle" + ], + "location": { + "lat": 51.5187944, + "lng": -0.1684379 + } + }, + "Elephant and Castle": { + "lines": [ + "Northern", + "Bakerloo" + ], + "location": { + "lat": 51.4959129, + "lng": -0.1004748 + } + }, + "Elm Park": { + "lines": [ + "District" + ], + "location": { + "lat": 51.548497, + "lng": 0.1999531 + } + }, + "Embankment": { + "lines": [ + "District", + "Bakerloo", + "Northern", + "Circle" + ], + "location": { + "lat": 51.5099822, + "lng": -0.117819 + } + }, + "Epping": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5448108, + "lng": -0.1078512 + } + }, + "Euston": { + "lines": [ + "Northern", + "Victoria", + "London Overground" + ], + "location": { + "lat": 51.5268962, + "lng": -0.1321509 + } + }, + "Euston Square": { + "lines": [ + "Metropolitan", + "Circle", + "Hammersmith and City" + ], + "location": { + "lat": 51.5275799, + "lng": -0.1310657 + } + }, + "Fairlop": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.6018926, + "lng": 0.08420119999999999 + } + }, + "Farringdon": { + "lines": [ + "Metropolitan", + "Circle", + "Hammersmith and City", + "Elizabeth Line" + ], + "location": { + "lat": 51.5215324, + "lng": -0.1042414 + } + }, + "Finchley Central": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.6009829, + "lng": -0.19248 + } + }, + "Finchley Road": { + "lines": [ + "Metropolitan", + "Jubilee" + ], + "location": { + "lat": 51.5611866, + "lng": -0.197096 + } + }, + "Finsbury Park": { + "lines": [ + "Piccadilly", + "Victoria" + ], + "location": { + "lat": 51.56457839999999, + "lng": -0.1047257 + } + }, + "Fulham Broadway": { + "lines": [ + "District" + ], + "location": { + "lat": 51.4801568, + "lng": -0.1962971 + } + }, + "Gants Hill": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5767812, + "lng": 0.0661732 + } + }, + "Gloucester Road": { + "lines": [ + "District", + "Piccadilly", + "Circle" + ], + "location": { + "lat": 51.4952605, + "lng": -0.1829272 + } + }, + "Golders Green": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.5740013, + "lng": -0.1987725 + } + }, + "Goldhawk Road": { + "lines": [ + "Circle", + "Hammersmith and City" + ], + "location": { + "lat": 51.5004522, + "lng": -0.2350711 + } + }, + "Goodge Street": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.5195829, + "lng": -0.1351969 + } + }, + "Grange Hill": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.4081683, + "lng": -0.0878301 + } + }, + "Great Portland Street": { + "lines": [ + "Metropolitan", + "Circle", + "Hammersmith and City" + ], + "location": { + "lat": 51.5199714, + "lng": -0.1426427 + } + }, + "Greenford": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.531797, + "lng": -0.34622 + } + }, + "Green Park": { + "lines": [ + "Piccadilly", + "Victoria", + "Jubilee" + ], + "location": { + "lat": 51.5039052, + "lng": -0.1438484 + } + }, + "Gunnersbury": { + "lines": [ + "District", + "London Overground" + ], + "location": { + "lat": 51.49335199999999, + "lng": -0.278887 + } + }, + "Hainault": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.6083149, + "lng": 0.119312 + } + }, + "Hammersmith": { + "lines": [ + "District", + "Piccadilly", + "Circle", + "Hammersmith and City" + ], + "location": { + "lat": 51.491187, + "lng": -0.223731 + } + }, + "Hampstead": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.5556461, + "lng": -0.1761749 + } + }, + "Hanger Lane": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5217238, + "lng": -0.2915098 + } + }, + "Harlesden": { + "lines": [ + "Bakerloo", + "London Overground" + ], + "location": { + "lat": 51.53710090000001, + "lng": -0.2427341 + } + }, + "Harrow and Wealdstone": { + "lines": [ + "Bakerloo" + ], + "location": { + "lat": 51.59172359999999, + "lng": -0.3343587 + } + }, + "Harrow-on-the-Hill": { + "lines": [ + "Metropolitan" + ], + "location": { + "lat": 51.5706321, + "lng": -0.3396363 + } + }, + "Hatton Cross": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.4651031, + "lng": -0.4157165 + } + }, + "Heathrow Terminals 2 and 3": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.4712872, + "lng": -0.4522179 + } + }, + "Heathrow Terminal 4": { + "lines": [ + "Piccadilly", + "Elizabeth Line" + ], + "location": { + "lat": 51.4596965, + "lng": -0.4473622 + } + }, + "Heathrow Terminal 5": { + "lines": [ + "Piccadilly", + "Elizabeth Line" + ], + "location": { + "lat": 51.471466, + "lng": -0.4879806 + } + }, + "Hendon Central": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.5833094, + "lng": -0.226453 + } + }, + "High Barnet": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.6569225, + "lng": -0.1949252 + } + }, + "Highbury and Islington": { + "lines": [ + "Victoria" + ], + "location": { + "lat": 51.5569649, + "lng": -0.0979656 + } + }, + "Hillingdon": { + "lines": [ + "Metropolitan", + "Piccadilly" + ], + "location": { + "lat": 51.5351832, + "lng": -0.4481378 + } + }, + "Holborn": { + "lines": [ + "Central", + "Piccadilly" + ], + "location": { + "lat": 51.5175802, + "lng": -0.1145147 + } + }, + "Holland Park": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5032252, + "lng": -0.2036668 + } + }, + "Holloway Road": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.5570465, + "lng": -0.118404 + } + }, + "Hornchurch": { + "lines": [ + "District" + ], + "location": { + "lat": 51.562254, + "lng": 0.218605 + } + }, + "Hounslow Central": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.5134026, + "lng": -0.108187 + } + }, + "Hounslow East": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.4730435, + "lng": -0.3564961 + } + }, + "Hounslow West": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.469435, + "lng": -0.3837709 + } + }, + "Hyde Park Corner": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.5029347, + "lng": -0.1508562 + } + }, + "Ickenham": { + "lines": [ + "Metropolitan", + "Piccadilly" + ], + "location": { + "lat": 51.563966, + "lng": -0.442299 + } + }, + "Kennington": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.4879753, + "lng": -0.1119362 + } + }, + "Kensal Green": { + "lines": [ + "Bakerloo", + "London Overground" + ], + "location": { + "lat": 51.5345071, + "lng": -0.2250186 + } + }, + "Kensington (Olympia)": { + "lines": [ + "District", + "London Overground" + ], + "location": { + "lat": 51.4979934, + "lng": -0.2099576 + } + }, + "Kentish Town": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.5490952, + "lng": -0.144084 + } + }, + "Kenton": { + "lines": [ + "Bakerloo", + "London Overground" + ], + "location": { + "lat": 51.5865039, + "lng": -0.3068038 + } + }, + "Kew Gardens": { + "lines": [ + "District", + "London Overground" + ], + "location": { + "lat": 51.4787438, + "lng": -0.295573 + } + }, + "Kilburn": { + "lines": [ + "Jubilee" + ], + "location": { + "lat": 51.539813, + "lng": -0.1985291 + } + }, + "Kilburn Park": { + "lines": [ + "Bakerloo" + ], + "location": { + "lat": 51.53504119999999, + "lng": -0.1938988 + } + }, + "Kingsbury": { + "lines": [ + "Jubilee" + ], + "location": { + "lat": 51.5853499, + "lng": -0.2782546 + } + }, + "King's Cross St Pancras": { + "lines": [ + "Metropolitan", + "Northern", + "Piccadilly", + "Circle", + "Victoria", + "Hammersmith and City" + ], + "location": { + "lat": 51.5306323, + "lng": -0.1231167 + } + }, + "Knightsbridge": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.4991195, + "lng": -0.1644394 + } + }, + "Ladbroke Grove": { + "lines": [ + "Circle", + "Hammersmith and City" + ], + "location": { + "lat": 51.5178769, + "lng": -0.2103321 + } + }, + "Lambeth North": { + "lines": [ + "Bakerloo" + ], + "location": { + "lat": 51.4989177, + "lng": -0.1121086 + } + }, + "Lancaster Gate": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5115786, + "lng": -0.1802482 + } + }, + "Latimer Road": { + "lines": [ + "Circle", + "Hammersmith and City" + ], + "location": { + "lat": 51.5178453, + "lng": -0.2241217 + } + }, + "Leicester Square": { + "lines": [ + "Piccadilly", + "Northern" + ], + "location": { + "lat": 51.510676, + "lng": -0.1306432 + } + }, + "Leyton": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5702225, + "lng": -0.0146938 + } + }, + "Leytonstone": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5649624, + "lng": 0.0088141 + } + }, + "Liverpool Street": { + "lines": [ + "Metropolitan", + "Central", + "Circle", + "Hammersmith and City", + "London Overground", + "Elizabeth Line" + ], + "location": { + "lat": 51.5174846, + "lng": -0.0826291 + } + }, + "London Bridge": { + "lines": [ + "Northern", + "Jubilee" + ], + "location": { + "lat": 51.5078788, + "lng": -0.0877321 + } + }, + "Loughton": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.655942, + "lng": 0.068161 + } + }, + "Maida Vale": { + "lines": [ + "Bakerloo" + ], + "location": { + "lat": 51.5299092, + "lng": -0.1860307 + } + }, + "Manor House": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.57088599999999, + "lng": -0.0960443 + } + }, + "Mansion House": { + "lines": [ + "District", + "Circle" + ], + "location": { + "lat": 51.51300699999999, + "lng": -0.08950899999999999 + } + }, + "Marble Arch": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5133224, + "lng": -0.1589956 + } + }, + "Marylebone": { + "lines": [ + "Bakerloo" + ], + "location": { + "lat": 51.5188746, + "lng": -0.1498955 + } + }, + "Mile End": { + "lines": [ + "District", + "Central", + "Hammersmith and City" + ], + "location": { + "lat": 51.52354529999999, + "lng": -0.0330122 + } + }, + "Mill Hill East": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.6078622, + "lng": -0.2082474 + } + }, + "Monument": { + "lines": [ + "District", + "Circle" + ], + "location": { + "lat": 51.510143, + "lng": -0.08594199999999999 + } + }, + "Moorgate": { + "lines": [ + "Metropolitan", + "Northern", + "Circle", + "Hammersmith and City" + ], + "location": { + "lat": 51.5170923, + "lng": -0.0887609 + } + }, + "Moor Park": { + "lines": [ + "Metropolitan" + ], + "location": { + "lat": 51.629278, + "lng": -0.432108 + } + }, + "Morden": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.394943, + "lng": -0.19446 + } + }, + "Mornington Crescent": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.5334445, + "lng": -0.1404782 + } + }, + "Neasden": { + "lines": [ + "Jubilee" + ], + "location": { + "lat": 51.5626819, + "lng": -0.2536987 + } + }, + "Newbury Park": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5734779, + "lng": 0.08897669999999999 + } + }, + "North Acton": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5219526, + "lng": -0.2580821 + } + }, + "North Ealing": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.5175662, + "lng": -0.2890518 + } + }, + "North Greenwich": { + "lines": [ + "Jubilee" + ], + "location": { + "lat": 51.4859576, + "lng": 0.007494900000000001 + } + }, + "North Harrow": { + "lines": [ + "Metropolitan" + ], + "location": { + "lat": 51.58412800000001, + "lng": -0.3644343 + } + }, + "North Wembley": { + "lines": [ + "Bakerloo", + "London Overground" + ], + "location": { + "lat": 51.5625616, + "lng": -0.3175595 + } + }, + "Northfields": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.5041461, + "lng": -0.3186474 + } + }, + "Northolt": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.546615, + "lng": -0.373103 + } + }, + "Northwick Park": { + "lines": [ + "Metropolitan" + ], + "location": { + "lat": 51.5736485, + "lng": -0.3124122 + } + }, + "Northwood": { + "lines": [ + "Metropolitan" + ], + "location": { + "lat": 51.61028899999999, + "lng": -0.428278 + } + }, + "Northwood Hills": { + "lines": [ + "Metropolitan" + ], + "location": { + "lat": 51.6004243, + "lng": -0.4098652 + } + }, + "Notting Hill Gate": { + "lines": [ + "District", + "Central", + "Circle" + ], + "location": { + "lat": 51.5090613, + "lng": -0.1966222 + } + }, + "Oakwood": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.6481224, + "lng": -0.1309298 + } + }, + "Old Street": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.5255551, + "lng": -0.0883113 + } + }, + "Osterley": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.4819722, + "lng": -0.3494553 + } + }, + "Oval": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.4841476, + "lng": -0.1170091 + } + }, + "Oxford Circus": { + "lines": [ + "Central", + "Bakerloo", + "Victoria" + ], + "location": { + "lat": 51.5152543, + "lng": -0.1420395 + } + }, + "Paddington": { + "lines": [ + "District", + "Circle", + "Bakerloo", + "Hammersmith and City" + ], + "location": { + "lat": 51.515973, + "lng": -0.174943 + } + }, + "Park Royal": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.5278587, + "lng": -0.2618239 + } + }, + "Parsons Green": { + "lines": [ + "District" + ], + "location": { + "lat": 51.47312789999999, + "lng": -0.199893 + } + }, + "Perivale": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.532222, + "lng": -0.324477 + } + }, + "Piccadilly Circus": { + "lines": [ + "Bakerloo", + "Piccadilly" + ], + "location": { + "lat": 51.5098597, + "lng": -0.1342809 + } + }, + "Pimlico": { + "lines": [ + "Victoria" + ], + "location": { + "lat": 51.490744, + "lng": -0.1403619 + } + }, + "Pinner": { + "lines": [ + "Metropolitan" + ], + "location": { + "lat": 51.595172, + "lng": -0.378002 + } + }, + "Plaistow": { + "lines": [ + "District", + "Hammersmith and City" + ], + "location": { + "lat": 51.5268317, + "lng": 0.0308143 + } + }, + "Preston Road": { + "lines": [ + "Metropolitan" + ], + "location": { + "lat": 51.5743525, + "lng": 0.010698 + } + }, + "Putney Bridge": { + "lines": [ + "District" + ], + "location": { + "lat": 51.4669712, + "lng": -0.2129034 + } + }, + "Queen's Park": { + "lines": [ + "Bakerloo", + "London Overground" + ], + "location": { + "lat": 51.5345448, + "lng": -0.2043853 + } + }, + "Queensbury": { + "lines": [ + "Jubilee" + ], + "location": { + "lat": 51.5942132, + "lng": -0.2853683 + } + }, + "Queensway": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5137906, + "lng": -0.1877513 + } + }, + "Ravenscourt Park": { + "lines": [ + "District" + ], + "location": { + "lat": 51.4952687, + "lng": -0.2397848 + } + }, + "Rayners Lane": { + "lines": [ + "Metropolitan", + "Piccadilly" + ], + "location": { + "lat": 51.5762173, + "lng": -0.3702536 + } + }, + "Redbridge": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5748179, + "lng": 0.0507539 + } + }, + "Regent's Park": { + "lines": [ + "Bakerloo" + ], + "location": { + "lat": 51.5312705, + "lng": -0.1569694 + } + }, + "Richmond": { + "lines": [ + "District", + "London Overground" + ], + "location": { + "lat": 51.46132619999999, + "lng": -0.3035268 + } + }, + "Rickmansworth": { + "lines": [ + "Metropolitan" + ], + "location": { + "lat": 51.638763, + "lng": -0.4741309999999999 + } + }, + "Roding Valley": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.6171644, + "lng": 0.0438972 + } + }, + "Royal Oak": { + "lines": [ + "Circle", + "Hammersmith and City" + ], + "location": { + "lat": 51.5295681, + "lng": -0.0693181 + } + }, + "Ruislip": { + "lines": [ + "Metropolitan", + "Piccadilly" + ], + "location": { + "lat": 51.574593, + "lng": -0.4250547000000001 + } + }, + "Ruislip Gardens": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5625928, + "lng": -0.4133833 + } + }, + "Ruislip Manor": { + "lines": [ + "Metropolitan", + "Piccadilly" + ], + "location": { + "lat": 51.5708186, + "lng": -0.411653 + } + }, + "Russell Square": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.52175, + "lng": -0.12589 + } + }, + "St James's Park": { + "lines": [ + "District", + "Circle" + ], + "location": { + "lat": 51.502967, + "lng": -0.1339534 + } + }, + "St John's Wood": { + "lines": [ + "Jubilee" + ], + "location": { + "lat": 51.5343581, + "lng": -0.1742065 + } + }, + "St Paul's": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5138453, + "lng": -0.0983506 + } + }, + "Seven Sisters": { + "lines": [ + "Victoria", + "London Overground" + ], + "location": { + "lat": 51.582652, + "lng": -0.0747232 + } + }, + "Shepherd's Bush": { + "lines": [ + "Central", + "London Overground" + ], + "location": { + "lat": 51.5051913, + "lng": -0.22469 + } + }, + "Shepherd's Bush Market": { + "lines": [ + "Circle", + "Hammersmith and City" + ], + "location": { + "lat": 51.5038833, + "lng": -0.2265418 + } + }, + "Sloane Square": { + "lines": [ + "District", + "Circle" + ], + "location": { + "lat": 51.4927684, + "lng": -0.1570174 + } + }, + "Snaresbrook": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.58567859999999, + "lng": 0.0084531 + } + }, + "South Ealing": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.5013134, + "lng": -0.3069393 + } + }, + "South Harrow": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.5683717, + "lng": -0.3553483 + } + }, + "South Kensington": { + "lines": [ + "District", + "Piccadilly", + "Circle" + ], + "location": { + "lat": 51.4953291, + "lng": -0.1789909 + } + }, + "South Kenton": { + "lines": [ + "Bakerloo", + "London Overground" + ], + "location": { + "lat": 51.5702641, + "lng": -0.3084333 + } + }, + "South Ruislip": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.55507919999999, + "lng": -0.399575 + } + }, + "South Wimbledon": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.41570919999999, + "lng": -0.192514 + } + }, + "South Woodford": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5912671, + "lng": 0.0264721 + } + }, + "Southfields": { + "lines": [ + "District" + ], + "location": { + "lat": 51.44522, + "lng": -0.2037324 + } + }, + "Southgate": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.62476119999999, + "lng": -0.1240456 + } + }, + "Southwark": { + "lines": [ + "Jubilee" + ], + "location": { + "lat": 51.502781, + "lng": -0.087738 + } + }, + "Stamford Brook": { + "lines": [ + "District" + ], + "location": { + "lat": 51.4904257, + "lng": -0.2341454 + } + }, + "Stanmore": { + "lines": [ + "Jubilee" + ], + "location": { + "lat": 51.617676, + "lng": -0.311451 + } + }, + "Stepney Green": { + "lines": [ + "District", + "Hammersmith and City" + ], + "location": { + "lat": 51.51688960000001, + "lng": -0.0498182 + } + }, + "Stockwell": { + "lines": [ + "Northern", + "Victoria" + ], + "location": { + "lat": 51.4760412, + "lng": -0.1263271 + } + }, + "Stonebridge Park": { + "lines": [ + "Bakerloo", + "London Overground" + ], + "location": { + "lat": 51.5445824, + "lng": -0.2608244 + } + }, + "Stratford": { + "lines": [ + "Central", + "Jubilee", + "London Overground", + "Elizabeth Line", + "DLR" + ], + "location": { + "lat": 51.5426313, + "lng": -0.0010369 + } + }, + "Sudbury Hill": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.5539267, + "lng": -0.337085 + } + }, + "Sudbury Town": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.5503085, + "lng": -0.316207 + } + }, + "Swiss Cottage": { + "lines": [ + "Jubilee" + ], + "location": { + "lat": 51.5425872, + "lng": -0.1724737 + } + }, + "Temple": { + "lines": [ + "District", + "Circle" + ], + "location": { + "lat": 51.511287, + "lng": -0.113685 + } + }, + "Theydon Bois": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.672497, + "lng": 0.09447 + } + }, + "Tooting Bec": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.436753, + "lng": -0.1618404 + } + }, + "Tooting Broadway": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.436753, + "lng": -0.1618404 + } + }, + "Tottenham Court Road": { + "lines": [ + "Central", + "Northern", + "Elizabeth Line" + ], + "location": { + "lat": 51.5203145, + "lng": -0.1340121 + } + }, + "Tottenham Hale": { + "lines": [ + "Victoria" + ], + "location": { + "lat": 51.5892808, + "lng": -0.06105049999999999 + } + }, + "Totteridge and Whetstone": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.630241, + "lng": -0.179144 + } + }, + "Tower Hill": { + "lines": [ + "District", + "Circle" + ], + "location": { + "lat": 51.5097246, + "lng": -0.0739256 + } + }, + "Tufnell Park": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.55633479999999, + "lng": -0.1374941 + } + }, + "Turnham Green": { + "lines": [ + "District", + "Piccadilly" + ], + "location": { + "lat": 51.49203019999999, + "lng": -0.2663174 + } + }, + "Turnpike Lane": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.5893292, + "lng": -0.1082987 + } + }, + "Upminster": { + "lines": [ + "District", + "London Overground" + ], + "location": { + "lat": 51.55591399999999, + "lng": 0.248894 + } + }, + "Upminster Bridge": { + "lines": [ + "District" + ], + "location": { + "lat": 51.55908600000001, + "lng": 0.235112 + } + }, + "Upney": { + "lines": [ + "District" + ], + "location": { + "lat": 51.5384288, + "lng": 0.1015655 + } + }, + "Upton Park": { + "lines": [ + "District", + "Hammersmith and City" + ], + "location": { + "lat": 51.53471750000001, + "lng": 0.0337596 + } + }, + "Uxbridge": { + "lines": [ + "Metropolitan", + "Piccadilly" + ], + "location": { + "lat": 51.5485, + "lng": -0.479611 + } + }, + "Vauxhall": { + "lines": [ + "Victoria" + ], + "location": { + "lat": 51.4861671, + "lng": -0.12172 + } + }, + "Victoria": { + "lines": [ + "District", + "Circle", + "Victoria" + ], + "location": { + "lat": 51.49554149999999, + "lng": -0.1435602 + } + }, + "Walthamstow Central": { + "lines": [ + "Victoria", + "London Overground" + ], + "location": { + "lat": 51.5828251, + "lng": -0.0198182 + } + }, + "Wanstead": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5767971, + "lng": 0.0249881 + } + }, + "Warren Street": { + "lines": [ + "Northern", + "Victoria" + ], + "location": { + "lat": 51.5240516, + "lng": -0.1399547 + } + }, + "Warwick Avenue": { + "lines": [ + "Bakerloo" + ], + "location": { + "lat": 51.5063258, + "lng": -0.3039068 + } + }, + "Waterloo": { + "lines": [ + "Waterloo and City", + "Bakerloo", + "Northern", + "Jubilee" + ], + "location": { + "lat": 51.5048541, + "lng": -0.1136621 + } + }, + "Watford": { + "lines": [ + "Metropolitan" + ], + "location": { + "lat": 51.66112829999999, + "lng": -0.3970199 + } + }, + "Wembley Central": { + "lines": [ + "Bakerloo", + "London Overground" + ], + "location": { + "lat": 51.55214429999999, + "lng": -0.2965274 + } + }, + "Wembley Park": { + "lines": [ + "Metropolitan", + "Jubilee" + ], + "location": { + "lat": 51.5585935, + "lng": -0.2796126 + } + }, + "West Acton": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.5179559, + "lng": -0.2816394 + } + }, + "West Brompton": { + "lines": [ + "District", + "London Overground" + ], + "location": { + "lat": 51.483494, + "lng": -0.196802 + } + }, + "West Finchley": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.6095641, + "lng": -0.18844 + } + }, + "West Ham": { + "lines": [ + "District", + "Hammersmith and City", + "Jubilee", + "DLR" + ], + "location": { + "lat": 51.538008, + "lng": 0.0124751 + } + }, + "West Hampstead": { + "lines": [ + "Jubilee", + "London Overground" + ], + "location": { + "lat": 51.5490183, + "lng": -0.1971205 + } + }, + "West Harrow": { + "lines": [ + "Metropolitan" + ], + "location": { + "lat": 51.5777829, + "lng": -0.3532719 + } + }, + "West Kensington": { + "lines": [ + "District" + ], + "location": { + "lat": 51.4898621, + "lng": -0.2070008 + } + }, + "West Ruislip": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.56971799999999, + "lng": -0.432239 + } + }, + "Westbourne Park": { + "lines": [ + "Circle", + "Hammersmith and City" + ], + "location": { + "lat": 51.51769299999999, + "lng": -0.194941 + } + }, + "Westminster": { + "lines": [ + "District", + "Circle", + "Jubilee" + ], + "location": { + "lat": 51.4974948, + "lng": -0.1356583 + } + }, + "White City": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.51146199999999, + "lng": -0.2376332 + } + }, + "Whitechapel": { + "lines": [ + "District", + "Hammersmith and City", + "London Overground", + "Elizabeth Line" + ], + "location": { + "lat": 51.5148198, + "lng": -0.0650534 + } + }, + "Willesden Green": { + "lines": [ + "Jubilee" + ], + "location": { + "lat": 51.5476301, + "lng": -0.2369246 + } + }, + "Willesden Junction": { + "lines": [ + "Bakerloo", + "London Overground" + ], + "location": { + "lat": 51.53211630000001, + "lng": -0.2433689 + } + }, + "Wimbledon": { + "lines": [ + "District" + ], + "location": { + "lat": 51.41833889999999, + "lng": -0.2206288 + } + }, + "Wimbledon Park": { + "lines": [ + "District" + ], + "location": { + "lat": 51.43455230000001, + "lng": -0.2003717 + } + }, + "Wood Green": { + "lines": [ + "Piccadilly" + ], + "location": { + "lat": 51.5971722, + "lng": -0.1098021 + } + }, + "Wood Lane": { + "lines": [ + "Circle", + "Hammersmith and City" + ], + "location": { + "lat": 51.5123373, + "lng": -0.2249677 + } + }, + "Woodford": { + "lines": [ + "Central" + ], + "location": { + "lat": 51.6041751, + "lng": 0.0325025 + } + }, + "Woodside Park": { + "lines": [ + "Northern" + ], + "location": { + "lat": 51.6162352, + "lng": -0.1874339 + } + }, + "Acton Central": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5087257, + "lng": -0.2630447 + } + }, + "Anerley": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.4119109, + "lng": -0.06797779999999999 + } + }, + "Barking Riverside": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.51864759999999, + "lng": 0.1187702 + } + }, + "Battersea Park": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.4791075, + "lng": -0.1564981 + } + }, + "Brockley": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.46377160000001, + "lng": -0.0366031 + } + }, + "Brondesbury Park": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5430698, + "lng": -0.2155038 + } + }, + "Brondesbury": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5453971, + "lng": -0.2004112 + } + }, + "Bruce Grove": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.596249, + "lng": -0.0723585 + } + }, + "Bush Hill Park": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.6445559, + "lng": -0.06559659999999999 + } + }, + "Bushey": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.406715, + "lng": -0.2332488 + } + }, + "Caledonian Road & Barnsbury": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.543659, + "lng": -0.1175446 + } + }, + "Cambridge Heath": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5310934, + "lng": -0.05761819999999999 + } + }, + "Camden Road": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5479475, + "lng": -0.1303297 + } + }, + "Canonbury": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5437366, + "lng": -0.1005803 + } + }, + "Carpenders Park": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.6285418, + "lng": -0.3836965 + } + }, + "Cheshunt": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.70264050000001, + "lng": -0.0343787 + } + }, + "Chingford": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.6320667, + "lng": 0.007362700000000001 + } + }, + "Clapham High Street": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.4633989, + "lng": -0.1335422 + } + }, + "Clapham Junction": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.4651737, + "lng": -0.1708113 + } + }, + "Clapton": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5584687, + "lng": -0.0559163 + } + }, + "Crouch Hill": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5742323, + "lng": -0.1212981 + } + }, + "Crystal Palace": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.4197233, + "lng": -0.0830638 + } + }, + "Dalston Junction": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5459999, + "lng": -0.0751485 + } + }, + "Dalston Kingsland": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5481846, + "lng": -0.05471409999999999 + } + }, + "Denmark Hill": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.4671373, + "lng": -0.0907882 + } + }, + "Edmonton Green": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.6248126, + "lng": -0.0581806 + } + }, + "Emerson Park": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.57201, + "lng": 0.225394 + } + }, + "Enfield Town": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.6522994, + "lng": -0.0807119 + } + }, + "Finchley Road & Frognal": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.54947000000001, + "lng": -0.1815625 + } + }, + "Forest Hill": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.4397781, + "lng": -0.0546416 + } + }, + "Gospel Oak": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5563205, + "lng": -0.1492765 + } + }, + "Hackney Central": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5482344, + "lng": -0.05738749999999999 + } + }, + "Hackney Downs": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5553095, + "lng": -0.06908249999999999 + } + }, + "Hackney Wick": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.544579, + "lng": -0.0256935 + } + }, + "Haggerston": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5373275, + "lng": -0.0748971 + } + }, + "Hampstead Heath": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5608294, + "lng": -0.1629416 + } + }, + "Harringay Green Lanes": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5771902, + "lng": -0.0979994 + } + }, + "Harrow & Wealdstone": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5960603, + "lng": -0.3334858 + } + }, + "Hatch End": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.6104236, + "lng": -0.370708 + } + }, + "Headstone Lane": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5994347, + "lng": -0.3565829 + } + }, + "Highams Park": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.6083754, + "lng": 0.0014712 + } + }, + "Highbury & Islington": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5569649, + "lng": -0.0979656 + } + }, + "Homerton": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5478609, + "lng": -0.0425903 + } + }, + "Honor Oak Park": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.4498706, + "lng": -0.0467182 + } + }, + "Hoxton": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5495005, + "lng": -0.0596855 + } + }, + "Imperial Wharf": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.4750534, + "lng": -0.1826773 + } + }, + "Kensal Rise": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5345071, + "lng": -0.2250186 + } + }, + "Kentish Town West": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5468507, + "lng": -0.1466131 + } + }, + "Kilburn High Road": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5409107, + "lng": -0.1967427 + } + }, + "Leyton Midland Road": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5696203, + "lng": -0.0071086 + } + }, + "Leytonstone High Road": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.56357089999999, + "lng": 0.0082927 + } + }, + "London Fields": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5422817, + "lng": -0.06146169999999999 + } + }, + "New Cross Gate": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.47503680000001, + "lng": -0.04034210000000001 + } + }, + "New Cross": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.4769047, + "lng": -0.0336897 + } + }, + "Norwood Junction": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.39731, + "lng": -0.07501000000000001 + } + }, + "Peckham Rye": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.4603208, + "lng": -0.0603961 + } + }, + "Penge West": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.41753509999999, + "lng": -0.06076210000000001 + } + }, + "Queens Road Peckham": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.4734184, + "lng": -0.0573532 + } + }, + "Rectory Road": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5587785, + "lng": -0.0697009 + } + }, + "Romford": { + "lines": [ + "London Overground", + "Elizabeth Line" + ], + "location": { + "lat": 51.5775231, + "lng": 0.1785814 + } + }, + "Rotherhithe": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.49628999999999, + "lng": -0.0411931 + } + }, + "St. James Street": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5806816, + "lng": -0.0329162 + } + }, + "Shadwell": { + "lines": [ + "London Overground", + "DLR" + ], + "location": { + "lat": 51.51450620000001, + "lng": -0.05711619999999999 + } + }, + "Shoreditch High Street": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5244134, + "lng": -0.0772945 + } + }, + "Silver Street": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.6161564, + "lng": -0.0762981 + } + }, + "South Acton": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.50177799999999, + "lng": -0.2886079 + } + }, + "South Hampstead": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.54608899999999, + "lng": -0.1790479 + } + }, + "South Tottenham": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5840101, + "lng": -0.0757587 + } + }, + "Southbury": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.6502818, + "lng": -0.0634049 + } + }, + "Stamford Hill": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5696514, + "lng": -0.0750199 + } + }, + "Stoke Newington": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.561175, + "lng": -0.08349229999999999 + } + }, + "Surrey Quays": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.4944172, + "lng": -0.0383063 + } + }, + "Sydenham": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.42476, + "lng": -0.059399 + } + }, + "Theobalds Grove": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.6932469, + "lng": -0.0452353 + } + }, + "Turkey Street": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.67249349999999, + "lng": -0.0470747 + } + }, + "Upper Holloway": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5652158, + "lng": -0.1334871 + } + }, + "Walthamstow Queen's Road": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5814217, + "lng": -0.0238481 + } + }, + "Wandsworth Road": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.4744356, + "lng": -0.1324144 + } + }, + "Wanstead Park": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.568725, + "lng": 0.0433573 + } + }, + "Wapping": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5041774, + "lng": -0.0563736 + } + }, + "Watford High Street": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.6453053, + "lng": -0.3687871 + } + }, + "Watford Junction": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.66371280000001, + "lng": -0.3964488 + } + }, + "West Croydon": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.39294289999999, + "lng": -0.0936709 + } + }, + "White Hart Lane": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.6061468, + "lng": -0.0884635 + } + }, + "Wood Street": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5164907, + "lng": -0.0940443 + } + }, + "Woodgrange Park": { + "lines": [ + "London Overground" + ], + "location": { + "lat": 51.5492795, + "lng": 0.04464630000000001 + } + }, + "Abbey Wood": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.4926116, + "lng": 0.1188182 + } + }, + "Acton Main Line": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.51718, + "lng": -0.26673 + } + }, + "Brentwood": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.6124418, + "lng": -0.1884545 + } + }, + "Burnham": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5888802, + "lng": -0.2207775 + } + }, + "Chadwell Heath": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5722283, + "lng": 0.141995 + } + }, + "Custom House": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5098734, + "lng": 0.026251 + } + }, + "Forest Gate": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5439215, + "lng": 0.0264272 + } + }, + "Gidea Park": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5792473, + "lng": 0.2076649 + } + }, + "Goodmayes": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5662262, + "lng": 0.1102974 + } + }, + "Hanwell": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5066369, + "lng": -0.3383229 + } + }, + "Harold Wood": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5910397, + "lng": 0.2328613 + } + }, + "Hayes & Harlington": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.49299999999999, + "lng": -0.4350000000000001 + } + }, + "Heathrow Terminals 2 & 3": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.4724711, + "lng": -0.4544586 + } + }, + "Ilford": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5562328, + "lng": 0.0718341 + } + }, + "Iver": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5201939, + "lng": -0.509572 + } + }, + "Langley": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5732079, + "lng": 0.0339955 + } + }, + "London Paddington": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.515973, + "lng": -0.174943 + } + }, + "Maidenhead": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.52182089999999, + "lng": -0.7242048 + } + }, + "Manor Park": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.550487, + "lng": 0.0514117 + } + }, + "Maryland": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5477587, + "lng": 0.0005166999999999999 + } + }, + "Reading": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.45512009999999, + "lng": -0.9787475 + } + }, + "Seven Kings": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5636064, + "lng": 0.098511 + } + }, + "Shenfield": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5311885, + "lng": -0.0786898 + } + }, + "Slough": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5813912, + "lng": -0.2659813 + } + }, + "Southall": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.511073, + "lng": -0.377232 + } + }, + "Taplow": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5310102, + "lng": -0.0932808 + } + }, + "Twyford": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.5089583, + "lng": -0.2810741 + } + }, + "West Drayton": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.502985, + "lng": -0.4654099999999999 + } + }, + "West Ealing": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.51141, + "lng": -0.3180165 + } + }, + "Woolwich": { + "lines": [ + "Elizabeth Line" + ], + "location": { + "lat": 51.4894752, + "lng": 0.06758829999999999 + } + }, + "Tower Gateway": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5106145, + "lng": -0.0743443 + } + }, + "Limehouse": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5110598, + "lng": -0.0366652 + } + }, + "Westferry": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.50942999999999, + "lng": -0.02672 + } + }, + "Stratford International": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.54492, + "lng": -0.008709999999999999 + } + }, + "Pudding Mill Lane": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5332016, + "lng": -0.0121133 + } + }, + "Bow Church": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5287753, + "lng": -0.0167013 + } + }, + "Devons Road": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5221628, + "lng": -0.0182983 + } + }, + "Langdon Park": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.516331, + "lng": -0.0133953 + } + }, + "All Saints": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5072178, + "lng": -0.1275862 + } + }, + "Poplar": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5123792, + "lng": -0.0087975 + } + }, + "Blackwall": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5079838, + "lng": -0.007195699999999999 + } + }, + "East India": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5093325, + "lng": -0.0022012 + } + }, + "Stratford High Street": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5380043, + "lng": -0.0005563 + } + }, + "Abbey Road": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5370938, + "lng": -0.1833814 + } + }, + "Star Lane": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5201798, + "lng": 0.0082419 + } + }, + "West Silvertown": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.502838, + "lng": 0.022487 + } + }, + "Pontoon Dock": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5022738, + "lng": 0.0332177 + } + }, + "London City Airport": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5048437, + "lng": 0.049518 + } + }, + "King George V": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5020067, + "lng": 0.06237910000000001 + } + }, + "Woolwich Arsenal": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.49201900000001, + "lng": 0.0704643 + } + }, + "Royal Victoria": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5093346, + "lng": 0.0185375 + } + }, + "Custom House for ExCeL": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5089277, + "lng": 0.0243774 + } + }, + "Prince Regent": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.45382559999999, + "lng": -0.1065024 + } + }, + "Royal Albert": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5009088, + "lng": -0.177366 + } + }, + "Beckton Park": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5104913, + "lng": 0.0496832 + } + }, + "Cyprus": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5091192, + "lng": 0.0633823 + } + }, + "Gallions Reach": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5166002, + "lng": 0.0783827 + } + }, + "Beckton": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5156376, + "lng": 0.0799792 + } + }, + "West India Quay": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.5076549, + "lng": -0.0238721 + } + }, + "Heron Quays": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.502354, + "lng": -0.024651 + } + }, + "South Quay": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.4994034, + "lng": -0.0159393 + } + }, + "Crossharbour": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.495794, + "lng": -0.0144615 + } + }, + "Mudchute": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.4920932, + "lng": -0.0104424 + } + }, + "Island Gardens": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.4870307, + "lng": -0.008158799999999999 + } + }, + "Cutty Sark for Maritime Greenwich": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.48167, + "lng": -0.01078 + } + }, + "Greenwich": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.4933675, + "lng": 0.0098214 + } + }, + "Deptford Bridge": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.4744304, + "lng": -0.0224546 + } + }, + "Elverson Road": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.4684887, + "lng": -0.018247 + } + }, + "Lewisham": { + "lines": [ + "DLR" + ], + "location": { + "lat": 51.46115090000001, + "lng": -0.0073177 + } + } +} \ No newline at end of file diff --git a/lib/backend/live_information.dart b/lib/backend/live_information.dart index e598910..9c6dc59 100644 --- a/lib/backend/live_information.dart +++ b/lib/backend/live_information.dart @@ -12,6 +12,7 @@ import 'package:bus_infotainment/backend/modules/announcement.dart'; import 'package:bus_infotainment/backend/modules/commands.dart'; import 'package:bus_infotainment/backend/modules/synced_time.dart'; import 'package:bus_infotainment/backend/modules/tracker.dart'; +import 'package:bus_infotainment/backend/modules/tube_info.dart'; import 'package:bus_infotainment/tfl_datasets.dart'; import 'package:bus_infotainment/utils/audio%20wrapper.dart'; import 'package:bus_infotainment/utils/delegates.dart'; @@ -57,6 +58,12 @@ class LiveInformation { print("Failed to load bus sequences from TFL. Using local copy."); } + // Load tube stations + print("Loading tube stations from assets"); + tubeStations = TubeStations.fromJson(json.decode(await rootBundle.loadString("assets/datasets/tube_stations.json"))); + print("Loaded tube stations from assets"); + + String sessionID = "test"; commandModule = CommandModule(sessionID); @@ -87,6 +94,7 @@ class LiveInformation { late AnnouncementModule announcementModule; late SyncedTimeModule syncedTimeModule; late TrackerModule trackerModule; + late TubeStations tubeStations; // Important variables BusRouteVariant? _currentRouteVariant; diff --git a/lib/backend/modules/announcement.dart b/lib/backend/modules/announcement.dart index 93da25e..0efae87 100644 --- a/lib/backend/modules/announcement.dart +++ b/lib/backend/modules/announcement.dart @@ -1,14 +1,17 @@ import 'dart:async'; +import 'dart:io'; import 'dart:typed_data'; import 'package:bus_infotainment/audio_cache.dart'; import 'package:bus_infotainment/backend/live_information.dart'; +import 'package:bus_infotainment/backend/modules/tube_info.dart'; import 'package:bus_infotainment/tfl_datasets.dart'; import 'package:bus_infotainment/utils/audio%20wrapper.dart'; import 'package:bus_infotainment/utils/delegates.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'info_module.dart'; @@ -33,12 +36,26 @@ class AnnouncementModule extends InfoModule { return _bundleBytes!; } else { - if (kIsWeb) { - throw Exception("Cannot load bundle bytes on web"); + // Try to load them from shared preferences + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String fileLocation = prefs.getString("AnnouncementsFileLocation")!; + + File file = File(fileLocation); + setBundleBytes(file.readAsBytesSync()); + return _bundleBytes!; + } catch (e) { + throw Exception("Loading announcements from assets has been deprecated."); } - final bytes = await rootBundle.load(_bundleLocation); - return bytes.buffer.asUint8List(); + + + // if (kIsWeb) { + // throw Exception("Cannot load bundle bytes on web"); + // } + // + // final bytes = await rootBundle.load(_bundleLocation); + // return bytes.buffer.asUint8List(); } } @@ -274,11 +291,21 @@ class AnnouncementModule extends InfoModule { String audioRoute = "R_${routeVariant.busRoute.routeNumber}_001.mp3"; print("Checkpoint 5"); - await announcementCache.loadAnnouncementsFromBytes(await getBundleBytes(), [audioRoute, "R_RAIL_REPLACEMENT_SERVICE_001.mp3"]); + await announcementCache.loadAnnouncementsFromBytes(await getBundleBytes(), [audioRoute]); print("Checkpoint 6"); AudioWrapperSource sourceRoute = !routeNumber.toLowerCase().startsWith("ul") ? AudioWrapperByteSource(announcementCache[audioRoute]!) : - AudioWrapperByteSource(announcementCache["R_RAIL_REPLACEMENT_SERVICE_001.mp3"]!); + // AudioWrapperByteSource(announcementCache["R_RAIL_REPLACEMENT_SERVICE_001.mp3"]!); + AudioWrapperAssetSource("audio/R_RAIL_REPLACEMENT_SERVICE_001.mp3"); + + if (routeNumber.toLowerCase().startsWith("ul")) { + + TubeLine? closestLine = liveInformation.tubeStations.getClosestLine(routeVariant); + + sourceRoute = closestLine?.getAudio() ?? sourceRoute; + routeNumber = closestLine?.getShortName() ?? routeNumber; + } + print("Checkpoint 6.1"); AudioWrapperSource sourceDestination = AudioWrapperByteSource(await routeVariant.destination!.getAudioBytes()); print("Checkpoint 7"); diff --git a/lib/backend/modules/tracker.dart b/lib/backend/modules/tracker.dart index 650433f..8d31ad9 100644 --- a/lib/backend/modules/tracker.dart +++ b/lib/backend/modules/tracker.dart @@ -90,7 +90,9 @@ class TrackerModule extends InfoModule { int stopIndex = liveInformation.getRouteVariant()!.busStops.indexOf(closestStop); - closestStop = liveInformation.getRouteVariant()!.busStops[stopIndex + 1]; + int maxStops = liveInformation.getRouteVariant()!.busStops.length; + + closestStop = liveInformation.getRouteVariant()!.busStops[min(stopIndex + 1, maxStops)]; print("Closest stop is now: ${closestStop.formattedStopName}"); } else { diff --git a/lib/backend/modules/tube_info.dart b/lib/backend/modules/tube_info.dart new file mode 100644 index 0000000..18291b1 --- /dev/null +++ b/lib/backend/modules/tube_info.dart @@ -0,0 +1,133 @@ + + +import 'package:bus_infotainment/utils/OrdinanceSurveyUtils.dart'; +import 'package:bus_infotainment/utils/audio%20wrapper.dart'; +import 'package:vector_math/vector_math.dart'; + +import '../../tfl_datasets.dart'; + +class TubeStations { + + List stations = []; + List lines = []; + + TubeStations.fromJson(Map json) { + /* + Example: + "Acton Town": { + "lines": [ + "District", + "Piccadilly" + ], + "location": { + "lat": 51.5029984, + "lng": -0.2803455 + } + }, + */ + + for (String stationName in json.keys) { + TubeStation station = TubeStation(); + station.name = stationName; + + Map stationData = json[stationName]; + + station.latitude = stationData["location"]["lat"]; + station.longitude = stationData["location"]["lng"]; + + for (String lineName in stationData["lines"]) { + + // check if the line already exists + TubeLine line = lines.firstWhere((element) => element.name == lineName, orElse: () => TubeLine()..name = lineName); + + if (!lines.contains(line)) { + lines.add(line); + } + + station.lines.add(line); + } + + stations.add(station); + } + + } + + // Get the london underground line that is a close match to a given bus route variant + TubeLine? getClosestLine(BusRouteVariant variant) { + + Map lineMatches = {}; + + for (TubeLine line in lines) { + lineMatches[line] = 0; + } + + for (BusRouteStop stop in variant.busStops) { + for (TubeStation station in stations) { + + // get the distance between the bus stop and the tube station + double distance = Vector2(stop.easting.toDouble(), stop.northing.toDouble()).distanceTo(OSGrid.toNorthingEasting(station.latitude, station.longitude)); + + // if the distance is less than 100m, then we can assume that the bus stop is near the tube station + if (distance < 200) { + for (TubeLine line in station.lines) { + lineMatches[line] = lineMatches[line]! + 1; + } + } + } + } + + TubeLine? closestLine; + int closestMatch = 0; + for (TubeLine line in lineMatches.keys) { + if (lineMatches[line]! > closestMatch) { + closestLine = line; + closestMatch = lineMatches[line]!; + } + } + + return closestLine; + + } + +} + +class TubeStation { + + late final String name; + + late final double latitude; + late final double longitude; + + List lines = []; + +} + +class TubeLine { + + late final String name; + + AudioWrapperSource? getAudio() { + + String lineName = name.toLowerCase().replaceAll(" ", "_"); + lineName = lineName.replaceAll("and", "N"); + lineName = lineName.toUpperCase(); + + return AudioWrapperAssetSource("audio/rail_replacement/$lineName.mp3"); + } + + // Get short name +// example: "District" -> "DIS", "London Overground" -> "LO", "TfL Rail" -> "TFL", "Waterloo and City" -> "W&C" + String getShortName() { + String shortName = name; + + List words = shortName.split(" "); + shortName = words.map((e) => e[0]).join(""); + + if (shortName.length < 3) { + shortName = name.substring(0, 3).toUpperCase(); + } + + return shortName; + } + +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index a301b5d..093ba11 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -88,7 +88,6 @@ class MyApp extends StatelessWidget { // '/': (context) => TfL_Dataset_Test(), '/': (context) => remaster.InitialStartup(), '/application': (context) => TfL_Dataset_Test(), - '/dashboard': (context) => Dashboard(), }, ); diff --git a/lib/remaster/InitialStartup.dart b/lib/remaster/InitialStartup.dart index 5282013..6cb0b1c 100644 --- a/lib/remaster/InitialStartup.dart +++ b/lib/remaster/InitialStartup.dart @@ -12,6 +12,7 @@ import 'package:flutter/widgets.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class InitialStartup extends StatefulWidget { @@ -324,6 +325,8 @@ class _page3 extends InitialStartupPage { class _page3State extends State<_page3> { + bool _loadingAudio = false; + Future _announcementsUploaded() async { try { Uint8List bytes = await LiveInformation().announcementModule.getBundleBytes(); @@ -393,6 +396,7 @@ class _page3State extends State<_page3> { height: 4, ), + if (!_loadingAudio) FutureBuilder( future: _announcementsUploaded(), builder: (context, val) { @@ -412,6 +416,10 @@ class _page3State extends State<_page3> { text: Text(text), onPressed: () async { + setState(() { + _loadingAudio = true; + }); + FilePickerResult? result = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: [ @@ -419,6 +427,8 @@ class _page3State extends State<_page3> { ] ); + + if (result != null) { late Uint8List bytes; @@ -434,13 +444,25 @@ class _page3State extends State<_page3> { AnnouncementCache cache = LiveInformation().announcementModule.announcementCache; + if (!kIsWeb) { + + // Use shared preferences to store the file location + SharedPreferences prefs = await SharedPreferences.getInstance(); + + prefs.setString("AnnouncementsFileLocation", result.files.single.path!); + } + // load a random announcement to ensure that the file is usable await cache.loadAnnouncementsFromBytes(bytes, ["S_WALTHAMSTOW_CENTRAL_001.mp3"]); setState(() { - + _loadingAudio = false; }); + } else { + setState(() { + _loadingAudio = false; + }); } @@ -450,11 +472,62 @@ class _page3State extends State<_page3> { ); }, ) + else + CircularProgressIndicator(), + + ], ), ), ), + SizedBox( + height: 16, + ), + + FutureBuilder( + future: _announcementsUploaded(), + builder: (context, val) { + bool isEnabled = true; + String text = "Continue"; + Color color = Colors.white; + + if (val.hasData) { + isEnabled = val.data!; + } + if (!isEnabled) { + text = "Complete the prerequisites to continue"; + } + + return ShadButton( + text: Text(text), + onPressed: () async { + + showShadDialog( + context: context, + builder: (context) => ShadDialog.alert( + title: Text("You're all setup"), + description: Text("You can now continue to the application. \nTry not to annoy anyone on the bus! ;)"), + actions: [ + ShadButton( + text: Text("Continue to application"), + onPressed: () { + Navigator.pushNamed(context, "/"); + }, + ) + ], + ), + barrierDismissible: false + ); + + }, + enabled: isEnabled, + backgroundColor: color, + width: double.infinity, + ); + }, + ) + ], ), diff --git a/lib/remaster/RemasteredMain.dart b/lib/remaster/RemasteredMain.dart index 6ef19bc..321f56f 100644 --- a/lib/remaster/RemasteredMain.dart +++ b/lib/remaster/RemasteredMain.dart @@ -2,6 +2,7 @@ import 'package:bus_infotainment/remaster/InitialStartup.dart'; +import 'package:bus_infotainment/remaster/dashboard.dart'; import 'package:flutter/material.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; @@ -17,7 +18,11 @@ class RemasteredApp extends StatelessWidget { ), routes: { - '/': (context) => InitialStartup() + '/setup': (context) => InitialStartup(), + '/': (context) => HomePage_Re(), + '/routes': (context) => RoutePage(), + '/enroute': (context) => EnRoutePage(), + }, ); diff --git a/lib/remaster/dashboard.dart b/lib/remaster/dashboard.dart index 5d47c60..39dc6a3 100644 --- a/lib/remaster/dashboard.dart +++ b/lib/remaster/dashboard.dart @@ -1,365 +1,510 @@ +import 'package:bus_infotainment/backend/live_information.dart'; import 'package:bus_infotainment/pages/components/ibus_display.dart'; +import 'package:bus_infotainment/tfl_datasets.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'dart:io' show Platform; + import 'package:flutter/widgets.dart'; +import 'package:flutter_carousel_widget/flutter_carousel_widget.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:text_scroll/text_scroll.dart'; +import '../backend/modules/tube_info.dart'; + Color rgb(int r, int g, int b) { return Color.fromRGBO(r, g, b, 1); } -class Dashboard extends StatelessWidget { +class HomePage_Re extends StatefulWidget { + + @override + State createState() => _HomePage_ReState(); +} + +class _HomePage_ReState extends State { + Future _shouldRedirectToSetup() async { + + List perms = []; + + perms.addAll([ + await Permission.manageExternalStorage.isGranted, + await Permission.location.isGranted + ]); + + bool shouldRedirectA = !perms.contains(false); + bool shouldRedirectB = true; + + try { + Uint8List bytes = await LiveInformation().announcementModule.getBundleBytes(); + shouldRedirectB = true; + } catch (e) { + print("Failed to load bundle"); + shouldRedirectB = false; + } + + print("Should redirect to setup: ${shouldRedirectA || shouldRedirectB}"); + print("Permissions: $shouldRedirectA"); + print("Bundle: $shouldRedirectB"); + print("Permissions_indv: $perms"); + + return !shouldRedirectA || !shouldRedirectB; + } + + + @override + void initState() { + super.initState(); + + + _shouldRedirectToSetup().then((value) { + if (value) { + Navigator.pushNamed(context, "/setup"); + } + }); + } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( + body: Container( - backgroundColor: Colors.grey.shade900, - - bottomNavigationBar: ((defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS)) ? null : BottomAppBar( + padding: EdgeInsets.all(16), - color: Colors.grey.shade800, + alignment: Alignment.center, - child: Row( - children: [ - IconButton( - icon: Icon(Icons.home), - onPressed: () { - Navigator.pushNamed(context, "/dashboard"); - }, - ), - IconButton( - icon: Icon(Icons.search), - onPressed: () { - Navigator.pushNamed(context, "/search"); - }, - ), - IconButton( - icon: Icon(Icons.settings), - onPressed: () { - Navigator.pushNamed(context, "/settings"); - }, - ), - ], - ), - ), + child: SizedBox( - body: Column( - children: [ + width: double.infinity, - Container( + child: Column( - margin: EdgeInsets.all(16), - padding: EdgeInsets.all(8), + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, - decoration: BoxDecoration( + children: [ - borderRadius: BorderRadius.circular(10), - - color: Colors.grey.shade800 - - ), - - // height: 100, - - child: ibus_display() - ), - - Expanded( - child: Row( - - children: [ - - if (false) - if (!(defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS)) - NavigationRail( - - selectedIndex: 0, - - groupAlignment: 1, - - destinations: [ - - NavigationRailDestination( - icon: Icon(Icons.home), - label: Text("Dashboard"), - ), - - NavigationRailDestination( - icon: Icon(Icons.search), - label: Text("Routes") - ), - - NavigationRailDestination( - icon: Icon(Icons.settings), - label: Text("Settings") - ) - - ], - - ), - - Expanded( - child: Container( - - child: Column( - - children: [ - - Container( - - padding: EdgeInsets.symmetric( - horizontal: 16 - ), - - child: IntrinsicHeight( - child: Row( - - children: [ - - Expanded( - child: Container( - - decoration: BoxDecoration( - - borderRadius: BorderRadius.circular(10), - - color: Colors.grey.shade800 - - ), - - padding: EdgeInsets.all(16), - - child: IntrinsicWidth( - child: Column( - - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - - children: [ - - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - - children: [ - Text( - "Bus Route:", - style: GoogleFonts.interTight( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.white - ) - ), - - SizedBox( - width: 16, - ), - - Text( - "11", - style: GoogleFonts.interTight( - fontSize: 18, - fontWeight: FontWeight.normal, - color: Colors.white - ) - ) - ], - ), - Row( - // mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Destination:", - style: GoogleFonts.interTight( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.white - ) - ), - - const SizedBox( - width: 16, - ), - - Text( - "Fullham Broadway", - style: GoogleFonts.interTight( - fontSize: 18, - fontWeight: FontWeight.normal, - color: Colors.white - ) - ) - ], - ), - - const SizedBox( - height: 8, - ), - - Row( - // mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Next Stop:", - style: GoogleFonts.interTight( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.white - ) - ), - - const SizedBox( - width: 16, - ), - - Text( - "St Thomas Hospital / County Hall", - style: GoogleFonts.interTight( - fontSize: 18, - fontWeight: FontWeight.normal, - color: Colors.white - ), - overflow: TextOverflow.fade, - ) - ], - ), - - Row( - // mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Last Stop:", - style: GoogleFonts.interTight( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.white - ), - overflow: TextOverflow.fade, - ), - - const SizedBox( - width: 16, - ), - - Text( - "Fullham Town Hall", - style: GoogleFonts.interTight( - fontSize: 18, - fontWeight: FontWeight.normal, - color: Colors.white - ), - overflow: TextOverflow.fade, - ) - ], - ), - - - - ], - - ), - ) - - ), - ), - - // SizedBox( - // width: 16, - // ), - // - // Column( - // - // children: [ - // - // SizedBox( - // - // width: 150, - // - // child: FloatingActionButton( - // onPressed: () { - // - // }, - // - // backgroundColor: Colors.red, - // - // child: Text( - // "Bus Stopping", - // style: GoogleFonts.interTight( - // fontSize: 18, - // fontWeight: FontWeight.bold, - // color: Colors.white - // ) - // - // ), - // ), - // ), - // - // SizedBox( - // height: 16 - // ), - // - // SizedBox( - // - // width: 150, - // height: 100, - // - // child: FloatingActionButton( - // onPressed: () { - // - // }, - // - // backgroundColor: Colors.grey.shade600, - // - // child: Text( - // "Acknowledge Bus Stop", - // style: GoogleFonts.interTight( - // fontSize: 18, - // fontWeight: FontWeight.bold, - // color: Colors.white - // ), - // textAlign: TextAlign.center, - // - // ), - // ), - // ) - // - // ], - // - // ) - - ], - - ), - ), - ) - - - - ], - - ), - - ), + Text( + "Choose mode:", + style: TextStyle( + fontSize: 32, + fontWeight: FontWeight.w600, ) - - ], - - ), - ), - ], - ) + ), + SizedBox( + height: 16, + ), + + ShadCard( + title: Text("Solo mode"), + width: double.infinity, + description: Text( + "Choose this mode if you are only using this device. (No internet required)" + ), + content: Column( + + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + + children: [ + + SizedBox( + height: 4, + ), + + ShadButton.secondary( + onPressed: () { + Navigator.pushNamed(context, "/routes"); + }, + text: Text("Continue"), + ) + + ], + ), + ), + + SizedBox( + height: 16, + ), + + ShadCard( + title: Text("Multi mode"), + width: double.infinity, + description: Text( + "Choose this mode if you are using multiple devices. (Internet required)" + ), + content: Column( + + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + + children: [ + + SizedBox( + height: 4, + ), + + ShadButton.secondary( + onPressed: () { + Navigator.pushNamed(context, "/application"); + }, + text: Text("Continue"), + ) + + ], + ), + ) + + ], + + ), + ) + + ), ); } -} \ No newline at end of file +} + +class RoutePage extends StatelessWidget { + + + + @override + Widget build(BuildContext context) { + + + + return Scaffold( + body: Container( + + padding: EdgeInsets.all(16), + + alignment: Alignment.center, + + child: SizedBox( + + width: double.infinity, + + child: Column( + + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + + children: [ + + Text( + "Routes", + style: ShadTheme.of(context).textTheme.h1, + ), + + Text( + "Nearby routes", + style: ShadTheme.of(context).textTheme.h4, + ), + + ExpandableCarousel( + options: CarouselOptions( + + ), + items: [ + ShadCard( + title: Text("Route 34"), + content: ShadSelect( + options: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text("Choose a variant"), + ) + ], + selectedOptionBuilder: (BuildContext context, value) { + return Text("Choose a variant"); + }, + ), + padding: EdgeInsets.all(10), + ) + ], + ), + + Divider(), + + RouteSearch() + + ], + + ), + ) + + ), + ); + + } + + List _getNearbyRoutes() { + + final variants = [ + "Walthamstow Central to Barnet Church", + "Walthamstow Central to Barnet Church", + ]; + + List widgets = []; + + for (int i = 0; i < variants.length; i++) { + widgets.add( + ShadOption( + value: i, + child: Text(variants[i]!), + ) + ); + } + + return widgets; + + } + +} + +class RouteSearch extends StatefulWidget { + + @override + State createState() => _RouteSearchState(); +} + +class _RouteSearchState extends State { + TextEditingController controller = TextEditingController(); + + @override + Widget build(BuildContext context) { + + List routes = []; + + for (BusRoute route in LiveInformation().busSequences.routes.values.toList()) { + + if (controller.text.isNotEmpty && !route.routeNumber.toLowerCase().contains(controller.text.toLowerCase())) { + continue; + } + + routes.add(RouteCard(route: route)); + } + + return Expanded( + child: Column( + + children: [ + + ShadInput( + placeholder: Text("Search for a route..."), + controller: controller, + onChanged: (value) { + setState(() { + + }); + }, + ), + + SizedBox( + height: 4, + ), + + Expanded( + child: GridView.count( + crossAxisCount: 3, + children: [ + ...routes + ], + ), + ) + + ], + + ), + ); + } +} + +class RouteCard extends StatelessWidget { + + BusRoute route; + + RouteCard({required this.route}); + + @override + Widget build(BuildContext context) { + // TODO: implement build + + Map variants = {}; + + for (BusRouteVariant variant in route.routeVariants.values) { + String variantLabel = "${variant.busStops.first.formattedStopName} -> ${variant.busStops.last.formattedStopName}"; + + variants[variantLabel] = ShadOption( + value: variant.routeVariant.toString(), + child: Text(variantLabel), + ); + } + + return AspectRatio( + aspectRatio: 1, + child: Container( + child: ShadButton.secondary( + text: Text( + "Route \n ${route.routeNumber}", + style: ShadTheme.of(context).textTheme.h3 + ), + padding: EdgeInsets.all(8), + width: 105, + height: 105, + + onPressed: () { + showShadSheet( + side: ShadSheetSide.bottom, + context: context, + builder: (context) { + + List variantWidgets = []; + + for (BusRouteVariant variant in route.routeVariants.values) { + String variantLabel = "${variant.busStops.first.formattedStopName} -> ${variant.busStops.last.formattedStopName}"; + + variantWidgets.add( + ShadButton.outline( + text: SizedBox( + width: 800-490, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("${variant.busStops.first.formattedStopName} ->"), + SizedBox( + height: 2, + ), + Text(variant.busStops.last.formattedStopName) + ], + ), + ), + width: double.infinity, + height: 50, + padding: EdgeInsets.all(8), + onPressed: () async { + LiveInformation liveInformation = LiveInformation(); + await liveInformation.setRouteVariant(variant); + + liveInformation.announcementModule.queueAnnouncementByRouteVariant(routeVariant: variant); + + Navigator.pushNamed(context, "/enroute"); + }, + ) + ); + + variantWidgets.add(SizedBox( + height: 4, + )); + } + + return ShadSheet( + title: Text("Route ${route.routeNumber} - Variants"), + + content: Container( + width: 2000, + alignment: Alignment.center, + child: Column( + children: [ + ...variantWidgets + ], + ), + ), + padding: EdgeInsets.all(8), + + ); + + + } + + + ); + }, + ), + ), + ); + + } + +} + +class EnRoutePage extends StatelessWidget { + + @override + Widget build(BuildContext context) { + // TODO: implement build + return Scaffold( + body: Column( + + children: [ + + Container( + padding: EdgeInsets.all(8), + child: ibus_display() + ), + + Divider( + height: 1, + ), + + ShadButton( + text: Text("Route scanner"), + onPressed: () { + + LiveInformation liveInformation = LiveInformation(); + + TubeLine? line = liveInformation.tubeStations.getClosestLine(liveInformation.getRouteVariant()!); + + ShadToaster.of(context).show( + ShadToast( + title: Text("Closest line"), + description: Text(line == null ? "No line found" : line.name), + duration: Duration(seconds: 5), + ) + ); + + }, + ), + + ShadButton( + text: Text("dest"), + onPressed: () { + LiveInformation liveInformation = LiveInformation(); + liveInformation.announcementModule.queueAnnouncementByRouteVariant(routeVariant: liveInformation.getRouteVariant()!); + }, + ) + + ], + + ), + ); + } + +} + +/* +ShadSelectFormField( + options: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text("Choose a variant"), + ), + ...variants.values + ], + selectedOptionBuilder: (context, value) => value == + 'none' + ? const Text('Select a verified email to display') + : Text(variants.keys.toList()[int.parse(value)-1]!), + placeholder: Text("Choose a variant"), + +), + */ \ No newline at end of file diff --git a/lib/tfl_datasets.dart b/lib/tfl_datasets.dart index 13a35c2..a5daadd 100644 --- a/lib/tfl_datasets.dart +++ b/lib/tfl_datasets.dart @@ -98,6 +98,18 @@ class BusSequences extends InfoModule { print("Loaded ${routes.length} routes"); + // Remove any empty routes + List emptyRoutes = []; + for (String routeNumber in routes.keys) { + if (routes[routeNumber]!.routeVariants.isEmpty) { + emptyRoutes.add(routeNumber); + } + } + + for (String routeNumber in emptyRoutes) { + routes.remove(routeNumber); + } + } } @@ -225,6 +237,7 @@ class BusRouteStop { // Replace space with underscore stopName = stopName.replaceAll(' ', '_'); + stopName = stopName.replaceAll('-', '_'); // convert to all caps stopName = stopName.toUpperCase(); @@ -263,6 +276,7 @@ class BusDestination { // Replace space with underscore name = name.replaceAll(' ', '_'); + name = name.replaceAll('-', '_'); // convert to all caps name = name.toUpperCase(); diff --git a/lib/utils/NameBeautify.dart b/lib/utils/NameBeautify.dart index 520ee3b..1f88a3a 100644 --- a/lib/utils/NameBeautify.dart +++ b/lib/utils/NameBeautify.dart @@ -27,8 +27,8 @@ class NameBeautify { stopName = stopName.replaceAll(RegExp(r'\>.*\<'), ''); - // remove any special characters except & and / - stopName = stopName.replaceAll(RegExp(r'[^a-zA-Z0-9&/ ]'), ''); + // remove any special characters except & and / and - + stopName = stopName.replaceAll(RegExp(r'[^a-zA-Z0-9&\/\- ]'), ''); // remove any double spaces stopName = stopName.replaceAll(RegExp(r' '), ' '); @@ -41,12 +41,17 @@ class NameBeautify { stopName = stopName.replaceAll(RegExp(phrase, caseSensitive: false), Longify[phrase]!); } + // remove any spaces at the start or end of the string + stopName = stopName.trim(); + stopName = stopName.replaceAll(' ', ' '); stopName = stopName.toLowerCase(); // Capitalify the first letter of each word try { stopName = stopName.split(' ').map((word) => word[0].toUpperCase() + word.substring(1)).join(' '); - } catch (e) {} + } catch (e) { + print("Error capitalifying stop name: $stopName"); + } return stopName; diff --git a/lib/utils/audio wrapper.dart b/lib/utils/audio wrapper.dart index 0dea43b..8aee000 100644 --- a/lib/utils/audio wrapper.dart +++ b/lib/utils/audio wrapper.dart @@ -25,14 +25,14 @@ class AudioWrapper { print("AudioWrapper mode: $mode"); - mode = AudioWrapper_Mode.Mobile; + mode = AudioWrapper_Mode.Web; } justaudio.AudioSource _convertSource_JustAudio(AudioWrapperSource source){ if (source is AudioWrapperByteSource){ return _ByteSource(source.bytes); } else if (source is AudioWrapperAssetSource){ - return justaudio.AudioSource.asset(source.assetPath); + return justaudio.AudioSource.asset("assets/" + source.assetPath); } else { throw Exception("Unknown source type"); } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 55cb301..9f5615a 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,6 +7,7 @@ #include "generated_plugin_registrant.h" #include +#include #include #include #include @@ -16,6 +17,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin"); audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar); + g_autoptr(FlPluginRegistrar) rive_common_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "RivePlugin"); + rive_plugin_register_with_registrar(rive_common_registrar); g_autoptr(FlPluginRegistrar) screen_retriever_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index d8082e0..e3de0c0 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_linux + rive_common screen_retriever url_launcher_linux window_manager diff --git a/pubspec.lock b/pubspec.lock index a0272de..1a40d23 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: "direct main" description: name: appwrite - sha256: "335faac24642aaf66627c21ce26a5c64bcbc3911e624c61b9dfbe0eec7be1342" + sha256: d95476adb1bbb72ae9eb31adffb9205b5cfe24abc4f483982eadceb396d433fb url: "https://pub.dev" source: hosted - version: "12.0.1" + version: "12.0.3" archive: dependency: "direct main" description: name: archive - sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" + sha256: "0763b45fa9294197a2885c8567927e2830ade852e5c896fd4ab7e0e348d0f373" url: "https://pub.dev" source: hosted - version: "3.4.10" + version: "3.5.0" args: dependency: transitive description: @@ -145,14 +145,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" - convert: - dependency: transitive - description: - name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" - source: hosted - version: "3.1.1" cookie_jar: dependency: transitive description: @@ -197,10 +189,10 @@ packages: dependency: transitive description: name: device_info_plus - sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" + sha256: eead12d1a1ed83d8283ab4c2f3fca23ac4082f29f25f29dff0f758f57d06ec91 url: "https://pub.dev" source: hosted - version: "9.1.2" + version: "10.1.0" device_info_plus_platform_interface: dependency: transitive description: @@ -245,10 +237,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: b6283d7387310ad83bc4f3bc245b75d223a032ae6eba275afcd585de2b9a1476 + sha256: "29c90806ac5f5fb896547720b73b17ee9aed9bba540dc5d91fe29f8c5745b10a" url: "https://pub.dev" source: hosted - version: "8.0.1" + version: "8.0.3" fixnum: dependency: transitive description: @@ -270,6 +262,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.5.0" + flutter_carousel_widget: + dependency: "direct main" + description: + name: flutter_carousel_widget + sha256: "37b9e55e4cafffe358152b016db24153e756152aa07c4214cfe6ee902cd69a01" + url: "https://pub.dev" + source: hosted + version: "2.2.0" flutter_lints: dependency: "direct dev" description: @@ -437,14 +437,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.18.1" - js: - dependency: transitive - description: - name: js - sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf - url: "https://pub.dev" - source: hosted - version: "0.7.1" just_audio: dependency: "direct main" description: @@ -473,10 +465,10 @@ packages: dependency: "direct main" description: name: just_audio_windows - sha256: "7b8801f3987e98a2002cd23b5600b2daf162248ff1413266fb44c84448c1c0d3" + sha256: "48ab2dec79cf6097550602fe07b1a644f341450e138dc8fdc23e42ce0ed2d928" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.2.1" latlong2: dependency: transitive description: @@ -573,14 +565,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" - modular_ui: - dependency: "direct main" - description: - name: modular_ui - sha256: c7bfa2d509f6b55f45833ea5a46c7b40030c2a3c8fb26f5211eab440235ee99f - url: "https://pub.dev" - source: hosted - version: "0.0.5" ntp: dependency: "direct main" description: @@ -593,18 +577,18 @@ packages: dependency: transitive description: name: package_info_plus - sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017" + sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0 url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "8.0.0" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" + sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.0" path: dependency: transitive description: @@ -741,14 +725,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" - pointycastle: - dependency: transitive - description: - name: pointycastle - sha256: "79fbafed02cfdbe85ef3fd06c7f4bc2cbcba0177e61b765264853d4253b21744" - url: "https://pub.dev" - source: hosted - version: "3.9.0" polylabel: dependency: transitive description: @@ -769,18 +745,18 @@ packages: dependency: transitive description: name: rive - sha256: ec44b6cf7341e21727c4b0e762f4ac82f9a45f7e52df3ebad2d1289a726fbaaf + sha256: "95690a0fb4f6e195c53b217ab3cc0e0b0f443c670adbdee9d57d636a36b82b18" url: "https://pub.dev" source: hosted - version: "0.13.1" + version: "0.13.2" rive_common: dependency: transitive description: name: rive_common - sha256: "0f070bc0e764c570abd8b34d744ef30d1292bd4051f2e0951bbda755875fce6a" + sha256: "3eee68fcab3e0882090cea5a8cf7acea7967f469a34a2580322575603b094435" url: "https://pub.dev" source: hosted - version: "0.3.3" + version: "0.4.5" rxdart: dependency: transitive description: @@ -801,10 +777,10 @@ packages: dependency: "direct main" description: name: shadcn_ui - sha256: "06c25555efbabe6d1d6acd6397bff4940e931d53ac3448e3a4f6a2287ab6798d" + sha256: df9aedbd18bd60160c1c54717eef47fe4f90422f843070ccbc32786193803f7d url: "https://pub.dev" source: hosted - version: "0.3.3" + version: "0.4.1" shared_preferences: dependency: "direct main" description: @@ -1110,10 +1086,10 @@ packages: dependency: transitive description: name: win32 - sha256: "0a989dc7ca2bb51eac91e8fd00851297cfffd641aa7538b165c62637ca0eaa4a" + sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "5.5.0" win32_registry: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e0f0776..ccc186a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,8 +50,8 @@ dependencies: vector_math: ^2.1.4 permission_handler: ^11.3.0 file_picker: ^8.0.0+1 - shadcn_ui: ^0.3.3 - modular_ui: ^0.0.5 + shadcn_ui: ^0.4.1 + flutter_carousel_widget: ^2.2.0 # The following adds the Cupertino Icons font to your application. @@ -90,6 +90,9 @@ flutter: - assets/datasets/bus-blinds.csv - assets/audio/5-seconds-of-silence.mp3 - assets/version.txt + - assets/audio/R_RAIL_REPLACEMENT_SERVICE_001.mp3 + - assets/datasets/tube_stations.json + - assets/audio/rail_replacement/ # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg