Add stations
This commit is contained in:
@ -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`
|
||||
|
||||
# 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)
|
||||
|
||||
|
@ -18,7 +18,7 @@ exports.getStationData = functions.https.onRequest((request, response) => {
|
||||
// fetch the "stations" collection
|
||||
admin.firestore().collection('stations').get().then((snapshot) => {
|
||||
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;
|
||||
}
|
||||
// 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
|
||||
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-Credentials', 'true');
|
||||
|
||||
cors(request, response, () => {
|
||||
var batchWrite = db.batch();
|
||||
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, () => {
|
||||
axios.get('http://api.irishrail.ie/realtime/realtime.asmx/getAllStationsXML')
|
||||
.then((res) => {
|
||||
// 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
|
||||
parseString(res.data, function(err, result) {
|
||||
let jsonStr = JSON.stringify(result);
|
||||
let jsonObj = JSON.parse(jsonStr);
|
||||
let jsonData = jsonObj.ArrayOfObjStation.objStation;
|
||||
let jsonData = parseJSON(result)
|
||||
|
||||
// batch delete all of the "stations" collection's documents
|
||||
var db = admin.firestore();
|
||||
// batch delete all of the station collection's documents
|
||||
var db = admin.firestore()
|
||||
admin.firestore().collection('stations').get().then((snapshot) => {
|
||||
var batchDelete = db.batch();
|
||||
var batchDelete = db.batch()
|
||||
snapshot.forEach(doc => {
|
||||
batchDelete.delete(doc.ref);
|
||||
});
|
||||
batchDelete.delete(doc.ref)
|
||||
})
|
||||
|
||||
batchDelete.commit().then(function() {
|
||||
// batch write all station JSON objects to the "stations" collection
|
||||
var batchWrite = db.batch();
|
||||
// store all dart codes into a hashset
|
||||
// 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) => {
|
||||
// set the station's ID as the document ID
|
||||
var docID = db.collection('stations').doc(doc["StationCode"][0]);
|
||||
batchWrite.set(docID, doc);
|
||||
});
|
||||
dartCodes.add(doc["StationCode"][0])
|
||||
})
|
||||
|
||||
batchWrite.commit().then(function () {
|
||||
response.send({data: "Successfully fetched and uploaded station data from Irish Rail"});
|
||||
});
|
||||
// fetch all train stations
|
||||
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,12 +126,12 @@ exports.getLiveTrainData = functions.https.onRequest((request, response) => {
|
||||
|
||||
// function to populate the Firestore database with live train data from Irish Rail
|
||||
exports.postLiveTrainData = functions.https.onRequest((request, response) => {
|
||||
// helper function to parse train objects
|
||||
// helper function to parse train JSON objects
|
||||
function parseJSON(result) {
|
||||
let jsonStr = JSON.stringify(result);
|
||||
let jsonObj = JSON.parse(jsonStr);
|
||||
let jsonData = jsonObj.ArrayOfObjTrainPositions.objTrainPositions;
|
||||
return jsonData;
|
||||
let jsonStr = JSON.stringify(result);
|
||||
let jsonObj = JSON.parse(jsonStr);
|
||||
let jsonData = jsonObj.ArrayOfObjTrainPositions.objTrainPositions;
|
||||
return jsonData;
|
||||
}
|
||||
|
||||
// helper function to write to the database
|
||||
@ -126,7 +156,7 @@ exports.postLiveTrainData = functions.https.onRequest((request, response) => {
|
||||
response.set('Access-Control-Allow-Origin', '*');
|
||||
response.set('Access-Control-Allow-Credentials', 'true');
|
||||
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 => {
|
||||
// XML to JSON
|
||||
parseString(res.data, function(err, result) {
|
||||
@ -141,20 +171,20 @@ exports.postLiveTrainData = functions.https.onRequest((request, response) => {
|
||||
});
|
||||
|
||||
batchDelete.commit().then(function() {
|
||||
// batch write all station JSON objects to the liveTrainData collection
|
||||
batchWriteDB(request, response, db, jsonData, "M");
|
||||
// batch write all train JSON objects to the liveTrainData collection
|
||||
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 => {
|
||||
parseString(res.data, function(err, 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 => {
|
||||
parseString(res.data, function(err, 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"});
|
||||
})
|
||||
})
|
||||
|
@ -32,6 +32,7 @@ describe('Firebase cloud function tests', function() {
|
||||
expect(result.body.data[0]).haveOwnProperty('StationLongitude');
|
||||
expect(result.body.data[0]).haveOwnProperty('StationCode');
|
||||
expect(result.body.data[0]).haveOwnProperty('StationId');
|
||||
expect(result.body.data[0]).haveOwnProperty('StationType');
|
||||
}),
|
||||
|
||||
this.timeout(100000);
|
||||
|
@ -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;
|
||||
}
|
@ -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 |
@ -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
BIN
src/assets/station.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div id = "statsDiv">
|
||||
<div id = "lateGraph">
|
||||
<div id="statsDiv">
|
||||
<div id="lateGraph">
|
||||
<Bar
|
||||
id="my-chart-id"
|
||||
:options="chartOptions"
|
||||
@ -8,58 +8,44 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script scoped>
|
||||
import { Bar } from 'vue-chartjs'
|
||||
import {store} from '../store/store'
|
||||
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale } from 'chart.js'
|
||||
import { resolveDirective } from 'vue'
|
||||
<script scoped>
|
||||
import { Bar } from 'vue-chartjs'
|
||||
import { store } from '../store/store'
|
||||
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale } from 'chart.js'
|
||||
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale)
|
||||
|
||||
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale)
|
||||
|
||||
export default {
|
||||
name: 'BarChart',
|
||||
components: { Bar },
|
||||
data() {
|
||||
return {
|
||||
chartData: {
|
||||
labels: [ 'Punctuality Percentage' ],
|
||||
datasets: [ {
|
||||
label: 'Late',
|
||||
backgroundColor: '#FF0000',
|
||||
data: [store.insights["percentageLate"]]
|
||||
},
|
||||
{
|
||||
label: 'Early/On Time',
|
||||
backgroundColor: '#4ADC57',
|
||||
data: [store.insights["percentageNotLate"]]
|
||||
|
||||
}
|
||||
]
|
||||
export default {
|
||||
name: 'BarChart',
|
||||
components: { Bar },
|
||||
data() {
|
||||
return {
|
||||
chartData: {
|
||||
labels: ['Punctuality Percentage'],
|
||||
datasets: [{
|
||||
label: 'Late',
|
||||
backgroundColor: '#FF0000',
|
||||
data: [store.insights["percentageLate"]]
|
||||
},
|
||||
chartOptions: {
|
||||
responsive: true
|
||||
|
||||
}
|
||||
{
|
||||
label: 'Early/ontime',
|
||||
backgroundColor: '#4ADC57',
|
||||
data: [store.insights["percentageNotLate"]]
|
||||
}]
|
||||
},
|
||||
chartOptions: {
|
||||
responsive: true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
|
||||
|
||||
#lateGraph{
|
||||
position: relative;
|
||||
height:48%;
|
||||
left:0px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
@ -33,9 +33,7 @@
|
||||
|
||||
<script>
|
||||
import app from "../api/firebase"
|
||||
import {getAuth, onAuthStateChanged, signOut} from "firebase/auth"
|
||||
|
||||
|
||||
import { getAuth, onAuthStateChanged, signOut } from "firebase/auth"
|
||||
|
||||
export default {
|
||||
name: "Navbar",
|
||||
@ -66,17 +64,15 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.router-link-active{
|
||||
color: rgb(0, 0, 0);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a{
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
</style>
|
@ -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>
|
66
src/components/StationSidebar.vue
Normal file
66
src/components/StationSidebar.vue
Normal 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>
|
69
src/components/TrainSidebar.vue
Normal file
69
src/components/TrainSidebar.vue
Normal 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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -1,47 +1,55 @@
|
||||
<template>
|
||||
<div id = "statsDiv">
|
||||
<div id = "trainPie">
|
||||
<Pie
|
||||
:data="chartData"
|
||||
:options="chartOptions"
|
||||
/>
|
||||
<div id="statsDiv">
|
||||
<div id="trainPie">
|
||||
<Pie :data="trainData" :options="chartOptions" />
|
||||
</div>
|
||||
<div id="stationPie">
|
||||
<Pie :data="stationData" :options="chartOptions" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'
|
||||
import { Pie } from 'vue-chartjs'
|
||||
import {store} from '../store/store.js'
|
||||
|
||||
import { store } from '../store/store.js'
|
||||
ChartJS.register(ArcElement, Tooltip, Legend)
|
||||
|
||||
export default {
|
||||
name: 'pieChart',
|
||||
components: { Pie },
|
||||
data() {
|
||||
return{
|
||||
chartData:{
|
||||
labels: ['Mainland', 'Suburban', 'Darts'],
|
||||
datasets: [{
|
||||
backgroundColor: ['#41B883', '#E46651', '#00D8FF'],
|
||||
data: [store.insights["numMainland"], store.insights["numSuburban"], store.insights["numDart"]]
|
||||
}]
|
||||
},
|
||||
chartOptions:{
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
radius: 80,
|
||||
hoverOffset: 0
|
||||
name: 'pieChart',
|
||||
|
||||
components: {
|
||||
Pie
|
||||
},
|
||||
|
||||
data() {
|
||||
return{
|
||||
trainData: {
|
||||
labels: ['Trains', 'Darts'],
|
||||
datasets: [{
|
||||
backgroundColor: ['#41B883', '#E46651', '#00D8FF'],
|
||||
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: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
radius: 80,
|
||||
hoverOffset: 0
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
</style>
|
@ -1,35 +1,46 @@
|
||||
<template>
|
||||
<Navbar />
|
||||
|
||||
|
||||
<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 = "trainTotalText">
|
||||
<div id="trainTotalText">
|
||||
<p>Total number of trains: {{ this.insights["totalNumTrains"] }}</p>
|
||||
<ul>
|
||||
<li><p>Mainland: {{ this.insights["numMainland"] }}</p></li>
|
||||
<li><p>Suburban: {{ this.insights["numSuburban"] }}</p></li>
|
||||
<li><p>Darts: {{ this.insights["numDart"] }}</p></li>
|
||||
<li><p>Trains: {{ this.insights["numTrains"] }}</p></li>
|
||||
<li><p>Darts: {{ this.insights["numDarts"] }}</p></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="trainTotalChart">
|
||||
<pieChart id="trainPie" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<p>Number of actively running trains: {{ this.insights["numRunningTrains"] }}</p>
|
||||
<p>Percentage late: {{ this.insights["percentageLate"] }}%</p>
|
||||
<p>Percentage early or ontime: {{ this.insights["percentageNotLate"] }}%</p>
|
||||
<div id ="statsDiv">
|
||||
<BarChart id ="lateGraph" />
|
||||
<div id="statsDiv">
|
||||
<BarChart id="lateGraph" />
|
||||
</div>
|
||||
|
||||
<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>
|
||||
|
||||
|
||||
<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 id="stationTotalChart">
|
||||
<pieChart id="stationPie" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="ads">
|
||||
<h2>AdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAds
|
||||
AdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAdsAds
|
||||
@ -41,15 +52,12 @@
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
|
||||
<h1>Leaderboard</h1>
|
||||
<div v-for="item in orderedTrains">
|
||||
<h2>{{ this.rawData[item.jsonIndex]["TrainCode"][0] }}</h2>
|
||||
<p v-if="item.time > 0">{{ item.time }} mins late</p>
|
||||
<p v-else>{{ item.time * -1}} mins early</p>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -75,7 +83,6 @@ export default {
|
||||
Navbar,
|
||||
pieChart,
|
||||
BarChart
|
||||
|
||||
},
|
||||
|
||||
created() {
|
||||
@ -89,24 +96,25 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#insightDiv{
|
||||
#insightDiv {
|
||||
color: black;
|
||||
padding-left: 10px;
|
||||
width:80%;
|
||||
background-color: antiquewhite;
|
||||
}
|
||||
|
||||
#trainTotal{
|
||||
#trainTotal, #stationTotal {
|
||||
height: 280px;
|
||||
|
||||
}
|
||||
#trainTotalText{
|
||||
#trainTotalText, #stationTotalText {
|
||||
position: relative;
|
||||
width: 30%;
|
||||
height: 100%;
|
||||
right:0px;
|
||||
}
|
||||
#trainTotalChart{
|
||||
|
||||
#trainTotalChart, #stationTotalChart {
|
||||
position: absolute;
|
||||
top:114px;
|
||||
width: 50%;
|
||||
@ -114,18 +122,17 @@ export default {
|
||||
left: 20%;
|
||||
}
|
||||
|
||||
#lateGraph{
|
||||
#lateGraph {
|
||||
position: absolute;
|
||||
height:50%;
|
||||
|
||||
left:0px;
|
||||
}
|
||||
|
||||
#statsDiv{
|
||||
#statsDiv {
|
||||
height: 230px;
|
||||
}
|
||||
|
||||
#ads{
|
||||
#ads {
|
||||
position: absolute;
|
||||
background-color:rgb(78, 232, 109);
|
||||
right: 0px;
|
||||
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<Navbar />
|
||||
<!-- <button id="hoverButton" @click="postLiveTrainData">Populate Database</button> -->
|
||||
|
||||
<!--Sidebar, fades out on click of X button-->
|
||||
<transition id="sidebar" name="slideLeft">
|
||||
<div v-if="store.display && store.selectedDataMap">
|
||||
<SidebarPanel />
|
||||
<div v-if="store.displaySelectedTrain && store.selectedTrain">
|
||||
<TrainSidebar />
|
||||
</div>
|
||||
<div v-else-if="store.displaySelectedStation && store.selectedStation">
|
||||
<StationSidebar />
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
@ -15,44 +15,52 @@
|
||||
<ol-source-osm />
|
||||
</ol-tile-layer>
|
||||
|
||||
<template v-for="coordinate, i in coordinates" :position="inline-block">
|
||||
<!-- overlay offset is the size of the image so that it is centered-->
|
||||
<ol-overlay :position="coordinate" :positioning="center-center" :offset="[-14,-16]">
|
||||
<div class="overlay-content" @click="getSelectedTrain(i)">
|
||||
<div v-if="getTrainType(i) === 'D'">
|
||||
<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 src="../assets/train-tram-solid.svg" class="trainMapIcon" alt="Not Running DART Icon">
|
||||
</div>
|
||||
<div v-else>
|
||||
<img v-if="isTrainRunning(i) && isTrainLate(i)" src="../assets/red-train-solid.png" class="trainMapIcon" alt="Late 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">
|
||||
</div>
|
||||
<!-- train overlay -->
|
||||
<template v-for="coordinate, i in trainCoordinates" :position="inline-block">
|
||||
<ol-overlay :position="coordinate" :positioning="center-center" :offset="[-14,-16]">
|
||||
<div class="overlay-content" @click="getSelectedTrain(i)">
|
||||
<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-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">
|
||||
</div>
|
||||
<div v-else>
|
||||
<img v-if="isTrainRunning(i) && isTrainLate(i)" src="../assets/red-train-solid.png" class="trainMapIcon" alt="Late 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">
|
||||
</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>
|
||||
</ol-overlay>
|
||||
</ol-overlay>
|
||||
</template>
|
||||
</ol-map>
|
||||
|
||||
<div>
|
||||
<MarqueeText v-if="publicMessages.length > 0" id="publicMessageTicker" :paused="isPaused" :duration="800" :repeat="1"
|
||||
@mouseenter="isPaused = !isPaused" @mouseleave="isPaused = false">
|
||||
<!-- <span v-for="message in publicMessages"> {{ "---" + message + "---" }} </span> -->
|
||||
<span v-for="message in publicMessages"> {{ message + " • " }} </span>
|
||||
</MarqueeText>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {store} from '../store/store'
|
||||
import { store } from '../store/store'
|
||||
import { ref } from 'vue';
|
||||
import {fromLonLat, toLonLat} from 'ol/proj.js';
|
||||
import app from '../api/firebase';
|
||||
import { fromLonLat, toLonLat } from 'ol/proj.js';
|
||||
import { getFunctions, httpsCallable, connectFunctionsEmulator } from "firebase/functions";
|
||||
import app from '../api/firebase';
|
||||
import Navbar from '../components/Navbar.vue'
|
||||
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 {
|
||||
name: "MapPage",
|
||||
@ -77,46 +85,55 @@ export default {
|
||||
strokeColor,
|
||||
fillColor,
|
||||
|
||||
coordinates: [],
|
||||
dbLiveTrainData: [],
|
||||
allDataMap: {},
|
||||
store,
|
||||
|
||||
trainCoordinates: [],
|
||||
stationCoordinates: [],
|
||||
allTrains: {},
|
||||
allStations: {},
|
||||
publicMessages: [],
|
||||
isPaused: false,
|
||||
store,
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
Navbar,
|
||||
MarqueeText,
|
||||
SidebarPanel
|
||||
TrainSidebar,
|
||||
StationSidebar
|
||||
},
|
||||
|
||||
created() {
|
||||
let host = window.location.hostname
|
||||
if (host === '127.0.0.1' || host === 'localhost') {
|
||||
this.postLiveTrainData();
|
||||
this.postTrainAndStationData();
|
||||
}
|
||||
else {
|
||||
this.getLiveTrainData();
|
||||
this.getTrainAndStationData();
|
||||
}
|
||||
// request new data every 60 seconds
|
||||
// window.setInterval(this.getLiveTrainData, 60000);
|
||||
},
|
||||
|
||||
methods: {
|
||||
// method to assign a single train's data to the selected hashmap
|
||||
// method to display a selected train
|
||||
getSelectedTrain(i) {
|
||||
store.setSelectedDataMap(this.allDataMap[i]);
|
||||
store.setDisplay(true)
|
||||
store.setSelectedTrain(this.allTrains[i]);
|
||||
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
|
||||
isTrainLate(i) {
|
||||
// check if the train is running
|
||||
if (this.allDataMap[i]["TrainStatus"][0] == "R") {
|
||||
let publicMessage = this.allDataMap[i]["PublicMessage"][0];
|
||||
if (this.allTrains[i]["TrainStatus"][0] == "R") {
|
||||
let publicMessage = this.allTrains[i]["PublicMessage"][0];
|
||||
let startTimeStr = publicMessage.indexOf("(");
|
||||
|
||||
// check if the train is late
|
||||
@ -129,7 +146,7 @@ export default {
|
||||
|
||||
// method to determine whether or not a selected train is running
|
||||
isTrainRunning(i) {
|
||||
if (this.allDataMap[i]["TrainStatus"][0] == "R") {
|
||||
if (this.allTrains[i]["TrainStatus"][0] == "R") {
|
||||
return true;
|
||||
}
|
||||
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) {
|
||||
return this.allDataMap[i]["TrainType"][0];
|
||||
return this.allTrains[i]["TrainType"][0];
|
||||
},
|
||||
|
||||
// method to fetch live train data from the database
|
||||
getLiveTrainData() {
|
||||
// method to fetch live train and station data from the database
|
||||
getTrainAndStationData() {
|
||||
const functions = getFunctions(app);
|
||||
let host = window.location.hostname
|
||||
if (host === '127.0.0.1' || host == 'localhost') {
|
||||
connectFunctionsEmulator(functions, host, 5001);
|
||||
}
|
||||
const getData = httpsCallable(functions, 'getLiveTrainData');
|
||||
const getTrainData = httpsCallable(functions, 'getLiveTrainData');
|
||||
let loader = this.$loading.show({
|
||||
loader: 'dots',
|
||||
container: this.$refs.container,
|
||||
canCancel: false
|
||||
});
|
||||
|
||||
getData().then((response) => {
|
||||
getTrainData().then((response) => {
|
||||
try {
|
||||
this.dbLiveTrainData = response.data;
|
||||
if (!this.dbLiveTrainData) throw new Error("Error fetching live train data from the database");
|
||||
|
||||
if (!response.data) throw new Error("Error fetching live train data from the database");
|
||||
var insights = {
|
||||
"numRunningTrains": 0,
|
||||
"numLateRunningTrains": 0,
|
||||
"numMainland": 0,
|
||||
"numSuburban": 0,
|
||||
"numDart": 0
|
||||
"totalNumTrains": 0,
|
||||
"numRunningTrains": 0,
|
||||
"numLateRunningTrains": 0,
|
||||
"numTrains": 0,
|
||||
"numDarts": 0,
|
||||
"totalNumStations": 0,
|
||||
"numTrainStations": 0,
|
||||
"numDartStations": 0
|
||||
};
|
||||
var unorderedTrains = [];
|
||||
var currentMessages = [];
|
||||
var latest = null;
|
||||
var earliest = null;
|
||||
var currLatestTime = null;
|
||||
var currEarliestTime = null;
|
||||
|
||||
var currentMessages = [];
|
||||
|
||||
// create an array of coordinates and hashmap with the key-values {index: JSON obj}
|
||||
for (var i=0; i<this.dbLiveTrainData.length; i++) {
|
||||
let train = this.dbLiveTrainData[i];
|
||||
for (var i=0; i<response.data.length; 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, ". ");
|
||||
|
||||
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];
|
||||
currentMessages.push(publicMessage);
|
||||
|
||||
// check if the train is running
|
||||
if (this.dbLiveTrainData[i]["TrainStatus"][0] == "R") {
|
||||
if (train["TrainStatus"][0] == "R") {
|
||||
insights["numRunningTrains"] += 1;
|
||||
let startTimeStr = publicMessage.indexOf("(");
|
||||
let timeEnd = publicMessage.indexOf(" ", startTimeStr+1);
|
||||
@ -204,7 +219,6 @@ export default {
|
||||
// check if the train is late
|
||||
if (publicMessage[startTimeStr+1] != "-" && publicMessage[startTimeStr+1] != "0") {
|
||||
insights["numLateRunningTrains"] += 1;
|
||||
|
||||
if (!latest) latest = train;
|
||||
|
||||
// check for a new latest train
|
||||
@ -228,18 +242,32 @@ export default {
|
||||
|
||||
insights["percentageLate"] = ((insights["numLateRunningTrains"] / insights["numRunningTrains"]) * 100).toFixed(2);
|
||||
insights["percentageNotLate"] = (100 - insights["percentageLate"]).toFixed(2);
|
||||
insights["totalNumTrains"] = Object.keys(this.allDataMap).length;
|
||||
insights["latestTime"] = currLatestTime;
|
||||
insights["earliestTime"] = currEarliestTime;
|
||||
this.publicMessages = currentMessages;
|
||||
|
||||
// assign the results to the Vue Store
|
||||
store.setInsights(insights);
|
||||
store.setEarliestTrain(earliest);
|
||||
store.setLatestTrain(latest);
|
||||
store.setRawData(this.dbLiveTrainData);
|
||||
store.setRawData(response.data);
|
||||
store.setOrderedTrains(unorderedTrains);
|
||||
loader.hide();
|
||||
|
||||
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();
|
||||
})
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error)
|
||||
@ -249,15 +277,20 @@ export default {
|
||||
},
|
||||
|
||||
// method to populate the database for local testing
|
||||
postLiveTrainData() {
|
||||
postTrainAndStationData() {
|
||||
const functions = getFunctions(app);
|
||||
let host = window.location.hostname
|
||||
if (host === '127.0.0.1' || host === 'localhost') {
|
||||
connectFunctionsEmulator(functions, host, 5001);
|
||||
}
|
||||
const postData = httpsCallable(functions, 'postLiveTrainData');
|
||||
postData().then((response) => {
|
||||
this.getLiveTrainData()
|
||||
// post live train data
|
||||
const postTrainData = httpsCallable(functions, 'postLiveTrainData');
|
||||
postTrainData().then((response) => {
|
||||
// post station data
|
||||
const postStationData = httpsCallable(functions, 'postStationData');
|
||||
postStationData().then((reponse) => {
|
||||
this.getTrainAndStationData()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -269,8 +302,6 @@ export default {
|
||||
width: 1%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.trainMapIcon {
|
||||
width: 28px;
|
||||
height: 32px;
|
||||
@ -282,6 +313,18 @@ export default {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.stationMapIcon {
|
||||
width: 14px;
|
||||
height: 17px;
|
||||
}
|
||||
|
||||
.stationMapIcon:hover {
|
||||
width: 16px;
|
||||
height: 19px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
#sidebar{
|
||||
position: absolute;
|
||||
height: 80%;
|
||||
@ -329,6 +372,6 @@ export default {
|
||||
color: black;
|
||||
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
|
||||
text-align: bottom;
|
||||
font-size: 17px;
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
|
@ -5,9 +5,11 @@ export const store = reactive({
|
||||
latestTrain: {},
|
||||
earliestTrain: {},
|
||||
orderedTrains: [],
|
||||
selectedDataMap: {},
|
||||
selectedTrain: {},
|
||||
selectedStation: {},
|
||||
rawData: {},
|
||||
display: false,
|
||||
displaySelectedTrain: false,
|
||||
displayedSelectedStation: false,
|
||||
|
||||
setInsights(insights) {
|
||||
this.insights = insights
|
||||
@ -33,11 +35,19 @@ export const store = reactive({
|
||||
this.orderedTrains = unorderedTrains
|
||||
},
|
||||
|
||||
setSelectedDataMap(selectedDataMap) {
|
||||
this.selectedDataMap = selectedDataMap
|
||||
setSelectedTrain(selectedTrain) {
|
||||
this.selectedTrain = selectedTrain
|
||||
},
|
||||
|
||||
setDisplay(bool) {
|
||||
this.display = bool
|
||||
setSelectedStation(selectedStation) {
|
||||
this.selectedStation = selectedStation
|
||||
},
|
||||
|
||||
setDisplaySelectedTrain(bool) {
|
||||
this.displaySelectedTrain = bool
|
||||
},
|
||||
|
||||
setDisplaySelectedStation(bool) {
|
||||
this.displaySelectedStation = bool
|
||||
}
|
||||
})
|
Reference in New Issue
Block a user