From 3f68f9ae72e1e51b44f135bd2dd578e24d77c723 Mon Sep 17 00:00:00 2001 From: Christian Lawson-Perfect Date: Sun, 9 Feb 2025 20:35:44 +0000 Subject: [PATCH] first commit --- .gitignore | 5 + .watchmakerc | 2 + Makefile | 11 + README.txt | 22 + Sanchez-Regular.otf | Bin 0 -> 96172 bytes Sanchez-Regular.ttf | Bin 0 -> 66620 bytes cgi-bin/platform_data.py | 31 + elm.json | 25 + index.html | 25 + lines.json | 1 + load-app.js | 61 + manifest.json | 25 + metro_logo.png | Bin 0 -> 13460 bytes metro_logo.svg | 55 + metro_logo_192.png | Bin 0 -> 4911 bytes metro_map.svg | 7504 ++++++++++++++++++++++++++++++++++++++ platforms.json | 1 + show-error.mjs | 21 + src/App.elm | 610 ++++ src/LatLonDistance.elm | 50 + station-directions.json | 63 + station-latlons.json | 1 + station-positions.json | 79 + stations.json | 63 + style.css | 148 + 25 files changed, 8803 insertions(+) create mode 100644 .gitignore create mode 100644 .watchmakerc create mode 100644 Makefile create mode 100644 README.txt create mode 100644 Sanchez-Regular.otf create mode 100644 Sanchez-Regular.ttf create mode 100755 cgi-bin/platform_data.py create mode 100644 elm.json create mode 100644 index.html create mode 100644 lines.json create mode 100644 load-app.js create mode 100644 manifest.json create mode 100644 metro_logo.png create mode 100644 metro_logo.svg create mode 100644 metro_logo_192.png create mode 100644 metro_map.svg create mode 100644 platforms.json create mode 100644 show-error.mjs create mode 100644 src/App.elm create mode 100644 src/LatLonDistance.elm create mode 100644 station-directions.json create mode 100644 station-latlons.json create mode 100644 station-positions.json create mode 100644 stations.json create mode 100644 style.css diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bfcec8d --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.make.* +app.js +elm-stuff/ +error.txt +platform_data/ \ No newline at end of file diff --git a/.watchmakerc b/.watchmakerc new file mode 100644 index 0000000..285f521 --- /dev/null +++ b/.watchmakerc @@ -0,0 +1,2 @@ +extensions: + - .elm \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..092ea03 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +DIRNAME=$(notdir $(CURDIR)) + +ELMS=$(wildcard src/*.elm) + +app.js: src/App.elm $(ELMS) + -elm make $< --output=$@ 2> error.txt + @cat error.txt + +upload: app.js index.html style.css + rsync -avz . clpland:~/domains/somethingorotherwhatever.com/html/$(DIRNAME) + @echo "Uploaded to https://somethingorotherwhatever.com/$(DIRNAME)" \ No newline at end of file diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..4c486ea --- /dev/null +++ b/README.txt @@ -0,0 +1,22 @@ +JSON files: + +lines.json + Maps line names ("GREEN" or "YELLOW") to a list of stations along that line, with properties "key" (string) and "position" (list of two floats). + +platforms.json + From https://github.com/danielgjackson/metro-rti/blob/main/data/platforms.json + Maps station keys to objects: + + "platformNumber": int + "direction": string, "IN" or "OUT" + "helperText": string, readable description of the direction of travel + +station-directions.json + Maps station keys to a dictionary mapping platform numbers to the angle of that platform on the map. + +station-positions.json + Maps line names ("green" or "yellow") to a list of pairs of floats, giving the position on the map of each station on the line. + +stations.json + From https://github.com/danielgjackson/metro-rti/blob/main/data/stations.json + Maps station keys to readable station names. \ No newline at end of file diff --git a/Sanchez-Regular.otf b/Sanchez-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..6cbd80f9ec55f8dd42b25e1f40f7a98418ddd4e6 GIT binary patch literal 96172 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 z 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 V$cgrP-Iy)B9eXF&fA)y${{yZdu*U!Z literal 0 HcmV?d00001 diff --git a/Sanchez-Regular.ttf b/Sanchez-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3c6c134ec127dc3cf6be0f603b2a2464b8ea2395 GIT binary patch literal 66620 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 literal 0 HcmV?d00001 diff --git a/cgi-bin/platform_data.py b/cgi-bin/platform_data.py new file mode 100755 index 0000000..b1a1c14 --- /dev/null +++ b/cgi-bin/platform_data.py @@ -0,0 +1,31 @@ +#!/srv/think.somethingorotherwhatever.com/venv/bin/python + +import cgi +import cgitb +import requests +from pathlib import Path +from datetime import datetime, timedelta +import getpass + +cgitb.enable() + +form = cgi.FieldStorage() + +print('Content-Type: application/json\n') + +station = form['station'].value +platform = form['platform'].value + +data_root = Path('..') / 'platform_data' + +data_root.mkdir(exist_ok=True) + +fname = data_root / f'{station}-{platform}.json' + +if (not fname.exists()) or (datetime.now() - datetime.fromtimestamp(fname.stat().st_mtime) > timedelta(minutes=1)): + req = requests.get(f'https://metro-rti.nexus.org.uk/api/times/{station}/{platform}') + with open(fname, 'w') as f: + f.write(req.text) + +with open(fname) as f: + print(f.read()) \ No newline at end of file diff --git a/elm.json b/elm.json new file mode 100644 index 0000000..3bcf1ee --- /dev/null +++ b/elm.json @@ -0,0 +1,25 @@ +{ + "type": "application", + "source-directories": [ + "src" + ], + "elm-version": "0.19.1", + "dependencies": { + "direct": { + "elm/browser": "1.0.2", + "elm/core": "1.0.5", + "elm/html": "1.0.0", + "elm/json": "1.1.3", + "elm/svg": "1.0.1", + "elm/time": "1.0.0" + }, + "indirect": { + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.3" + } + }, + "test-dependencies": { + "direct": {}, + "indirect": {} + } +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..959fccf --- /dev/null +++ b/index.html @@ -0,0 +1,25 @@ + + + + + + Metro info + + + + + + +
+

Elm app by clp

+
+
+

This is an app which will either load succesfully, and you'll wonder whether you saw this text at all, or fail ignominiously, showing you only this text.

+

On balance of probabilities: I'm sorry I couldn't be bothered to make this work for you.

+
+ + + + + + \ No newline at end of file diff --git a/lines.json b/lines.json new file mode 100644 index 0000000..ec7cee3 --- /dev/null +++ b/lines.json @@ -0,0 +1 @@ +{"GREEN": [{"key": "APT", "map_position": [51.80868911743164, 44.98532485961914], "latlon": [55.03577004909116, -1.7111265174504924]}, {"key": "CAL", "map_position": [71.1099624633789, 44.98532485961914], "latlon": [55.02800273311085, -1.7036043935197853]}, {"key": "BFT", "map_position": [90.9191665649414, 44.477394104003906], "latlon": [55.013882728284536, -1.6781041455925452]}, {"key": "KSP", "map_position": [109.71251678466797, 44.98532485961914], "latlon": [55.01438146469378, -1.6652818995828114]}, {"key": "FAW", "map_position": [129.0137939453125, 44.98532485961914], "latlon": [55.013643304033884, -1.644878111966044]}, {"key": "WBR", "map_position": [148.822998046875, 44.477394104003906], "latlon": [55.01415869459053, -1.6350085965165628]}, {"key": "RGC", "map_position": [167.61634826660156, 44.98532485961914], "latlon": [55.011944179047475, -1.6217731502181474]}, {"key": "SGF", "map_position": [181.33041381835938, 54.12803268432617], "latlon": [55.00606753485614, -1.6082308131224372]}, {"key": "ILF", "map_position": [187.42555236816406, 65.30245971679688], "latlon": [55.000116635459875, -1.6112647564719849]}, {"key": "WJS", "map_position": [193.52069091796875, 76.98480987548828], "latlon": [54.99361592984736, -1.6098224294795975]}, {"key": "JES", "map_position": [200.63168334960938, 87.65130615234375], "latlon": [54.982874914767116, -1.605933105358663]}, {"key": "HAY", "map_position": [206.21888732910156, 98.31779479980469], "latlon": [54.97752938692041, -1.6139804105070166]}, {"key": "MTS", "map_position": [214.8536834716797, 110.5080795288086], "latlon": [54.973881889528585, -1.6132900680751794]}, {"key": "CEN", "map_position": [221.9646759033203, 124.73007202148438], "latlon": [54.96909836537041, -1.6163186051663263]}, {"key": "GHD", "map_position": [254.4720916748047, 152.66612243652344], "latlon": [54.969052114768495, -1.6164731976943978]}, {"key": "GST", "map_position": [282.91607666015625, 152.66612243652344], "latlon": [54.96188025721301, -1.6041557776219313]}, {"key": "FEL", "map_position": [309.8362731933594, 152.66612243652344], "latlon": [54.957693981514865, -1.5893810383609956]}, {"key": "HTH", "map_position": [339.8040466308594, 152.66612243652344], "latlon": [54.95309923554773, -1.5722545639671113]}, {"key": "PLW", "map_position": [364.9465026855469, 152.158203125], "latlon": [54.95151785157374, -1.555949157488558]}, {"key": "FGT", "map_position": [396.94598388671875, 173.49119567871094], "latlon": [54.95263065551402, -1.5420148963164952]}, {"key": "BYW", "map_position": [434.0247497558594, 173.49119567871094], "latlon": [54.957372994523446, -1.4860452243342233]}, {"key": "EBO", "map_position": [466.53216552734375, 173.49119567871094], "latlon": [54.959403577124995, -1.4612794918514254]}, {"key": "SBN", "map_position": [517.8328857421875, 182.1259765625], "latlon": [54.946410152193415, -1.420438895595007]}, {"key": "SFC", "map_position": [518.3408203125, 191.77662658691406], "latlon": [54.92919863560131, -1.3864468319158785]}, {"key": "MSP", "map_position": [518.3408203125, 203.45896911621094], "latlon": [54.91811279473953, -1.3830167967642568]}, {"key": "SUN", "map_position": [518.8487548828125, 226.01097106933594], "latlon": [54.91100033399111, -1.3842235196597212]}, {"key": "PLI", "map_position": [509.7060546875, 239.21710205078125], "latlon": [54.90232769869331, -1.3848277824608908]}, {"key": "UNI", "map_position": [484.81756591796875, 239.21710205078125], "latlon": [54.90289486139476, -1.3923841760816358]}, {"key": "MLF", "map_position": [459.4211730957031, 239.21710205078125], "latlon": [54.90651021486448, -1.4008007892610852]}, {"key": "PAL", "map_position": [434.0247497558594, 239.21710205078125], "latlon": [54.91279641288439, -1.4178747825280822]}, {"key": "SHL", "map_position": [408.6283264160156, 238.7091827392578], "latlon": [54.90420705667461, -1.4479615115961655]}], "YELLOW": [{"key": "SJM", "map_position": [199.61582946777344, 111.52393341064453], "latlon": [54.97421054355723, -1.6210669283330124]}, {"key": "MTW", "map_position": [218.4091796875, 111.52393341064453], "latlon": [54.97384152715182, -1.6131521777521154]}, {"key": "MAN", "map_position": [267.67822265625, 111.52393341064453], "latlon": [54.974003328855375, -1.6048376504104354]}, {"key": "BYK", "map_position": [294.0904846191406, 112.0318603515625], "latlon": [54.97612749380666, -1.5799477880096582]}, {"key": "CRD", "map_position": [320.5027770996094, 111.01600646972656], "latlon": [54.98285607730816, -1.5719087070215574]}, {"key": "WKG", "map_position": [347.5245666503906, 111.01600646972656], "latlon": [54.98545678531661, -1.559210279263152]}, {"key": "WSD", "map_position": [374.44476318359375, 111.52393341064453], "latlon": [54.98953748564867, -1.5325426692860964]}, {"key": "HDR", "map_position": [400.8570251464844, 111.52393341064453], "latlon": [54.99225461830697, -1.515811836724129]}, {"key": "HOW", "map_position": [428.28515625, 111.52393341064453], "latlon": [54.99569609087364, -1.4945280145610538]}, {"key": "PCM", "map_position": [455.7132873535156, 111.01600646972656], "latlon": [54.99947831652944, -1.4751452540962111]}, {"key": "MWL", "map_position": [482.63348388671875, 111.52393341064453], "latlon": [55.001529323712575, -1.4659424709549598]}, {"key": "NSH", "map_position": [508.537841796875, 111.52393341064453], "latlon": [55.00840630699378, -1.4485778014421224]}, {"key": "TYN", "map_position": [518.6964111328125, 98.82572174072266], "latlon": [55.017274418617184, -1.4282651033617741]}, {"key": "CUL", "map_position": [518.1884765625, 78.00066375732422], "latlon": [55.03511093879241, -1.4365420359749002]}, {"key": "WTL", "map_position": [518.6964111328125, 57.175601959228516], "latlon": [55.039919289125834, -1.4429108035106921]}, {"key": "MSN", "map_position": [485.1731262207031, 44.477394104003906], "latlon": [55.04230033202882, -1.45816343817505]}, {"key": "WMN", "map_position": [447.0784912109375, 43.96946716308594], "latlon": [55.04027612958929, -1.4767711487297275]}, {"key": "SMR", "map_position": [408.98388671875, 43.96946716308594], "latlon": [55.03689435582033, -1.504929962316373]}, {"key": "NPK", "map_position": [370.3813171386719, 44.477394104003906], "latlon": [55.03308042684014, -1.5196901723469216]}, {"key": "PMV", "map_position": [331.2708435058594, 43.46154022216797], "latlon": [55.02386950238466, -1.5406811448399973]}, {"key": "BTN", "map_position": [293.1762390136719, 43.96946716308594], "latlon": [55.013811103383524, -1.567622747731078]}, {"key": "FLE", "map_position": [254.87841796875, 43.96946716308594], "latlon": [55.01011405563572, -1.5786328955533446]}, {"key": "LBN", "map_position": [216.27586364746094, 43.96946716308594], "latlon": [55.00873413367473, -1.5914776477978119]}, {"key": "SGF", "map_position": [186.30810546875, 54.12803268432617], "latlon": [55.00614726487779, -1.6082661067096817]}, {"key": "ILF", "map_position": [191.3873748779297, 64.79452514648438], "latlon": [55.00023693236148, -1.6108770317154648]}, {"key": "WJS", "map_position": [197.99044799804688, 76.98480987548828], "latlon": [54.99364830600748, -1.6098926355353478]}, {"key": "JES", "map_position": [204.59352111816406, 87.14337158203125], "latlon": [54.982881393953804, -1.6060319576069106]}, {"key": "HAY", "map_position": [210.68865966796875, 97.80986785888672], "latlon": [54.97749613672549, -1.6140033882511216]}, {"key": "MTS", "map_position": [218.8155059814453, 112.0318603515625], "latlon": [54.973896759913636, -1.6132420712024815]}, {"key": "CEN", "map_position": [225.926513671875, 124.22213745117188], "latlon": [54.96915879926773, -1.6162313682444793]}, {"key": "GHD", "map_position": [254.37049865722656, 148.6027069091797], "latlon": [54.96180675090266, -1.6040450471579535]}, {"key": "GST", "map_position": [283.830322265625, 147.5868377685547], "latlon": [54.957727365407564, -1.5886927377600415]}, {"key": "FEL", "map_position": [309.73468017578125, 147.5868377685547], "latlon": [54.95315392240506, -1.5720446784266642]}, {"key": "HTH", "map_position": [339.70245361328125, 147.5868377685547], "latlon": [54.9515340871179, -1.5555837237871162]}, {"key": "PLW", "map_position": [365.098876953125, 148.6027069091797], "latlon": [54.95254705425207, -1.5420476251169053]}, {"key": "HEB", "map_position": [391.10479736328125, 148.0947723388672], "latlon": [54.97561399582167, -1.520598379333017]}, {"key": "JAR", "map_position": [409.8981628417969, 148.0947723388672], "latlon": [54.97942548654144, -1.4938358063276782]}, {"key": "BDE", "map_position": [429.7073669433594, 148.0947723388672], "latlon": [54.97439112480099, -1.4660181960559018]}, {"key": "SMD", "map_position": [449.5165710449219, 147.5868377685547], "latlon": [54.971690254811655, -1.4553294024831502]}, {"key": "TDK", "map_position": [469.83367919921875, 148.0947723388672], "latlon": [54.976276978091505, -1.4415400265511848]}, {"key": "CHI", "map_position": [489.1349792480469, 148.0947723388672], "latlon": [54.98635066064746, -1.4318251855177448]}, {"key": "SSS", "map_position": [509.96002197265625, 148.0947723388672], "latlon": [54.99763337335481, -1.433002848691932]}]} \ No newline at end of file diff --git a/load-app.js b/load-app.js new file mode 100644 index 0000000..c00b567 --- /dev/null +++ b/load-app.js @@ -0,0 +1,61 @@ +import show_error from './show-error.mjs'; +async function init_app() { + + const params = new URLSearchParams(location.search); + const station = params.get('station'); + + + const station_names = await (await fetch('stations.json')).json(); + const station_directions = await (await fetch('station-directions.json')).json(); + const station_latlons = await (await fetch('station-latlons.json')).json(); + + const platforms = await (await fetch('platforms.json')).json(); + + station_names['MON'] = 'Monument'; + station_latlons['MON'] = station_latlons['MTW']; + delete station_names['MTW']; + delete station_names['MTS']; + delete station_latlons['MTW']; + delete station_latlons['MTS']; + Object.entries(platforms).forEach(([station_key,platforms]) => { + platforms.forEach(p => { + p.station_key = station_key; + }); + }) + platforms['MON'] = platforms['MTS'].concat(platforms['MTW']); + station_directions['MON'] = Object.assign({}, station_directions['MTS'], station_directions['MTW']); + + const stations = Object.keys(station_names).map(key => { + return {key, name: station_names[key], platforms: platforms[key], directions: station_directions[key], latlon: station_latlons[key]}; + }); + + const lines = await (await fetch('lines.json')).json(); + Object.values(lines).map(stops => stops.forEach(stop => { + stop.key = (stop.key == 'MTS' || stop.key == 'MTW') ? 'MON' : stop.key; + })) + const compilation_error = await show_error; + if(compilation_error) { + return; + } + const app = Elm.App.init({node: document.body, flags: {stations, lines, station}}); + + app.ports.request_data.subscribe(async ([station,platformNumber]) => { + const url = `cgi-bin/platform_data.py?station=${station}&platform=${platformNumber}`; + const data = await (await fetch(url)).json(); + station = (station == 'MTS' || station == 'MTW') ? 'MON' : station; + const out = {time: (new Date()).toISOString(), station, platformNumber, data} + app.ports.receive_platform_data.send(out); + }); + + app.ports.request_location.subscribe(async () => { + navigator.geolocation.getCurrentPosition( + (r) => { + console.log(r); + app.ports.receive_location.send(r); + }, + (e) => console.error(e) + ); + }); +} + +init_app(); \ No newline at end of file diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..b476133 --- /dev/null +++ b/manifest.json @@ -0,0 +1,25 @@ +{ + "name": "Metro info", + "description": "See the next trains from Metro stations", + "start_url": "https://metro-info.think.somethingorotherwhatever.com/", + "display": "standalone", + "background_color": "#feb300", + "theme_color": "#feb300", + "icons": [ + { + "src": "metro_logo.svg", + "type": "image/svg+xml", + "sizes": "any" + }, + { + "src": "metro_logo.png", + "type": "image/png", + "sizes": "512x512" + }, + { + "src": "metro_logo_192.png", + "type": "image/png", + "sizes": "192x192" + } + ] +} \ No newline at end of file diff --git a/metro_logo.png b/metro_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0c6bc28c883d1413ee6f0c3ba202e223b03578dd GIT binary patch literal 13460 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 + + + + + + + + diff --git a/metro_logo_192.png b/metro_logo_192.png new file mode 100644 index 0000000000000000000000000000000000000000..672c83cb4892e85b86a870237cf72077a6bbf2c7 GIT binary patch literal 4911 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 M_CU4to>j + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platforms.json b/platforms.json new file mode 100644 index 0000000..c3005f5 --- /dev/null +++ b/platforms.json @@ -0,0 +1 @@ +{"APT":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"BDE":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James via Whitley Bay"}],"BFT":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"BTN":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James via Whitley Bay"}],"BYK":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James"}],"BYW":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"CAL":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"CEN":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton and South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport and St. James via Whitley Bay"}],"CHI":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James via Whitley Bay"}],"CRD":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James"}],"CUL":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James"}],"EBO":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"FAW":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"FEL":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton and South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport and St. James via Whitley Bay"}],"FGT":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"FLE":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James via Whitley Bay"}],"GHD":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton and South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport and St. James via Whitley Bay"}],"GST":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton and South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport and St. James via Whitley Bay"}],"HAY":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton and South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport and St. James via Whitley Bay"}],"HDR":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James"}],"HEB":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James via Whitley Bay"}],"HOW":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James"}],"HTH":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton and South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport and St. James via Whitley Bay"}],"ILF":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton and South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport and St. James via Whitley Bay"}],"JAR":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James via Whitley Bay"}],"JES":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton and South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport and St. James via Whitley Bay"}],"KSP":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"LBN":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James via Whitley Bay"}],"MAN":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James"}],"MLF":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"MSN":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James via Whitley Bay"}],"MSP":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"MTS":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton and South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport and St. James via Whitley Bay"}],"MTW":[{"platformNumber":3,"direction":"IN","helperText":"Towards South Shields via Whitley Bay"},{"platformNumber":4,"direction":"OUT","helperText":"Towards St. James"}],"MWL":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James"}],"NPK":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James via Whitley Bay"}],"NSH":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James"}],"PAL":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"PCM":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James"}],"PLI":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"PLW":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton and South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport and St. James via Whitley Bay"}],"PMV":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James via Whitley Bay"}],"RGC":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"SBN":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"SFC":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"SGF":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton and South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport and St. James via Whitley Bay"}],"SHL":[{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"SJM":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards South Shields"}],"SMD":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James via Whitley Bay"}],"SMR":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James via Whitley Bay"}],"SSS":[{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James via Whitley Bay"}],"SUN":[{"platformNumber":2,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":3,"direction":"OUT","helperText":"Towards Airport"}],"TDK":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James via Whitley Bay"}],"TYN":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James"}],"UNI":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"WBR":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport"}],"WJS":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Hylton and South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards Airport and St. James via Whitley Bay"}],"WKG":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James"}],"WMN":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James via Whitley Bay"}],"WSD":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James"}],"WTL":[{"platformNumber":1,"direction":"IN","helperText":"Towards South Shields"},{"platformNumber":2,"direction":"OUT","helperText":"Towards St. James"}]} \ No newline at end of file diff --git a/show-error.mjs b/show-error.mjs new file mode 100644 index 0000000..0c9d53e --- /dev/null +++ b/show-error.mjs @@ -0,0 +1,21 @@ +export default fetch('/error.txt').then(r=>{ + if(r.ok) { + return r.text(); + } else { + throw(''); + } +}).then(text => { + if(!text) { + return false; + } + document.body.innerHTML = ''; + const error_show = document.createElement('pre'); + error_show.setAttribute('id','build-error'); + error_show.style.background = 'black'; + error_show.style.color = 'white'; + error_show.style.padding = '1em'; + error_show.style['font-size'] = '16px'; + error_show.textContent = text; + document.body.appendChild(error_show); + return true; +}).catch(e => false); diff --git a/src/App.elm b/src/App.elm new file mode 100644 index 0000000..c6589c5 --- /dev/null +++ b/src/App.elm @@ -0,0 +1,610 @@ +port module App exposing (..) + +import Browser +import Browser.Events exposing (Visibility(..)) +import Dict exposing (Dict) +import Html as H exposing (Html) +import Html.Attributes as HA +import Html.Events as HE +import Json.Decode as JD +import LatLonDistance exposing (lat_lon_distance, LatLon) +import Svg as Svg +import Svg.Attributes as SA +import Svg.Events as SE +import Task +import Time +import Tuple exposing (pair, first, second) + + +port request_data : (String, Int) -> Cmd msg +port request_location : () -> Cmd msg +port receive_platform_data : (JD.Value -> msg) -> Sub msg +port receive_location : (JD.Value -> msg) -> Sub msg + +main = Browser.document + { init = init + , update = update + , subscriptions = subscriptions + , view = view + } + +type alias Model = + { stations : (List Station) + , lines : Dict String (List Stop) + , error : Maybe JD.Error + , current_station : Maybe String + , time : Time.Posix + , time_zone : Time.Zone + , window_visible : Visibility + , current_position : Maybe LatLon + } + +type alias Stop = + { station : String + , position: (Float, Float) + , latlon : (Float, Float) + } + +type alias Station = + { key : String + , name : String + , platforms : List Platform + , latlon : LatLon + } + +blank_station = + { key = "???" + , name = "Unknown station" + , platforms = [] + , latlon = {lat = 0, lon = 0} + } + +type alias Platform = + { last_checked : Time.Posix + , platformNumber : Int + , direction : PlatformDirection + , angle : Float + , helperText : String + , trains : List TrainInfo + , station_key : String + } + +type alias TrainInfo = + { train : String + , last_event : TrainEvent + , last_event_location : (String, Int) + , last_event_time : String + , destination : String + , due_in : Int + , line : String + } + +type alias PlatformDataResponse = + { time : String + , station : String + , platformNumber : Int + , data : List TrainInfo + } + +type TrainEvent + = Departed + | Arrived + | ReadyToStart + | Approaching + +event_description : TrainEvent -> String +event_description event = case event of + Departed -> "Departed" + Arrived -> "Arrived" + ReadyToStart -> "Starting" + Approaching -> "Approaching" + +type PlatformDirection + = In + | Out + +dir_arrow : PlatformDirection -> String +dir_arrow d = case d of + In -> "←" + Out -> "→" + +decode_station : JD.Decoder Station +decode_station = + JD.map4 Station + (JD.field "key" JD.string) + (JD.field "name" JD.string) + (JD.field "directions" (JD.dict JD.float) |> JD.andThen + (\directions -> (JD.field "platforms" (JD.list (decode_platform directions)))) + ) + (JD.field "latlon" decode_latlon) + +decode_platform : Dict String Float -> JD.Decoder Platform +decode_platform directions = + JD.map4 + (\platformNumber direction helperText station_key -> + let + angle = Dict.get (String.fromInt platformNumber) directions |> Maybe.withDefault 0 + in + { last_checked = (Time.millisToPosix 0) + , platformNumber = platformNumber + , direction = direction + , helperText = helperText + , station_key = station_key + , trains = [] + , angle = angle + } + ) + (JD.field "platformNumber" JD.int) + (JD.field "direction" decode_direction) + (JD.field "helperText" JD.string) + (JD.field "station_key" JD.string) + +decode_direction : JD.Decoder PlatformDirection +decode_direction = + JD.string |> JD.andThen + (\s -> case s of + "IN" -> JD.succeed In + "OUT" -> JD.succeed Out + _ -> JD.fail <| "Unrecognised direction " ++ s + ) + +decode_stop : JD.Decoder Stop +decode_stop = + JD.map3 Stop + (JD.field "key" JD.string) + (JD.field "map_position" (JD.map2 pair (JD.field "0" JD.float) (JD.field "1" JD.float))) + (JD.field "latlon" (JD.map2 pair (JD.field "0" JD.float) (JD.field "1" JD.float))) + +decode_train_info : Model -> JD.Decoder TrainInfo +decode_train_info model = + JD.map7 TrainInfo + (JD.field "trn" JD.string) + (JD.field "lastEvent" decode_train_event) + (JD.field "lastEventLocation" (decode_event_location model)) + (JD.field "lastEventTime" JD.string) + (JD.field "destination" JD.string) + (JD.field "dueIn" JD.int) + (JD.field "line" JD.string) + +decode_event_location : Model -> JD.Decoder (String, Int) +decode_event_location model = + JD.string + |> JD.map (\s -> + let + i = String.indices "Platform" s |> List.head |> Maybe.withDefault 0 + name = String.slice 0 (i-1) s |> station_name_to_key model |> Maybe.withDefault ("???" ++ s) + platform_number = String.dropLeft (i+9) s |> String.toInt |> Maybe.withDefault 0 + in + (name, platform_number) + ) + +decode_train_event : JD.Decoder TrainEvent +decode_train_event = + JD.string |> JD.andThen + (\s -> case s of + "ARRIVED" -> JD.succeed Arrived + "DEPARTED" -> JD.succeed Departed + "READY_TO_START" -> JD.succeed ReadyToStart + "APPROACHING" -> JD.succeed Approaching + _ -> JD.fail <| "Unrecognised train event " ++ s + ) + +decode_platform_data_response model = + JD.map4 PlatformDataResponse + (JD.field "time" JD.string) + (JD.field "station" JD.string) + (JD.field "platformNumber" JD.int) + (JD.field "data" (JD.list (decode_train_info model))) + +decode_geolocation : JD.Decoder LatLon +decode_geolocation = + JD.map2 LatLon + (JD.at ["coords", "latitude"] JD.float) + (JD.at ["coords", "longitude"] JD.float) + +decode_latlon : JD.Decoder LatLon +decode_latlon = + JD.map2 LatLon + (JD.field "0" JD.float) + (JD.field "1" JD.float) + +type Msg + = ReceivePlatformData JD.Value + | ReceiveLocation JD.Value + | TriggerTrainInfoRequest Station + | SetCurrentStation Station + | ClearCurrentStation + | UpdatePlatformData + | Tick Time.Posix + | SetTimeZone Time.Zone + | VisibilityChange Visibility + | RequestLocation + +init : (JD.Value) -> (Model, Cmd Msg) +init flags = + let + model = init_model flags + in + ( model + , Cmd.batch + [ request_all_platform_data (station_with_key model (model.current_station |> Maybe.withDefault "MSN")) + , Task.perform SetTimeZone Time.here + ] + ) + +blank_model = + { stations = [] + , lines = Dict.empty + , error = Nothing + , current_station = Just "MSN" + , time = Time.millisToPosix 0 + , time_zone = Time.utc + , window_visible = Visible + , current_position = Nothing + } + +type alias Flags = + { stations : List Station + , lines : Dict String (List Stop) + , station : String + } + + +decode_flags = + JD.map3 Flags + (JD.field "stations" (JD.list decode_station)) + (JD.field "lines" (JD.dict (JD.list decode_stop))) + (JD.oneOf + [ JD.field "station" JD.string + , JD.succeed "MSN" + ] + ) + +init_model : JD.Value -> Model +init_model flags = + flags + |> JD.decodeValue decode_flags + |> (\r -> case r of + Ok flagdata -> { blank_model | stations = flagdata.stations, lines = flagdata.lines, current_station = Just flagdata.station } + Err err -> { blank_model | error = Just (Debug.log "error" err) } + ) + +nocmd model = (model, Cmd.none) + +request_all_platform_data station = + Cmd.batch (List.map (\p -> request_data (p.station_key, p.platformNumber)) station.platforms) + +update msg model = case msg of + TriggerTrainInfoRequest station -> + ( model + , request_all_platform_data station + ) + + ReceivePlatformData data -> + case JD.decodeValue (decode_platform_data_response model) data of + Err err -> {model | error = Just err } |> nocmd + Ok d -> add_platform_data d model |> nocmd + + RequestLocation -> (model, request_location ()) + + ReceiveLocation data -> + case JD.decodeValue (decode_geolocation) data of + Err err -> {model | error = Just (Debug.log "err" err) } |> nocmd + Ok latlon -> + let + closest_stations = geo_closest_stations model latlon + nstation = case List.head closest_stations of + Nothing -> model.current_station + Just (s,_) -> Just s.key + in + { model | current_position = Just latlon, current_station = nstation } |> nocmd + + SetCurrentStation station -> { model | current_station = Just station.key } |> update (TriggerTrainInfoRequest station) + + ClearCurrentStation -> + { model | current_station = Nothing } |> nocmd + + UpdatePlatformData -> + ( model + , case (model.window_visible, model.current_station) of + (Visible, Just s) -> station_with_key model s |> request_all_platform_data + _ -> Cmd.none + ) + + Tick time -> { model | time = time } |> nocmd + + SetTimeZone zone -> { model | time_zone = zone } |> nocmd + + VisibilityChange visible -> { model | window_visible = visible } |> nocmd + +add_platform_data d model = + let + nstations = + model.stations + |> List.map (\s -> + if s.key == d.station then + { s | platforms = List.map (\p -> if p.platformNumber == d.platformNumber then { p | trains = d.data, last_checked = model.time } else p) s.platforms} + else s + ) + in + { model | stations = nstations } + +subscriptions model = + Sub.batch + [ receive_platform_data ReceivePlatformData + , receive_location ReceiveLocation + , Time.every (1000 * 60) (\_ -> UpdatePlatformData) + , Time.every 100 Tick + , Browser.Events.onVisibilityChange VisibilityChange + ] + +view : Model -> Browser.Document Msg +view model = + let + current_station = model.current_station |> Maybe.map (station_with_key model) + + next_trains = + current_station + |> Maybe.map (\s -> + s.platforms + |> List.filterMap (\p -> p.trains |> List.head |> Maybe.map (\t -> "P" ++ (String.fromInt p.platformNumber) ++ ": " ++ (String.fromInt t.due_in))) + |> String.join ", " + |> \times -> times ++ " @ " ++ s.name + ) + |> Maybe.withDefault "" + + view_station open station = + H.article + [ HE.on "toggle" ( + JD.succeed (TriggerTrainInfoRequest station) + ) + , HA.class "station" + ] + [ H.ol + [ HA.class "platforms"] + (List.map (view_platform station) station.platforms) + ] + + format_time t = + let + zone = model.time_zone + hour = Time.toHour zone t + minute = Time.toMinute zone t + pad = String.fromInt >> String.padLeft 2 '0' + in + (pad hour) ++ ":" ++ (pad minute) + + closest_stations = case model.current_position of + Nothing -> [] + Just pos -> geo_closest_stations model pos + + view_platform : Station -> Platform -> Html Msg + view_platform station platform = + H.li + [ HA.value <| String.fromInt platform.platformNumber + ] + [ table + [ H.a + [ HA.href <| "https://metro-rti.nexus.org.uk/api/times/" ++ station.key ++ "/" ++ (String.fromInt platform.platformNumber) + , HA.target "_blank" + ] + [H.text <| + "Platform " + ++ (String.fromInt platform.platformNumber) + ++ ": " + ++ platform.helperText + ] + , H.small [ HA.class "last-checked" ] [ H.text <| format_time platform.last_checked] + ] + ["Due", "Destination", "Last seen", "Event", "Train"] + (List.map (\t -> + let + last_station = station_with_key model (first t.last_event_location) + time_expected = + (Time.posixToMillis model.time) + (1000 * 60 * t.due_in) + |> Time.millisToPosix + + due_string = + if t.due_in > 1 then + (String.fromInt t.due_in) ++ " (" ++ format_time time_expected ++ ")" + else + "here" + in + [ due_string + , t.destination + , last_station.name + , event_description t.last_event + , t.train + ] + ) platform.trains + ) + ] + + + in + + { + title = "Metro info (" ++ next_trains ++ ")", + body = + [ H.div + [ HA.id "controls" ] + [ H.label + [ HA.for "station" ] + [ H.text "Trains from"] + , H.select + [ HA.id "station" + , HA.value <| Maybe.withDefault "" <| model.current_station + , HE.on "input" + ( JD.field "target" (JD.field "value" JD.string) + |> JD.andThen + ( station_with_key model + >> SetCurrentStation >> JD.succeed + ) + ) + ] + (List.map + (\s -> + H.option + [ HA.value s.key] + [H.text s.name] + ) + (List.sortBy .name model.stations) + ) + , H.button + [ HA.type_ "button" + , HE.onClick UpdatePlatformData + ] + [ H.text "⟳" ] + + , H.button + [ HA.type_ "button" + , HE.onClick RequestLocation + ] + [ H.text "🧭" ] + + , H.a + [ HA.href "https://www.nexus.org.uk/metro/updates" + , HA.target "_blank" + ] + [ H.text "Disruptions" ] + ] + , case current_station of + Nothing -> H.text "" + Just station -> view_station True station + , view_map model + + , H.footer + [] + [ H.p + [] + [ H.text "Made unofficially by " + , H.a [ HA.href "https://somethingorotherwhatever.com" ] [ H.text "clp" ] + , H.text ". Uses data from the " + , H.a [ HA.href "https://github.com/danielgjackson/metro-rti" ] [ H.text "Tyne and Wear Metro Real-Time Information API" ] + , H.text "." + ] + ] + ] + } + +station_position : Model -> String -> String -> Maybe (Float, Float) +station_position model line station_key = + Dict.get line model.lines + |> Maybe.andThen (List.filter (\s -> s.station == station_key) >> List.head >> Maybe.map .position) + +platform_direction : Model -> String -> Int -> Maybe Float +platform_direction model station_key platform_number = + model.stations |> List.filter (.key >> (==) station_key) |> List.head |> Maybe.andThen (.platforms >> List.filter (.platformNumber >> (==) platform_number) >> List.head) |> Maybe.map (.angle) + +station_name_to_key : Model -> String -> Maybe String +station_name_to_key model name = + model.stations + |> List.map (\s -> (s.name, s.key)) + |> Dict.fromList + |> Dict.get name + +station_with_key : Model -> String -> Station +station_with_key model key = model.stations |> List.filter (.key >> (==) key) |> List.head |> Maybe.withDefault { blank_station | key = key } + +geo_closest_stations model pos = model.stations |> List.map (\s -> (s, lat_lon_distance pos s.latlon)) |> List.sortBy second + +all_trains : Model -> List TrainInfo +all_trains model = + List.concatMap (.platforms >> List.concatMap (.trains)) (List.filter (\s -> model.current_station == Just s.key) model.stations) + |> List.sortBy (\t -> (t.train, t.last_event_time)) + +view_map model = + let + trains = + all_trains model |> List.filterMap (\t -> + let + (key, platformNumber) = t.last_event_location + position = station_position model t.line key + direction = platform_direction model key platformNumber |> Maybe.withDefault 0 + in + position + |> Maybe.map (\(x,y) -> + Svg.g + [ SA.class "train" + , SA.transform <| "translate(" ++ (String.fromFloat x)++" "++(String.fromFloat y)++")" + ] + [ Svg.circle + [ SA.r "5" + ] + [] + , Svg.path + [ SA.d "M 0 0 L 8 0 M 5 2 L 8 0 L 5 -2" + , SA.fill "none" + , SA.stroke "black" + , SA.strokeWidth "5" + , SA.transform <| "rotate(" ++ (String.fromFloat direction) ++ ")" + ] + [] + , Svg.text_ + [ SA.fontSize "4" + , SA.fill "white" + , SA.textAnchor "middle" + , SA.dominantBaseline "middle" + ] + [ Svg.text t.train ] + ] + ) + ) + + lines : List (String, List Stop) + lines = + model.lines + |> Dict.toList + + stations = + lines + |> List.concatMap (\(line,stops) -> + stops + |> List.map (\stop -> station_with_key model stop.station |> (\s -> + Svg.circle + [ SA.r "6" + , SA.cx <| String.fromFloat (first stop.position) + , SA.cy <| String.fromFloat (second stop.position) + , SA.fillOpacity <| if model.current_station == Just stop.station then "0.5" else "0.01" + , SA.fill "blue" + , SE.onClick (SetCurrentStation s) + ] + [] + )) + ) + in + H.div + [ HA.id "map-container" ] + [ Svg.svg + [ HA.attribute "viewBox" "0 0 595.275 280.63" + , HA.id "map" + ] + [ Svg.use + [ HA.attribute "href" "metro_map.svg#map" + ] + [] + , Svg.g [] (trains) + , Svg.g [] (stations) + ] + ] + +table : List (Html Msg) -> List String -> List (List String) -> Html Msg +table caption headers rows = + H.table + [] + [ H.caption + [] + caption + , H.thead + [] + [H.tr [] (List.map (\h -> H.th [] [H.text h]) headers)] + , H.tbody + [] + ( (List.map (\row -> H.tr [] (List.map (\c -> H.td [] [H.text c]) row)) rows) + |> pad_list 4 (H.tr [HA.class "empty"] (List.map (\_ -> H.td [] [ H.text "-"]) headers)) + ) + ] + +pad_list : Int -> a -> List a -> List a +pad_list n blank list = + List.append list (List.repeat (n - (List.length list)) blank) \ No newline at end of file diff --git a/src/LatLonDistance.elm b/src/LatLonDistance.elm new file mode 100644 index 0000000..aea69bf --- /dev/null +++ b/src/LatLonDistance.elm @@ -0,0 +1,50 @@ +module LatLonDistance exposing (lat_lon_distance, LatLon) + +type alias LatLon = { lat : Float, lon : Float } + +lat_lon_distance : LatLon -> LatLon -> Float +lat_lon_distance p1 p2 = + let + a = 6378137.0 + b = 6356752.314245 + f = 1 / 298.257223563 + lat1 = degrees p1.lat + lat2 = degrees p2.lat + lon1 = degrees p1.lon + lon2 = degrees p2.lon + + dlon = (lon2 - lon1) + + tanU1 = (1-f) * (tan lat1) + cosU1 = 1 / (sqrt (1 + tanU1 * tanU1)) + sinU1 = tanU1 * cosU1 + + tanU2 = (1-f) * (tan lat2) + cosU2 = 1 / (sqrt (1 + tanU2*tanU2)) + sinU2 = tanU2 * cosU2 + + approx lon = + let + sinlon = sin lon + coslon = cos lon + sinSqsigma = (cosU2*sinlon) * (cosU2*sinlon) + (cosU1*sinU2-sinU1*cosU2*coslon) ^ 2 + sinsigma = sqrt sinSqsigma + cossigma = sinU1*sinU2 + cosU1*cosU2*coslon + sigma = atan2 sinsigma cossigma + sinalpha = cosU1 * cosU2 * sinlon / sinsigma + cosSqalpha = 1 - sinalpha*sinalpha + cos2sigma_m = cossigma - 2*sinU1*sinU2/cosSqalpha + c = f/16*cosSqalpha*(4+f*(4-3*cosSqalpha)) + lon_ = dlon + (1-c) * f * sinalpha * (sigma + c*sinsigma*(cos2sigma_m+c*cossigma*(-1+2*cos2sigma_m*cos2sigma_m))) + + uSq = cosSqalpha * (a*a - b*b) / (b*b) + biga = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq))) + bigb = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq))) + deltasigma = bigb*sinsigma*(cos2sigma_m+bigb/4*(cossigma*(-1+2*cos2sigma_m*cos2sigma_m)-bigb/6*cos2sigma_m*(-3+4*sinsigma*sinsigma)*(-3+4*cos2sigma_m*cos2sigma_m))) + + s = b*biga*(sigma-deltasigma) + + in + if abs (lon - lon_) > 1e-12 then (approx lon_) else s + in + approx dlon \ No newline at end of file diff --git a/station-directions.json b/station-directions.json new file mode 100644 index 0000000..fe7bf4d --- /dev/null +++ b/station-directions.json @@ -0,0 +1,63 @@ +{ + "APT": {"1": 0, "2": 180}, + "CAL": {"1": 0, "2": 180}, + "BFT": {"1": 0, "2": 180}, + "KSP": {"1": 0, "2": 180}, + "FAW": {"1": 0, "2": 180}, + "WBR": {"1": 0, "2": 180}, + "RGC": {"1": 0, "2": 180}, + "SGF": {"1": 60, "2": 240}, + "ILF": {"1": 60, "2": 240}, + "WJS": {"1": 60, "2": 240}, + "JES": {"1": 60, "2": 240}, + "HAY": {"1": 60, "2": 240}, + "MTS": {"1": 60, "2": 240}, + "MTW": {"3": 0, "4": 180}, + "CEN": {"1": 60, "2": 240}, + "GHD": {"1": 0, "2": 180}, + "GST": {"1": 0, "2": 180}, + "FEL": {"1": 0, "2": 180}, + "HTH": {"1": 0, "2": 180}, + "PLW": {"1": 0, "2": 180}, + "HEB": {"1": 0, "2": 180}, + "JAR": {"1": 0, "2": 180}, + "BDE": {"1": 0, "2": 180}, + "SMD": {"1": 0, "2": 180}, + "TDK": {"1": 0, "2": 180}, + "CHI": {"1": 0, "2": 180}, + "SSS": {"1": 0, "2": 180}, + "FGT": {"1": 0, "2": 180}, + "BYW": {"1": 0, "2": 180}, + "EBO": {"1": 0, "2": 180}, + "SBN": {"1": 90, "2": 270}, + "SFC": {"1": 90, "2": 270}, + "MSP": {"1": 90, "2": 270}, + "SUN": {"1": 90, "2": 270}, + "PLI": {"1": 180, "2": 0}, + "UNI": {"1": 180, "2": 0}, + "MLF": {"1": 180, "2": 0}, + "PAL": {"1": 180, "2": 0}, + "SHL": {"1": 180, "2": 0}, + "SJM": {"1": 0, "2": 180}, + "MAN": {"1": 0, "2": 180}, + "BYK": {"1": 0, "2": 180}, + "CRD": {"1": 0, "2": 180}, + "WKG": {"1": 0, "2": 180}, + "WSD": {"1": 0, "2": 180}, + "HDR": {"1": 0, "2": 180}, + "HOW": {"1": 0, "2": 180}, + "PCM": {"1": 0, "2": 180}, + "MWL": {"1": 0, "2": 180}, + "NSH": {"1": 0, "2": 180}, + "TYN": {"1": 270, "2": 90}, + "CUL": {"1": 270, "2": 90}, + "WTL": {"1": 270, "2": 90}, + "MSN": {"1": 180, "2": 0}, + "WMN": {"1": 180, "2": 0}, + "SMR": {"1": 180, "2": 0}, + "NPK": {"1": 180, "2": 0}, + "PMV": {"1": 180, "2": 0}, + "BTN": {"1": 180, "2": 0}, + "FLE": {"1": 180, "2": 0}, + "LBN": {"1": 180, "2": 0} +} \ No newline at end of file diff --git a/station-latlons.json b/station-latlons.json new file mode 100644 index 0000000..257b28d --- /dev/null +++ b/station-latlons.json @@ -0,0 +1 @@ +{"APT": [55.03577004909116, -1.7111265174504924], "CAL": [55.02800273311085, -1.7036043935197853], "BFT": [55.013882728284536, -1.6781041455925452], "KSP": [55.01438146469378, -1.6652818995828114], "FAW": [55.013643304033884, -1.644878111966044], "WBR": [55.01415869459053, -1.6350085965165628], "RGC": [55.011944179047475, -1.6217731502181474], "SGF": [55.00614726487779, -1.6082661067096817], "ILF": [55.00023693236148, -1.6108770317154648], "WJS": [54.99364830600748, -1.6098926355353478], "JES": [54.982881393953804, -1.6060319576069106], "HAY": [54.97749613672549, -1.6140033882511216], "MTS": [54.973896759913636, -1.6132420712024815], "CEN": [54.96915879926773, -1.6162313682444793], "GHD": [54.96180675090266, -1.6040450471579535], "GST": [54.957727365407564, -1.5886927377600415], "FEL": [54.95315392240506, -1.5720446784266642], "HTH": [54.9515340871179, -1.5555837237871162], "PLW": [54.95254705425207, -1.5420476251169053], "FGT": [54.95263065551402, -1.5420148963164952], "BYW": [54.957372994523446, -1.4860452243342233], "EBO": [54.959403577124995, -1.4612794918514254], "SBN": [54.946410152193415, -1.420438895595007], "SFC": [54.92919863560131, -1.3864468319158785], "MSP": [54.91811279473953, -1.3830167967642568], "SUN": [54.91100033399111, -1.3842235196597212], "PLI": [54.90232769869331, -1.3848277824608908], "UNI": [54.90289486139476, -1.3923841760816358], "MLF": [54.90651021486448, -1.4008007892610852], "PAL": [54.91279641288439, -1.4178747825280822], "SHL": [54.90420705667461, -1.4479615115961655], "SJM": [54.97421054355723, -1.6210669283330124], "MTW": [54.97384152715182, -1.6131521777521154], "MAN": [54.974003328855375, -1.6048376504104354], "BYK": [54.97612749380666, -1.5799477880096582], "CRD": [54.98285607730816, -1.5719087070215574], "WKG": [54.98545678531661, -1.559210279263152], "WSD": [54.98953748564867, -1.5325426692860964], "HDR": [54.99225461830697, -1.515811836724129], "HOW": [54.99569609087364, -1.4945280145610538], "PCM": [54.99947831652944, -1.4751452540962111], "MWL": [55.001529323712575, -1.4659424709549598], "NSH": [55.00840630699378, -1.4485778014421224], "TYN": [55.017274418617184, -1.4282651033617741], "CUL": [55.03511093879241, -1.4365420359749002], "WTL": [55.039919289125834, -1.4429108035106921], "MSN": [55.04230033202882, -1.45816343817505], "WMN": [55.04027612958929, -1.4767711487297275], "SMR": [55.03689435582033, -1.504929962316373], "NPK": [55.03308042684014, -1.5196901723469216], "PMV": [55.02386950238466, -1.5406811448399973], "BTN": [55.013811103383524, -1.567622747731078], "FLE": [55.01011405563572, -1.5786328955533446], "LBN": [55.00873413367473, -1.5914776477978119], "HEB": [54.97561399582167, -1.520598379333017], "JAR": [54.97942548654144, -1.4938358063276782], "BDE": [54.97439112480099, -1.4660181960559018], "SMD": [54.971690254811655, -1.4553294024831502], "TDK": [54.976276978091505, -1.4415400265511848], "CHI": [54.98635066064746, -1.4318251855177448], "SSS": [54.99763337335481, -1.433002848691932]} \ No newline at end of file diff --git a/station-positions.json b/station-positions.json new file mode 100644 index 0000000..fdee053 --- /dev/null +++ b/station-positions.json @@ -0,0 +1,79 @@ +{ + "green": [ + {"map": [51.80868911743164, 44.98532485961914], "latlon": [55.03577004909116, -1.7111265174504924]}, + {"map": [71.1099624633789, 44.98532485961914], "latlon": [55.02800273311085, -1.7036043935197853]}, + {"map": [90.9191665649414, 44.477394104003906], "latlon": [55.013882728284536, -1.6781041455925452]}, + {"map": [109.71251678466797, 44.98532485961914], "latlon": [55.01438146469378, -1.6652818995828114]}, + {"map": [129.0137939453125, 44.98532485961914], "latlon": [55.013643304033884, -1.644878111966044]}, + {"map": [148.822998046875, 44.477394104003906], "latlon": [55.01415869459053, -1.6350085965165628]}, + {"map": [167.61634826660156, 44.98532485961914], "latlon": [55.011944179047475, -1.6217731502181474]}, + {"map": [181.33041381835938, 54.12803268432617], "latlon": [55.00606753485614, -1.6082308131224372]}, + {"map": [187.42555236816406, 65.30245971679688], "latlon": [55.000116635459875, -1.6112647564719849]}, + {"map": [193.52069091796875, 76.98480987548828], "latlon": [54.99361592984736, -1.6098224294795975]}, + {"map": [200.63168334960938, 87.65130615234375], "latlon": [54.982874914767116, -1.605933105358663]}, + {"map": [206.21888732910156, 98.31779479980469], "latlon": [54.97752938692041, -1.6139804105070166]}, + {"map": [214.8536834716797, 110.5080795288086], "latlon": [54.973881889528585, -1.6132900680751794]}, + {"map": [221.9646759033203, 124.73007202148438], "latlon": [54.96909836537041, -1.6163186051663263]}, + {"map": [254.4720916748047, 152.66612243652344], "latlon": [54.969052114768495, -1.6164731976943978]}, + {"map": [282.91607666015625, 152.66612243652344], "latlon": [54.96188025721301, -1.6041557776219313]}, + {"map": [309.8362731933594, 152.66612243652344], "latlon": [54.957693981514865, -1.5893810383609956]}, + {"map": [339.8040466308594, 152.66612243652344], "latlon": [54.95309923554773, -1.5722545639671113]}, + {"map": [364.9465026855469, 152.158203125], "latlon": [54.95151785157374, -1.555949157488558]}, + {"map": [396.94598388671875, 173.49119567871094], "latlon": [54.95263065551402, -1.5420148963164952]}, + {"map": [434.0247497558594, 173.49119567871094], "latlon": [54.957372994523446, -1.4860452243342233]}, + {"map": [466.53216552734375, 173.49119567871094], "latlon": [54.959403577124995, -1.4612794918514254]}, + {"map": [517.8328857421875, 182.1259765625], "latlon": [54.946410152193415, -1.420438895595007]}, + {"map": [518.3408203125, 191.77662658691406], "latlon": [54.92919863560131, -1.3864468319158785]}, + {"map": [518.3408203125, 203.45896911621094], "latlon": [54.91811279473953, -1.3830167967642568]}, + {"map": [518.8487548828125, 226.01097106933594], "latlon": [54.91100033399111, -1.3842235196597212]}, + {"map": [509.7060546875, 239.21710205078125], "latlon": [54.90232769869331, -1.3848277824608908]}, + {"map": [484.81756591796875, 239.21710205078125], "latlon": [54.90289486139476, -1.3923841760816358]}, + {"map": [459.4211730957031, 239.21710205078125], "latlon": [54.90651021486448, -1.4008007892610852]}, + {"map": [434.0247497558594, 239.21710205078125], "latlon": [54.91279641288439, -1.4178747825280822]}, + {"map": [408.6283264160156, 238.7091827392578], "latlon": [54.90420705667461, -1.4479615115961655]} + ], + "yellow": [ + {"map": [199.61582946777344, 111.52393341064453], "latlon": [54.97421054355723, -1.6210669283330124]}, + {"map": [218.4091796875, 111.52393341064453], "latlon": [54.97384152715182, -1.6131521777521154]}, + {"map": [267.67822265625, 111.52393341064453], "latlon": [54.974003328855375, -1.6048376504104354]}, + {"map": [294.0904846191406, 112.0318603515625], "latlon": [54.97612749380666, -1.5799477880096582]}, + {"map": [320.5027770996094, 111.01600646972656], "latlon": [54.98285607730816, -1.5719087070215574]}, + {"map": [347.5245666503906, 111.01600646972656], "latlon": [54.98545678531661, -1.559210279263152]}, + {"map": [374.44476318359375, 111.52393341064453], "latlon": [54.98953748564867, -1.5325426692860964]}, + {"map": [400.8570251464844, 111.52393341064453], "latlon": [54.99225461830697, -1.515811836724129]}, + {"map": [428.28515625, 111.52393341064453], "latlon": [54.99569609087364, -1.4945280145610538]}, + {"map": [455.7132873535156, 111.01600646972656], "latlon": [54.99947831652944, -1.4751452540962111]}, + {"map": [482.63348388671875, 111.52393341064453], "latlon": [55.001529323712575, -1.4659424709549598]}, + {"map": [508.537841796875, 111.52393341064453], "latlon": [55.00840630699378, -1.4485778014421224]}, + {"map": [518.6964111328125, 98.82572174072266], "latlon": [55.017274418617184, -1.4282651033617741]}, + {"map": [518.1884765625, 78.00066375732422], "latlon": [55.03511093879241, -1.4365420359749002]}, + {"map": [518.6964111328125, 57.175601959228516], "latlon": [55.039919289125834, -1.4429108035106921]}, + {"map": [485.1731262207031, 44.477394104003906], "latlon": [55.04230033202882, -1.45816343817505]}, + {"map": [447.0784912109375, 43.96946716308594], "latlon": [55.04027612958929, -1.4767711487297275]}, + {"map": [408.98388671875, 43.96946716308594], "latlon": [55.03689435582033, -1.504929962316373]}, + {"map": [370.3813171386719, 44.477394104003906], "latlon": [55.03308042684014, -1.5196901723469216]}, + {"map": [331.2708435058594, 43.46154022216797], "latlon": [55.02386950238466, -1.5406811448399973]}, + {"map": [293.1762390136719, 43.96946716308594], "latlon": [55.013811103383524, -1.567622747731078]}, + {"map": [254.87841796875, 43.96946716308594], "latlon": [55.01011405563572, -1.5786328955533446]}, + {"map": [216.27586364746094, 43.96946716308594], "latlon": [55.00873413367473, -1.5914776477978119]}, + {"map": [186.30810546875, 54.12803268432617], "latlon": [55.00614726487779, -1.6082661067096817]}, + {"map": [191.3873748779297, 64.79452514648438], "latlon": [55.00023693236148, -1.6108770317154648]}, + {"map": [197.99044799804688, 76.98480987548828], "latlon": [54.99364830600748, -1.6098926355353478]}, + {"map": [204.59352111816406, 87.14337158203125], "latlon": [54.982881393953804, -1.6060319576069106]}, + {"map": [210.68865966796875, 97.80986785888672], "latlon": [54.97749613672549, -1.6140033882511216]}, + {"map": [218.8155059814453, 112.0318603515625], "latlon": [54.973896759913636, -1.6132420712024815]}, + {"map": [225.926513671875, 124.22213745117188], "latlon": [54.96915879926773, -1.6162313682444793]}, + {"map": [254.37049865722656, 148.6027069091797], "latlon": [54.96180675090266, -1.6040450471579535]}, + {"map": [283.830322265625, 147.5868377685547], "latlon": [54.957727365407564, -1.5886927377600415]}, + {"map": [309.73468017578125, 147.5868377685547], "latlon": [54.95315392240506, -1.5720446784266642]}, + {"map": [339.70245361328125, 147.5868377685547], "latlon": [54.9515340871179, -1.5555837237871162]}, + {"map": [365.098876953125, 148.6027069091797], "latlon": [54.95254705425207, -1.5420476251169053]}, + {"map": [391.10479736328125, 148.0947723388672], "latlon": [54.97561399582167, -1.520598379333017]}, + {"map": [409.8981628417969, 148.0947723388672], "latlon": [54.97942548654144, -1.4938358063276782]}, + {"map": [429.7073669433594, 148.0947723388672], "latlon": [54.97439112480099, -1.4660181960559018]}, + {"map": [449.5165710449219, 147.5868377685547], "latlon": [54.971690254811655, -1.4553294024831502]}, + {"map": [469.83367919921875, 148.0947723388672], "latlon": [54.976276978091505, -1.4415400265511848]}, + {"map": [489.1349792480469, 148.0947723388672], "latlon": [54.98635066064746, -1.4318251855177448]}, + {"map": [509.96002197265625, 148.0947723388672], "latlon": [54.99763337335481, -1.433002848691932]} + ] +} \ No newline at end of file diff --git a/stations.json b/stations.json new file mode 100644 index 0000000..a6bab4f --- /dev/null +++ b/stations.json @@ -0,0 +1,63 @@ +{ + "APT":"Airport", + "BDE":"Bede", + "BFT":"Bank Foot", + "BTN":"Benton", + "BYK":"Byker", + "BYW":"Brockley Whins", + "CAL":"Callerton Parkway", + "CEN":"Central", + "CHI":"Chichester", + "CRD":"Chillingham Road", + "CUL":"Cullercoats", + "EBO":"East Boldon", + "FAW":"Fawdon", + "FEL":"Felling", + "FGT":"Fellgate", + "FLE":"Four Lane Ends", + "GHD":"Gateshead", + "GST":"Gateshead Stadium", + "HAY":"Haymarket", + "HDR":"Hadrian Road", + "HEB":"Hebburn", + "HOW":"Howdon", + "HTH":"Heworth", + "ILF":"Ilford Road", + "JAR":"Jarrow", + "JES":"Jesmond", + "KSP":"Kingston Park", + "LBN":"Longbenton", + "MAN":"Manors", + "MLF":"Millfield", + "MSN":"Monkseaton", + "MSP":"St Peters", + "MTS":"Monument N-S", + "MTW":"Monument W-E", + "MWL":"Meadow Well", + "NPK":"Northumberland Park", + "NSH":"North Shields", + "PAL":"Pallion", + "PCM":"Percy Main", + "PLI":"Park Lane", + "PLW":"Pelaw", + "PMV":"Palmersville", + "RGC":"Regent Centre", + "SBN":"Seaburn", + "SFC":"Stadium of Light", + "SGF":"South Gosforth", + "SHL":"South Hylton", + "SJM":"St James", + "SMD":"Simonside", + "SMR":"Shiremoor", + "SSS":"South Shields", + "SUN":"Sunderland", + "TDK":"Tyne Dock", + "TYN":"Tynemouth", + "UNI":"University", + "WBR":"Wansbeck Road", + "WJS":"West Jesmond", + "WKG":"Walkergate", + "WMN":"West Monkseaton", + "WSD":"Wallsend", + "WTL":"Whitley Bay" +} \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..49a7716 --- /dev/null +++ b/style.css @@ -0,0 +1,148 @@ +:root { + --brand-color: #feb300; + --bg-color: white; + --text-color: black; + --link-color: #551a8b; +} + +@font-face { + font-family: 'Sanchez'; + src: local('Calvert'), + url('Sanchez-Regular.otf') format('opentype'); +} + +@media (prefers-color-scheme: dark) { + :root { + --bg-color: black; + --text-color: white; + --link-color: #6666ff; + } + + #map-container { + filter: invert(100%) hue-rotate(180deg); + } +} + +input, select { + font-family: inherit; + font-size: 1em; +} + +html { + font-size: min(16px,4svw); + font-family: Sanchez, sans-serif; +} + +body { + margin: 0; + background-color: var(--bg-color); + color: var(--text-color); + line-height: 1.5; +} + +a { + color: var(--link-color); +} + +#controls { + display: flex; + gap: 0.5em; + color: black; + flex-wrap: wrap; + justify-content: space-evenly; + font-size: 1.5rem; + background: var(--brand-color); + padding: 0.5em; + font-weight: bold; + + & :is(select, input, button) { + font-size: 1em; + } + + & select { + background: white; + border: thin solid black; + } + + & button { + background: none; + border: none; + cursor: pointer; + + &:active { + opacity: 0.2; + } + } + + & a { + color: inherit; + } +} + +.platforms { + display: flex; + flex-wrap: wrap; + gap: 4em; + list-style: none; + justify-content: center; + + & table { + width: 36em; + max-width: 95svw; + font-variant: tabular-nums; + border-collapse: collapse; + + & caption { + font-size: 1.1em; + font-weight: bold; + padding-bottom: 0.5rem; + + & .last-checked { + margin-left: 1em; + } + } + + & tr:not(:first-child) { + border-top: thin dashed currentColor; + } + + & :is(td,th) { + padding: 0.2em 0.5em; + + border: thin dashed currentColor; + } + + & td { + text-align: end; + } + + & th { + text-align: center; + border-bottom-width: medium; + border-bottom-style: solid; + } + } + padding-left: 0; +} + +#map-container { + overflow: auto; + background: white; +} + +#map { + min-width: 1000px; +} + +footer { + margin: 5em 1em 0 1em; +} + +@media (max-width: 37em) { + #controls { + font-size: 1.2rem; + } + table :is(td,th):is(:nth-child(4),:nth-child(5)) { + display: none; + } +} \ No newline at end of file