Merge pull request #44 from 0hAodha/addStations

Add stations
This commit is contained in:
2023-03-11 18:35:42 +00:00
committed by GitHub
21 changed files with 436 additions and 447 deletions

View File

@ -16,7 +16,7 @@ To kill the npm process do `CTRL + C` in your terminal.
To kill the firebase emulators run `fg` to bring the process to the foreground, then do `CTRL + C` To kill the firebase emulators run `fg` to bring the process to the foreground, then do `CTRL + C`
# Links # Links
Main site: [irishrailtracker.web.app](https://irishrailtracker.web.app/) Deployed site: [irishrailtracker.web.app](https://irishrailtracker.web.app/)
Jira: [trainenthusiasts.atlassian.net](https://trainenthusiasts.atlassian.net/jira/software/projects/TE/boards/1) Jira: [trainenthusiasts.atlassian.net](https://trainenthusiasts.atlassian.net/jira/software/projects/TE/boards/1)

View File

@ -18,7 +18,7 @@ exports.getStationData = functions.https.onRequest((request, response) => {
// fetch the "stations" collection // fetch the "stations" collection
admin.firestore().collection('stations').get().then((snapshot) => { admin.firestore().collection('stations').get().then((snapshot) => {
if (snapshot.empty) { if (snapshot.empty) {
response.send({data: "Error fetching station data from the database"}) response.status(404).send({data: "Error fetching station data from the database"})
return; return;
} }
// iterate through each of the collection's documents // iterate through each of the collection's documents
@ -32,39 +32,69 @@ exports.getStationData = functions.https.onRequest((request, response) => {
// function to populate the Firestore database with station data from Irish Rail // function to populate the Firestore database with station data from Irish Rail
exports.postStationData = functions.https.onRequest((request, response) => { exports.postStationData = functions.https.onRequest((request, response) => {
// helper functon to parse station JSON objects
function parseJSON(result) {
let jsonStr = JSON.stringify(result);
let jsonObj = JSON.parse(jsonStr);
let jsonData = jsonObj.ArrayOfObjStation.objStation;
return jsonData;
}
// helper function to write to the database
function batchWriteDB(request, response, db, jsonData, dartCodes, stationTypeCode) {
response.set('Access-Control-Allow-Origin', '*'); response.set('Access-Control-Allow-Origin', '*');
response.set('Access-Control-Allow-Credentials', 'true'); response.set('Access-Control-Allow-Credentials', 'true');
cors(request, response, () => { cors(request, response, () => {
axios.get('http://api.irishrail.ie/realtime/realtime.asmx/getAllStationsXML') var batchWrite = db.batch();
.then((res) => { jsonData.forEach((doc) => {
// append if the dartCodes hashset is empty or the current station is not present
if (dartCodes.size == 0 || !dartCodes.has(doc["StationCode"][0])) {
doc["StationType"] = [stationTypeCode]
var docID = db.collection('stations').doc(doc["StationCode"][0])
batchWrite.set(docID, doc);
}
})
batchWrite.commit()
})
}
response.set('Access-Control-Allow-Origin', '*');
response.set('Access-Control-Allow-Credentials', 'true');
cors(request, response, () => {
// fetch dart stations and classify as dart stations
axios.get('http://api.irishrail.ie/realtime/realtime.asmx/getAllStationsXML_WithStationType?StationType=D').then(res => {
// XML to JSON // XML to JSON
parseString(res.data, function(err, result) { parseString(res.data, function(err, result) {
let jsonStr = JSON.stringify(result); let jsonData = parseJSON(result)
let jsonObj = JSON.parse(jsonStr);
let jsonData = jsonObj.ArrayOfObjStation.objStation;
// batch delete all of the "stations" collection's documents // batch delete all of the station collection's documents
var db = admin.firestore(); var db = admin.firestore()
admin.firestore().collection('stations').get().then((snapshot) => { admin.firestore().collection('stations').get().then((snapshot) => {
var batchDelete = db.batch(); var batchDelete = db.batch()
snapshot.forEach(doc => { snapshot.forEach(doc => {
batchDelete.delete(doc.ref); batchDelete.delete(doc.ref)
}); })
batchDelete.commit().then(function() { batchDelete.commit().then(function() {
// batch write all station JSON objects to the "stations" collection // store all dart codes into a hashset
var batchWrite = db.batch(); // compare these with the station call with code "all" to avoid duplicates
let dartCodes = new Set()
batchWriteDB(request, response, db, jsonData, dartCodes, "Dart")
// populate the dartCodes hashset
jsonData.forEach((doc) => { jsonData.forEach((doc) => {
// set the station's ID as the document ID dartCodes.add(doc["StationCode"][0])
var docID = db.collection('stations').doc(doc["StationCode"][0]); })
batchWrite.set(docID, doc);
});
batchWrite.commit().then(function () { // fetch all train stations
response.send({data: "Successfully fetched and uploaded station data from Irish Rail"}); axios.get('http://api.irishrail.ie/realtime/realtime.asmx/getAllStationsXML_WithStationType?StationType=A').then(res => {
}); parseString(res.data, function(err, result) {
let jsonData = parseJSON(result)
batchWriteDB(request, response, db, jsonData, dartCodes, "Train")
response.send({data: "Successfully fetched and upload station data from Irish Rail"})
})
})
}) })
}) })
}) })
@ -96,7 +126,7 @@ exports.getLiveTrainData = functions.https.onRequest((request, response) => {
// function to populate the Firestore database with live train data from Irish Rail // function to populate the Firestore database with live train data from Irish Rail
exports.postLiveTrainData = functions.https.onRequest((request, response) => { exports.postLiveTrainData = functions.https.onRequest((request, response) => {
// helper function to parse train objects // helper function to parse train JSON objects
function parseJSON(result) { function parseJSON(result) {
let jsonStr = JSON.stringify(result); let jsonStr = JSON.stringify(result);
let jsonObj = JSON.parse(jsonStr); let jsonObj = JSON.parse(jsonStr);
@ -126,7 +156,7 @@ exports.postLiveTrainData = functions.https.onRequest((request, response) => {
response.set('Access-Control-Allow-Origin', '*'); response.set('Access-Control-Allow-Origin', '*');
response.set('Access-Control-Allow-Credentials', 'true'); response.set('Access-Control-Allow-Credentials', 'true');
cors(request, response, () => { cors(request, response, () => {
// fetch mainland trains // fetch mainland trains and classify as trains
axios.get('https://api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML_WithTrainType?TrainType=M').then(res => { axios.get('https://api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML_WithTrainType?TrainType=M').then(res => {
// XML to JSON // XML to JSON
parseString(res.data, function(err, result) { parseString(res.data, function(err, result) {
@ -141,20 +171,20 @@ exports.postLiveTrainData = functions.https.onRequest((request, response) => {
}); });
batchDelete.commit().then(function() { batchDelete.commit().then(function() {
// batch write all station JSON objects to the liveTrainData collection // batch write all train JSON objects to the liveTrainData collection
batchWriteDB(request, response, db, jsonData, "M"); batchWriteDB(request, response, db, jsonData, "Train");
// fetch suburban trains // fetch suburban trains and classify as trains
axios.get('https://api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML_WithTrainType?TrainType=S').then(res => { axios.get('https://api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML_WithTrainType?TrainType=S').then(res => {
parseString(res.data, function(err, result) { parseString(res.data, function(err, result) {
let jsonData = parseJSON(result) let jsonData = parseJSON(result)
batchWriteDB(request, response, db, jsonData, "S"); batchWriteDB(request, response, db, jsonData, "Train");
// fetch dart trains // fetch dart trains and classify as darts
axios.get('https://api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML_WithTrainType?TrainType=D').then(res => { axios.get('https://api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML_WithTrainType?TrainType=D').then(res => {
parseString(res.data, function(err, result) { parseString(res.data, function(err, result) {
let jsonData = parseJSON(result) let jsonData = parseJSON(result)
batchWriteDB(request, response, db, jsonData, "D"); batchWriteDB(request, response, db, jsonData, "Dart");
response.send({data: "Successfully fetched and uploaded live train data from Irish Rail"}); response.send({data: "Successfully fetched and uploaded live train data from Irish Rail"});
}) })
}) })

View File

@ -32,6 +32,7 @@ describe('Firebase cloud function tests', function() {
expect(result.body.data[0]).haveOwnProperty('StationLongitude'); expect(result.body.data[0]).haveOwnProperty('StationLongitude');
expect(result.body.data[0]).haveOwnProperty('StationCode'); expect(result.body.data[0]).haveOwnProperty('StationCode');
expect(result.body.data[0]).haveOwnProperty('StationId'); expect(result.body.data[0]).haveOwnProperty('StationId');
expect(result.body.data[0]).haveOwnProperty('StationType');
}), }),
this.timeout(100000); this.timeout(100000);

View File

@ -1,74 +0,0 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
position: relative;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition: color 0.5s, background-color 0.5s;
line-height: 1.6;
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69" xmlns:v="https://vecta.io/nano"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

Before

Width:  |  Height:  |  Size: 308 B

View File

@ -1,35 +0,0 @@
@import './base.css';
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
}
@media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}
@media (min-width: 1024px) {
body {
display: flex;
place-items: center;
}
#app {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
}
}

BIN
src/assets/station.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -14,8 +14,6 @@
import { Bar } from 'vue-chartjs' import { Bar } from 'vue-chartjs'
import { store } from '../store/store' import { store } from '../store/store'
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale } from 'chart.js' import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale } from 'chart.js'
import { resolveDirective } from 'vue'
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale) ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale)
export default { export default {
@ -31,35 +29,23 @@
data: [store.insights["percentageLate"]] data: [store.insights["percentageLate"]]
}, },
{ {
label: 'Early/On Time', label: 'Early/ontime',
backgroundColor: '#4ADC57', backgroundColor: '#4ADC57',
data: [store.insights["percentageNotLate"]] data: [store.insights["percentageNotLate"]]
}]
}
]
}, },
chartOptions: { chartOptions: {
responsive: true responsive: true
} }
} }
} }
} }
</script> </script>
<style scoped> <style scoped>
#lateGraph{ #lateGraph{
position: relative; position: relative;
height:48%; height:48%;
left:0px; left:0px;
} }
</style> </style>

View File

@ -35,8 +35,6 @@
import app from "../api/firebase" import app from "../api/firebase"
import { getAuth, onAuthStateChanged, signOut } from "firebase/auth" import { getAuth, onAuthStateChanged, signOut } from "firebase/auth"
export default { export default {
name: "Navbar", name: "Navbar",
@ -66,7 +64,6 @@ export default {
</script> </script>
<style scoped> <style scoped>
.router-link-active{ .router-link-active{
color: rgb(0, 0, 0); color: rgb(0, 0, 0);
font-weight: 600; font-weight: 600;
@ -78,5 +75,4 @@ a{
color: black; color: black;
font-weight: 100; font-weight: 100;
} }
</style> </style>

View File

@ -1,70 +0,0 @@
<template>
<div id= "sidebarDiv">
<div id = "sidebarHeader">
<img id = "headerImage" src="../assets/train-solid.svg" alt="Train Icon">
<div v-on:click="store.setDisplay(false)" id="xButton">X</div>
</div>
<div id= "sidebarDiv">
<h2>Train Code: {{ store.selectedDataMap["TrainCode"] }}</h2>
<p id = "typeP">Type: {{ store.selectedDataMap["TrainType"] }}</p>
<p id = "dateP">Date: {{ store.selectedDataMap["TrainDate"] }}</p>
<p id = "dateP">Status: {{ store.selectedDataMap["TrainStatus"] }}</p>
<p id = "dateP">Train Position - Long: {{ store.selectedDataMap["TrainLongitude"] }} Lat: {{ store.selectedDataMap["TrainLatitude"] }}</p>
<p id = "directionP">Direction: {{ store.selectedDataMap["Direction"] }}</p>
<p id = "messageP">Public Message: {{ store.selectedDataMap["PublicMessage"] }}</p>
</div>
</div>
</template>
<script>
import {store} from '../store/store'
export default {
name: "SidebarPanel",
data() {
return {
store
}
}
}
</script>
<style scoped>
#sidebarHeader{
position: relative;
top:0%;
height: 15%;
width: 100%;
overflow: hidden;
}
#sidebarDiv{
position: absolute;
height: 80%;
width: 100%;
color: white;
}
#headerImage{
height: 80%;
width: auto;
overflow: hidden;
position: relative;
top: 10px;
}
#xButton{
font-size: 80%;
font-family: Georgia;
color: white;
position: absolute;
top:10px;
right:10px;
}
#xButton:hover{
color:red;
}
</style>

View File

@ -0,0 +1,66 @@
<template>
<div id="sidebarDiv">
<div id="sidebarHeader">
<img id="headerImage" src="../assets/station.png" alt="Station Icon">
<div v-on:click="store.setDisplaySelectedStation(false)" id="xButton">X</div>
</div>
<div id="sidebarDiv">
<h2>Station Code: {{ store.selectedStation["StationCode"][0] }}</h2>
<p id="typeP">Type: {{ store.selectedStation["StationType"][0] }}</p>
<p id="dateP">Station Position - Long: {{ store.selectedStation["StationLongitude"][0] }} Lat: {{ store.selectedStation["StationLatitude"][0] }}</p>
<p id="directionP">Description: {{ store.selectedStation["StationDesc"][0] }}</p>
</div>
</div>
</template>
<script>
import { store } from '../store/store'
export default {
name: "StationSidebar",
data() {
return {
store
}
}
}
</script>
<style scoped>
#sidebarHeader{
position: relative;
top:0%;
height: 15%;
width: 100%;
overflow: hidden;
}
#sidebarDiv{
position: absolute;
height: 80%;
width: 100%;
color: white;
}
#headerImage{
height: 80%;
width: auto;
overflow: hidden;
position: relative;
top: 10px;
}
#xButton{
font-size: 80%;
font-family: Georgia;
color: white;
position: absolute;
top:10px;
right:10px;
}
#xButton:hover{
color:red;
}
</style>

View File

@ -0,0 +1,69 @@
<template>
<div id="sidebarDiv">
<div id="sidebarHeader">
<img id="headerImage" src="../assets/train-solid.svg" alt="Train Icon">
<div v-on:click="store.setDisplaySelectedTrain(false)" id="xButton">X</div>
</div>
<div id="sidebarDiv">
<h2>Train Code: {{ store.selectedTrain["TrainCode"][0] }}</h2>
<p id="typeP">Type: {{ store.selectedTrain["TrainType"][0] }}</p>
<p id="dateP">Date: {{ store.selectedTrain["TrainDate"][0] }}</p>
<p id="dateP">Status: {{ store.selectedTrain["TrainStatus"][0] }}</p>
<p id="dateP">Train Position - Long: {{ store.selectedTrain["TrainLongitude"][0] }} Lat: {{ store.selectedTrain["TrainLatitude"][0] }}</p>
<p id="directionP">Direction: {{ store.selectedTrain["Direction"][0] }}</p>
<p id="messageP">Public Message: {{ store.selectedTrain["PublicMessage"][0] }}</p>
</div>
</div>
</template>
<script>
import { store } from '../store/store'
export default {
name: "TrainSidebar",
data() {
return {
store
}
}
}
</script>
<style scoped>
#sidebarHeader{
position: relative;
top:0%;
height: 15%;
width: 100%;
overflow: hidden;
}
#sidebarDiv{
position: absolute;
height: 80%;
width: 100%;
color: white;
}
#headerImage{
height: 80%;
width: auto;
overflow: hidden;
position: relative;
top: 10px;
}
#xButton{
font-size: 80%;
font-family: Georgia;
color: white;
position: absolute;
top:10px;
right:10px;
}
#xButton:hover{
color:red;
}
</style>

View File

@ -1,7 +0,0 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
/>
</svg>
</template>

View File

@ -1,7 +0,0 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
<path
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
/>
</svg>
</template>

View File

@ -1,7 +0,0 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
<path
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
/>
</svg>
</template>

View File

@ -1,7 +0,0 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
/>
</svg>
</template>

View File

@ -1,19 +0,0 @@
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true"
role="img"
class="iconify iconify--mdi"
width="24"
height="24"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 24 24"
>
<path
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
fill="currentColor"
></path>
</svg>
</template>

View File

@ -1,10 +1,10 @@
<template> <template>
<div id="statsDiv"> <div id="statsDiv">
<div id="trainPie"> <div id="trainPie">
<Pie <Pie :data="trainData" :options="chartOptions" />
:data="chartData" </div>
:options="chartOptions" <div id="stationPie">
/> <Pie :data="stationData" :options="chartOptions" />
</div> </div>
</div> </div>
</template> </template>
@ -13,35 +13,43 @@
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js' import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'
import { Pie } from 'vue-chartjs' import { Pie } from 'vue-chartjs'
import { store } from '../store/store.js' import { store } from '../store/store.js'
ChartJS.register(ArcElement, Tooltip, Legend) ChartJS.register(ArcElement, Tooltip, Legend)
export default { export default {
name: 'pieChart', name: 'pieChart',
components: { Pie },
components: {
Pie
},
data() { data() {
return{ return{
chartData:{ trainData: {
labels: ['Mainland', 'Suburban', 'Darts'], labels: ['Trains', 'Darts'],
datasets: [{ datasets: [{
backgroundColor: ['#41B883', '#E46651', '#00D8FF'], backgroundColor: ['#41B883', '#E46651', '#00D8FF'],
data: [store.insights["numMainland"], store.insights["numSuburban"], store.insights["numDart"]] data: [store.insights["numTrains"], store.insights["numDarts"]]
}] }]
}, },
stationData: {
labels: ['Train Stations', 'Dart Stations'],
datasets: [{
backgroundColor: ['#41B883', '#E46651', '#00D8FF'],
data: [store.insights["numTrainStations"], store.insights["numDartStations"]]
}]
},
chartOptions: { chartOptions: {
responsive: true, responsive: true,
maintainAspectRatio: false, maintainAspectRatio: false,
radius: 80, radius: 80,
hoverOffset: 0 hoverOffset: 0
} }
} }
} }
} }
</script> </script>
<style scoped> <style scoped>
</style> </style>

View File

@ -1,23 +1,20 @@
<template> <template>
<Navbar /> <Navbar />
<div v-if="this.insights" id="insightDiv"> <div v-if="this.insights" id="insightDiv">
<h1 style="padding-left: 10px; ">Insights</h1> <h1 style="padding-left: 10px;">Train Insights</h1>
<div id="trainTotal"> <div id="trainTotal">
<div id="trainTotalText"> <div id="trainTotalText">
<p>Total number of trains: {{ this.insights["totalNumTrains"] }}</p> <p>Total number of trains: {{ this.insights["totalNumTrains"] }}</p>
<ul> <ul>
<li><p>Mainland: {{ this.insights["numMainland"] }}</p></li> <li><p>Trains: {{ this.insights["numTrains"] }}</p></li>
<li><p>Suburban: {{ this.insights["numSuburban"] }}</p></li> <li><p>Darts: {{ this.insights["numDarts"] }}</p></li>
<li><p>Darts: {{ this.insights["numDart"] }}</p></li>
</ul> </ul>
</div> </div>
<div id="trainTotalChart"> <div id="trainTotalChart">
<pieChart id="trainPie" /> <pieChart id="trainPie" />
</div> </div>
</div> </div>
<hr> <hr>
<p>Number of actively running trains: {{ this.insights["numRunningTrains"] }}</p> <p>Number of actively running trains: {{ this.insights["numRunningTrains"] }}</p>
<p>Percentage late: {{ this.insights["percentageLate"] }}%</p> <p>Percentage late: {{ this.insights["percentageLate"] }}%</p>
@ -25,11 +22,25 @@
<div id="statsDiv"> <div id="statsDiv">
<BarChart id="lateGraph" /> <BarChart id="lateGraph" />
</div> </div>
<p v-if="this.latestTrain['TrainCode']">Latest train: {{ this.latestTrain["TrainCode"][0] }}, {{ this.insights["latestTime"] }} mins late</p> <p v-if="this.latestTrain['TrainCode']">Latest train: {{ this.latestTrain["TrainCode"][0] }}, {{ this.insights["latestTime"] }} mins late</p>
<p v-if="this.earliestTrain['TrainCode']">Earliest train: {{ this.earliestTrain["TrainCode"][0] }}, {{ this.insights["earliestTime"] * -1 }} mins early</p> <p v-if="this.earliestTrain['TrainCode']">Earliest train: {{ this.earliestTrain["TrainCode"][0] }}, {{ this.insights["earliestTime"] * -1 }} mins early</p>
<h1 style="padding-left: 10px;">Station Insights</h1>
<div id="stationTotal">
<div id="stationTotalText">
<p>Total number of stations: {{ this.insights["totalNumStations"] }}</p>
<ul>
<li><p>Trains: {{ this.insights["numTrainStations"] }}</p></li>
<li><p>Darts: {{ this.insights["numDartStations"] }}</p></li>
</ul>
</div> </div>
<div id="stationTotalChart">
<pieChart id="stationPie" />
</div>
</div>
</div>
<div id="ads"> <div id="ads">
<h2>AdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAds <h2>AdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAds
AdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAds AdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAds
@ -41,15 +52,12 @@
</div> </div>
<hr> <hr>
<h1>Leaderboard</h1> <h1>Leaderboard</h1>
<div v-for="item in orderedTrains"> <div v-for="item in orderedTrains">
<h2>{{ this.rawData[item.jsonIndex]["TrainCode"][0] }}</h2> <h2>{{ this.rawData[item.jsonIndex]["TrainCode"][0] }}</h2>
<p v-if="item.time > 0">{{ item.time }} mins late</p> <p v-if="item.time > 0">{{ item.time }} mins late</p>
<p v-else>{{ item.time * -1}} mins early</p> <p v-else>{{ item.time * -1}} mins early</p>
</div> </div>
</template> </template>
<script> <script>
@ -75,7 +83,6 @@ export default {
Navbar, Navbar,
pieChart, pieChart,
BarChart BarChart
}, },
created() { created() {
@ -96,17 +103,18 @@ export default {
background-color: antiquewhite; background-color: antiquewhite;
} }
#trainTotal{ #trainTotal, #stationTotal {
height: 280px; height: 280px;
} }
#trainTotalText{ #trainTotalText, #stationTotalText {
position: relative; position: relative;
width: 30%; width: 30%;
height: 100%; height: 100%;
right:0px; right:0px;
} }
#trainTotalChart{
#trainTotalChart, #stationTotalChart {
position: absolute; position: absolute;
top:114px; top:114px;
width: 50%; width: 50%;
@ -117,7 +125,6 @@ export default {
#lateGraph { #lateGraph {
position: absolute; position: absolute;
height:50%; height:50%;
left:0px; left:0px;
} }

View File

@ -1,11 +1,11 @@
<template> <template>
<Navbar /> <Navbar />
<!-- <button id="hoverButton" @click="postLiveTrainData">Populate Database</button> -->
<!--Sidebar, fades out on click of X button-->
<transition id="sidebar" name="slideLeft"> <transition id="sidebar" name="slideLeft">
<div v-if="store.display && store.selectedDataMap"> <div v-if="store.displaySelectedTrain && store.selectedTrain">
<SidebarPanel /> <TrainSidebar />
</div>
<div v-else-if="store.displaySelectedStation && store.selectedStation">
<StationSidebar />
</div> </div>
</transition> </transition>
@ -15,11 +15,11 @@
<ol-source-osm /> <ol-source-osm />
</ol-tile-layer> </ol-tile-layer>
<template v-for="coordinate, i in coordinates" :position="inline-block"> <!-- train overlay -->
<!-- overlay offset is the size of the image so that it is centered--> <template v-for="coordinate, i in trainCoordinates" :position="inline-block">
<ol-overlay :position="coordinate" :positioning="center-center" :offset="[-14,-16]"> <ol-overlay :position="coordinate" :positioning="center-center" :offset="[-14,-16]">
<div class="overlay-content" @click="getSelectedTrain(i)"> <div class="overlay-content" @click="getSelectedTrain(i)">
<div v-if="getTrainType(i) === 'D'"> <div v-if="getTrainType(i) === 'Dart'">
<img v-if="isTrainRunning(i) && isTrainLate(i)" src="../assets/red-train-tram-solid.png" class="trainMapIcon" alt="Late DART Icon"> <img v-if="isTrainRunning(i) && isTrainLate(i)" src="../assets/red-train-tram-solid.png" class="trainMapIcon" alt="Late DART Icon">
<img v-else-if="isTrainRunning(i) && !isTrainLate(i)" src="../assets/green-train-tram-solid.png" class="trainMapIcon" alt="On-Time DART Icon"> <img v-else-if="isTrainRunning(i) && !isTrainLate(i)" src="../assets/green-train-tram-solid.png" class="trainMapIcon" alt="On-Time DART Icon">
<img v-else src="../assets/train-tram-solid.svg" class="trainMapIcon" alt="Not Running DART Icon"> <img v-else src="../assets/train-tram-solid.svg" class="trainMapIcon" alt="Not Running DART Icon">
@ -29,7 +29,15 @@
<img v-else-if="isTrainRunning(i) && !isTrainLate(i)" src="../assets/green-train-solid.png" class="trainMapIcon" alt="On-Time Train Icon"> <img v-else-if="isTrainRunning(i) && !isTrainLate(i)" src="../assets/green-train-solid.png" class="trainMapIcon" alt="On-Time Train Icon">
<img v-else src="../assets/train-solid.svg" class="trainMapIcon" alt="Not Running Train Icon"> <img v-else src="../assets/train-solid.svg" class="trainMapIcon" alt="Not Running Train Icon">
</div> </div>
</div>
</ol-overlay>
</template>
<!-- station overlay -->
<template v-for="coordinate, i in stationCoordinates" :position="inline-block">
<ol-overlay :position="coordinate" :positioning="center-center" :offset="[-14,-16]">
<div class="overlay-content" @click="getSelectedStation(i)">
<img src="../assets/station.png" class="stationMapIcon" alt="Station Icon">
</div> </div>
</ol-overlay> </ol-overlay>
</template> </template>
@ -38,7 +46,6 @@
<div> <div>
<MarqueeText v-if="publicMessages.length > 0" id="publicMessageTicker" :paused="isPaused" :duration="800" :repeat="1" <MarqueeText v-if="publicMessages.length > 0" id="publicMessageTicker" :paused="isPaused" :duration="800" :repeat="1"
@mouseenter="isPaused = !isPaused" @mouseleave="isPaused = false"> @mouseenter="isPaused = !isPaused" @mouseleave="isPaused = false">
<!-- <span v-for="message in publicMessages"> {{ "---" + message + "---" }} </span> -->
<span v-for="message in publicMessages"> {{ message + " • " }} </span> <span v-for="message in publicMessages"> {{ message + " • " }} </span>
</MarqueeText> </MarqueeText>
</div> </div>
@ -48,11 +55,12 @@
import { store } from '../store/store' import { store } from '../store/store'
import { ref } from 'vue'; import { ref } from 'vue';
import { fromLonLat, toLonLat } from 'ol/proj.js'; import { fromLonLat, toLonLat } from 'ol/proj.js';
import app from '../api/firebase';
import { getFunctions, httpsCallable, connectFunctionsEmulator } from "firebase/functions"; import { getFunctions, httpsCallable, connectFunctionsEmulator } from "firebase/functions";
import app from '../api/firebase';
import Navbar from '../components/Navbar.vue' import Navbar from '../components/Navbar.vue'
import MarqueeText from 'vue-marquee-text-component' import MarqueeText from 'vue-marquee-text-component'
import SidebarPanel from '../components/SidebarPanel.vue'; import TrainSidebar from '../components/TrainSidebar.vue';
import StationSidebar from '../components/StationSidebar.vue';
export default { export default {
name: "MapPage", name: "MapPage",
@ -77,46 +85,55 @@ export default {
strokeColor, strokeColor,
fillColor, fillColor,
coordinates: [], trainCoordinates: [],
dbLiveTrainData: [], stationCoordinates: [],
allDataMap: {}, allTrains: {},
store, allStations: {},
publicMessages: [], publicMessages: [],
isPaused: false, isPaused: false,
store,
} }
}, },
components: { components: {
Navbar, Navbar,
MarqueeText, MarqueeText,
SidebarPanel TrainSidebar,
StationSidebar
}, },
created() { created() {
let host = window.location.hostname let host = window.location.hostname
if (host === '127.0.0.1' || host === 'localhost') { if (host === '127.0.0.1' || host === 'localhost') {
this.postLiveTrainData(); this.postTrainAndStationData();
} }
else { else {
this.getLiveTrainData(); this.getTrainAndStationData();
} }
// request new data every 60 seconds // request new data every 60 seconds
// window.setInterval(this.getLiveTrainData, 60000); // window.setInterval(this.getLiveTrainData, 60000);
}, },
methods: { methods: {
// method to assign a single train's data to the selected hashmap // method to display a selected train
getSelectedTrain(i) { getSelectedTrain(i) {
store.setSelectedDataMap(this.allDataMap[i]); store.setSelectedTrain(this.allTrains[i]);
store.setDisplay(true) if (store.displaySelectedStation) store.setDisplaySelectedStation(false)
store.setDisplaySelectedTrain(true)
},
// method to display a selected station
getSelectedStation(i) {
store.setSelectedStation(this.allStations[i]);
if (store.displaySelectedTrain) store.setDisplaySelectedTrain(false)
store.setDisplaySelectedStation(true)
}, },
// method to determine whether or not a selected train is late // method to determine whether or not a selected train is late
isTrainLate(i) { isTrainLate(i) {
// check if the train is running // check if the train is running
if (this.allDataMap[i]["TrainStatus"][0] == "R") { if (this.allTrains[i]["TrainStatus"][0] == "R") {
let publicMessage = this.allDataMap[i]["PublicMessage"][0]; let publicMessage = this.allTrains[i]["PublicMessage"][0];
let startTimeStr = publicMessage.indexOf("("); let startTimeStr = publicMessage.indexOf("(");
// check if the train is late // check if the train is late
@ -129,7 +146,7 @@ export default {
// method to determine whether or not a selected train is running // method to determine whether or not a selected train is running
isTrainRunning(i) { isTrainRunning(i) {
if (this.allDataMap[i]["TrainStatus"][0] == "R") { if (this.allTrains[i]["TrainStatus"][0] == "R") {
return true; return true;
} }
else { else {
@ -137,64 +154,62 @@ export default {
} }
}, },
// method that returns the type of train in string form "D" for dart, "S" for suburban, "M" for mainland // method that returns the type of train (either "Train" or "Dart")
getTrainType(i) { getTrainType(i) {
return this.allDataMap[i]["TrainType"][0]; return this.allTrains[i]["TrainType"][0];
}, },
// method to fetch live train data from the database // method to fetch live train and station data from the database
getLiveTrainData() { getTrainAndStationData() {
const functions = getFunctions(app); const functions = getFunctions(app);
let host = window.location.hostname let host = window.location.hostname
if (host === '127.0.0.1' || host == 'localhost') { if (host === '127.0.0.1' || host == 'localhost') {
connectFunctionsEmulator(functions, host, 5001); connectFunctionsEmulator(functions, host, 5001);
} }
const getData = httpsCallable(functions, 'getLiveTrainData'); const getTrainData = httpsCallable(functions, 'getLiveTrainData');
let loader = this.$loading.show({ let loader = this.$loading.show({
loader: 'dots', loader: 'dots',
container: this.$refs.container, container: this.$refs.container,
canCancel: false canCancel: false
}); });
getData().then((response) => { getTrainData().then((response) => {
try { try {
this.dbLiveTrainData = response.data; if (!response.data) throw new Error("Error fetching live train data from the database");
if (!this.dbLiveTrainData) throw new Error("Error fetching live train data from the database");
var insights = { var insights = {
"totalNumTrains": 0,
"numRunningTrains": 0, "numRunningTrains": 0,
"numLateRunningTrains": 0, "numLateRunningTrains": 0,
"numMainland": 0, "numTrains": 0,
"numSuburban": 0, "numDarts": 0,
"numDart": 0 "totalNumStations": 0,
"numTrainStations": 0,
"numDartStations": 0
}; };
var unorderedTrains = []; var unorderedTrains = [];
var currentMessages = [];
var latest = null; var latest = null;
var earliest = null; var earliest = null;
var currLatestTime = null; var currLatestTime = null;
var currEarliestTime = null; var currEarliestTime = null;
var currentMessages = [];
// create an array of coordinates and hashmap with the key-values {index: JSON obj} // create an array of coordinates and hashmap with the key-values {index: JSON obj}
for (var i=0; i<this.dbLiveTrainData.length; i++) { for (var i=0; i<response.data.length; i++) {
let train = this.dbLiveTrainData[i]; let train = response.data[i];
this.allTrains[i] = train;
this.trainCoordinates[i] = ref(fromLonLat([train["TrainLongitude"][0], train["TrainLatitude"][0]]))
insights["totalNumTrains"] += 1
// filtering out \n in public message if (train["TrainType"][0] == "Train") insights["numTrains"] += 1;
else if (train["TrainType"][0] == "Dart") insights["numDarts"] += 1;
// filter out \n in public messages
train["PublicMessage"][0] = train["PublicMessage"][0].replace(/\\n/g, ". "); train["PublicMessage"][0] = train["PublicMessage"][0].replace(/\\n/g, ". ");
this.coordinates[i] = ref(fromLonLat([train["TrainLongitude"][0], train["TrainLatitude"][0]]))
this.allDataMap[i] = train;
if (train["TrainType"][0] == "M") insights["numMainland"] += 1;
else if (train["TrainType"][0] == "S") insights["numSuburban"] += 1;
else if (train["TrainType"][0] == "D") insights["numDart"] += 1;
let publicMessage = train["PublicMessage"][0]; let publicMessage = train["PublicMessage"][0];
currentMessages.push(publicMessage); currentMessages.push(publicMessage);
// check if the train is running // check if the train is running
if (this.dbLiveTrainData[i]["TrainStatus"][0] == "R") { if (train["TrainStatus"][0] == "R") {
insights["numRunningTrains"] += 1; insights["numRunningTrains"] += 1;
let startTimeStr = publicMessage.indexOf("("); let startTimeStr = publicMessage.indexOf("(");
let timeEnd = publicMessage.indexOf(" ", startTimeStr+1); let timeEnd = publicMessage.indexOf(" ", startTimeStr+1);
@ -204,7 +219,6 @@ export default {
// check if the train is late // check if the train is late
if (publicMessage[startTimeStr+1] != "-" && publicMessage[startTimeStr+1] != "0") { if (publicMessage[startTimeStr+1] != "-" && publicMessage[startTimeStr+1] != "0") {
insights["numLateRunningTrains"] += 1; insights["numLateRunningTrains"] += 1;
if (!latest) latest = train; if (!latest) latest = train;
// check for a new latest train // check for a new latest train
@ -228,18 +242,32 @@ export default {
insights["percentageLate"] = ((insights["numLateRunningTrains"] / insights["numRunningTrains"]) * 100).toFixed(2); insights["percentageLate"] = ((insights["numLateRunningTrains"] / insights["numRunningTrains"]) * 100).toFixed(2);
insights["percentageNotLate"] = (100 - insights["percentageLate"]).toFixed(2); insights["percentageNotLate"] = (100 - insights["percentageLate"]).toFixed(2);
insights["totalNumTrains"] = Object.keys(this.allDataMap).length;
insights["latestTime"] = currLatestTime; insights["latestTime"] = currLatestTime;
insights["earliestTime"] = currEarliestTime; insights["earliestTime"] = currEarliestTime;
this.publicMessages = currentMessages; this.publicMessages = currentMessages;
// assign the results to the Vue Store // assign the results to the Vue Store
store.setInsights(insights);
store.setEarliestTrain(earliest); store.setEarliestTrain(earliest);
store.setLatestTrain(latest); store.setLatestTrain(latest);
store.setRawData(this.dbLiveTrainData); store.setRawData(response.data);
store.setOrderedTrains(unorderedTrains); store.setOrderedTrains(unorderedTrains);
const getStationData = httpsCallable(functions, 'getStationData');
getStationData().then((response) => {
if (!response.data) throw new Error("Error fetching station from the database");
for (var i=0; i<response.data.length; i++) {
let station = response.data[i];
this.allStations[i] = station;
this.stationCoordinates[i] = ref(fromLonLat([station["StationLongitude"][0], station["StationLatitude"][0]]))
insights["totalNumStations"] += 1
if (station["StationType"][0] == "Dart") insights["numDartStations"] += 1;
else if (station["StationType"][0] == "Train") insights["numTrainStations"] += 1;
}
store.setInsights(insights);
loader.hide(); loader.hide();
})
} }
catch (error) { catch (error) {
console.log(error) console.log(error)
@ -249,15 +277,20 @@ export default {
}, },
// method to populate the database for local testing // method to populate the database for local testing
postLiveTrainData() { postTrainAndStationData() {
const functions = getFunctions(app); const functions = getFunctions(app);
let host = window.location.hostname let host = window.location.hostname
if (host === '127.0.0.1' || host === 'localhost') { if (host === '127.0.0.1' || host === 'localhost') {
connectFunctionsEmulator(functions, host, 5001); connectFunctionsEmulator(functions, host, 5001);
} }
const postData = httpsCallable(functions, 'postLiveTrainData'); // post live train data
postData().then((response) => { const postTrainData = httpsCallable(functions, 'postLiveTrainData');
this.getLiveTrainData() postTrainData().then((response) => {
// post station data
const postStationData = httpsCallable(functions, 'postStationData');
postStationData().then((reponse) => {
this.getTrainAndStationData()
})
}) })
} }
} }
@ -269,8 +302,6 @@ export default {
width: 1%; width: 1%;
} }
.trainMapIcon { .trainMapIcon {
width: 28px; width: 28px;
height: 32px; height: 32px;
@ -282,6 +313,18 @@ export default {
cursor: pointer; cursor: pointer;
} }
.stationMapIcon {
width: 14px;
height: 17px;
}
.stationMapIcon:hover {
width: 16px;
height: 19px;
cursor: pointer;
}
#sidebar{ #sidebar{
position: absolute; position: absolute;
height: 80%; height: 80%;
@ -329,6 +372,6 @@ export default {
color: black; color: black;
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif; font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
text-align: bottom; text-align: bottom;
font-size: 17px; font-size: 16px;
} }
</style> </style>

View File

@ -5,9 +5,11 @@ export const store = reactive({
latestTrain: {}, latestTrain: {},
earliestTrain: {}, earliestTrain: {},
orderedTrains: [], orderedTrains: [],
selectedDataMap: {}, selectedTrain: {},
selectedStation: {},
rawData: {}, rawData: {},
display: false, displaySelectedTrain: false,
displayedSelectedStation: false,
setInsights(insights) { setInsights(insights) {
this.insights = insights this.insights = insights
@ -33,11 +35,19 @@ export const store = reactive({
this.orderedTrains = unorderedTrains this.orderedTrains = unorderedTrains
}, },
setSelectedDataMap(selectedDataMap) { setSelectedTrain(selectedTrain) {
this.selectedDataMap = selectedDataMap this.selectedTrain = selectedTrain
}, },
setDisplay(bool) { setSelectedStation(selectedStation) {
this.display = bool this.selectedStation = selectedStation
},
setDisplaySelectedTrain(bool) {
this.displaySelectedTrain = bool
},
setDisplaySelectedStation(bool) {
this.displaySelectedStation = bool
} }
}) })