66 Commits

Author SHA1 Message Date
74e1828f8c Add missing signaller change from Bathampton - Westbury Map
Extend Bathampton - Westbury Map to include Westbury South Jn
2026-03-01 23:55:43 +00:00
d52987a3be Add maps for Melksham line and Trowbridge Lines. Complete Reading-Bristol and improve inter-map links 2026-03-01 23:43:50 +00:00
25e2d0f0b2 Minor schema based corrections 2026-03-01 20:10:28 +00:00
756bbbe66f Minor schema corrections 2026-03-01 20:08:20 +00:00
e70b55a5f0 Add schema validation in IDE for route map definition files 2026-03-01 20:05:55 +00:00
bda2e8a0be Fix electrification change feature - display of electrification was incorrect. Fix missing 'kind' from crossing on route 2421 2026-02-28 21:43:43 +00:00
5ce8ca7c0e Add Gloucester to Severn Tunnel Junction 2026-02-28 21:01:42 +00:00
40b4fc94a6 Improve search bar clarity 2026-02-14 21:56:16 +00:00
97703a081a Complete 2420 - Westerleigh - Gloucester and ensure linking is working. 2026-02-14 21:54:59 +00:00
e7530846f2 Improve map loading functions 2026-02-14 21:54:25 +00:00
660b38950f Relocate transpiles maps 2026-02-14 21:54:14 +00:00
437ce6c3e0 Remove map files from build, relocate transpiled map files to $lib for cache improvements. 2026-02-14 21:54:05 +00:00
765923ca0a Complete map 0230 to Standish Jn 2026-02-14 20:14:42 +00:00
1393f458db Adjust tunnel component to offer mid-tunnel section. 2026-02-13 22:30:48 +00:00
3f1ad2cf39 Ensure 'Kemble Tunnel' name is present 2026-02-13 22:28:02 +00:00
2016497c9e Route 0230: Draft covers as far as Kemble from Swindon 2026-02-13 22:27:34 +00:00
454561e481 Add additional meta tags 2026-02-12 20:49:43 +00:00
488217923f Update favicon 2026-02-12 20:12:36 +00:00
6232277de1 Re-introduce visible SVG overflow. Add electrification type to electrificationChange component.
Begin route 0230 (Swindon Jn - Standish Jn
2026-02-12 16:28:13 +00:00
cff62fa343 Remove 'checked' values from incomplete maps 2026-02-11 21:03:55 +00:00
f359938d78 Standardize junction abbreviation to Jn 2026-02-11 21:02:14 +00:00
7e68192312 Standardise & styles and improve performance on small displays.
Add inter-route linking from Junctions.
2026-02-11 20:58:01 +00:00
e94b0e811a Adjust sizes on manifest icons 2026-02-11 19:17:43 +00:00
04f6a28100 Provide new 'short_name' OB Maps to better fit on Android app drawer.
Update theme colour to accent colour to provide thematic separation of notification bar and header bar when running full screen on android.
2026-02-11 19:03:49 +00:00
54e3483a39 Include tabler icons and switch buttons to use icons 2026-02-11 19:01:24 +00:00
117d1f752e Switch to new resized logo with text 2026-02-11 18:37:10 +00:00
1207edf12b Add placeholders for next routes 2026-02-11 10:57:01 +00:00
b3d9eb6f33 Move to 'updated' and 'checked' dates, rather than 'created' 'checked & updated' 2026-02-11 10:52:15 +00:00
869a7296e8 Adjust margins/padding 2026-02-10 22:13:15 +00:00
7777671cbb Run prettify 2026-02-10 22:11:17 +00:00
353fd07b92 Fix: Lint warnings about non-interavtive content and onclick handlers. 2026-02-10 22:10:07 +00:00
a036b0ba63 Add apple-touch-icon <link> 2026-02-10 22:06:39 +00:00
0b53f8ed81 Add base for Wooton Basset Jn - Stoke Gifford Jn 2026-02-10 22:06:26 +00:00
ee92b52552 Add icons to webmanifest 2026-02-10 22:06:04 +00:00
4cc6140d86 Add typing 2026-02-10 22:05:49 +00:00
a458cc5e76 Ensure types are configured 2026-02-10 22:05:37 +00:00
cb9f826943 Adjust filtering to enable filtering by map contents 2026-02-10 22:05:25 +00:00
fa1da3686f Adjust 'home link' to use logo 2026-02-10 22:05:09 +00:00
d5270dc889 Add PIN logo SVG source 2026-02-10 20:55:07 +00:00
6366cb601b Add manifest icons. 2026-02-10 20:55:07 +00:00
fe1061875b Add new logo 2026-02-10 20:55:07 +00:00
699f2b760d Switch to Svelte resolve API 2026-02-10 20:54:31 +00:00
c8790e2aae Switch to new logo for favicon & provide ico fallback. 2026-02-10 20:54:30 +00:00
3fbffc7589 Adjust map boundaries.
Now: PAD-RDG, RDG-BRI

Was: PAD-Reading West Jn, Reading West Jn - BRI
2026-02-09 22:55:49 +00:00
84312abc75 Fix global CSS Import 2026-02-09 22:35:44 +00:00
c6fa8794e0 Add id field to RouteRow components 2026-02-09 22:24:42 +00:00
8e65fd396e Add manifest generator 2026-02-09 22:24:29 +00:00
8a05d8358f Update route information and enable direct linking. 2026-02-09 22:24:22 +00:00
a8c3117259 Add webmanifest and improve Vite build 2026-02-09 22:23:20 +00:00
2b2095604c Use standardised fonts 2026-02-09 22:23:01 +00:00
08f3d30e44 Update favicon 2026-02-07 23:11:00 +00:00
39511dd428 Improve 'branding' and mobile layout 2026-02-07 20:50:52 +00:00
cfcf651e41 Add favicon 2026-02-07 20:50:39 +00:00
cfa1d44dbc Run prettier 2026-02-07 00:38:56 +00:00
ca5e78e2b1 Add OwlBoard styling 2026-02-07 00:31:37 +00:00
6c10cb628e Fix linter warnings 2026-02-06 23:54:50 +00:00
46c897efcf Add OwlBoard logo 2026-02-06 23:30:44 +00:00
16dec07270 NPM Update 2026-02-06 23:30:39 +00:00
d53748f8ae Add homepage and make bridges look nice 2026-02-06 23:09:56 +00:00
d406db9d18 Add name for Henley Branch Junction 2026-02-06 12:04:32 +00:00
07e5e4023c Correct name of West Drayton East 2026-02-06 12:00:46 +00:00
cf0b1396ea Add Hayes East 2026-02-06 11:59:17 +00:00
39ca73b015 Correct Old Oak Common Junctions & add siteof Old Oak Cpmmmon 2026-02-06 11:53:50 +00:00
f38ba5a971 Update static/mapFiles/yaml/0001.yaml 2026-02-06 11:49:24 +00:00
379180df7d Hijack requests to self and redirect to local URL 2026-02-05 23:46:38 +00:00
7ae9951fda Update junction electrification states 2026-02-05 23:39:03 +00:00
57 changed files with 6968 additions and 1732 deletions

View File

@@ -3,7 +3,7 @@ run-name: ${{ gitea.actor }} is building and pushing
on: on:
create: create:
tags: "*" tags: '*'
env: env:
GITEA_DOMAIN: git.fjla.uk GITEA_DOMAIN: git.fjla.uk

2
.gitignore vendored
View File

@@ -2,6 +2,8 @@ node_modules
# Transpiled JSON # Transpiled JSON
/static/mapFiles/json/ /static/mapFiles/json/
/static/map-index.json
/src/lib/assets/route/*.json
# Output # Output
.output .output

View File

@@ -6,4 +6,4 @@ bun.lock
bun.lockb bun.lockb
# Miscellaneous # Miscellaneous
/static/ #/static/

8
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,8 @@
{
"yaml.schemas": {
"./static/mapFiles/yaml/mapFiles.schema.json": "static/mapFiles/yaml/*.yaml"
},
"yaml.format.enable": true,
"yaml.validate": true,
"yaml.schemaStore.enable": false
}

View File

@@ -6,7 +6,7 @@ RUN npm ci
COPY . . COPY . .
RUN node ./scripts/parse-maps.js RUN node ./scripts/parse-maps.js
RUN rm -r ./static/mapFiles/yaml RUN rm -r ./static/mapFiles
RUN npm run build RUN npm run build
RUN npm prune --production RUN npm prune --production

261
package-lock.json generated
View File

@@ -13,6 +13,7 @@
"@sveltejs/adapter-node": "^5.5.2", "@sveltejs/adapter-node": "^5.5.2",
"@sveltejs/kit": "^2.50.1", "@sveltejs/kit": "^2.50.1",
"@sveltejs/vite-plugin-svelte": "^6.2.4", "@sveltejs/vite-plugin-svelte": "^6.2.4",
"@tabler/icons-svelte": "^3.36.1",
"@types/node": "^22", "@types/node": "^22",
"eslint": "^9.39.2", "eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
@@ -29,9 +30,9 @@
} }
}, },
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
"integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -46,9 +47,9 @@
} }
}, },
"node_modules/@esbuild/android-arm": { "node_modules/@esbuild/android-arm": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
"integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -63,9 +64,9 @@
} }
}, },
"node_modules/@esbuild/android-arm64": { "node_modules/@esbuild/android-arm64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
"integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -80,9 +81,9 @@
} }
}, },
"node_modules/@esbuild/android-x64": { "node_modules/@esbuild/android-x64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
"integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -97,9 +98,9 @@
} }
}, },
"node_modules/@esbuild/darwin-arm64": { "node_modules/@esbuild/darwin-arm64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
"integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -114,9 +115,9 @@
} }
}, },
"node_modules/@esbuild/darwin-x64": { "node_modules/@esbuild/darwin-x64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
"integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -131,9 +132,9 @@
} }
}, },
"node_modules/@esbuild/freebsd-arm64": { "node_modules/@esbuild/freebsd-arm64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
"integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -148,9 +149,9 @@
} }
}, },
"node_modules/@esbuild/freebsd-x64": { "node_modules/@esbuild/freebsd-x64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
"integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -165,9 +166,9 @@
} }
}, },
"node_modules/@esbuild/linux-arm": { "node_modules/@esbuild/linux-arm": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
"integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -182,9 +183,9 @@
} }
}, },
"node_modules/@esbuild/linux-arm64": { "node_modules/@esbuild/linux-arm64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
"integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -199,9 +200,9 @@
} }
}, },
"node_modules/@esbuild/linux-ia32": { "node_modules/@esbuild/linux-ia32": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
"integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -216,9 +217,9 @@
} }
}, },
"node_modules/@esbuild/linux-loong64": { "node_modules/@esbuild/linux-loong64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
"integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
@@ -233,9 +234,9 @@
} }
}, },
"node_modules/@esbuild/linux-mips64el": { "node_modules/@esbuild/linux-mips64el": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
"integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
"cpu": [ "cpu": [
"mips64el" "mips64el"
], ],
@@ -250,9 +251,9 @@
} }
}, },
"node_modules/@esbuild/linux-ppc64": { "node_modules/@esbuild/linux-ppc64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
"integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -267,9 +268,9 @@
} }
}, },
"node_modules/@esbuild/linux-riscv64": { "node_modules/@esbuild/linux-riscv64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
"integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@@ -284,9 +285,9 @@
} }
}, },
"node_modules/@esbuild/linux-s390x": { "node_modules/@esbuild/linux-s390x": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
"integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@@ -301,9 +302,9 @@
} }
}, },
"node_modules/@esbuild/linux-x64": { "node_modules/@esbuild/linux-x64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
"integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -318,9 +319,9 @@
} }
}, },
"node_modules/@esbuild/netbsd-arm64": { "node_modules/@esbuild/netbsd-arm64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
"integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -335,9 +336,9 @@
} }
}, },
"node_modules/@esbuild/netbsd-x64": { "node_modules/@esbuild/netbsd-x64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
"integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -352,9 +353,9 @@
} }
}, },
"node_modules/@esbuild/openbsd-arm64": { "node_modules/@esbuild/openbsd-arm64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
"integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -369,9 +370,9 @@
} }
}, },
"node_modules/@esbuild/openbsd-x64": { "node_modules/@esbuild/openbsd-x64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
"integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -386,9 +387,9 @@
} }
}, },
"node_modules/@esbuild/openharmony-arm64": { "node_modules/@esbuild/openharmony-arm64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
"integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -403,9 +404,9 @@
} }
}, },
"node_modules/@esbuild/sunos-x64": { "node_modules/@esbuild/sunos-x64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
"integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -420,9 +421,9 @@
} }
}, },
"node_modules/@esbuild/win32-arm64": { "node_modules/@esbuild/win32-arm64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
"integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -437,9 +438,9 @@
} }
}, },
"node_modules/@esbuild/win32-ia32": { "node_modules/@esbuild/win32-ia32": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
"integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -454,9 +455,9 @@
} }
}, },
"node_modules/@esbuild/win32-x64": { "node_modules/@esbuild/win32-x64": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
"integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1344,6 +1345,34 @@
"vite": "^6.3.0 || ^7.0.0" "vite": "^6.3.0 || ^7.0.0"
} }
}, },
"node_modules/@tabler/icons": {
"version": "3.36.1",
"resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.36.1.tgz",
"integrity": "sha512-f4Jg3Fof/Vru5ioix/UO4GX+sdDsF9wQo47FbtvG+utIYYVQ/QVAC0QYgcBbAjQGfbdOh2CCf0BgiFOF9Ixtjw==",
"dev": true,
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/codecalm"
}
},
"node_modules/@tabler/icons-svelte": {
"version": "3.36.1",
"resolved": "https://registry.npmjs.org/@tabler/icons-svelte/-/icons-svelte-3.36.1.tgz",
"integrity": "sha512-f48RDkXJr7dMbbWHho81rR91QiPleHTlOwJUM5uFhTqo7dXH4mNZxJo3tksQNmlIauh7PqoS3i+RY7YlZxg5yg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@tabler/icons": ""
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/codecalm"
},
"peerDependencies": {
"svelte": ">=3 <6 || >=5.0.0-next.0"
}
},
"node_modules/@types/cookie": { "node_modules/@types/cookie": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
@@ -1366,9 +1395,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.19.8", "version": "22.19.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.8.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.9.tgz",
"integrity": "sha512-ebO/Yl+EAvVe8DnMfi+iaAyIqYdK0q/q0y0rw82INWEKJOBe6b/P3YWE8NW7oOlF/nXFNrHwhARrN/hdgDkraA==", "integrity": "sha512-PD03/U8g1F9T9MI+1OBisaIARhSzeidsUjQaf51fOxrfjeiKN9bLVO06lHuHYjxdnqLWJijJHfqXPSJri2EM2A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -1907,9 +1936,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/esbuild": { "node_modules/esbuild": {
"version": "0.27.2", "version": "0.27.3",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
"integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
@@ -1920,32 +1949,32 @@
"node": ">=18" "node": ">=18"
}, },
"optionalDependencies": { "optionalDependencies": {
"@esbuild/aix-ppc64": "0.27.2", "@esbuild/aix-ppc64": "0.27.3",
"@esbuild/android-arm": "0.27.2", "@esbuild/android-arm": "0.27.3",
"@esbuild/android-arm64": "0.27.2", "@esbuild/android-arm64": "0.27.3",
"@esbuild/android-x64": "0.27.2", "@esbuild/android-x64": "0.27.3",
"@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-arm64": "0.27.3",
"@esbuild/darwin-x64": "0.27.2", "@esbuild/darwin-x64": "0.27.3",
"@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.3",
"@esbuild/freebsd-x64": "0.27.2", "@esbuild/freebsd-x64": "0.27.3",
"@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm": "0.27.3",
"@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-arm64": "0.27.3",
"@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-ia32": "0.27.3",
"@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-loong64": "0.27.3",
"@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-mips64el": "0.27.3",
"@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-ppc64": "0.27.3",
"@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-riscv64": "0.27.3",
"@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-s390x": "0.27.3",
"@esbuild/linux-x64": "0.27.2", "@esbuild/linux-x64": "0.27.3",
"@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.3",
"@esbuild/netbsd-x64": "0.27.2", "@esbuild/netbsd-x64": "0.27.3",
"@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.3",
"@esbuild/openbsd-x64": "0.27.2", "@esbuild/openbsd-x64": "0.27.3",
"@esbuild/openharmony-arm64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.3",
"@esbuild/sunos-x64": "0.27.2", "@esbuild/sunos-x64": "0.27.3",
"@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-arm64": "0.27.3",
"@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-ia32": "0.27.3",
"@esbuild/win32-x64": "0.27.2" "@esbuild/win32-x64": "0.27.3"
} }
}, },
"node_modules/escape-string-regexp": { "node_modules/escape-string-regexp": {
@@ -3079,9 +3108,9 @@
} }
}, },
"node_modules/semver": { "node_modules/semver": {
"version": "7.7.3", "version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"bin": { "bin": {
@@ -3186,9 +3215,9 @@
} }
}, },
"node_modules/svelte": { "node_modules/svelte": {
"version": "5.49.2", "version": "5.50.0",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.49.2.tgz", "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.50.0.tgz",
"integrity": "sha512-PYLwnngYzyhKzqDlGVlCH4z+NVI8mC0/bTv15vw25CcdOhxENsOHIbQ36oj5DIf3oBazM+STbCAvaskpxtBmWA==", "integrity": "sha512-FR9kTLmX5i0oyeQ5j/+w8DuagIkQ7MWMuPpPVioW2zx9Dw77q+1ufLzF1IqNtcTXPRnIIio4PlasliVn43OnbQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

View File

@@ -19,6 +19,7 @@
"@sveltejs/adapter-node": "^5.5.2", "@sveltejs/adapter-node": "^5.5.2",
"@sveltejs/kit": "^2.50.1", "@sveltejs/kit": "^2.50.1",
"@sveltejs/vite-plugin-svelte": "^6.2.4", "@sveltejs/vite-plugin-svelte": "^6.2.4",
"@tabler/icons-svelte": "^3.36.1",
"@types/node": "^22", "@types/node": "^22",
"eslint": "^9.39.2", "eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",

View File

@@ -3,14 +3,58 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
const inputDir = './static/mapFiles/yaml'; const inputDir = './static/mapFiles/yaml';
const outputDir = './static/mapFiles/json'; const outputDir = './src/lib/assets/route';
const indexFile = './static/map-index.json';
const noiseRegex = /\s+(single line|junction|jn|junc|jct|gf|north|south|east|west)\.?$/i;
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true }); if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
const mapList = [];
fs.readdirSync(inputDir).forEach((file) => { fs.readdirSync(inputDir).forEach((file) => {
if (file.endsWith('.yaml')) { if (file.endsWith('.yaml')) {
const content = yaml.load(fs.readFileSync(path.join(inputDir, file), 'utf8')); const fullPath = path.join(inputDir, file);
const content = yaml.load(fs.readFileSync(fullPath, 'utf8'));
const fileName = file.replace('.yaml', '.json'); const fileName = file.replace('.yaml', '.json');
fs.writeFileSync(path.join(outputDir, fileName), JSON.stringify(content)); fs.writeFileSync(path.join(outputDir, fileName), JSON.stringify(content));
const contentSet = new Set();
if (Array.isArray(content.routeDetail)) {
content.routeDetail.forEach((item) => {
if ((item.type === 'junction' || item.type === 'station') && item.name) {
let cleanName = item.name;
// Run the replacement in a loop or twice to catch nested noise
// e.g., "Reading West Junction"
// Pass 1: "Reading West"
// Pass 2: "Reading"
let previousName;
do {
previousName = cleanName;
cleanName = cleanName.replace(noiseRegex, '').trim();
} while (cleanName !== previousName);
if (cleanName) {
contentSet.add(cleanName);
}
} }
}); });
}
mapList.push({
routeId: content.routeId || null,
routeStart: content.routeStart || null,
routeEnd: content.routeEnd || null,
updated: content.updated || null,
checked: content.checked || null,
contents: Array.from(contentSet)
});
}
});
fs.writeFileSync(indexFile, JSON.stringify(mapList));
console.log(`Generated ${mapList.length} map files and index.`);

View File

@@ -6,6 +6,14 @@
name="viewport" name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"
/> />
<link rel="manifest" href="/manifest.webmanifest" />
<meta name="title" content="OwlBoard Maps | Railway route schematics to assist with learning & refreshing routes" />
<meta name="description" content="Schematic route diagrams showing stations, junctions, crossings, bridges and more" />
<meta name="theme-color" content="#4fd1d1" />
<link rel="canonical" href="https://maps.owlboard.info" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://maps.owlboard.info" />
<title>OwlBoard Maps</title>
%sveltekit.head% %sveltekit.head%
</head> </head>
<body data-sveltekit-preload-data="hover"> <body data-sveltekit-preload-data="hover">

20
src/hooks.server.ts Normal file
View File

@@ -0,0 +1,20 @@
import { type HandleFetch } from '@sveltejs/kit';
export const handleFetch: HandleFetch = async ({ request, fetch }) => {
if (request.url.startsWith('https://maps.owlboard.info')) {
const newUrl = request.url.replace('https://maps.owlboard.info', 'http://localhost:3000');
const headers = new Headers(request.headers);
headers.set('host', 'maps.owlboard.info');
request = new Request(newUrl, {
method: request.method,
headers: headers,
body: request.body,
// @ts-expect-error - 'duplex' is needed for node fetch with bodies
duplex: 'half'
});
}
return fetch(request);
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

21
src/lib/assets/global.css Normal file
View File

@@ -0,0 +1,21 @@
/* FONTS */
@font-face {
font-family: 'urwgothic';
src:
url('/fonts/urwgothic/urwgothic.woff2') format('woff2'),
url('/font/urwgothic/urwgothic.woff') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'urwgothic';
src:
url('/fonts/urwgothic/urwgothicDemi.woff2') format('woff2'),
url('/font/urwgothic/urwgothicDemi.woff') format('woff');
font-weight: 900;
font-style: normal;
}
[id] {
scroll-margin-top: 100px;
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" viewBox="0 0 562.09 562.09"><defs><radialGradient xlink:href="#a" id="b" cx="256" cy="256" r="254.04" fx="256" fy="256" gradientTransform="translate(27 27)" gradientUnits="userSpaceOnUse"/><linearGradient id="a"><stop offset="0" stop-color="#2b343c"/><stop offset="1" stop-color="#404c55"/></linearGradient></defs><path fill="url(#b)" d="M694.32 283a411.32 393.92 0 0 1-410.68 393.92 411.32 393.92 0 0 1-411.96-392.69 411.32 393.92 0 0 1 409.4-395.14A411.32 393.92 0 0 1 694.3 280.54" transform="translate(-1.96 -1.96)"/><path fill="#4fd1d1" d="M281.04 88.47c-70.95 0-128.48 57.53-128.48 128.48 0 14.11 2.33 28.12 6.88 41.47l-.01-.01 7.36 17.3a124.7 124.7 0 0 0 5.25 9.1l.97 1.69 108.03 187.1 108.03-187.1.98-1.7a128.5 128.5 0 0 0 5.25-9.08l7.35-17.28v-.02a128.48 128.48 0 0 0 6.87-41.47C409.52 146 352 88.47 281.04 88.47Zm-84.01 49.27 2.52 2.38c7.74 7.29 16.97 12.23 28.03 15.02 4.64 1.17 4.62 1.17 8.1-.34 25.92-11.2 64.62-11.22 91.01-.03 7.27 3.08 25.1-4.12 36.09-14.59a30.17 30.17 0 0 1 2.43-2.18c.35 0 .48 6.13.2 9.02a49.7 49.7 0 0 1-9.29 24.82l-1.47 2.06.7.94c38.07 51.55-7.46 115.42-60.6 85-1.3-.74-2.45-1.24-2.57-1.11-.2.2-5.5 10.18-9.4 17.68-1.76 3.39-1.4 3.4-3.26-.14-4.77-9.12-9.38-17.7-9.5-17.7-.08 0-1.18.58-2.45 1.31-52.6 30.1-99.03-34.13-60.98-84.33.78-1.02 1.1-1.66.95-1.87-7.68-10.44-11.39-21.86-10.7-32.92zm44.85 39.23a35.62 35.62 0 0 0-15.48 3.92c-35.26 17.47-16.98 71.79 21.65 64.33a35.5 35.5 0 0 0 22.5-14.71c1.25-1.78 1.67-2.22 1.88-1.93.14.2 2.14 3.61 4.44 7.59 4.87 8.43 4.1 7.82 5.9 4.67a497.41 497.41 0 0 1 7.32-12.44c.14-.08.96.9 1.86 2.22 16.09 23.62 52.73 18.48 61.43-8.6 8.52-26.58-16.98-51.8-43.57-43.1-12.13 3.98-20.13 12.66-27.95 30.34l-.7 1.56-1.38-3.13c-8.8-20-22.71-30.89-37.9-30.72zm76.41 18.05c4.34-.12 8.8 1.43 12.33 4.95 11.83 11.83.35 31.55-16.05 27.58-8.43-2.04-13.82-10.73-12.05-19.4 1.65-8.07 8.54-12.92 15.77-13.13zm-75.7.04c8.54-.48 16.99 5.68 17.6 15.6.66 11.02-10.1 19.75-20.85 16.88-12.5-3.33-16.72-18.35-7.72-27.46a16.6 16.6 0 0 1 10.97-5.02zm6.22 4.93c-1.1.07-2.17.56-3 1.54-3.25 3.86 1.28 9.24 5.7 6.74a4.52 4.52 0 0 0 2.21-3.84c0-2.82-2.5-4.61-4.9-4.44zm63.97.06c-3.15.2-5.02 3.3-3.63 6.36 1.05 2.31 4.26 3.26 6.3 1.87 3.77-2.56 2.44-7.95-2.03-8.23a5.19 5.19 0 0 0-.64 0z"/></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" viewBox="0 0 508.09 508.09"><defs><radialGradient xlink:href="#a" id="b" cx="256" cy="256" r="254.04" fx="256" fy="256" gradientUnits="userSpaceOnUse"/><linearGradient id="a"><stop offset="0" stop-color="#2b343c"/><stop offset="1" stop-color="#404c55"/></linearGradient></defs><circle cx="256" cy="256" r="254.04" fill="url(#b)" transform="translate(-1.96 -1.96)"/><path fill="#4fd1d1" d="M254.04 61.47c-70.95 0-128.48 57.53-128.48 128.48 0 14.11 2.33 28.12 6.88 41.47l-.01-.01 7.36 17.3a124.7 124.7 0 0 0 5.25 9.1l.97 1.69 108.03 187.1 108.03-187.1.98-1.7a128.5 128.5 0 0 0 5.25-9.08l7.35-17.28v-.02a128.48 128.48 0 0 0 6.87-41.47C382.52 119 325 61.47 254.04 61.47Zm-84.01 49.27 2.52 2.38c7.74 7.29 16.97 12.23 28.03 15.02 4.64 1.17 4.62 1.17 8.1-.34 25.92-11.2 64.62-11.22 91.01-.03 7.27 3.08 25.1-4.12 36.09-14.59a30.17 30.17 0 0 1 2.43-2.18c.35 0 .48 6.13.2 9.02a49.7 49.7 0 0 1-9.29 24.82l-1.47 2.06.7.94c38.07 51.55-7.46 115.42-60.6 85-1.3-.74-2.45-1.24-2.57-1.11-.2.2-5.5 10.18-9.4 17.68-1.76 3.39-1.4 3.4-3.26-.14-4.77-9.12-9.38-17.7-9.5-17.7-.08 0-1.18.58-2.45 1.31-52.6 30.1-99.03-34.13-60.98-84.33.78-1.02 1.1-1.66.95-1.87-7.68-10.44-11.39-21.86-10.7-32.92zm44.85 39.23a35.62 35.62 0 0 0-15.48 3.92c-35.26 17.47-16.98 71.79 21.65 64.33a35.5 35.5 0 0 0 22.5-14.71c1.25-1.78 1.67-2.22 1.88-1.93.14.2 2.14 3.61 4.44 7.59 4.87 8.43 4.1 7.82 5.9 4.67a497.41 497.41 0 0 1 7.32-12.44c.14-.08.96.9 1.86 2.22 16.09 23.62 52.73 18.48 61.43-8.6 8.52-26.58-16.98-51.8-43.57-43.1-12.13 3.98-20.13 12.66-27.95 30.34l-.7 1.56-1.38-3.13c-8.8-20-22.71-30.89-37.9-30.72zm76.41 18.05c4.34-.12 8.8 1.43 12.33 4.95 11.83 11.83.35 31.55-16.05 27.58-8.43-2.04-13.82-10.73-12.05-19.4 1.65-8.07 8.54-12.92 15.77-13.13zm-75.7.04c8.54-.48 16.99 5.68 17.6 15.6.66 11.02-10.1 19.75-20.85 16.88-12.5-3.33-16.72-18.35-7.72-27.46a16.6 16.6 0 0 1 10.97-5.02zm6.22 4.93c-1.1.07-2.17.56-3 1.54-3.25 3.86 1.28 9.24 5.7 6.74a4.52 4.52 0 0 0 2.21-3.84c0-2.82-2.5-4.61-4.9-4.44zm63.97.06c-3.15.2-5.02 3.3-3.63 6.36 1.05 2.31 4.26 3.26 6.3 1.87 3.77-2.56 2.44-7.95-2.03-8.23a5.19 5.19 0 0 0-.64 0z"/></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1,23 +1,34 @@
<script lang="ts"> <script lang="ts">
import { components } from '$lib/mapRegistry'; import { components } from '$lib/mapRegistry';
import type { ElecType } from '$lib/railStyles';
import { IconArrowNarrowRight } from '@tabler/icons-svelte';
export let feature: any; // Raw Object type featureType = "station" | "junction" | "crossovers" | "siteof" | "bridge" | "minorBridge" | "crossover" | "crossing" | "loop" | "loops" | "signallerChange" | "electrificationChange" | "default" | "tunnel";
export let activeElec: string; // Active Electrification Type export let feature: {name: string; type: featureType; goto?: string; entryPoint?: string; miles: number; chains: number; description?: string}; // Raw Object
export let activeElec: ElecType; // Active Electrification Type
export let reversed: boolean = false; export let reversed: boolean = false;
$: Icon = components[feature.type] || components.default; $: Icon = components[feature.type] || components.default;
// Linking Logic
$: isLinkable = !!(feature.goto && feature.entryPoint);
$: href = `/map/${feature.goto}#${feature.entryPoint}`;
const slugify = (str?: string) =>
str?.toLocaleLowerCase().trim().replace(/\s+/g, '-') ?? 'unknown';
</script> </script>
<div class="row-container"> <div class="row-container" id={slugify(feature.name)}>
<div class="mileage-col"> <div class="mileage-col">
<span class="miles">{feature.miles + 'm' || ''}</span> <span class="miles">{feature.miles + 'm' || ''}</span>
<span class="chains">{feature.chains + 'ch' || ''}</span> <span class="chains">{feature.chains + 'ch' || ''}</span>
</div> </div>
<div class="icon-col"> <div class="icon-col">
<svelte:component this={Icon} {feature} {activeElec} {reversed} /> <svelte:component this={Icon} feature={feature as any} {activeElec} {reversed} />
</div> </div>
<svelte:element this={isLinkable ? 'a' : 'div'} {...(isLinkable ? { href } : {})} class="link-wrapper">
<div class="label-col"> <div class="label-col">
{#if feature.name} {#if feature.name}
<div class="feature-name">{feature.name}</div> <div class="feature-name">{feature.name}</div>
@@ -26,18 +37,31 @@
<div class="feature-desc">{feature.description}</div> <div class="feature-desc">{feature.description}</div>
{/if} {/if}
</div> </div>
{#if isLinkable}
<div class="link-indicator">
<IconArrowNarrowRight />
</div>
{/if}
</svelte:element>
</div> </div>
<style> <style>
a {
cursor: pointer;
text-decoration: none;
}
.row-container { .row-container {
display: grid; display: grid;
/* Balanced columns: 1fr on both sides keeps the 64px icon in the dead center */
grid-template-columns: 3.5rem 64px 1fr; grid-template-columns: 3.5rem 64px 1fr;
width: 100%; width: 100%;
height: 64px; height: 64px;
max-height: 64px;
align-items: center; align-items: center;
margin: 0; margin: 0;
padding: 0; padding: 0;
overflow: hidden;
} }
.mileage-col { .mileage-col {
@@ -47,7 +71,7 @@
padding-right: 12px; padding-right: 12px;
font-family: 'Courier New', Courier, monospace; font-family: 'Courier New', Courier, monospace;
font-size: 0.85rem; font-size: 0.85rem;
color: #64748b; /* Adjusted slightly for contrast */ color: #64748b;
} }
.miles { .miles {
@@ -59,13 +83,46 @@
font-size: 0.7rem; font-size: 0.7rem;
} }
.icon-col { .icon-col {
width: 64px; width: 64px;
height: 64px; height: 64px;
/* Ensure the icon itself is centered if the SVG is smaller than 64px */
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
overflow: visible;
}
.link-wrapper {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 100%;
height: 100%;
text-decoration: none;
color: inherit;
padding: 0;
box-sizing: border-box;
}
.link-indicator {
display: flex;
align-items: center;
margin-left: 5px;
margin-right: 8px;
flex-shrink: 0;
color: #e1ebeb;
background-color: #3c6f79;
padding: 2px 2px;
border-radius: 999px;
transition: all 0.3s ease;
}
.link-wrapper:hover .link-indicator {
background-color: #404c55;
transform: rotate(-45deg);
} }
.label-col { .label-col {
@@ -73,20 +130,16 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
/* FIX: Allow children to manage their own wrapping */
overflow: hidden; overflow: hidden;
/* min-width: 0 is critical for flex children to allow truncation */
min-width: 0; min-width: 0;
} }
.feature-name { .feature-name {
font-weight: 700; font-weight: 700;
font-family: sans-serif;
color: #1e293b; color: #1e293b;
font-size: 0.8rem; font-size: 0.8rem;
text-transform: capitalize; text-transform: capitalize;
/* Allow the title to wrap naturally onto multiple lines */
white-space: normal; white-space: normal;
line-height: 1.2; line-height: 1.2;
margin-bottom: 2px; margin-bottom: 2px;
@@ -95,12 +148,11 @@
.feature-desc { .feature-desc {
display: -webkit-box; display: -webkit-box;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
font-family: sans-serif;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
line-clamp: 2;
overflow: hidden; overflow: hidden;
/* Firefox Fix: Ensure white-space is normal here too */
white-space: normal; white-space: normal;
line-height: 1.2rem; line-height: 1.2rem;
max-height: 2.4rem; max-height: 2.4rem;
font-size: 0.75rem; font-size: 0.75rem;
@@ -108,21 +160,26 @@
word-break: break-word; word-break: break-word;
} }
/* Tablet and Desktop scaling */ @media (max-width: 320px) {
.feature-desc {
display: none;
}
}
@media (min-width: 480px) { @media (min-width: 480px) {
.feature-name { .feature-name {
font-size: 1rem; /* The larger title you requested */ font-size: 1rem;
margin-bottom: 4px; margin-bottom: 4px;
} }
.feature-desc { .feature-desc {
font-size: 0.85rem; /* Slightly larger desc to match */ font-size: 0.85rem;
line-height: 1.3rem; line-height: 1.3rem;
max-height: 2.6rem; max-height: 2.6rem;
} }
.label-col { .label-col {
padding-left: 24px; /* More "breathing room" on big screens */ padding-left: 24px;
} }
} }
</style> </style>

View File

@@ -1,80 +1,207 @@
<script lang="ts"> <script lang="ts">
import BaseTrack from '$lib/components/mapIcons/BaseTrack.svelte'; import BaseTrack from '$lib/components/mapIcons/BaseTrack.svelte';
import type { ElecType } from '$lib/railStyles';
export let feature: { export let feature: {
position: 'over' | 'under'; position: 'over' | 'under';
category: 'rail' | 'footbridge' | 'aroad' | 'minorRoad' | 'motorway' | 'waterway'; category:
| 'rail'
| 'stream'
| 'foot'
| 'aroad'
| 'minorRoad'
| 'motorway'
| 'waterway'
| 'pipeline';
roadName?: string; roadName?: string;
}; };
export let activeElec: ElecType;
export let activeElec: string;
const bridgeStyles = { const bridgeStyles = {
rail: { color: '#ffffff', stroke: '#000000', width: 20, text: '#000000' }, rail: {
footbridge: { color: '#ffffff', stroke: '#475569', width: 10, text: '#475569' }, stroke: '#0f172a',
aroad: { color: '#00703c', stroke: '#004d29', width: 24, text: '#FFEB3B' }, width: 22,
minorRoad: { color: '#ffffff', stroke: '#64748b', width: 20, text: '#334155' }, bg: '#334155',
motorway: { color: '#005da1', stroke: '#003e6b', width: 32, text: '#ffffff' }, text: '#ffffff',
waterway: { color: '#bae6fd', stroke: '#0369a1', width: 24, text: '#075985' }, textBg: '#000000',
pipeline: { color: '#334155', stroke: '#1e293b', width: 4, text: '#334155' } textBorder: '',
type: 'rail'
},
foot: {
stroke: '#475569',
width: 10,
bg: '#94a3b8',
text: '#ffffff',
textBg: '#000000',
textBorder: '',
type: 'path'
},
aroad: {
stroke: '#065f46',
width: 32,
bg: '#059669',
text: '#ffd200',
textBg: '#00703c',
textBorder: '#ffd200',
type: 'road'
},
minorRoad: {
stroke: '#334155',
width: 24,
bg: '#475569',
text: '#000000',
textBg: '#ffffff',
textBorder: '#000000',
type: 'road'
},
motorway: {
stroke: '#1e40af',
width: 40,
bg: '#3b82f6',
text: '#ffffff',
textBg: '#007ac1',
textBorder: '#ffffff',
type: 'road'
},
waterway: {
stroke: '#0369a1',
width: 32,
bg: '#bae6fd',
text: '#075985',
textBg: '#000000',
textBorder: '',
type: 'water'
},
stream: {
stroke: '#0369a1',
width: 4,
bg: '#bae6fd',
text: '#075985',
textBg: '#000000',
textBorder: '',
type: 'water'
},
pipeline: {
stroke: '#1e293b',
width: 4,
bg: '#1e293b',
text: '#ffffff',
textBg: '#000000',
textBorder: '',
type: 'path'
}
}; };
$: s = bridgeStyles[feature.category] || bridgeStyles.minorRoad; $: s = bridgeStyles[feature.category] || bridgeStyles.minorRoad;
$: isOver = feature.position === 'over'; $: isOver = feature.position === 'over';
$: topY = 32 - s.width / 2; $: xLeft = 6;
$: bottomY = 32 + s.width / 2; $: xRight = 58;
$: topPath = `M 0 ${topY} Q 8 ${topY} 8 ${topY - 4} M 56 ${topY - 4} Q 56 ${topY} 64 ${topY}`; const yTop = 16;
$: bottomPath = `M 0 ${bottomY} Q 8 ${bottomY} 8 ${bottomY + 4} M 56 ${bottomY + 4} Q 56 ${bottomY} 64 ${bottomY}`; const yBottom = 48;
$: bodyPath = `M 0 ${topY} L 64 ${topY} L 64 ${bottomY} L 0 ${bottomY} Z`;
</script> </script>
<svg viewBox="0 0 64 64" width="64" height="64" class="bridge"> <svg viewBox="0 0 64 64" width="64" height="64" class="bridge">
{#if !isOver} {#if isOver}
<path d={bodyPath} fill={s.color} />
<path d={`M 0 ${topY} L 64 ${topY}`} stroke={s.stroke} stroke-width="2" />
<path d={`M 0 ${bottomY} L 64 ${bottomY}`} stroke={s.stroke} stroke-width="2" />
<BaseTrack {activeElec} height={64} /> <BaseTrack {activeElec} height={64} />
<!-- White edge, to provide separation between abutment line and bridge content -->
<rect x="0" y={32 - s.width / 2 - 4} width="64" height={s.width + 8} fill="white" />
<rect x="6" y={32 - s.width / 2} width="52" height={s.width} fill={s.bg} />
{#if s.type === 'road'}
<line
x1={xLeft}
y1="32"
x2={xRight}
y2="32"
stroke="white"
stroke-width="1.5"
stroke-dasharray="4 3"
opacity="0.6"
/>
{:else if s.type === 'rail'}
<line x1={xLeft} y1="28" x2={xRight} y2="28" stroke="#cbd5e1" stroke-width="1.5" />
<line x1={xLeft} y1="36" x2={xRight} y2="36" stroke="#cbd5e1" stroke-width="1.5" />
{/if}
<g fill="none" stroke={s.stroke} stroke-width="2.5" stroke-linecap="square">
<path
d={`M 20 ${32 - s.width / 2 - 6} L 20 ${32 - s.width / 2 - 4} L 44 ${32 - s.width / 2 - 4} L 44 ${32 - s.width / 2 - 6}`}
/>
<path
d={`M 20 ${32 + s.width / 2 + 6} L 20 ${32 + s.width / 2 + 4} L 44 ${32 + s.width / 2 + 4} L 44 ${32 + s.width / 2 + 6}`}
/>
</g>
{:else} {:else}
<BaseTrack {activeElec} height={64} /> <rect x="6" y={32 - s.width / 2} width="52" height={s.width} fill={s.bg} />
<path d={bodyPath} fill="white" /> <path d={bodyPath} fill={s.color} /> {#if s.type === 'road'}
<line
x1={xLeft}
y1="32"
x2={xRight}
y2="32"
stroke="white"
stroke-width="1.5"
stroke-dasharray="4 3"
opacity="0.6"
/>
{:else if s.type === 'rail'}
<line x1={xLeft} y1="28" x2={xRight} y2="28" stroke="#cbd5e1" stroke-width="1.5" />
<line x1={xLeft} y1="36" x2={xRight} y2="36" stroke="#cbd5e1" stroke-width="1.5" />
{/if}
<g fill="none" stroke={s.stroke} stroke-width="2.5" stroke-linecap="round"> <rect x="26" y={yTop} width="12" height="64" fill="white" />
<path d={`M 0 ${topY} L 64 ${topY}`} /> <g stroke={s.stroke} stroke-width="2.5" fill="none" stroke-linecap="square">
<path d={`M 0 ${bottomY} L 64 ${bottomY}`} /> <path
<path d={`M 0 ${topY} Q 4 ${topY} 4 ${topY - 6}`} /> d={`M 24 ${32 - s.width / 2 - 5} L 25 ${32 - s.width / 2 - 5} L 25 ${32 - s.width / 2 + s.width + 5} L 24 ${32 - s.width / 2 + s.width + 5}`}
<path d={`M 64 ${topY} Q 60 ${topY} 60 ${topY - 6}`} /> />
<path d={`M 0 ${bottomY} Q 4 ${bottomY} 4 ${bottomY + 6}`} />
<path d={`M 64 ${bottomY} Q 60 ${bottomY} 60 ${bottomY + 6}`} /> <path
d={`M 40 ${32 - s.width / 2 - 5} L 39 ${32 - s.width / 2 - 5} L 39 ${32 - s.width / 2 + s.width + 5} L 40 ${32 - s.width / 2 + s.width + 5}`}
/>
</g> </g>
<BaseTrack {activeElec} height={64} />
{/if}
{#if feature.roadName} {#if feature.roadName}
<g class="label-group">
<rect
x={32 - feature.roadName.length * 3 - 4}
y="27"
width={feature.roadName.length * 6 + 8}
height="10"
stroke={s.textBorder || s.textBg}
stroke-width="1"
fill={s.textBg}
rx="3"
ry="4"
/>
<text <text
x="32" x="32"
y="32" y="32"
text-anchor="middle" text-anchor="middle"
dominant-baseline="central" dominant-baseline="central"
fill={s.text} fill={s.text}
font-family="sans-serif" font-family="ui-monospace, monospace"
font-weight="bold" font-weight="900"
font-size={s.width > 20 ? '10' : '8'} font-size="8"
style="pointer-events: none; text-transform: uppercase; letter-spacing: 0.5px;"
> >
{feature.roadName} {feature.roadName}
</text> </text>
{/if} </g>
{/if} {/if}
</svg> </svg>
<style> <style>
svg {
display: block;
overflow: visible;
}
text { text {
user-select: none; text-transform: uppercase;
letter-spacing: 0.5px;
pointer-events: none;
} }
</style> </style>

View File

@@ -9,10 +9,9 @@
$: type = feature.kind.toLowerCase(); $: type = feature.kind.toLowerCase();
$: isFoot = type === 'foot'; $: isFoot = type === 'foot';
$: filterCategory = isFoot ? 'foot' : type === 'uwc' ? 'uwc' : 'level-crossing';
</script> </script>
<svg viewBox="0 0 64 64" width="64" height="64" class={filterCategory}> <svg viewBox="0 0 64 64" width="64" height="64">
<BaseTrack {activeElec} height={64} /> <BaseTrack {activeElec} height={64} />
{#if type === 'foot'} {#if type === 'foot'}

View File

@@ -1,13 +1,14 @@
<script lang="ts"> <script lang="ts">
import { getElecColour } from '$lib/railStyles'; import { getElecColour } from '$lib/railStyles';
import type { ElecType } from '$lib/railStyles';
export let feature: { export let feature: {
from: { from: {
elec: string; elec: ElecType;
eco?: string; eco?: string;
}; };
to: { to: {
elec: string; elec: ElecType;
eco?: string; eco?: string;
}; };
}; };
@@ -36,13 +37,13 @@
<g font-family="sans-serif" font-size="10" font-weight="800" text-anchor="start"> <g font-family="sans-serif" font-size="10" font-weight="800" text-anchor="start">
{#if showFromEco} {#if showFromEco}
<text x="75" y="24" fill="#b91c1c" style="text-transform: uppercase;"> <text x="75" y="24" fill="#b91c1c" style="text-transform: uppercase;">
ECO: {feature.from.eco} ECO: {feature.from.eco} | Type: {feature.from.elec}
</text> </text>
{/if} {/if}
{#if showToEco} {#if showToEco}
<text x="75" y="48" fill="#b91c1c" style="text-transform: uppercase;"> <text x="75" y="48" fill="#b91c1c" style="text-transform: uppercase;">
ECO: {feature.to.eco} ECO: {feature.to.eco} | Type: {feature.to.elec}
</text> </text>
{/if} {/if}
</g> </g>

View File

@@ -6,15 +6,21 @@
direction: 'up' | 'down'; direction: 'up' | 'down';
diverges: 'left' | 'right' | 'both'; diverges: 'left' | 'right' | 'both';
elecBranch?: string; elecBranch?: string;
goto?: string;
entryPoint?: string;
}; };
export let activeElec: any; export let activeElec: any;
export let reversed: boolean = false; export let reversed: boolean = false;
$: isUp = feature.direction === 'up'; $: isUp = feature.direction === 'up';
$: visualUp = reversed ? !isUp : isUp; $: visualUp = reversed ? !isUp : isUp;
$: visualSide = (side: 'left' | 'right') => {
if (!reversed) return side;
return side === 'left' ? 'right' : 'left';
};
const getPath = (side: 'left' | 'right') => { const getPath = (side: 'left' | 'right') => {
const yStart = visualUp ? 64 : 0; const yStart = visualUp ? 64 : 8;
const yEnd = visualUp ? 8 : 56; const yEnd = visualUp ? 8 : 56;
const xEnd = side === 'right' ? 56 : 8; const xEnd = side === 'right' ? 56 : 8;
return `M 32 ${yStart} Q 32 32 ${xEnd} ${yEnd}`; return `M 32 ${yStart} Q 32 32 ${xEnd} ${yEnd}`;
@@ -22,14 +28,15 @@
$: paths = (() => { $: paths = (() => {
if (feature.diverges === 'both') return [getPath('left'), getPath('right')]; if (feature.diverges === 'both') return [getPath('left'), getPath('right')];
return [getPath(feature.diverges)]; const effectiveSide = visualSide(feature.diverges as 'left' | 'right');
return [getPath(effectiveSide)];
})(); })();
$: branchColour = getElecColour(feature.elecBranch || activeElec); $: branchColour = getElecColour(feature.elecBranch || activeElec);
</script> </script>
<svg viewBox="0 0 64 64" width="64" height="64" class="junction"> <svg viewBox="0 0 64 64" width="64" height="64" class="junction">
{#each paths as d} {#each paths as d (d)}
<path {d} fill="none" stroke={branchColour} stroke-width="5" stroke-linecap="round" /> <path {d} fill="none" stroke={branchColour} stroke-width="5" stroke-linecap="round" />
{/each} {/each}
<BaseTrack {activeElec} height={64} /> <BaseTrack {activeElec} height={64} />

View File

@@ -1,30 +1,31 @@
<script lang="ts"> <script lang="ts">
import { IconArrowNarrowRight } from '@tabler/icons-svelte';
export let feature: { export let feature: {
routeName: string; routeName: string;
routeId: string; routeId: string;
entryPoint: string;
}; };
</script> </script>
<div class="link-wrapper"> <div class="link-wrapper">
<a href="/map/{feature.routeId}" class="wide-button"> <a href="/map/{feature.routeId}#{feature.entryPoint}" class="wide-button">
<div class="accent-bar"></div>
<div class="content"> <div class="content">
<span class="sub-text">Continue to next map</span> <div class="header-row">
<span class="sub-text">Go to</span>
<span class="route-id-chip">{feature.routeId}</span>
</div>
<span class="main-text">{feature.routeName}</span> <span class="main-text">{feature.routeName}</span>
</div> </div>
<div class="icon">
<svg viewBox="0 0 24 24" width="20" height="20"> <div class="icon-circle">
<path d="M5 12h14M12 5l7 7-7 7" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/> <IconArrowNarrowRight />
</svg>
</div> </div>
</a> </a>
</div> </div>
<style> <style>
.link-wrapper { .link-wrapper {
position: relative; padding: 16px;
z-index: 100;
padding: 12px;
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
} }
@@ -33,61 +34,82 @@
display: flex; display: flex;
align-items: center; align-items: center;
background: #ffffff; background: #ffffff;
border: 2px solid #e2e8f0; border: 1px solid #e2e8f0;
border-radius: 12px; border-radius: 16px;
text-decoration: none; text-decoration: none;
overflow: hidden; padding: 12px 16px;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
.accent-bar {
width: 6px;
align-self: stretch;
background: #475569;
} }
.content { .content {
flex: 1; flex: 1;
padding: 12px 16px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 2px; gap: 4px;
min-width: 0;
}
.header-row {
display: flex;
align-items: center;
gap: 8px;
} }
.sub-text { .sub-text {
font-size: 10px; font-size: 0.65rem;
font-weight: 700; font-weight: 700;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.05em; letter-spacing: 0.05em;
color: #94a3b8; font-family: "urwgothic";
color: #64748b;
}
.route-id-chip {
font-size: 0.6rem;
font-weight: 800;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
background: #f1f5f9;
color: #475569;
padding: 2px 6px;
border-radius: 6px;
text-transform: uppercase;
} }
.main-text { .main-text {
font-size: 16px; font-family: "urwgothic";
font-size: 1rem;
font-weight: 800; font-weight: 800;
color: #1e293b; color: #0f172a;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.icon { .icon-circle {
padding-right: 20px; display: flex;
color: #cbd5e1; align-items: center;
transition: transform 0.2s ease; justify-content: center;
}
color: #e1ebeb;
background-color: #3c6f79;
padding: 4px 4px;
border-radius: 999px;
transition: all 0.3s ease;
}
.wide-button:hover { .wide-button:hover {
border-color: #94a3b8; border-color: #cbd5e1;
background: #f8fafc; background: #fdfdfd;
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.08);
} }
.wide-button:hover .icon { .wide-button:hover .icon-circle {
color: #475569; background-color: #404c55;
transform: translateX(4px); transform: rotate(-45deg);
} }
.wide-button:active { .wide-button:active {
transform: scale(0.98); transform: scale(0.98);
background: #f1f5f9;
} }
</style> </style>

View File

@@ -6,7 +6,14 @@
to: string; to: string;
}; };
export let reversed: boolean = false;
export let activeElec: any; export let activeElec: any;
$: effectiveFeature = {
from: reversed ? feature.to : feature.from,
to: reversed ? feature.from : feature.to
};
</script> </script>
<svg viewBox="0 0 64 64" width="64" height="64" style="overflow: visible;"> <svg viewBox="0 0 64 64" width="64" height="64" style="overflow: visible;">
@@ -24,10 +31,10 @@
<g font-family="sans-serif" font-weight="800" font-size="11"> <g font-family="sans-serif" font-weight="800" font-size="11">
<text x="70" y="24" fill="#4338ca" style="text-transform: uppercase;"> <text x="70" y="24" fill="#4338ca" style="text-transform: uppercase;">
{feature.from} {effectiveFeature.from}
</text> </text>
<text x="70" y="46" fill="#4338ca" style="text-transform: uppercase;"> <text x="70" y="46" fill="#4338ca" style="text-transform: uppercase;">
{feature.to} {effectiveFeature.to}
</text> </text>
</g> </g>
</svg> </svg>

View File

@@ -1,11 +1,18 @@
<script lang="ts"> <script lang="ts">
import BaseTrack from '$lib/components/mapIcons/BaseTrack.svelte'; import BaseTrack from '$lib/components/mapIcons/BaseTrack.svelte';
import type { ElecType } from '$lib/railStyles';
export let activeElec: string; export let activeElec: ElecType;
export let feature: any; export let feature: any;
export let reversed: boolean; export let reversed: boolean;
const pinSVG: string = `<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 256.96 385.14"><path fill="#4fd1d1" d="M128.48 0C57.52 0 0 57.52 0 128.48c0 14.1 2.32 28.12 6.88 41.47l-.01-.01 7.35 17.3c1.63 3.1 3.38 6.14 5.25 9.1l.98 1.68 108.03 187.12 108.03-187.12.98-1.69a128.5 128.5 0 0 0 5.25-9.09l7.34-17.28a128.48 128.48 0 0 0 6.88-41.48C256.96 57.52 199.44 0 128.48 0ZM44.47 49.27l2.52 2.37c7.74 7.3 16.96 12.23 28.02 15.02 4.65 1.17 4.62 1.17 8.11-.34 25.92-11.2 64.61-11.21 91-.03 7.28 3.09 25.1-4.12 36.1-14.58a30.17 30.17 0 0 1 2.43-2.18c.34 0 .47 6.13.19 9.02a49.7 49.7 0 0 1-9.28 24.82l-1.48 2.06.7.94c38.07 51.55-7.45 115.42-60.6 85-1.3-.74-2.45-1.24-2.56-1.12-.2.2-5.5 10.18-9.4 17.68-1.77 3.4-1.41 3.4-3.26-.13-4.77-9.13-9.38-17.71-9.5-17.71-.09 0-1.19.6-2.45 1.32-52.6 30.1-99.04-34.14-60.98-84.34.77-1.02 1.1-1.66.94-1.86-7.67-10.45-11.38-21.86-10.7-32.92zM89.3 88.5a35.62 35.62 0 0 0-15.47 3.91c-35.27 17.48-16.99 71.8 21.65 64.34a35.5 35.5 0 0 0 22.49-14.72c1.26-1.78 1.68-2.21 1.88-1.93.15.2 2.15 3.62 4.44 7.6 4.88 8.42 4.11 7.82 5.91 4.67a554.85 554.85 0 0 1 7.31-12.45c.15-.07.97.91 1.86 2.23 16.1 23.61 52.74 18.48 61.43-8.6 8.53-26.59-16.97-51.8-43.57-43.1-12.12 3.98-20.12 12.66-27.95 30.34l-.69 1.56-1.38-3.14c-8.81-20-22.72-30.88-37.9-30.71zm76.42 18.05c4.34-.12 8.8 1.42 12.33 4.95 11.83 11.83.35 31.55-16.05 27.58-8.43-2.05-13.82-10.73-12.05-19.41 1.64-8.06 8.54-12.91 15.77-13.12zm-75.7.03c8.54-.47 16.98 5.68 17.59 15.6.67 11.03-10.1 19.76-20.84 16.89-12.5-3.34-16.73-18.36-7.72-27.46a16.6 16.6 0 0 1 10.97-5.03zm6.22 4.93c-1.1.08-2.18.56-3 1.54-3.25 3.87 1.28 9.25 5.69 6.75a4.52 4.52 0 0 0 2.22-3.85c0-2.81-2.5-4.6-4.91-4.44zm63.97.07c-3.15.2-5.02 3.3-3.64 6.36 1.05 2.3 4.27 3.26 6.3 1.87 3.77-2.56 2.44-7.96-2.02-8.23a5.19 5.19 0 0 0-.64 0z"/></svg>`;
</script> </script>
<!--
Check for 'feature.side' and place map pin on that side of the track.
Use 'reversed' to switch sides if the route is reversed.
-->
<svg viewBox="0 0 64 64" width="64" height="64" class="loops"> <svg viewBox="0 0 64 64" width="64" height="64" class="loops">
<BaseTrack {activeElec} height={64} /> <BaseTrack {activeElec} height={64} />
</svg> </svg>

View File

@@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import BaseTrack from "./BaseTrack.svelte"; import BaseTrack from './BaseTrack.svelte';
export let feature: { export let feature: {
tunnelType: "start" | "whole" | "end"; tunnelType: 'start' | 'whole' | 'end' | 'mid';
length: string; length: string;
}; };
@@ -10,10 +10,10 @@
export let reversed: boolean = false; export let reversed: boolean = false;
const portalColour = "#475569"; // Slate grey const portalColour = '#475569'; // Slate grey
$: effectiveType = (() => { $: effectiveType = (() => {
if (!reversed || feature.tunnelType === 'whole') return feature.tunnelType; if (!reversed || feature.tunnelType === 'whole' || feature.tunnelType === 'mid') return feature.tunnelType;
return feature.tunnelType === 'start' ? 'end' : 'start'; return feature.tunnelType === 'start' ? 'end' : 'start';
})(); })();
</script> </script>
@@ -25,16 +25,14 @@
{#if effectiveType === 'whole'} {#if effectiveType === 'whole'}
<path d="M 16 12 Q 32 24 48 12" /> <path d="M 16 12 Q 32 24 48 12" />
<path d="M 16 52 Q 32 40 48 52" /> <path d="M 16 52 Q 32 40 48 52" />
{:else if effectiveType === 'start'} {:else if effectiveType === 'start'}
<path d="M 16 12 Q 32 24 48 12" /> <path d="M 16 12 Q 32 24 48 12" />
{:else if effectiveType === 'end'} {:else if effectiveType === 'end'}
<path d="M 16 52 Q 32 40 48 52" /> <path d="M 16 52 Q 32 40 48 52" />
{/if} {/if}
</g> </g>
{#if feature.tunnelType === 'whole' && feature.length} {#if feature.length}
<rect x="12" y="26" width="40" height="12" fill="white" /> <rect x="12" y="26" width="40" height="12" fill="white" />
<text <text
x="32" x="32"
@@ -51,6 +49,11 @@
</svg> </svg>
<style> <style>
svg { display: block; overflow: visible; } svg {
.t-text { font-family: ui-monospace, monospace; } display: block;
overflow: visible;
}
.t-text {
font-family: ui-monospace, monospace;
}
</style> </style>

View File

@@ -16,6 +16,7 @@ export const components = {
crossovers: Crossover, crossovers: Crossover,
siteof: SiteOf, siteof: SiteOf,
bridge: Bridge, bridge: Bridge,
minorBridge: Bridge,
crossover: Crossover, crossover: Crossover,
crossing: Crossing, crossing: Crossing,
loop: Loop, loop: Loop,
@@ -23,5 +24,5 @@ export const components = {
signallerChange: SignallerChange, signallerChange: SignallerChange,
electrificationChange: ElectrificationChange, electrificationChange: ElectrificationChange,
default: BaseTrack, default: BaseTrack,
tunnel: Tunnel, tunnel: Tunnel
}; };

View File

@@ -1,11 +1,14 @@
<script lang="ts"> <script lang="ts">
import favicon from '$lib/assets/favicon.svg'; import favicon from '$lib/assets/round-logo.svg';
import appleLogo from '$lib/assets/apple-touch-icon.png';
import '$lib/assets/global.css';
let { children } = $props(); let { children } = $props();
</script> </script>
<svelte:head> <svelte:head>
<link rel="icon" href={favicon} /> <link rel="icon" href={favicon} />
<link rel="apple-touch-icon" href={appleLogo} />
</svelte:head> </svelte:head>
{@render children()} {@render children()}

View File

@@ -1,116 +1,315 @@
<script lang="ts"> <script lang="ts">
import RouteRow from '$lib/components/RouteRow.svelte'; import logo from '$lib/assets/round-logo-text.svg';
import type { PageData } from './$types';
import { resolve } from '$app/paths';
const dummyFeatures = [ let { data }: { data: PageData } = $props();
{ let searchTerm = $state('');
type: 'station',
miles: 0, let filteredMaps = $derived(
chains: 0, data.maps.filter((m) => {
name: 'Testington', const term = searchTerm.toLowerCase();
description: 'Terminus',
terminus: true, if (m.routeId.toString().includes(term)) return true;
elec: '25kvac'
}, return m.contents.some((location) => location.toLowerCase().includes(term));
{ })
type: 'station', );
miles: 0,
chains: 76, const vibrate = (pattern: number | number[] = 10) => {
name: 'Closeby', if (typeof window !== 'undefined' && window.navigator.vibrate) {
description: 'Temporarily closed', window.navigator.vibrate(pattern);
elec: '25kvac'
},
{
type: 'junction',
name: 'Test Junction',
diverges: 'right',
direction: 'down',
elec: '25kvac',
elecBranch: 'none',
miles: 1,
chains: 13
},
{
type: 'bridge',
position: 'over',
category: 'pipeline',
name: 'Waterway Bridge',
roadName: 'TfL (LU)',
elec: '25kvac',
miles: 1,
chains: 41
},
{
type: 'crossover',
name: 'Dolphin junction',
elec: '25kvac',
miles: 1,
chains: 42
},
{
type: 'junction',
name: 'Test Junction South',
description: 'To: Banbury West Junction & Birmingham',
diverges: 'left',
direction: 'down',
elec: '25kvac',
elecBranch: '650vdc',
miles: 1,
chains: 42
},
{
type: 'electrificationChange',
from: {
elec: '25kvac',
eco: 'Didcot'
},
to: {
elec: 'none'
} }
}, };
{
type: 'crossing', const isVerifiedRecently = (dateVal: string | null) => {
kind: 'ahb', if (!dateVal) return 'draft';
elec: '25kvac',
name: 'Swindon Lane', const checkedDate = new Date(dateVal).getTime();
description: 'Controlled by TVSC (Level Crossing WS)' const oneYearAgo = Date.now() - 365 * 24 * 60 * 60 * 1000;
},
{ return checkedDate > oneYearAgo ? 'verified' : 'stale';
type: 'loop', };
side: 'both',
elec: '25kvac', const formatDate = (dateVal: string | null) => {
elecLoop: 'none' if (!dateVal) return 'N/A';
},
{ const date = new Date(dateVal);
type: 'signallerChange',
from: 'TVSC - Didcot WS (SB)', // Check for invalid dates to avoid "NaN/NaN/NaN"
to: 'TVSC - Swindon WS (SN)', if (isNaN(date.getTime())) return 'Invalid Date';
elec: '25kvac'
}, const d = date.getDate().toString().padStart(2, '0');
{ const m = (date.getMonth() + 1).toString().padStart(2, '0');
type: 'station', const y = date.getFullYear().toString().slice(-2);
miles: 4,
chains: 13, return `${d}/${m}/${y}`;
name: 'Powerless', };
description: "Doesn't exist",
elec: '750vdc'
}
];
</script> </script>
<h1>Welcome to SvelteKit</h1> <div class="page-wrapper">
<p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p> <header class="main-header">
<div class="brand-container">
<img src={logo} alt="OwlBoard Logo" class="main-logo" />
</div>
</header>
<div class="map-container"> <div class="list-container">
{#each dummyFeatures as feature} <a href="https://owlboard.info" class="button-link">Go to OwlBoard Live Departures & PIS</a>
<RouteRow {feature} activeElec={feature.elec} />
<input type="text" bind:value={searchTerm} placeholder="Search Station/Jn" class="search-input" />
{#each filteredMaps as map (map.routeId)}
<a
href={resolve(`/map/${map.routeId.toString().padStart(4, '0')}`)}
class="card"
onclick={() => vibrate(10)}
>
<div class="card-top">
<span class="route-id">{map.routeId.toString().padStart(4, '0')}</span>
<span class="status-badge {isVerifiedRecently(map.checked)}">
{#if isVerifiedRecently(map.checked) === 'verified'}
Reviewed
{:else if isVerifiedRecently(map.checked) === 'stale'}
Needs review
{:else}
Draft
{/if}
</span>
</div>
<div class="card-body">
<div class="location origin">{map.routeStart}</div>
<div class="path-arrow">to</div>
<div class="location destination">{map.routeEnd}</div>
</div>
<div class="card-footer">
<span>Updated: {formatDate(map.updated)}</span>
{#if map.checked}
<span>• Checked: {formatDate(map.checked)}</span>
{/if}
</div>
</a>
{:else}
<div class="empty-state">No maps found.</div>
{/each} {/each}
</div> </div>
</div>
<style> <style>
.map-container { /* Mobile-First Base Styles */
width: 100%; :global(body) {
max-width: 600px; margin: 0;
background-color: #404c55;
background-image: radial-gradient(#2b343c, #404c55);
color: #0f172a;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.page-wrapper {
padding: 1rem;
max-width: 800px;
margin: 0 auto; margin: 0 auto;
font-family: sans-serif; }
.main-header {
margin-bottom: 1.5rem;
position: fixed;
height: 80px;
top: 0;
left: 0;
box-sizing: border-box;
width: 100%;
background: #3c6f79;
color: #ebebeb;
z-index: 100;
}
.brand-container {
padding: 15px;
display: flex;
width: 100%;
align-items: center;
box-sizing: border-box;
gap: 1rem;
}
.main-logo {
height: 52px;
width: auto;
display: block;
}
.search-input {
width: 100%;
max-width: 500px;
font-family: 'urwgothic';
margin: auto;
height: 40px;
padding: 0.8rem 1rem;
text-align: center;
border-radius: 30px;
border: none;
font-size: 1rem;
transition: all 0.3s ease;
box-sizing: border-box; /* Ensures padding doesn't break width */
outline: none;
}
.search-input:hover {
box-shadow: rgba(0, 0, 0, 0.46);
}
.list-container {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 90px 1rem 1rem 1rem;
isolation: isolate;
z-index: 1;
}
@media (max-width: 600px) {
.list-container {
padding-top: 90px;
gap: 0.75rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.card {
padding: 10px 12px;
margin: 0;
border-radius: 4px;
}
.location {
font-size: 1rem;
margin-bottom: 0.1rem;
font-weight: 500;
}
.path-arrow {
color: #fff;
font-size: 0.8rem;
margin: 0.1rem 0;
line-height: 0.75;
}
.card-footer {
font-size: 0.6rem;
line-height: 0.8rem;
padding-bottom: 2px;
}
}
.card {
display: block;
background: #3c6f79;
padding: 1.25rem;
border-radius: 35px;
text-decoration: none;
color: #4fd1d1;
text-shadow: 2px 1px 10px rgba(0, 0, 0, 0.29);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.482);
transition: transform 0.1s ease;
cursor: pointer;
}
.card:hover {
transform: scale(1.01);
z-index: 5;
}
.card:active {
transform: scale(0.99); /* Tactile feedback for mobile */
}
.card-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.75rem;
}
.route-id {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-weight: 800;
color: #fff;
background: #2d2d2d;
text-shadow: none;
padding: 0.2rem 0.5rem;
border-radius: 6px;
}
.card-body {
margin-bottom: 1rem;
}
.location {
font-family: 'urwgothic';
font-size: 1.23rem;
font-weight: 700;
}
.path-arrow {
color: #fff;
font-family: 'urwgothic';
font-size: 0.9rem;
margin: 0.2rem 0;
}
.card-footer {
font-size: 0.7rem;
color: #e2ebeb;
text-transform: uppercase;
font-weight: 600;
letter-spacing: 0.05em;
padding-top: 0;
}
.status-badge {
font-size: 0.65rem;
font-weight: 800;
text-shadow: none;
text-transform: uppercase;
padding: 0.3rem 0.6rem;
border-radius: 999px;
white-space: nowrap;
}
.verified {
background: #dcfce7;
color: #166534;
}
.stale {
background: #fef3c7;
color: #92400e;
border: 1px solid #fcd34d;
}
.draft {
background: #f1f5f9;
color: #475569;
}
.button-link {
text-decoration: none;
border: none;
background: #3c6f79;
max-width: 360px;
padding: 8px 25px;
margin: auto;
text-align: center;
border-radius: 25px;
width: auto;
color: #e2ebeb;
transition: 0.3s all ease;
font-weight: 620;
}
.button-link:hover {
background: #2b2b2b;
} }
</style> </style>

26
src/routes/+page.ts Normal file
View File

@@ -0,0 +1,26 @@
import type { PageLoad } from './$types';
export interface RouteMapIndex {
routeId: string | number;
routeStart: string;
routeEnd: string;
updated: string;
checked: string;
contents: string[];
}
export const load: PageLoad = async ({ fetch }) => {
const response = await fetch('map-index.json');
if (!response.ok) {
return { maps: [] };
}
const maps = await response.json();
return {
maps: maps.sort((a: any, b: any) => {
return Number(a.routeId) - Number(b.routeId);
}) as RouteMapIndex[]
};
};

View File

@@ -2,7 +2,10 @@ import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types'; import type { RequestHandler } from './$types';
export const GET: RequestHandler = () => { export const GET: RequestHandler = () => {
return json({ status: 'ok', uptime: process.uptime() }, { return json(
{ status: 'ok', uptime: process.uptime() },
{
status: 200 status: 200
}); }
);
}; };

View File

@@ -0,0 +1,45 @@
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import logo from '$lib/assets/round-logo.svg';
import maskableLogo from '$lib/assets/maps-logo-maskable.svg';
import appleLogo from '$lib/assets/apple-touch-icon.png';
export const prerender = true;
export const GET: RequestHandler = ({ url }) => {
const manifest = {
name: 'OwlBoard Maps',
short_name: 'OB Maps',
start_url: '/',
display: 'standalone',
theme_color: '#4fd1d1',
background_color: '#3d4952',
icons: [
{
src: logo,
sizes: '48x48 72x72 96x96 128x128 256x256 512x512 any',
type: 'image/svg+xml',
purpose: 'any'
},
{
src: maskableLogo,
sizes: '48x48 72x72 96x96 128x128 256x256 512x512 any',
type: 'image/svg+xml',
purpose: 'maskable'
},
{
src: appleLogo,
sizes: '180x180',
type: 'image/png',
purpose: 'any'
}
]
};
return json(manifest, {
headers: {
'Content-Type': 'application/manifest+json',
'Cache-Control': 'public, max-age=3600'
}
});
};

View File

@@ -2,14 +2,19 @@
import RouteRow from '$lib/components/RouteRow.svelte'; import RouteRow from '$lib/components/RouteRow.svelte';
import RouteEndLink from '$lib/components/mapIcons/RouteEndLink.svelte'; import RouteEndLink from '$lib/components/mapIcons/RouteEndLink.svelte';
import { slide } from 'svelte/transition'; import { slide } from 'svelte/transition';
import { resolve } from '$app/paths';
// data.route contains: routeName, routeId, elecStart, elecEnd, routeDetail[] import logo from '$lib/assets/round-logo.svg';
import { IconArrowsExchange, IconSettings } from '@tabler/icons-svelte';
// data.route contains: routeStart, routeEnd, routeId, elecStart, elecEnd, routeDetail[]
export let data; export let data;
let reversed = false; // Reverses Array, and passes value down to children let reversed = false; // Reverses Array, and passes value down to children
let visibleTypes = { let visibleTypes = {
station: true, station: true,
minorBridge: false,
bridge: true, bridge: true,
crossovers: true, crossovers: true,
loop: true, loop: true,
@@ -18,6 +23,7 @@
siteof: true, siteof: true,
junction: true, junction: true,
tunnel: true, tunnel: true,
crossing: true,
}; };
let showFilters = false; let showFilters = false;
@@ -59,23 +65,45 @@
<div class="map-layout"> <div class="map-layout">
<header class="top-nav"> <header class="top-nav">
<h1>{data.route.routeName}</h1> <div class="nav-cluster">
<span class="route-code">{data.route.routeId}</span> <a href={resolve('/')} aria-label="Home" class="home-link" title="Back to Index">
<img src={logo} alt="OwlBoard Logo" class="nav-logo" />
</a>
<div class="route-stack">
{#if data?.route}
<h1 class="primary-station">
{reversed ? data.route.routeEnd : data.route.routeStart}
</h1>
<span class="secondary-station">
<span class="route-stack-to">
to</span> {reversed ? data.route.routeStart : data.route.routeEnd}
</span>
{/if}
</div>
</div>
<div class="quick-actions"> <div class="quick-actions">
<button class="icon-btn" on:click={() => (reversed = !reversed)}> <button class="icon-btn" onclick={() => (reversed = !reversed)}>
{reversed ? 'UP' : 'DN'} <IconArrowsExchange />
</button> </button>
<button class="icon-btn" on:click={() => (showFilters = !showFilters)}> Settings </button> <button class="icon-btn" onclick={() => (showFilters = !showFilters)}> <IconSettings /> </button>
</div> </div>
</header> </header>
{#if showFilters} {#if showFilters}
<div class="backdrop" on:click={() => (showFilters = false)}></div> <div
class="backdrop"
role="button"
tabindex="0"
onclick={() => (showFilters = false)}
onkeydown={(e) => (e.key === 'Enter' || e.key === ' ') && (showFilters = false)}
></div>
<div class="filter-drawer" transition:slide> <div class="filter-drawer" transition:slide>
<div class="drawer-header"> <div class="drawer-header">
<h3>Visibility Filters</h3> <h3>Visibility Filters</h3>
<button class="close-icon" on:click={() => (showFilters = false)} aria-label="Close"> <button class="close-icon" onclick={() => (showFilters = false)} aria-label="Close">
<svg <svg
viewBox="0 0 24 24" viewBox="0 0 24 24"
width="20" width="20"
@@ -91,11 +119,11 @@
<div class="drawer-content"> <div class="drawer-content">
<div class="filter-flex"> <div class="filter-flex">
{#each Object.keys(visibleTypes) as type} {#each Object.keys(visibleTypes) as type (type)}
<button <button
class="filter-chip" class="filter-chip"
class:active={visibleTypes[type]} class:active={visibleTypes[type]}
on:click={() => toggleFilter(type)} onclick={() => toggleFilter(type)}
> >
{formatLabel(type)} {formatLabel(type)}
</button> </button>
@@ -108,7 +136,7 @@
<main class="map-spine"> <main class="map-spine">
<div class="container"> <div class="container">
{#each filteredFeatures as f, i (`${f.type}-${f.miles}-${f.chains}-${i}`)} {#each filteredFeatures as f, i (`${f.type}-${f.miles}-${f.chains}-${i}`)}
{#if (f.type === 'continues')} {#if f.type === 'continues'}
<RouteEndLink feature={f} /> <RouteEndLink feature={f} />
{:else} {:else}
<RouteRow feature={f} activeElec={f.activeElec} {reversed} /> <RouteRow feature={f} activeElec={f.activeElec} {reversed} />
@@ -117,46 +145,120 @@
</div> </div>
</main> </main>
</div> </div>
<style> <style>
.map-layout { .map-layout {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background: #ffffff; background: #ffffff;
min-height: 100vh; min-height: 100vh;
margin-top: 0;
padding-top: 0;
} }
.top-nav { .top-nav {
position: sticky; position: fixed;
width: 100%;
box-sizing: border-box;
top: 0; top: 0;
z-index: 100; height: 80px;
background: rgba(255, 255, 255, 0.9); left: 0;
backdrop-filter: blur(10px);
border-bottom: 1px solid #e2e8f0;
padding: 0.75rem 1rem;
display: flex; display: flex;
justify-content: space-between;
align-items: center; align-items: center;
justify-content: space-between;
padding: 0 0;
background: #3c6f79;
color: #e1ebeb;
gap: 1rem; gap: 1rem;
z-index: 10000;
} }
h1 { .nav-cluster {
flex: 1; display: flex;
align-items: center;
gap: 1rem;
min-width: 0; /* Prevents flex children from overflowing */
}
.home-link {
display: flex;
align-items: center;
justify-content: center;
width: 52px;
height: 52px;
padding-left: 0;
margin-left: 15px;
margin-right: 0;
padding-right: 0;
flex-shrink: 0;
transition: all 0.3s ease;
}
@media (max-width: 350px) {
.home-link {
width: 42px;
height: 42px;
}
}
.home-link:hover {
transform: translateY(-1px) scale(1.05);
filter: brightness(1.1);
}
.home-link:active {
transform: scale(0.95);
}
.route-stack {
display: flex;
font-family: "urwgothic";
flex-direction: column;
min-width: 0; min-width: 0;
margin: 0; margin-left: 0;
font-size: 1.1rem; }
.route-stack-to {
text-transform: lowercase;
}
.primary-station {
font-size: 1rem;
font-weight: 800; font-weight: 800;
line-height: 1.2; margin: 0;
color: #0f172a; white-space: nowrap;
letter-spacing: -0.02em; color: #cce9e9;
white-space: normal; overflow: hidden;
overflow-wrap: break-word; text-overflow: ellipsis;
}
.secondary-station {
font-size: 0.7rem;
color: #cce9e9;
text-transform: capitalize;
font-weight: 600;
}
.quick-actions {
display: flex;
gap: 0.5rem;
margin-right: 15px;
}
.map-spine {
padding-top: 72px;
}
@media (min-width: 536px) {
.primary-station {
font-size: 1.5rem;
}
.secondary-station {
font-size: 1rem;
}
} }
@media (min-width: 768px) { @media (min-width: 768px) {
.top-nav {
padding: 1rem 2rem;
}
h1 { h1 {
font-size: 1.5rem; font-size: 1.5rem;
letter-spacing: -0.03em; letter-spacing: -0.03em;
@@ -175,6 +277,7 @@
inset: 0; inset: 0;
background: rgba(0, 0, 0, 0.4); background: rgba(0, 0, 0, 0.4);
z-index: 150; z-index: 150;
transition: all 0.3s ease;
} }
.filter-drawer { .filter-drawer {
@@ -182,7 +285,7 @@
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
background: white; background: #3c6f79;
z-index: 200; z-index: 200;
border-radius: 20px 20px 0 0; border-radius: 20px 20px 0 0;
box-shadow: 0 -8px 20px rgba(0, 0, 0, 0.15); box-shadow: 0 -8px 20px rgba(0, 0, 0, 0.15);
@@ -195,17 +298,17 @@
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 1.25rem 1.5rem 0.75rem; padding: 1.25rem 1.5rem 0.75rem;
border-bottom: 1px solid #f1f5f9; border-bottom: 1px solid #e1ebeb;
} }
.drawer-header h3 { .drawer-header h3 {
margin: 0; margin: 0;
font-size: 1rem; font-size: 1rem;
color: #1e293b; color: #e1ebeb;
} }
.close-icon { .close-icon {
background: #f1f5f9; background: #404c55;
border: none; border: none;
border-radius: 50%; border-radius: 50%;
width: 32px; width: 32px;
@@ -214,7 +317,12 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
cursor: pointer; cursor: pointer;
color: #64748b; color: #e1ebeb;
transition: all 0.3s ease;
}
.close-icon:hover {
background: #2d2d2d;
} }
.drawer-content { .drawer-content {
@@ -231,15 +339,53 @@
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
border-radius: 999px; border-radius: 999px;
border: 1px solid #e2e8f0; border: 1px solid #e2e8f0;
background: #f8fafc; background: #ff6060;
font-size: 0.875rem; font-size: 0.875rem;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
} }
.filter-chip.active { .filter-chip.active {
background: #1e293b; background: #00725b;
color: white; color: white;
border-color: #1e293b; border-color: #1e293b;
} }
.quick-actions {
display: flex;
gap: 0.5rem;
flex-shrink: 0;
}
.icon-btn {
display: inline-flex;
align-items: center;
gap: 0.4rem;
border: none;
padding: 0.5rem 0.8rem;
border-radius: 12px;
background: #404c55;
color: #e1ebeb;
font-size: 0.75rem;
font-weight: 700;
cursor: pointer;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
text-transform: uppercase;
letter-spacing: 0.02em;
transition: all 0.3s ease;
}
@media (max-width: 390px) {
.icon-btn {
padding: 0.3rem 0.3rem;
}
}
.icon-btn:hover {
background: #2d2d2d;
}
.icon-btn:active {
transform: scale(0.96);
}
</style> </style>

View File

@@ -1,27 +1,30 @@
import type { PageLoad } from '/$types'; import type { PageLoad } from '/$types';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
export const load: PageLoad = async ({ params, fetch }) => { const mapDataFiles = import.meta.glob('$lib/assets/route/*.json', { query: '?json' });
export const load: PageLoad = async ({ params }) => {
const { slug } = params; const { slug } = params;
try { const path = `/src/lib/assets/route/${slug}.json`;
const res = await fetch(`/mapFiles/json/${slug}.json`); console.log(`Finding path: ${path}`);
if (!res.ok) { if (!(path in mapDataFiles)) {
throw error(404, { throw error(404, {
message: `Route ${slug} not found` message: `Route ${slug} not found`
}); });
} }
const rawData = await res.json(); try {
const rawData = await mapDataFiles[path]();
return { return {
route: rawData, route: rawData,
slug: slug slug: slug,
}; };
} catch (err) { } catch (err) {
console.error(`Error loading map ${slug}: `, err); console.error(`Error loading map ${slug}: `, err);
throw error(500, { throw error(500, {
message: `Failed to parse map data for ${slug}` message: `Failed to parse map data for ${slug}`
}); });

BIN
static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,5 +1,8 @@
routeName: Paddington - Reading West Jn routeStart: Paddington
routeId: 0001 routeEnd: Reading
routeId: "0001"
updated: 2026-02-09
checked: 2026-02-09
signallerStart: TVSC Paddington WS signallerStart: TVSC Paddington WS
signallerEnd: TVSC Reading WS signallerEnd: TVSC Reading WS
elecStart: elecStart:
@@ -49,7 +52,7 @@ routeDetail:
chains: 52 chains: 52
- type: crossovers - type: crossovers
name: Subway Junction name: Subway Jn
miles: 0 miles: 0
chains: 61 chains: 61
@@ -82,7 +85,7 @@ routeDetail:
chains: 18 chains: 18
- type: junction - type: junction
name: Westbourne Park Junction name: Westbourne Park Jn
diverges: left diverges: left
direction: up direction: up
description: to Crossrail Core Operating Section description: to Crossrail Core Operating Section
@@ -90,7 +93,7 @@ routeDetail:
chains: 21 chains: 21
- type: crossovers - type: crossovers
name: Portobello Junction name: Portobello Jn
miles: 1 miles: 1
chains: 33 chains: 33
@@ -117,8 +120,8 @@ routeDetail:
chains: 73 chains: 73
- type: junction - type: junction
name: Kensal Green South Junction name: Kensal Green East Jn
diverges: right diverges: both
direction: down direction: down
description: to Crossrail & North Pole Depots description: to Crossrail & North Pole Depots
miles: 2 miles: 2
@@ -141,16 +144,19 @@ routeDetail:
- type: junction - type: junction
name: Old Oak Common East name: Old Oak Common East
diverges: left diverges: left
direction: up direction: down
description: to Crossrail Depot description: to Crossrail Depot
miles: 2 miles: 2
chains: 62 chains: 62
- type: junction - type: siteof
name: Old Oak Common HS2 interchange
miles: 3
chains: 0
- type: siteof
name: Old Oak Common West name: Old Oak Common West
diverges: left description: Junction has been removed
direction: up
description: Out of Use for HS2 Works
miles: 3 miles: 3
chains: 20 chains: 20
@@ -169,10 +175,11 @@ routeDetail:
chains: 45 chains: 45
- type: junction - type: junction
name: Friars Junction name: Friars Jn
diverges: left diverges: left
direction: up direction: up
description: Out of Use for HS2 Works elecBranch: none
description: Out of Use for HS2 Works. Track temporarily disconnected.
miles: 3 miles: 3
chains: 53 chains: 53
@@ -198,7 +205,7 @@ routeDetail:
chains: 80 chains: 80
- type: junction - type: junction
name: Acton East Junction name: Acton East Jn
diverges: left diverges: left
direction: up direction: up
description: Up/Dn Poplar to Acton Wells Jn description: Up/Dn Poplar to Acton Wells Jn
@@ -303,9 +310,10 @@ routeDetail:
chains: 46 chains: 46
- type: junction - type: junction
name: West Ealing Junction name: West Ealing Jn
diverges: left diverges: left
direction: down direction: down
elecBranch: none
description: Dn/Up Greenford to Greenford & West Ealing Sidings description: Dn/Up Greenford to Greenford & West Ealing Sidings
miles: 6 miles: 6
chains: 54 chains: 54
@@ -318,9 +326,10 @@ routeDetail:
chains: 71 chains: 71
- type: junction - type: junction
name: Hanwell Junction name: Hanwell Jn
diverges: left diverges: left
direction: up direction: up
elecBranch: none
description: Dn/Up Loop to Greenford & West Ealing Sidings description: Dn/Up Loop to Greenford & West Ealing Sidings
miles: 7 miles: 7
chains: 19 chains: 19
@@ -363,6 +372,8 @@ routeDetail:
- type: loop - type: loop
position: left position: left
name: Hanwell Up & Dn Goods Loops name: Hanwell Up & Dn Goods Loops
description: Both on Up side
elecLoop: none
miles: 8 miles: 8
chains: 20 chains: 20
@@ -381,9 +392,10 @@ routeDetail:
chains: 46 chains: 46
- type: junction - type: junction
name: Southall East Junction name: Southall East Jn
diverges: right diverges: right
direction: up direction: up
elecBranch: none
description: Brentford Waste Terminal & Southall Depot description: Brentford Waste Terminal & Southall Depot
miles: 8 miles: 8
chains: 62 chains: 62
@@ -409,7 +421,7 @@ routeDetail:
chains: 53 chains: 53
- type: crossovers - type: crossovers
name: Southall West Junction name: Southall West Jn
description: Southall Sidings Diverge description: Southall Sidings Diverge
miles: 9 miles: 9
chains: 70 chains: 70
@@ -432,7 +444,7 @@ routeDetail:
- type: bridge - type: bridge
name: Yeading Brook name: Yeading Brook
position: under position: under
category: waterway category: stream
miles: 10 miles: 10
chains: 29 chains: 29
@@ -450,6 +462,11 @@ routeDetail:
miles: 10 miles: 10
chains: 32 chains: 32
- type: crossovers
name: Hayes East
miles: 10
chains: 45
- type: bridge - type: bridge
position: under position: under
category: waterway category: waterway
@@ -471,7 +488,7 @@ routeDetail:
chains: 77 chains: 77
- type: junction - type: junction
name: Heathrow Airport Junction name: Heathrow Airport Jn
diverges: both diverges: both
direction: down direction: down
miles: 11 miles: 11
@@ -487,7 +504,7 @@ routeDetail:
chains: 28 chains: 28
- type: crossovers - type: crossovers
name: Stockley Bridge Junction name: Stockley Bridge Jn
miles: 12 miles: 12
chains: 9 chains: 9
@@ -500,7 +517,7 @@ routeDetail:
chains: 22 chains: 22
- type: crossovers - type: crossovers
name: West Drayton East name: West Drayton East Jn
miles: 12 miles: 12
chains: 67 chains: 67
@@ -519,16 +536,17 @@ routeDetail:
- type: bridge - type: bridge
name: Station Road name: Station Road
positon: under position: under
category: minorRoad category: minorRoad
miles: 12 miles: 12
chains: 56 chains: 56
- type: junction - type: junction
name: West Drayton Junction name: West Drayton Jn
diverges: left diverges: left
direction: down direction: down
description: Colnbrook Freight (near Heathrow) description: Colnbrook Freight (near Heathrow)
elecBranch: none
miles: 13 miles: 13
chains: 31 chains: 31
@@ -568,7 +586,7 @@ routeDetail:
- type: bridge - type: bridge
name: Stream name: Stream
position: under position: under
category: waterway category: stream
miles: 14 miles: 14
chains: 10 chains: 10
@@ -616,7 +634,7 @@ routeDetail:
- type: bridge - type: bridge
name: Stream name: Stream
position: under position: under
category: waterway category: stream
miles: 15 miles: 15
chains: 61 chains: 61
@@ -624,6 +642,7 @@ routeDetail:
name: Langley GF name: Langley GF
diverges: left diverges: left
description: Langley Sidings description: Langley Sidings
elecBranch: none
direction: up direction: up
miles: 15 miles: 15
chains: 76 chains: 76
@@ -704,7 +723,7 @@ routeDetail:
chains: 42 chains: 42
- type: junction - type: junction
name: Windsor Branch Junction name: Windsor Branch Jn
diverges: right diverges: right
direction: down direction: down
description: Windsor Branch from platforms 1 & 2 Only description: Windsor Branch from platforms 1 & 2 Only
@@ -850,7 +869,7 @@ routeDetail:
- type: bridge - type: bridge
name: Stream name: Stream
position: under position: under
category: waterway category: stream
miles: 23 miles: 23
chains: 66 chains: 66
@@ -1027,9 +1046,11 @@ routeDetail:
chains: 1 chains: 1
- type: junction - type: junction
name: Henley Branch Jn
diverges: left diverges: left
direction: down direction: down
description: Henley-on-Thames from Platform 4 only description: Henley-on-Thames from Platform 4 only
elecBranch: none
miles: 31 miles: 31
chains: 4 chains: 4
@@ -1146,7 +1167,7 @@ routeDetail:
chains: 77 chains: 77
- type: crossovers - type: crossovers
name: Kennet Bridge Junction name: Kennet Bridge Jn
miles: 35 miles: 35
chains: 10 chains: 10
@@ -1159,10 +1180,11 @@ routeDetail:
chains: 28 chains: 28
- type: junction - type: junction
name: Reading New Junction name: Reading New Jn
diverges: right diverges: right
direction: up direction: up
description: to Southern Lines (Waterloo/Guildford) elecBranch: none
description: Up/Dn Reading Spur to Southern Lines (Waterloo/Guildford)
miles: 35 miles: 35
chains: 40 chains: 40
@@ -1170,13 +1192,15 @@ routeDetail:
position: under position: under
category: rail category: rail
name: Low Level Tunnel name: Low Level Tunnel
description: Reading Low Level line
miles: 35 miles: 35
chains: 40 chains: 40
- type: junction - type: junction
name: Reading East Junction name: Reading East Jn
diverges: left diverges: left
direction: up direction: up
elecBranch: none
description: to Southern Lines (Waterloo/Guildford) description: to Southern Lines (Waterloo/Guildford)
miles: 35 miles: 35
chains: 61 chains: 61
@@ -1193,43 +1217,7 @@ routeDetail:
miles: 35 miles: 35
chains: 78 chains: 78
- type: bridge
name: Caversham Road
position: under
category: minorRoad
miles: 35
chains: 11
- type: junction
diverges: right
direction: down
name: Westbury Line Junction
description: to Oxford Road Jn
miles: 36
chains: 17
- type: junction
diverges: right
direction: down
name: Caversham Road Junction
description: Reading Feeder Main/Relief diverge and pass under Reading Viaduct to Oxford Rd Jn
miles: 36
chains: 22
- type: crossovers
name: Reading High Level Junction
description: Down Reading Festival Connects to Down Main
miles: 36
chains: 47
- type: junction
diverges: right
direction: up
name: Reading West Junction
description: to Oxford Road Junction (From relief lines only)
miles: 37
chains: 17
- type: continues - type: continues
routeName: Reading West Jn - Bristol TM routeName: Reading - Bristol TM
routeId: "0002" routeId: '0002'
entryPoint: reading

View File

@@ -0,0 +1,634 @@
routeStart: Reading
routeEnd: Bristol TM
routeId: "0002"
updated: 2026-02-04
checked: 2026-03-01
signallerStart: TVSC Reading WS
signallerEnd: TVSC Temple Meads WS
elecStart:
elec: 25kvac
eco: Didcot (TVSC)
elecEnd:
elec: none
routeDetail:
- type: continues
routeName: Paddington - Reading
routeId: '0001'
entryPoint: reading
- type: station
name: Reading
miles: 35
chains: 78
- type: bridge
name: Caversham Road
position: under
category: minorRoad
miles: 35
chains: 11
- type: junction
diverges: right
direction: down
name: Westbury Line Jn
description: to Oxford Road Jn
goto: "0201"
entryPoint: "oxford-road-jn"
miles: 36
chains: 17
- type: junction
diverges: right
direction: down
name: Caversham Road Jn
description: Reading Feeder Main/Relief diverge and pass under Reading Viaduct to Oxford Rd Jn
goto: "0201"
entryPoint: "oxford-road-jn"
miles: 36
chains: 22
- type: signallerChange
from: TVSC Reading WS (T)
to: TVSC West Junction WS (T)
miles: 36
chains: 36
- type: crossovers
name: Reading High Level Jn
description: Down Reading Festival Connects to Down Main
miles: 36
chains: 47
- type: junction
diverges: right
direction: up
name: Reading West Jn
description: to Oxford Road Junction (From relief lines only)
goto: "0201"
entryPoint: "oxford-road-jn"
miles: 37
chains: 17
- type: junction
diverges: left
direction: up
name: Scours Lane Jn
description: Reading Up Passenger loop diverges
miles: 37
chains: 61
- type: crossovers
name: Tilehurst East Junction
miles: 38
chains: 40
- type: station
name: Tilehurst
miles: 38
chains: 52
- type: station
name: Pangbourne
description: Platforms on relief lines only
miles: 41
chains: 43
- type: siteof
name: White Cliffs of Pangbourne
miles: 41
chains: 60
- type: bridge
name: Basildon Skew Bridges
description: Two adjacent Bridges
roadName: A329
category: aroad
position: over
miles: 42
chains: 68
- type: bridge
name: Gatehampton Viaduct
position: under
category: waterway
description: River Thames
miles: 44
chains: 3
- type: station
name: Goring & Streatley
miles: 44
chains: 60
- type: bridge
name: Moulsford Viaduct
category: waterway
description: River Thames
position: under
miles: 47
chains: 30
- type: station
name: Cholsey
description: Cholsey & Wallingford Railway adjacent to platform 4 (UR)
miles: 48
chains: 37
- type: signallerChange
from: TVSC West Junction WS (T)
to: TVSC Didcot WS (SB)
miles: 50
chains: 0
- type: crossovers
name: Moreton Crossing
miles: 51
chains: 40
- type: crossovers
name: Didcot East Jn
description: Junction spans ~1 mile
miles: 51
chains: 71
- type: junction
name: Didcot East Jn
description: Up/Dn Didcot Avoiding to Oxforc
diverges: left
direction: down
elecBranch: none
miles: 52
chains: 66
- type: station
name: Didcot Parkway
description: Platform 5 - Oxford Only
miles: 53
chains: 10
- type: junction
name: Chester Line Jn
diverges: left
direction: down
description: Up/Dn Oxford to Oxford, Up relief ends
miles: 53
chains: 12
- type: bridge
name: Car Park Footbridge
category: foot
position: over
miles: 53
chains: 20
- type: junction
name: Foxhall Jn
diverges: left
description: Up/Dn Didcot West Curve to Oxford
direction: up
miles: 53
chains: 55
- type: loop
position: right
name: Down Steventon Goods Loop
miles: 55
chains: 60
- type: siteof
name: Steventon
description: Up Relief line diverges towards Didcot, two tracks towards Swindon
miles: 56
chains: 32
- type: crossing
kind: CCTV
name: Stocks Lane Level Crossing
description: CCTV by TVSC
miles: 56
chains: 58
- type: crossing
name: Causeway Level Crossing
kind: CCTV
description: CCTV by TVSC
miles: 56
chains: 72
- type: siteof
name: Wantage Road
description: Dn/Up Relief diverge towards Swindon
miles: 60
chains: 22
- type: crossing
kind: foot
name: Wantage Road Foot Crossing
miles: 60
chains: 58
- type: crossing
kind: uwc
name: Grove Level Crossing
miles: 61
chains: 37
- type: siteof
name: Challow
description: Up/Dn Relief diverge towards Didcot
miles: 64
chains: 0
- type: crossing
kind: foot
name: Hale Farm Foot Crossing
miles: 65
chains: 19
- type: signallerChange
from: TVSC Didcot WS (SB)
to: TVSC Swindon WS (SW)
miles: 66
chains: 0
- type: crossovers
name: Uffington
miles: 66
chains: 39
- type: crossing
kind: foot
name: Knighton Foot Crossing
miles: 69
chains: 12
- type: crossovers
name: Bourton
miles: 72
chains: 30
- type: junction
name: South Marston Jn
description: To South Maston Euroterminal
diverges: left
direction: down
elecBranch: none
miles: 74
chains: 48
- type: loop
name: Up Swindon Goods Loop
position: left
miles: 75
chains: 0
- type: junction
name: Highworth Junction
diverges: left
direction: up
elecBranch: none
description: to BMW Sidings
miles: 76
chains: 32
- type: loop
name: Down Swindon Goods Loop
description: Also, Swindon Down Yard
position: right
miles: 76
chains: 0
- type: junction
name: Cocklebury Sidings
diverges: left
direction: down
miles: 76
chains: 67
- type: station
name: Swindon
miles: 77
chains: 23
- type: junction
name: Swindon Jn
diverges: left
direction: down
description: Up/Dn Kemble towards Gloucester
goto: "0230"
entryPoint: swindon-jn
miles: 77
chains: 36
- type: crossovers
name: Rushey Platt Jn
description: Up Swindon relief diverges towards Swindon
miles: 78
chains: 36
- type: bridge
category: motorway
roadName: M4
position: over
miles: 80
chains: 8
- type: crossing
kind: msl
name: Upper Studley Foot Crossing
miles: 80
chains: 64
- type: crossing
name: Canalside 2 Foot Crossing
kind: foot
miles: 81
chains: 74
- type: junction
name: Wootton Bassett Jn
diverges: left
direction: down
description: Up/Dn Badminton to Bristol PW
goto: "0240"
entryPoint: wootton-bassett-jn
miles: 83
chains: 7
- type: junction
name: Wooton Bassett GF
diverges: right
direction: up
description: Access to Wootton Bassett Sidings
elecBranch: none
miles: 83
chains: 28
- type: siteof
name: Wootton Bassett Incline Signal Box
description: Disused
miles: 85
chains: 22
- type: crossing
kind: msl
name: Christian Malford Foot Crossing
miles: 88
chains: 79
- type: bridge
name: River Avon Viaduct
position: under
category: waterway
miles: 90
chains: 85
- type: electrificationChange
from:
elec: 25kvac
eco: Didcot
to:
elec: none
miles: 93
chains: 35
- type: station
name: Chippenham
miles: 93
chains: 76
- type: crossovers
name: Thingley Crossovers
miles: 95
chains: 32
- type: junction
name: Thingley Jn
diverges: right
direction: down
description: to Melksham & Trowbridge
goto: "0250"
entryPoint: thingley-jn
miles: 96
chains: 10
- type: signallerChange
from: TVSC Swindon WS (SW)
to: TVSC Bath WS (BL)
miles: 99
chains: 8
- type: tunnel
tunnelType: whole
name: Box Tunnel
length: 1mi 1452yd
description: 8 Emergency phones - Up side and Down entrance signal
miles: 100
chains: 40
- type: tunnel
tunnelType: whole
name: Middle Hill Tunnel
length: 0mi 196yd
miles: 101
chains: 45
- type: crossing
name: Bathampton Foot Crossing
kind: foot
miles: 103
chains: 18
- type: junction
name: Bathampton Jn
diverges: right
direction: up
description: Up/Dn Trowbridge towards Westbury
miles: 104
chains: 45
- type: loop
position: left
name: Up Bathampton Loop
miles: 105
chains: 0
- type: tunnel
name: Sydney Gardens East Tunnel
tunnelType: whole
length: 0mi 77yd
miles: 106
chains: 28
- type: tunnel
name: Sydney Gardens West Tunnel
tunnelType: whole
length: 0mi 99yd
miles: 106
chains: 29
- type: bridge
name: Pultney Road
category: road
miles: 106
chains: 42
position: under
- type: bridge
name: Dolemeads Viaduct
category: waterway
description: River Avon
miles: 106
chains: 55
position: under
- type: station
name: Bath Spa
miles: 106
chains: 71
- type: bridge
position: under
name: St James Viaduct
category: waterway
description: River Avon
miles: 107
chains: 0
- type: crossovers
name: Bath West Crossovers
miles: 107
chains: 25
- type: loop
position: right
name: Bath West Goods Loop
miles: 107
chains: 55
- type: station
name: Oldfield Park
miles: 107
chains: 72
- type: tunnel
name: Twerton Short Tunnel
tunnelType: whole
length: 0mi 45yd
miles: 108
chains: 71
- type: tunnel
name: Twerton Long Tunnel
tunnelType: whole
length: 0mi 264yd
miles: 109
chains: 9
- type: bridge
name: Bristol Road
roadName: A4
position: over
category: aroad
miles: 109
chains: 50
- type: tunnel
name: Saltford Tunnel
tunnelType: whole
length: 0mi 176yd
miles: 111
chains: 60
- type: station
name: Keynsham
miles: 113
chains: 63
- type: bridge
name: Avon Ring Road
roadName: A4174
position: over
category: aroad
miles: 114
chains: 68
- type: tunnel
tunnelType: whole
name: St. Annes Park No.3 Tunnel
length: 0mi 1017yd
miles: 116
chains: 0
- type: tunnel
tunnelType: whole
name: St. Annes Park No.2 Tunnel
length: 0mi 154yd
miles: 116
chains: 44
- type: siteof
name: St Annes
description: Former station
miles: 116
chains: 56
- type: loop
position: right
name: East Depot Goods Loop
description: Also provides access to East Depot
miles: 117
chains: 19
- type: junction
diverges: right
direction: up
name: North Somerset Jn
description: to St. Phillips Marh
miles: 117
chains: 46
- type: junction
name: Feeder Bridge Jn
description: Up/Dn Bristol Loops to Dr. Days Jn
diverges: left
direction: down
goto: "9999"
entryPoint: dr-days-jn
miles: 117
chains: 50
- type: junction
diverges: right
direction: down
name: Kingsland Rd Sidings GF
description: Access to Kingsland Road Sidings
miles: 117
chains: 57
- type: signallerChange
from: TVSC Bath WS (BL)
to: TVSC Temple Meads WS (BL)
miles: 117
chains: 73
- type: junction
name: Bristol East Jn
diverges: both
direction: up
description: Filton lines towards Filton on Up-side, Kingsland Road Sisings on right side
miles: 118
chains: 2
goto: "9999"
entryPoint: bristol-east-jn
- type: siteof
name: Bristol East Gantry
miles: 118
chains: 10
- type: station
name: Bristol Temple Meads
miles: 118
chains: 31

View File

@@ -0,0 +1 @@
# Bristol TM - Exeter SD

View File

@@ -0,0 +1 @@
# Reading - Taunton (via Westbury Line Jn)

View File

@@ -0,0 +1,772 @@
routeStart: Swindon Junction
routeEnd: Standish Junction
routeId: "0230"
updated: 2026-02-14
checked: 2026-02-14
signallerStart: TVSC Swindon WS
signallerEnd: Gloucester PSB
elecStart:
elec: 25kvac
eco: Didcot (TVSC)
elecEnd:
elec: none
routeDetail:
- type: continues
routeName: Reading - Bristol TM
routeId: '0002'
entryPoint: swindon
- type: junction
name: Swindon Jn
diverges: right
direction: down
goto: "0002"
entryPoint: swindon-jn
miles: 77
chains: 36
- type: electrificationChange
miles: 77
chains: 58
from:
elec: 25kvac
eco: Didcot
to:
elec: none
- type: minorBridge
name: Bruce Street
position: under
category: road
miles: 78
chains: 7
- type: crossovers
name: Rodbourne Jn
description: Limit of bidirectional running from Swindon
miles: 78
chains: 30
- type: bridge
name: River Hay
position: under
category: waterway
miles: 78
chains: 71
- type: minorBridge
name: Former MSWJ Railway
position: under
category: foot
miles: 79
chains: 20
- type: minorBridge
name: Edward's Bridge
position: under
category: road
miles: 79
chains: 33
- type: minorBridge
name: Mill Brook
position: under
category: stream
miles: 79
chains: 34
- type: bridge
name: Purton Road
description: Double bridge
position: over
category: road
roadName: B4534
miles: 79
chains: 53
- type: minorBridge
position: under
category: road
miles: 80
chains: 0
- type: minorBridge
position: over
category: road
roadName: B4553
miles: 80
chains: 30
- type: crossing
kind: foot
name: Purton No.72 Foot Crossing
miles: 80
chains: 63
- type: crossing
kind: AHB
name: Purton Collins Lane LC
miles: 81
chains: 9
- type: crossing
name: Purton No.64 Foot Crossing
kind: foot
miles: 81
chains: 25
- type: crossing
name: Purton No.60 Foot Crossing
kind: foot
miles: 81
chains: 46
- type: crossing
name: Purton Common Crossing
kind: MSL
miles: 81
chains: 65
- type: crossing
name: Purton No.129 Foot Crossing
kind: foot
miles: 82
chains: 9
- type: minorBridge
name: Farm Bridge
position: over
category: road
miles: 82
chains: 13
- type: crossing
name: Purton No.34 Foot Crossing
kind: foot
miles: 82
chains: 30
- type: minorBridge
name: Carpenter's
position: under
category: road
miles: 82
chains: 39
- type: minorBridge
name: Braydon Brook
position: under
category: stream
miles: 82
chains: 53
- type: minorBridge
name: Oak Hill (Coxhill Farm)
position: under
category: road
miles: 82
chains: 69
- type: crossing
kind: uwc
name: Clover's Foot Crossing
miles: 83
chains: 11
- type: minorBridge
name: Farm Bridge
position: over
category: road
miles: 83
chains: 28
- type: crossing
name: Gambols Farm Crossing
kind: uwc
miles: 83
chains: 57
- type: minorBridge
name: Black Dog Road
roadName: B4696
category: road
position: over
miles: 84
chains: 25
- type: crossing
kind: uwc
name: Gryphon Lodge Crossing
miles: 84
chains: 66
- type: minorBridge
position: under
category: stream
miles: 84
chains: 22
- type: minorBridge
position: over
name: Station Road
roadName: B4040
category: road
miles: 85
chains: 36
- type: siteof
name: Minety & Ashton Keynes
description: Former station
miles: 85
chains: 37
- type: crossing
kind: foot
name: Minety No.44 Foot Crossing
miles: 85
chains: 62
- type: crossing
kind: foot
name: Minety No.6 Foot Crossing
miles: 86
chains: 06
- type: minorBridge
nam: Askew Bridge
category: road
position: under
miles: 86
chains: 18
- type: crossing
kind: foot
name: Minety No.3 Foot Crossing
miles: 86
chains: 23
- type: minorBridge
position: under
category: road
name: Minety Moor
miles: 86
chains: 36
- type: crossing
kind: foot
name: Minety No.5 Foot Crossing
miles: 86
chains: 42
- type: crossing
kind: CCTV
name: Minety LC
description: CCTV by TVSC (LC Workstation)
miles: 86
chains: 74
- type: crossing
kind: foot
name: Minety No.26 Foot Crossing
miles: 87
chains: 4
- type: crossing
kind: foot
name: Minety No.26 Foot Crossing
miles: 87
chains: 31
- type: minorBridge
position: under
category: road
name: Slurt Bridge
miles: 87
chains: 63
- type: minorBridge
position: under
category: stream
name: Swill Brook
miles: 88
chains: 5
- type: crossing
kind: foot
name: Oaksey No.5 Foot Crossing
miles: 88
chains: 31
- type: minorBridge
position: over
name: Oaksey Road
category: road
miles: 88
chains: 37
- type: crossing
name: Oaksey No.4 Foot Crossing
kind: foot
miles: 88
chains: 56
- type: minorBridge
position: under
category: road
miles: 88
chains: 77
- type: crossing
name: West End Farm Foot Crossing
kind: foot
miles: 89
chains: 29
- type: minorBridge
name: Poole Keynes
position: under
category: road
miles: 89
chains: 30
- type: crossing
name: Kemble Wick Foot Crossing
kind: foot
miles: 89
chains: 59
- type: minorBridge
name: Wick Bridge
position: over
category: road
miles: 90
chains: 3
- type: tunnel
tunnelType: whole
name: Kemble Tunnel
length: 0mi 409yd
miles: 90
chains: 50
- type: crossovers
name: Kemble Jn
miles: 90
chains: 65
- type: junction
diverges: left
direction: down
description: Up Kemble Siding (Former platform)
miles: 90
chains: 74
name: Kemble GF
- type: station
name: Kemble
miles: 90
chains: 79
- type: minorBridge
name: Windmill/Tarlton Road
position: under
category: road
miles: 91
chains: 28
- type: minorBridge
name: Tetbury Road
position: under
roadName: A433
category: aroad
miles: 91
chains: 60
- type: crossing
kind: foot
name: Old Quarry Foot Crossing
miles: 91
chains: 77
- type: minorBridge
position: under
category: stream
name: Thames & Severn Canal
description: Disused Canal
miles: 92
chains: 67
- type: minorBridge
name: Coates
description: to Tarlton Road
category: road
position: under
miles: 92
chains: 77
- type: crossing
kind: foot
name: Coates Tunnel House Foot Crossing
miles: 93
chains: 21
- type: minorBridge
position: under
miles: 93
chains: 42
- type: minorBridge
position: over
miles: 93
chains: 66
- type: signallerChange
from: TVSC Swindon WS (SW)
to: Gloucester PSB Panel C (G)
miles: 93
chains: 79
- type: minorBridge
position: over
miles: 94
chains: 4
- type: minorBridge
name: Hailey Farm
position: over
miles: 94
chains: 40
- type: tunnel
tunnelType: whole
name: Sapperton Short Tunnel
length: 0mi 352yd
miles: 94
chains: 58
- type: tunnel
tunnelType: whole
name: Sapperton Long Tunnel
length: 1mi 104yd
miles: 95
chains: 40
- type: crossing
kind: foot
name: Frampton Common Foot Crossing
miles: 96
chains: 5
- type: crossing
kind: OMSL
name: Frampton Level Crossing
miles: 96
chains: 32
- type: crossing
kind: foot
name: Frampton Mansell Foot Crossing
miles: 96
chains: 43
- type: bridge
position: under
name: Frampton Mansell Viaduct
miles: 96
chians: 57
- type: minorBridge
position: under
category: road
name: Lower Manor
miles: 96
chains: 65
- type: minorBridge
position: over
miles: 97
chains: 4
- type: minorBridge
name: Marley Lane
position: over
category: road
miles: 97
chains: 39
- type: crossing
kind: foot
name: Chalford Foot Crossing
miles: 97
chains: 52
- type: bridge
name: Chalford Viaduct
position: under
miles: 97
chains: 61
- type: bridge
name: Cowcombe Hill
roadName: A419
category: aroad
position: over
miles: 98
chains: 4
- type: minorBridge
name: Chalford Bottom
position: under
miles: 98
chains: 36
- type: crossing
kind: foot
name: Hampton Road Foot Crossing
miles: 98
chains: 53
- type: crossing
name: St. Mary's Level Crossing
kind: MCG
miles: 98
chains: 64
- type: crossing
kind: foot
name: Brimscombe Foot Crossing
miles: 99
chains: 22
- type: minorBridge
name: Knap Lane
position: under
category: road
miles: 99
chains: 34
- type: bridge
name: Bourne Viaduct
position: under
miles: 99
chains: 51
- type: minorBridge
name: Brimscombe Hill
position: over
category: road
miles: 99
chains: 76
- type: minorBridge
name: Orchard Lane
position: under
category: road
miles: 100
chains: 12
- type: crossing
kind: foot
name: Bagpath Foot Crossing
miles: 100
chains: 37
- type: crossing
kind: MSL
name: Ham Mill Foot Crossing
miles: 100
chains: 63
- type: crossing
name: Thrupp Foot Crossing
kind: foot
miles: 100
chains: 75
- type: crossing
kind: foot
name: Butterrow Foot Crossing
miles: 101
chains: 21
- type: crossing
kind: msl
name: Bowbridge Foot Crossing
miles: 101
chains: 36
- type: minorBridge
name: Butterrow Hill
position: over
category: road
miles: 101
chains: 44
- type: bridge
name: Capels Viaduct
position: under
category: stream
description: Also crosses A419
miles: 101
chains: 66
- type: station
name: Stroud
miles: 102
chains: 13
- type: bridge
name: Watts Viaduct
roadName: A46
category: aroad
position: under
miles: 102
chains: 19
- type: bridge
name: Stratford Viaduct
position: under
miles: 102
chains: 32
- type: crossing
kind: omsl
name: Gannicox Foot Crossing
miles: 102
chains: 50
- type: minorBridge
name: Footbridge
position: over
category: foot
miles: 102
chains: 65
- type: bridge
category: aroad
position: under
roadName: A4171
miles: 103
chains: 3
- type: minorBridge
position: under
miles: 103
chains: 11
- type: minorBridge
position: over
category: road
name: Cashes Green Road
miles: 103
chains: 21
- type: crossing
kind: msl
name: Ebley Crossing
miles: 103
chains: 49
- type: minorBridge
name: Foxmoor Lane
position: under
category: road
miles: 103
chains: 41
- type: minorBridge
position: under
miles: 104
chains: 40
- type: minorBridge
position: over
miles: 104
chains: 45
- type: minorBridge
name: Brown's Lane
position: over
miles: 104
chains: 47
- type: crossing
kind: foot
name: Stonehouse 26 Foot Crossing
miles: 104
chains: 63
- type: station
name: Stonehouse
miles: 104
chains: 74
- type: minorBridge
name: Queen's Road
position: under
category: road
miles: 104
chains: 79
- type: crossing
kind: msl
name: Globe Inn Foot Crossing
miles: 105
chains: 10
- type: minorBridge
name: Gloucester Road
roadName: B4008
position: under
miles: 105
chains: 17
- type: bridge
name: Oldends Lane
position: under
category: road
miles: 105
chains: 35
- type: crossing
kind: foot
name: Little Australia Foot Crossing
miles: 105
chains: 56
- type: minorBridge
position: under
miles: 105
chains: 64
- type: minorBridge
name: Black Bridge
position: over
miles: 106
chains: 13
- type: minorBridge
name: Gloucester Road
category: minorRoad
roadName: B4008
position: over
miles: 106
chains: 60
- type: junction
name: Standish Jn
diverges: right
direction: up
description: Up to Gloucester, Dn to Bristol
miles: 106
chains: 74
goto: "2420"
entryPoint: standish-jn
- type: continues
routeName: "Westerleigh Jn - Gloucester"
entryPoint: standish-jn
routeId: "2420"

View File

@@ -0,0 +1,656 @@
routeStart: Wootton Bassett Jn
routeEnd: Stoke Gifford Jn
routeId: 0240
updated: 2026-02-11
checked: 2026-02-11
signallerStart: TVSC Swindon WS
signallerEnd: TVSC Stoke Gifford WS
elecStart:
elec: 25kvac
eco: Didcot (TVSC)
elecEnd:
elec: 25kvac
eco: Didcot (TVSC)
routeDetail:
- type: continues
routeName: Reading - Bristol TM
entryPoint: wootton-bassett-jn
routeId: '0002'
- type: junction
diverges: right
direction: down
name: Wootton Basset Jn
description: to Chippenham & Bristol via Bath
goto: "0002"
entryPoint: wootton-bassett-jn
miles: 83
chains: 7
- type: loop
name: Up Wootton Basset Goods Line
description: Loop spans junction
position: left
miles: 83
chains: 10
- type: bridge
position: over
name: Bath Road
roadName: A3102
category: aroad
miles: 83
chains: 29
- type: crossovers
name: Wootton Basset West
miles: 84
chains: 7
- type: minorBridge
name: Whitehill Lane
position: under
category: road
miles: 84
chains: 13
- type: minorBridge
name: Farm Bridge
position: under
category: road
miles: 84
chains: 39
- type: bridge
position: over
name: Motorway
roadName: M4
category: motorway
miles: 84
chains: 50
- type: minorBridge
name: Farm Bridge
position: under
category: road
miles: 85
chains: 15
- type: minorBridge
position: over
name: Callow Hill
category: road
miles: 85
chains: 29
- type: minorBridge
name: Farm Bridge
position: over
category: road
miles: 85
chains: 58
- type: minorBridge
name: Brinkworth Brooklands
position: over
category: road
miles: 86
chains: 75
- type: crossing
kind: foot
name: Brinkworth Foot Crossing
miles: 87
chains: 17
- type: minorBridge
name: Farm Bridge
category: road
position: over
miles: 88
chains: 12
- type: minorBridge
name: Farm Bridge
position: under
category: road
miles: 88
chains: 46
- type: minorBridge
name: School Hill
position: under
category: road
miles: 88
chains: 66
- type: minorBridge
name: Farm Bridge
position: under
category: road
miles: 89
chains: 9
- type: minorBridge
name: Farm Bridge
position: under
category: road
miles: 89
chains: 29
- type: minorBridge
name: Idover Lane
position: under
category: road
miles: 89
chains: 46
- type: minorBridge
name: The Street
position: under
category: road
miles: 89
chains: 70
- type: bridge
name: River Avon
position: under
category: waterway
miles: 90
chains: 26
- type: minorBridge
name: Farm Bridge
position: under
category: road
miles: 90
chains: 60
- type: minorBridge
name: Farm Bridge
position: under
category: road
miles: 91
chains: 36
- type: minorBridge
name: Startley Road
position: under
category: road
miles: 91
chains: 50
- type: minorBridge
name: Rodbourne Lane
description: or 'Pound Hill'
position: under
category: road
miles: 92
chains: 2
- type: minorBridge
name: Cabbage Lane
position: over
category: road
miles: 92
chains: 28
- type: crossing
name: Kingway Barn Foot Crossing
kind: foot
miles: 93
chains: 7
- type: bridge
name: Kingway
position: under
category: aroad
roadName: A429
miles: 93
chains: 30
- type: minorBridge
name: Farm Bridge
position: under
category: road
miles: 93
chains: 70
- type: loop
position: right
name: Down Hullavington Goods Loop
miles: 94
chains: 5
- type: minorBridge
position: under
name: Court Farm
category: road
miles: 94
chains: 9
- type: minorBridge
position: under
name: Bradfield Cottages
category: road
miles: 94
chains: 27
- type: loop
name: Up Hullavington Goods Loop
position: left
miles: 94
chains: 42
- type: crossovers
name: Hullavington
miles: 94
chains: 45
- type: minorBridge
name: Gills Hunting
position: over
category: road
miles: 94
chains: 62
- type: bridge
name: Aqueduct
position: over
category: stream
miles: 95
chains: 22
- type: minorBridge
name: Happy Lands
position: over
category: road
miles: 95
chains: 40
- type: minorBridge
name: Pig Lane
position: over
category: road
miles: 95
chains: 69
- type: minorBridge
name: Fosse Way
category: road
position: over
miles: 96
chains: 47
- type: minorBridge
name: Pipeline
category: pipeline
position: over
miles: 96
chains: 48
- type: minorBridge
name: Footbridge
position: over
category: foot
miles: 96
chains: 64
- type: tunnel
name: Alterton Tunnel
tunnelType: whole
length: 0mi 506yd
miles: 97
chains: 46
- type: signallerChange
from: TVSC Swindon WS (SW)
to: TVSC Stoke Gifford WS (BL)
miles: 98
chains: 0
- type: minorBridge
name: Footbridge
category: foot
position: over
miles: 98
chains: 2
- type: minorBridge
name: Footbridge
category: foot
positon: over
miles: 98
chains: 17
- type: minorBridge
name: Alderton Road
position: over
category: road
miles: 98
chains: 40
- type: minorBridge
name: Farm Bridge
position: under
category: road
miles: 98
chains: 76
- type: minorBridge
name: Farm Bridge
position: under
category: road
miles: 99
chains: 7
- type: minorBridge
name: Footbridge
category: foot
position: over
miles: 99
chains: 40
- type: bridge
name: Luckington Road
category: road
roadName: B4040
position: over
miles: 99
chains: 46
- type: minorBridge
name: Footbridge
category: foot
position: over
miles: 99
chains: 59
- type: siteof
name: Badminton
side: centre
miles: 100
chains: 1
- type: bridge
name: Station Road
position: over
category: road
miles: 100
chains: 4
- type: minorBridge
name: Footbridge
category: foot
position: over
miles: 100
chains: 33
- type: bridge
name: Aqueduct
position: over
category: stream
miles: 100
chains: 57
- type: minorBridge
name: Footbridge
category: foot
position: over
miles: 100
chains: 65
- type: tunnel
tunnelType: whole
length: 2mi 926yd
name: Chipping Sodbury Tunnel
miles: 102
chains: 21
description: 10 telephones on Up side
- type: minorBridge
position: over
name: Footbridge
category: foot
miles: 103
chains: 63
- type: bridge
name: Frome Bridge
position: over
category: stream
miles: 103
chains: 75
- type: minorBridge
name: Colt's Green Bridge
category: road
position: over
miles: 104
chains: 19
- type: loop
name: Chipping Sodbury Goods Loop
position: left
description: Bidirectional with access to Chipping Sodbury Sidings
miles: 104
chains: 31
- type: minorBridge
name: Farm Bridge
category: road
position: over
miles: 104
chains: 56
- type: minorBridge
name: Kingrove Lane
position: over
category: road
miles: 104
chains: 77
- type: bridge
name: Aqueduct
position: over
category: stream
miles: 105
chains: 18
- type: minorBridge
name: Dodington Road
category: road
position: over
miles: 105
chains: 19
- type: bridge
name: Aqueduct
category: stream
position: over
miles: 105
chains: 38
- type: minorBridge
name: Footbridge
category: foot
position: over
miles: 106
chains: 32
- type: bridge
name: Westerleigh Road
category: road
position: under
miles: 107
chains: 4
- type: junction
diverges: left
direction: up
name: Westerleigh Jn
goto: '2420'
entryPoint: westerleigh-jn
miles: 107
chains: 14
description: Up/Dn Charfield towards Gloucester
- type: bridge
name: Westerleigh Branch
category: rail
position: under
miles: 107
chains: 15
- type: minorBridge
name: Dodmoor Farm
category: road
position: under
miles: 107
chains: 33
- type: minorBridge
name: Farm Bridge
category: road
position: under
miles: 107
chains: 70
- type: minorBridge
name: Boxhenge Farm Lane
category: road
position: under
miles: 108
chains: 10
- type: minorBridge
name: The Dramway
position: over
category: foot
miles: 108
chains: 25
- type: minorBridge
name: Henfield Road
position: under
category: road
miles: 107
chains: 52
- type: bridge
name: Bristol Road
category: aroad
position: under
roadName: A432
miles: 108
chains: 78
- type: minorBridge
name: Park Lane
position: under
category: road
miles: 109
chains: 27
- type: bridge
name: Upford Viaduct
positon: under
category: waterway
miles: 109
chains: 58
- type: minorBridge
name: Hicks Common Lane
position: over
category: road
miles: 109
chains: 71
- type: minorBridge
name: Down Road
category: road
position: over
miles: 110
chains: 15
- type: minorBridge
name: Mill Lane
category: road
position: under
miles: 110
chains: 24
- type: bridge
name: Winterbourne Viaduct
category: road
position: under
miles: 110
chains: 38
- type: minorBridge
name: Farm Bridge
category: road
position: under
miles: 110
chains: 48
- type: bridge
name: M4 Motorway
category: motorway
roadName: M4
position: under
miles: 110
chains: 63
- type: minorBridge
name: Old Gloucester Road
category: road
roadName: B4427
position: under
miles: 110
chains: 70
- type: minorBridge
name: Curtis Lane
position: over
category: road
miles: 111
chains: 14
- type: crossovers
name: Stoke Gifford East Jn
miles: 111
chains: 20
- type: minorBridge
name: Pearson's Brick Yard
category: road
position: over
miles: 111
chains: 32
- type: station
name: Bristol Parkway
description: Stoke Gifford Dn Sidings on Down side
miles: 111
chains: 62
- type: bridge
name: Bristol Road
category: road
position: under
miles: 111
chains: 77
- type: junction
name: Stoke Gifford No.1 Jn
diverges: right
direction: down
description: Up/Dn Filton & Bristol TM
miles: 111
chains: 79
- type: continues
routeName: Bristol TM - Cardiff West Shunt
entryPoint: patchway-junction
routeId: '9999'

View File

@@ -0,0 +1,126 @@
routeStart: Thingley Junction
routeEnd: Bradford Junction
routeId: "0250"
updated: 2026-03-01
checked: 2026-03-01
signallerStart: TVSC Swindon WS
signallerEnd: Westbury PSB
elecStart:
elec: none
elecEnd:
elec: none
routeDetail:
- type: continues
routeName: Reading - Bristol TM
routeId: "0002"
entryPoint: thingley-jn
- type: junction
name: Thingley Jn
diverges: left
direction: down
description: Down Main towards Bath
goto: "0002"
entryPoint: thingley-jn
miles: 96
chains: 10
- type: signallerChange
from: TVSC Swindon WS (SW)
to: Westbury PSB (W)
miles: 96
chains: 30
- type: crossing
name: Laycock 6 Foot Crossing
kind: foot
miles: 97
chains: 30
- type: crossing
name: Laycock 2 Foot Crossing
kind: foot
miles: 98
chains: 8
- type: crossing
name: Melksham Without 85 Foot Crossing
kind: foot
miles: 99
chains: 12
- type: crossing
name: Melksham Without 92 Foot Crossing
kind: foot
miles: 99
chains: 41
- type: station
name: Melksham
miles: 100
chains: 13
- type: crossing
kind: foot
name: Melksham 22 Foot Crossing
miles: 100
chains: 32
- type: crossing
kind: uwc
miles: 101
chains: 39
name: Church Farm No.1 Crossing
- type: crossing
kind: foot
name: Broughton Gifford No.26 Foot Crossing
miles: 101
chains: 77
- type: crossing
kind: foot
name: Broughton Gifford No.25 Foot Crossing
miles: 102
chains: 2
- type: crossing
kind: uwc
name: Church Farm No.2 Crossing
miles: 102
chains: 10
- type: crossing
kind: foot
name: Holt No.1 Foot Crossing
miles: 102
chains: 23
- type: siteof
name: Holt Junction
description: Former junction
miles: 102
chains: 58
- type: crossing
kind: uwc
name: Avon View Farm Crossing
miles: 103
chains: 9
- type: junction
name: Bradford Jn
diverges: left
direction: up
description: Up/Dn Trowbridge towards Bath
goto: "0260"
entryPoint: bradford-jn
miles: 104
chains: 40
- type: continues
routeName: Bathampton Jn - Westbury
routeId: "0260"
entryPoint: bradford-jn

View File

@@ -0,0 +1,255 @@
routeStart: Bathampton Jn
routeEnd: Westbury South Jn
routeId: "0260"
updated: 2026-03-01
checked: 2026-03-01
signallerStart: TVSC Bath WS
signallerEnd: Westbury PSB
elecStart:
elec: none
elecEnd:
elec: none
routeDetail:
- type: continues
routeName: Reading - Bristol TM
entryPoint: bathampton-jn
routeId: "0002"
- type: junction
name: Bathampton Jn
description: Mileage change (0mi 0ch)
diverges: right
direction: down
goto: "0002"
entryPoint: bathampton-jn
miles: 0
chains: 0
- type: crossing
kind: omsl
name: Glass's Crossing
miles: 0
chains: 20
- type: crossing
kind: omsl
name: Claverton Crossing
miles: 1
chains: 73
- type: bridge
name: Dundas Aqueduct
position: over
category: waterway
description: Kennet & Avon Canal
miles: 3
chains: 12
- type: crossing
name: Young's Crossing
kind: uwc
miles: 3
chains: 25
- type: crossing
name: Fisher's Crossing
kind: uwc
miles: 3
chains: 50
- type: crossing
name: Limpley Stoke No.1 Foot Crossing
miles: 4
chains: 10
kind: foot
- type: crossing
name: Limpley Stoke No.2 Foot Crossing
kind: foot
miles: 4
chains: 14
- type: crossing
name: Freshford Station Crossing
kind: uwc
miles: 4
chains: 68
- type: station
name: Freshford
miles: 4
chains: 70
- type: bridge
name: Freshford Viaduct
category: waterway
description: River Avon
position: under
miles: 5
chains: 8
- type: bridge
name: Avoncliff Aqueduct
category: waterway
description: Kennet & Avon Canal
position: over
miles: 5
chains: 63
- type: station
name: Avoncliff
miles: 5
chains: 63
description: Local door operation
- type: crossing
name: Avoncliff Mill Crossing
kind: uwc
miles: 5
chains: 71
- type: signallerChange
from: TVSC Bath WS (BL)
to: Westbury PSB (W)
miles: 6
chains: 55
- type: crossing
kind: foot
name: Belcombe Road Foot Crossing
miles: 6
chains: 67
- type: crossing
kind: foot
name: Barton Orchard Foot Crossing
miles: 6
chains: 74
- type: station
name: Bradford-on-Avon
miles: 7
chains: 9
- type: tunnel
name: Bradford Tunnel
tunnelType: whole
length: 0mi 159yd
miles: 7
chains: 22
- type: crossing
kind: AHB
name: Greenland Mill Level Crossing
miles: 7
chains: 27
- type: crossing
kind: uwc
name: Cemetery Lane Crossing
miles: 8
chains: 1
- type: crossing
kind: uwc
name: Tuckers Crossing
miles: 8
chains: 18
- type: junction
name: Bradford Jn
diverges: right
direction: up
description: Melksham Single towards Chippenham, mileage change
goto: "0250"
entryPoint: bradford-jn
miles: 9
chains: 12
- type: bridge
name: Trowbridge Aqueduct
description: Kennet & Avon Canal
position: over
category: waterway
miles: 104
chains: 54
- type: station
name: Trowbridge
miles: 105
chains: 61
- type: crossing
kind: foot
name: White Horse Foot Crossing
miles: 107
chains: 8
- type: crossing
kind: foot
name: Yarnbrook Foot Crossing
miles: 107
chains: 34
- type: bridge
name: Yarnbrook Viaduct
position: under
category: aroad
roadName: A363
miles: 107
chains: 56
- type: crossing
kind: foot
name: Heywood 3 Foot Crossing
miles: 108
chains: 46
- type: crossing
kind: foot
name: Hawkeridge Foot Crossing
miles: 108
chains: 78
- type: junction
name: Hawkeridge Jn
diverges: right
direction: down
description: Lines change direction towards Westbury
goto: "0210"
entryPoint: heywood-road-jn
miles: 109
chains: 14
- type: junction
name: Westbury North Jn
diverges: right
direction: up
goto: "0210"
entryPoint: heywood-road-jn
miles: 109
chains: 49
- type: station
name: Westbury
miles: 109
chains: 64
- type: junction
name: Westbury South Jn
diverges: left
direction: down
description: Up/Dn Main to Fairwood Jn
goto: "0210"
entryPoint: "fairwood-jn"
miles: 110
chains: 7
- type: continues
routeName: Westbury - Southampton Ctl
routeId: "0265"
entryPoint: westbury

View File

@@ -0,0 +1 @@
# Southampton Ctl - Portsmouth Hbr

View File

@@ -0,0 +1 @@
# Castle Cary - Weymouth

View File

@@ -0,0 +1 @@
# Westbury - Southampton Ctl

View File

@@ -0,0 +1 @@
# Bristol TM - Cardiff West Shunt

View File

@@ -0,0 +1,681 @@
routeStart: Westerleigh Jn
routeEnd: Gloucester
routeId: 2420
updated: 2026-02-11
checked: 2026-02-11
signallerStart: TVSC Swindon WS
signallerEnd: Gloucester PSB
elecStart:
elec: 25kvac
eco: Didcot (TVSC)
elecEnd:
elec: none
routeDetail:
- type: junction
name: Westerleigh Jn
diverges: left
direction: down
goto: '0240'
entryPoint: westerleigh-jn
miles: 121
chains: 26
- type: bridge
name: Westerleigh Road
category: road
position: under
miles: 121
chains: 19
- type: minorBridge
position: under
name: Cattle Creep
miles: 121
chains: 20
- type: electrificationChange
from:
elec: 25kvac
eco: Didcot
to:
elec: none
miles: 120
chains: 67
- type: minorBridge
name: Westerleigh Road
position: under
category: road
miles: 120
chains: 63
- type: minorBridge
name: Farm Bridge
position: under
miles: 120
chains: 36
- type: junction
diverges: right
direction: up
name: Yate South Jn
description: to Westerleigh Oil Terminal
miles: 120
chains: 3
- type: station
name: Yate
miles: 119
chains: 60
- type: minorBridge
category: aroad
position: over
description: Between platforms
roadName: A432
name: Station Road
miles: 119
chains: 59
- type: junction
diverges: right
direction: down
name: Yate Middle Junction
description: Diverges between platforms, to Tytherington
miles: 119
chains: 57
- type: minorBridge
name: Stream
category: Stream
miles: 119
chains: 23
- type: crossing
kind: foot
name: Celestine Road Foot Crossing
miles: 119
chains: 23
- type: minorBridge
name: Goose Green Way
category: road
roadName: B4059
position: over
miles: 119
chains: 6
- type: minorBridge
name: Broad Lane
category: road
position: over
miles: 119
chains: 3
- type: minorBridge
category: road
miles: 118
chains: 56
position: over
- type: minorBridge
name: Limekiln Road
position: over
miles: 117
chains: 37
- type: minorBridge
name: Hall End Lane
category: road
position: over
miles: 116
chains: 61
- type: minorBridge
name: Westend Road
category: road
position: over
miles: 116
chains: 36
- type: minorBridge
name: Aqueduct
category: stream
position: over
miles: 116
chains: 25
- type: tunnel
name: Wickwar Tunnel
tunnelType: whole
length: 0mi 1401yd
miles: 116
chains: 60
- type: siteof
name: Wickwar
description: Former station
miles: 115
chains: 9
- type: minorBridge
name: Station Road
category: road
position: under
miles: 114
chains: 79
- type: minorBridge
position: under
miles: 113
chains: 74
- type: siteof
name: Charfield
description: Former & Future Station
miles: 113
chains: 14
- type: minorBridge
name: Wooton Road
category: road
roadName: B4058
position: over
miles: 113
chains: 9
- type: loop
position: both
name: Charfield Loops
miles: 112
chains: 72
- type: crossing
kind: foot
name: Charfield 8 Foot Crossing
miles: 112
chains: 42
- type: minorBridge
name: Little Avon River
category: stream
position: under
miles: 112
chains: 38
- type: minorBridge
name: Charfield Meadow
category: road
position: under
miles: 112
chains: 21
- type: minorBridge
name: Huntingford Bridge
category: road
position: under
miles: 112
chains: 10
- type: minorBridge
position: under
miles: 111
chains: 49
- type: minorBridge
name: Foot Bridge
category: foot
position: over
miles: 111
chains: 1
- type: minorBridge
category: road
name: Wick Bridge
position: over
miles: 110
chains: 60
- type: bridge
name: Motorway
roadName: M5
category: motorway
position: over
miles: 110
chains: 48
- type: crossing
kind: foot
name: Wick Mill No.2 Foot Crossing
miles: 110
chains: 9
- type: crossing
kind: foot
name: Wick Mill No.1 Foot Crossing
miles: 109
chains: 79
- type: minorBridge
name: Crossways
category: road
position: over
miiles: 109
chains: 63
- type: signallerChange
from: TVSC Stoke Gifford WS (BL)
to: Gloucester PSB Panel C (G)
miles: 109
chains: 60
- type: crossing
name: Goldwick Foot Crossing
kind: foot
miles: 109
chains: 47
- type: crossing
name: Alkington 14 Foot Crossing
kind: foot
miles: 109
chains: 38
- type: minorBridge
position: under
miles: 109
chains: 15
- type: crossing
name: Stinchcombe 6 Foot Crossing
kind: foot
miles: 108
chains: 46
- type: minorBridge
name: Cattle Creep
position: under
miles: 108
chains: 53
- type: bridge
name: Bristol Road
position: over
category: aroad
roadName: A38
miles: 108
chains: 4
- type: crossing
kind: foot
name: Thornbury 45 Footpath
miles: 107
chains: 72
- type: junction
diverges: right
direction: Up
name: Berkeley Road Jn
description: to Sharpness Docks
miles: 107
chains: 70
- type: minorBridge
name: Cattle Creep
position: under
miles: 107
chains: 23
- type: minorBridge
name: Tumpy Green
position: over
miles: 107
chains: 4
- type: crossing
name: Slimbridge 53 Foot Crossing
kind: foot
miles: 106
chains: 62
- type: crossing
name: Slimbridge 54 Foot Crossing
kind: foot
miles: 106
chains: 48
- type: bridge
name: Bristol Road
category: aroad
roadName: A38
position: over
miles: 106
chains: 24
- type: crossing
kind: foot
name: Slimbridge 46 Foot Crossing
miles: 105
chains: 79
- type: bridge
position: over
name: Motorway
roadName: M5
category: motorway
miles: 105
chains: 70
- type: minorBridge
position: over
category: aroad
roadName: A4135
miles: 105
chains: 57
- type: crossovers
name: Coaley GF
miles: 105
chains: 36
- type: station
name: Cam & Dursley
miles: 105
chains: 30
- type: minorBridge
name: Box Road
position: over
miles: 105
chains: 26
- type: crossing
name: Dursley Foot Crossing
kind: foot
miles: 105
chains: 14
- type: minorBridge
name: Westend
position: under
miles: 104
chains: 60
- type: crossing
name: Coaley 12 Foot Crossing
miles: 104
chains: 46
kind: foot
- type: minorBridge
name: Peter's Street
position: under
miles: 104
chains: 39
- type: crossing
name: Coaley 17 Foot Crossing
kind: foot
miles: 104
chains: 27
- type: crossing
name: Frocester 23 Foot Crossing
kind: foot
miles: 104
chains: 14
- type: siteof
name: Frocester
description: Former station
miles: 103
chains: 36
- type: minorBridge
name: Peter's Street
position: over
miles: 103
chains: 32
- type: minorBridge
name: Bath Road
position: over
miles: 103
chains: 16
- type: crossing
kind: foot
name: Leonard Stanley 4 Foot Crossing
miles: 102
chains: 60
- type: minorBridge
position: under
miles: 102
chains: 30
- type: bridge
name: Stonehouse Viaduct
position: under
miles: 102
chains: 9
- type: minorBridge
name: River Frome
category: waterway
position: under
miles: 101
chains: 75
- type: minorBridge
name: Bristol Road
roadName: A419
position: under
miles: 101
chains: 67
- type: minorBridge
name: Footbridge
category: foot
position: over
miles: 101
chains: 42
- type: crossing
name: Old Ends Level Crossing
kind: CCTV
description: CCTV by Gloucester
miles: 101
chains: 27
- type: crossing
kind: foot
name: Stonehouse 2 Foot Crossing
miles: 101
chains: 7
- type: minorBridge
name: Stagholt Farm
position: under
miles: 100
chains: 74
- type: crossing
kind: foot
name: Stagholt Foot Crossing
miles: 100
chains: 73
- type: minorBridge
name: Black Bridge
position: over
miles: 100
chains: 40
- type: minorBridge
name: Gloucester Road
category: road
roadName: B4008
miles: 100
chains: 2
- type: crossing
name: Standish 12 Footpath
kind: foot
miles: 99
chains: 71
- type: junction
name: Standish Jn
diverges: left
direction: up
goto: '0230'
entryPoint: standish-jn
miles: 99
chains: 69
- type: crossing
name: Standish 9 Foot Crossing
kind: foot
miles: 99
chains: 45
- type: minorBridge
name: Bridge Farm
position: over
miles: 99
chains: 29
- type: loop
name: Haresfield Down Goods Loop
position: left
miles: 99
chains: 0
- type: crossing
kind: R/G
name: Haresfield Foot Crossing
miles: 99
chains: 62
- type: loop
name: Haresfield Up Goods Loop
description: Exit signal usually blank
position: right
miles: 98
chains: 40
- type: minorBridge
position: over
miles: 98
chains: 38
- type: crossing
kind: foot
name: Brookthorpe Foot Crossing
miles: 97
chains: 01
- type: minorBridge
name: Grange Road
category: road
position: under
miles: 95
chains: 66
- type: minorBridge
name: Southern Avenue
category: aroad
roadName: A38
miles: 95
chains: 3
position: over
- type: crossovers
name: Tuffley Jn
description: Limit of bidirectional running from Gloucester
miles: 94
chains: 74
- type: minorBridge
name: Stroud Road
roadName: B4072
category: road
position: over
miles: 94
chains: 62
- type: minorBridge
name: Footbridge
position: over
miles: 93
chains: 72
- type: minorBridge
name: Tredworth Road
position: under
category: road
miles: 93
chains: 37
- type: minorBridge
name: Barton Street
category: road
position: under
roadName: B4073
miles: 93
chains: 10
- type: junction
name: Gloucester Yard Junction
diverges: left
direction: down
description: to Barnwood Junction
goto: "2422"
entryPoint: barnwood-jn
miles: 93
chains: 8
- type: loop
name: Curve Through Siding
position: left
miles: 113
chains: 20
description: Mileage Change from Gloucester Yard Jn
- type: bridge
name: Metz Way
category: aroad
roadName: A4032
position: over
miles: 113
chains: 24
- type: crossing
kind: MCB
name: Horton Road LC
description: Controlled by Gloucester PSB
miles: 113
chains: 56
- type: junction
diverges: left
direction: up
name: Horton Road Jn
description: to Barnwood Jn
goto: "2422"
entryPoint: horton-road-jn
miles: 113
chains: 61
- type: station
name: Gloucester
miles: 114
chains: 4
- type: continues
routeName: Gloucester - Severn Tunnel Jn
entryPoint: gloucester
routeId: '2421'

View File

@@ -0,0 +1,739 @@
# yaml-language-server: $schema=./mapFiles.schema.json
routeStart: Gloucester
routeEnd: Severn Tunnel Jn
routeId: "2421"
updated: 2026-02-28
checked: 2026-03-01
signallerStart: Gloucester PSB
signallerEnd: SWCC Severn Tunnel WS
elecStart:
elec: none
elecEnd:
elec: 25kvac
eco: Didcot (TVSC)
routeDetail:
- type: continues
routeName: Gloucester - Westerleigh Jn
entryPoint: gloucester
routeId: '2420'
- type: station
name: Gloucester
miles: 114
chains: 4
- type: minorBridge
name: Gloucester Viaduct
miles: 114
chains: 20
position: under
category: road
- type: crossovers
name: Gloucester West
miles: 114
chains: 40
- type: minorBridge
name: St. Catherines Street
position: under
category: road
miles: 114
chains: 47
- type: minorBridge
name: St. Oswalds Road
position: under
category: road
miles: 114
chains: 54
- type: minorBridge
name: St. Catherines Meadow Viaduct
category: minorRoad
position: under
miles: 115
chains: 2
- type: bridge
name: River Severn
position: under
category: waterway
miles: 114
chains: 10
- type: minorBridge
name: Ham Meadow Viaduct
category: minorRoad
position: under
miles: 115
chains: 20
- type: minorBridge
name: Over Causeway
position: over
category: aroad
roadName: A417
miles: 115
chains: 32
- type: minorBridge
category: minorRoad
position: under
miles: 115
chains: 37
- type: crossovers
name: Over Jn
miles: 115
chains: 43
- type: bridge
position: under
category: waterway
name: River Severn
miles: 115
chains: 48
- type: crossing
kind: uwc
name: Keens Crossing
miles: 116
chains: 3
- type: crossing
kind: foot
name: Over Farm Foot Crossing
miles: 116
chains: 48
- type: minorBridge
category: minorRoad
position: under
miles: 116
chains: 40
- type: crossing
kind: uwc
name: Poole's Crossing
miles: 116
chains: 46
- type: minorBridge
category: minorRoad
position: under
miles: 116
chains: 64
- type: minorBridge
category: aroad
name: A48
roadName: A48
position: over
miles: 117
chains: 5
- type: crossing
kind: foot
name: Hygrove Foot Crossing
miles: 117
chains: 39
- type: crossing
kind: uwc
name: Lower Barn Foot Crossing
miles: 118
chains: 17
- type: crossing
kind: foot
name: Churcham Court Foot Crossing
miles: 118
chains: 48
- type: minorBridge
category: minorRoad
name: Farm Bridge
position: over
miles: 118
chains: 55
- type: crossing
kind: foot
name: Churcham 66 Foot Crossing
miles: 118
chains: 69
- type: minorBridge
name: Oakle Street
position: over
category: road
miles: 119
chains: 35
- type: crossing
kind: foot
name: Oakle St No.1 Foot Crossing
miles: 119
chains: 59
- type: crossing
kind: foot
name: Oakle St No.2 Foot Crossing
miles: 119
chains: 66
- type: crossing
kind: foot
name: Hooks Farm Foot Crossing
miles: 120
chains: 1
- type: crossing
kind: CCTV
name: Ley Level Crossing
description: CCTV by Gloucester
miles: 120
chains: 20
- type: crossing
kind: uwc
name: Broken Cross Farm No.1 Crossing
miles: 120
chains: 49
- type: crossing
kind: r/g
name: Broken Cross Farm Level Crossing
miles: 120
chains: 66
- type: minorBridge
position: over
category: minorRoad
name: Grange Court Road
miles: 121
chains: 50
- type: crossing
kind: AHB
name: Westbury Level Crossing
miles: 122
chains: 11
- type: crossing
kind: foot
name: Longcroft Farm Foot Crossing
miles: 122
chains: 20
- type: crossing
kind: foot
name: Westbury Brook Foot Crossing
miles: 122
chains: 51
- type: minorBridge
position: under
name: Westbury-on-Severn Lane
miles: 122
chains: 72
category: road
- type: minorBridge
position: under
name: Jordan Hill
roadName: A48
category: aroad
miles: 122
chains: 78
- type: minorBridge
name: Farm Bridge
category: minorRoad
position: under
miles: 123
chains: 33
- type: minorBridge
name: Cattle Creep
category: minorRoad
position: under
miles: 123
chains: 53
- type: crossing
kind: omsl
name: Broadoak Crossing
miles: 123
chains: 76
- type: minorBridge
name: Broad Oak Bridge
roadName: A48
category: aroad
position: under
miles: 124
chains: 5
- type: minorBridge
name: Cattle Creep
category: minorRoad
position: under
miles: 124
chains: 25
- type: minorBridge
name: Hawkins Lane
category: road
position: under
miles: 124
chains: 40
- type: minorBridge
name: Cattle Creep
category: minorRoad
position: under
miles: 124
chains: 45
- type: minorBridge
position: over
name: Hyde Lane
category: road
miles: 125
chains: 3
- type: signallerChange
from: Gloucester PSB (G)
to: SWCC Severn Tunnel Workstation (NT)
miles: 125
chains: 10
- type: tunnel
tunnelType: whole
length: 0mi 235yd
name: Newnham Tunnel
miles: 125
chains: 14
- type: minorBridge
name: Farm Bridge
category: minorRoad
position: over
miles: 125
chains: 26
- type: crossing
kind: uwc
name: Ruddle Bridge Crossing
miles: 125
chains: 37
- type: minorBridge
name: Ruddle Road Bridge
roadName: A48
position: under
category: aroad
miles: 125
chains: 47
- type: crossing
kind: foot
name: Bullo Pill Foot Crossing
miles: 126
chains: 35
- type: minorBridge
name: Bullo Dock Viaduct
position: under
category: road
miles: 126
chains: 39
- type: minorBridge
name: Farm Bridge
category: minorRoad
position: over
miles: 126
chains: 74
- type: minorBridge
name: Awre Road Cockshoot Bridge
category: minorRoad
position: over
miles: 127
chains: 34
- type: crossing
kind: foot
name: Awre 15 Foot Crossing
miles: 128
chains: 9
- type: crossing
kind: CCTV
name: Awre Level Crossing
miles: 128
chains: 22
description: CCTV by SWCC
- type: minorBridge
name: Hewletts Road
position: over
category: road
miles: 128
chains: 46
- type: crossing
kind: foot
name: Awre 87 Foot Crossing
miles: 129
chains: 7
- type: crossing
kind: foot
name: Milkmaid Head Foot Crossing
miles: 129
chains: 45
- type: crossing
kind: foot
name: Gatcombe Foot Crossing
miles: 129
chains: 61
- type: crossing
kind: uwc
name: Aldridge Crossing
miles: 130
chains: 65
- type: minorBridge
name: Cliff Farm Bridge
category: minorRoad
position: over
miles: 131
chains: 79
- type: crossing
kind: foot
name: Plummers Farm Foot Crossing
miles: 132
chains: 19
- type: crossing
kind: AHB
name: Naas Level Crossing
miles: 132
chains: 68
- type: crossing
kind: foot
name: Lydney Foot Crossing
miles: 132
chains: 71
- type: loop
position: both
name: Lydney Goods Loops
description: Up loop gives access to Dean Forest Railway
miles: 133
chains: 21
- type: station
name: Lydney
miles: 133
chains: 37
- type: crossing
kind: CCTV
name: Lydney Level Crossing
description: CCTV by SWCC
miles: 133
chains: 40
- type: minorBridge
name: The Lyd
position: under
category: stream
miles: 133
chains: 42
- type: minorBridge
position: under
category: road
miles: 133
chains: 45
- type: crossing
kind: uwc
name: Garlands No.1 Crossing
miles: 133
chains: 72
- type: minorBridge
name: Garlands Bridge
category: minorRoad
position: under
miles: 134
chains: 26
- type: crossing
kind: uwc
name: Stockwell Lane Crossing
miles: 134
chains: 53
- type: crossing
kind: foot
name: Hardacre No.1 Foot Crossing
miles: 134
chains: 62
- type: crossing
kind: uwc
name: Hardacre No.2 Crossing
miles: 135
chains: 4
- type: crossing
kind: foot
name: Alvington 17 Foot Crossing
miles: 134
chains: 66
- type: crossing
kind: foot
name: Alvington 18 Foot Crossing
miles: 135
chains: 38
- type: crossing
kind: foot
name: Woolaston 117 Foot Crossing
miles: 135
chains: 56
- type: crossing
kind: foot
name: Woolaston 118 Foot Crossing
miles: 135
chains: 77
- type: crossing
kind: r/g
name: Woolaston Level Crossing
miles: 136
chains: 14
- type: minorBridge
name: Farm Bridge
category: minorRoad
position: over
miles: 136
chains: 27
- type: crossing
kind: uwc
name: High Hall Crossing
miles: 138
chains: 14
- type: minorBridge
name: Pill House Bridge
position: over
category: road
miles: 139
chains: 9
- type: minorBridge
name: Farm Bridge
category: minorRoad
position: under
miles: 139
chains: 56
- type: minorBridge
name: Snipe Hill Bridge
position: over
category: road
miles: 140
chains: 22
- type: minorBridge
name: Mead School Bridge
position: over
category: road
miles: 140
chains: 35
- type: minorBridge
name: Sedbury Lane
position: over
category: road
miles: 140
chains: 55
- type: tunnel
tunnelType: whole
name: Chepstow Tunnel
length: 0mi 359yd
description: May be illuminated
miles: 140
chains: 69
- type: minorBridge
name: Beachley Road
position: over
category: road
miles: 141
chains: 0
- type: bridge
name: Chepstow Bridge
position: under
category: waterway
description: Crosses River Wye
miles: 141
chains: 10
- type: station
name: Chepstow
miles: 141
chains: 33
- type: bridge
name: Motorway
position: over
category: motorway
roadName: M48
miles: 143
chains: 4
- type: crossing
kind: uwc
name: Sharpe's Crossing
miles: 143
chains: 15
- type: crossing
kind: omsl
name: Three Gates Crossing
miles: 143
chains: 35
- type: crossing
kind: uwc
name: Curb Hut Crossing
miles: 144
chains: 6
- type: minorBridge
name: Redcliffe Bridge
category: minorRoad
position: over
miles: 144
chains: 40
- type: crossing
kind: omsl
name: Mathern 21 Foot Crossing
miles: 144
chains: 68
- type: crossing
name: Ifton Hill Farm Crossing
kind: uwc
miles: 145
chains: 11
- type: minorBridge
name: New Passage Road
position: over
category: road
miles: 145
chains: 67
- type: minorBridge
category: foot
position: over
miles: 146
chains: 0
- type: minorBridge
name: Portskewett Road
category: minorRoad
position: over
miles: 146
chains: 32
- type: crossing
name: Caldicot Level Crossing
kind: CCTV
description: CCTV by SWCC
miles: 147
chains: 4
- type: crossing
name: Rhymers Crossing
kind: uwc
miles: 147
chains: 25
- type: minorBridge
name: Caldicot Road Bridge
position: over
category: road
miles: 147
chains: 51
- type: crossing
kind: uwc
name: Caldicot Station Crossing
miles: 148
chains: 0
- type: station
name: Caldicot
miles: 148
chains: 2
- type: electrificationChange
from:
elec: none
to:
elec: 25kvac
eco: Didcot
miles: 148
chains: 50
- type: minorBridge
name: Ifton Tramway Underbridge
category: minorRoad
position: under
miles: 148
chains: 50
- type: station
name: Severn Tunnel Junction
description: Platforms 1 & 2 on this line of route
miles: 148
chains: 61
- type: junction
name: Severn Tunnel Junction
diverges: left
direction: up
description: to Bristol via Severn Tunnel
goto: '0000'
entryPoint: severn-tunnel-junction
miles: 149
chains: 14
- type: continues
routeName: Bristol Parkway - Cardiff West Shunt
routeId: '0000'
entryPoint: severn-tunnel-junction

View File

@@ -0,0 +1 @@
# Gloucster - Malvern Wells

View File

@@ -0,0 +1 @@
# Romsey - Southampton Ctl via Easleigh

View File

@@ -0,0 +1 @@
# Easleigh - Fareham

View File

@@ -0,0 +1,576 @@
{
"$schema": "https://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"routeStart",
"routeEnd",
"routeId",
"updated",
"signallerStart",
"signallerEnd",
"elecStart",
"elecEnd",
"routeDetail"
],
"additionalProperties": false,
"properties": {
"routeStart": {
"type": "string",
"description": "The route start point"
},
"routeEnd": {
"type": "string",
"description": "The route end point"
},
"routeId": {
"type": "string",
"description": "The routes ID Code",
"pattern": "^[0-9]{4}$"
},
"updated": {
"type": "string",
"format": "date",
"description": "Date the route was last updated"
},
"checked": {
"type": "string",
"format": "date",
"description": "Date the route was last checked for accuracy"
},
"signallerStart": {
"type": "string",
"description": "The signaller at the start of the route"
},
"signallerEnd": {
"type": "string",
"description": "The signalller at the end of the route"
},
"elecStart": {
"$ref": "#/definitions/elecInfo",
"description": "Electrification details at the route start"
},
"elecEnd": {
"$ref": "#/definitions/elecInfo",
"description": "Electrification info at the end of the route"
},
"routeDetail": {
"type": "array",
"additionalItems": false,
"items": {
"anyOf": [
{
"$ref": "#/definitions/bridge"
},
{
"$ref": "#/definitions/crossing"
},
{
"$ref": "#/definitions/crossover"
},
{
"$ref": "#/definitions/electrificationChange"
},
{
"$ref": "#/definitions/junction"
},
{
"$ref": "#/definitions/loop"
},
{
"$ref": "#/definitions/routeContinuation"
},
{
"$ref": "#/definitions/siteof"
},
{
"$ref": "#/definitions/station"
},
{
"$ref": "#/definitions/tunnel"
},
{
"$ref": "#/definitions/signallerChange"
}
]
}
}
},
"definitions": {
"elecInfo": {
"type": "object",
"properties": {
"elec": {
"type": "string",
"enum": [
"25kvac",
"750vdc",
"650vdc",
"1500vdc",
"none"
]
},
"eco": {
"type": "string"
}
},
"required": [
"elec"
]
},
"bridge": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"bridge",
"minorBridge"
]
},
"roadName": {
"type": "string"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"position": {
"type": "string",
"enum": [
"over",
"under"
]
},
"category": {
"type": "string",
"enum": [
"road",
"aroad",
"motorway",
"foot",
"waterway",
"stream",
"rail",
"minorRoad",
"pipeline"
]
},
"miles": {
"type": "number"
},
"chains": {
"type": "number"
}
},
"required": [
"type",
"position",
"category",
"miles",
"chains"
],
"additionalProperties": false
},
"crossing": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "crossing"
},
"kind": {
"type": "string",
"enum": [
"CCTV",
"AHB",
"uwc",
"foot",
"omsl",
"r/g",
"msl",
"mcb",
"mg",
"mcg",
"aocl"
]
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"miles": {
"type": "number"
},
"chains": {
"type": "number"
}
},
"required": [
"type",
"kind",
"name",
"miles",
"chains"
],
"additionalProperties": false
},
"crossover": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "crossovers"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"miles": {
"type": "number"
},
"chains": {
"type": "number"
}
},
"required": [
"type",
"name",
"miles",
"chains"
],
"additionalProperties": false
},
"electrificationChange": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "electrificationChange"
},
"miles": {
"type": "number"
},
"chains": {
"type": "number"
},
"from": {
"type": "object",
"properties": {
"elec": {
"type": "string",
"enum": [
"25kvac",
"750vdc",
"650vdc",
"1500vdc",
"none"
]
},
"eco": {
"type": "string"
}
},
"required": [
"elec"
],
"additionalProperties": false
},
"to": {
"type": "object",
"properties": {
"elec": {
"type": "string",
"enum": [
"25kvac",
"750vdc",
"650vdc",
"1500vdc",
"none"
]
},
"eco": {
"type": "string"
}
},
"required": [
"elec"
],
"additionalProperties": false
}
},
"required": [
"miles",
"chains"
],
"additionalProperties": false
},
"junction": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "junction"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"diverges": {
"type": "string",
"enum": [
"left",
"right",
"both"
]
},
"direction": {
"type": "string",
"enum": [
"up",
"down",
"both"
]
},
"miles": {
"type": "number"
},
"chains": {
"type": "number"
},
"elecBranch": {
"type": "string",
"enum": [
"25kvac",
"750vdc",
"650vdc",
"1500vdc",
"none"
]
},
"goto": {
"type": "string"
},
"entryPoint": {
"type": "string"
}
},
"required": [
"name",
"diverges",
"direction",
"miles",
"chains",
"type"
],
"additionalProperties": false
},
"loop": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "loop"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"position": {
"type": "string",
"enum": [
"left",
"right",
"both"
]
},
"miles": {
"type": "number"
},
"chains": {
"type": "number"
},
"elecLoop": {
"type": "string",
"enum": [
"25kvac",
"750vdc",
"650vdc",
"1500vdc",
"none"
]
}
},
"required": [
"type",
"name",
"position",
"miles",
"chains"
],
"additionalProperties": false
},
"routeContinuation": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "continues"
},
"routeName": {
"type": "string",
"description": "Name to display"
},
"routeId": {
"type": "string",
"description": "Id of route"
},
"entryPoint": {
"type": "string",
"description": "Entrypoint of other route (lowercase, replacing whitespace with -, junction always referred to as jn"
}
},
"required": [
"type",
"routeName",
"routeId",
"entryPoint"
],
"additionalProperties": false
},
"siteof": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "siteof"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"miles": {
"type": "number"
},
"chains": {
"type": "number"
}
},
"required": [
"type",
"name",
"miles",
"chains"
],
"additionalProperties": false
},
"station": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "station"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"miles": {
"type": "number"
},
"chains": {
"type": "number"
}
},
"required": [
"type",
"name",
"miles",
"chains"
]
},
"tunnel": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "tunnel"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"tunnelType": {
"type": "string",
"enum": [
"start",
"end",
"mid",
"whole"
]
},
"length": {
"type": "string",
"pattern": ""
},
"miles": {
"type": "number"
},
"chains": {
"type": "number"
}
},
"required": [
"type",
"name",
"tunnelType",
"miles",
"chains"
],
"additionalProperties": false
},
"signallerChange": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "signallerChange"
},
"from": {
"type": "string"
},
"to": {
"type": "string"
},
"miles": {
"type": "number"
},
"chains": {
"type": "number"
}
},
"required": [
"from",
"to",
"miles",
"chains"
],
"additionalProperties": false
}
}
}

View File

@@ -2,5 +2,14 @@ import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
export default defineConfig({ export default defineConfig({
plugins: [sveltekit()] plugins: [sveltekit()],
build: {
assetsInlineLimit: 0,
cssCodeSplit: true,
sourcemap: false,
target: 'esnext',
modulePreload: {
polyfill: false
}
}
}); });