Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Walking-route display, UI overhaul #123

Merged
merged 14 commits into from
Dec 6, 2023
24 changes: 14 additions & 10 deletions back-end/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,19 @@ app.post("/getRoute", async (req, res) => {
const routeFinding = require("./getOptimizedRoute.js");
const busStops = {};
//parse stops into a dictionary of coordinates
for (let stopkey in req.body.stops) {
let latitude = req.body.stops[stopkey].latitude
let longitude = req.body.stops[stopkey].longitude
let stopID = req.body.stops[stopkey].stopId
busStops[stopID] = [latitude, longitude]
for (let stopKey in req.body.stops) {
let latitude = req.body.stops[stopKey].latitude;
let longitude = req.body.stops[stopKey].longitude;
let stopID = req.body.stops[stopKey].stopId;

// Initialize the object if it doesn't exist
if (!busStops[stopID]) {
busStops[stopID] = {};
}

busStops[stopID].geoLoc = [latitude, longitude];
busStops[stopID].stopId = stopID;
}
console.log('<----------Stops---------->')
console.log(busStops)

//parse routes into a dictionary of routes, each holding an
//array of stops
let routes = {};
Expand Down Expand Up @@ -87,8 +91,8 @@ app.post("/getRoute", async (req, res) => {
req.body.destination_lat,
req.body.destination_lng,
);
console.log(optimalRoute.onSameRoute);
res.send(optimalRoute.onSameRoute);
console.log(optimalRoute.originStop.stopId);
res.send(optimalRoute);
}
catch(err){
console.log(err);
Expand Down
106 changes: 80 additions & 26 deletions back-end/getOptimizedRoute.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
//this function creates a directed circular graph representation of of bus stops and the routes.
/*in the output graph, the key represents a bus stop, and the value represents all the bus stops that
are directly reachavble from the key bus stop.*/
async function createGraph(routes, busstops) {
async function createGraph(routes, busStops) {
let graph = {};

// Initialize the graph with all stops and an empty list of routes
for (let stop in busstops) {
let stopKey = busstops[stop].toString();
graph[stopKey] = { routes: [], connections: [] };
for (let stopKey in busStops) {
graph[stopKey] = { routes: [], connections: [], stopId: busStops[stopKey].stopId, geoLoc: busStops[stopKey].geoLoc };
}

for (let routeId in routes) {
let stops = routes[routeId];
for (let i = 0; i < stops.length; i++) {
let currentStop = stops[i];
let nextStop = stops[(i + 1) % stops.length];
let currentKey = currentStop.toString();
let currentStopId = stops[i].stopId;
let nextStopId = stops[(i + 1) % stops.length].stopId;

// Add the current route to the list of routes for this stop
if (!graph[currentKey].routes.includes(routeId)) {
graph[currentKey].routes.push(routeId);
if (!graph[currentStopId].routes.includes(routeId)) {
graph[currentStopId].routes.push(routeId);
}

// Add the connection information
graph[currentKey].connections.push({ coordinates: nextStop, route: graph[currentKey].routes });
// Assuming that you only need the next stop's ID
graph[currentStopId].connections.push({ stopId: nextStopId, coordinates: stops[i].geoLoc });
}
}
return graph;
Expand All @@ -31,25 +31,43 @@ async function createGraph(routes, busstops) {
function getEuclideanDistance (a, b) {
let x1 = a[0];
let y1 = a[1];
let x2 = b.split(',')[0];
let y2 = b.split(',')[1];
let x2;
let y2;
try{
x2 = b.split(',')[0];
y2 = b.split(',')[1];
}
catch(err){
x2 = b[0];
y2 = b[1];
}
console.log('x1: '+x1+', y1: '+y1+', x2: '+x2+', y2: '+y2)

return Math.sqrt(Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2));
}

//find bus stops that are relatively close in term of walking distance
async function findAllReachableStops(graph, origin, threshold=0.01) {
async function findAllReachableStops(graph, origin, threshold=0.008, maxThreshold=0.1) {

let resolvedGraph = await graph; // Waits for the graph Promise to resolve

console.log('resolvedGraph:',Object.keys(resolvedGraph))
let reachableStops = [];
for (let busstop in resolvedGraph) {
busstop = busstop.toString();
let distance = getEuclideanDistance(origin, busstop);
console.log('geoloc:', resolvedGraph[busstop].geoLoc)
let geoLoc = resolvedGraph[busstop].geoLoc;
let busstopId = resolvedGraph[busstop].stopId;
let distance = getEuclideanDistance(origin, geoLoc);
console.log('distance: '+distance)
if (distance < threshold) {
console.log('reachable stop found: ' + resolvedGraph[busstop].routes);
reachableStops.push({ coordinates: busstop, route: resolvedGraph[busstop].route });
console.log('reachable stop found for: '+ origin + ', ' + resolvedGraph[busstop].routes);
reachableStops.push({ coordinates: geoLoc, route: resolvedGraph[busstop].routes, stopId: busstopId });
}
}
//when no stops are found, keep calling itself with larger and larger threshold
if (reachableStops.length == 0 && threshold < maxThreshold) {
console.log('no reachable stop found, increasing threshold');
reachableStops = findAllReachableStops(graph, origin, threshold + 0.002)
}

return reachableStops;
}
Expand All @@ -67,15 +85,19 @@ async function getWalkingDistance(origin, destination) {
destination = destination.join('%2C');
}

let res = await fetch(`https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=${origin}&destinations=${destination}&key=${process.env.EXPRESS_APP_MAP_API_KEY}`)
let res = await fetch(`https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=${origin}&destinations=${destination}&mode=walking&key=${process.env.EXPRESS_APP_MAP_API_KEY}`)
let data = await res.json();
res = data.rows[0].elements[0].duration.value;
res = {
distance: data.rows[0].elements[0].distance.value,
time: data.rows[0].elements[0].duration.text
};
return res;
}

async function isOnSameRoute(graph, stop1, stop2, routes, busstops) {
let resolvedGraph = await graph;
let sharedRoutes = await resolvedGraph[stop1.coordinates].routes.filter(element => resolvedGraph[stop2.coordinates].routes.includes(element));
console.log(Object.keys(resolvedGraph))
let sharedRoutes = await resolvedGraph[stop1.stopId].routes.filter(element => resolvedGraph[stop2.stopId].routes.includes(element));
if(sharedRoutes.length == 0){
return false;
}
Expand All @@ -91,23 +113,49 @@ async function findOptimalRoute(graph, routes, busstops, origin_lat, origin_lng,
let reachableFromOrigin = await findAllReachableStops(graph, origin);

let reachableFromDestination = await findAllReachableStops(graph, destination);
console.log('TOTAL REACHABLE STOPS: '+Number(reachableFromOrigin.length + reachableFromDestination.length))
// Calculate route distances
for (let originStop of reachableFromOrigin) {
for (let destinationStop of reachableFromDestination) {
let onSameRoute = await isOnSameRoute(graph, originStop, destinationStop, routes, busstops);
console.log('on same route result: '+onSameRoute)
if (onSameRoute.length > 0) {
let distanceToOriginStop = await getWalkingDistance(origin, originStop.coordinates);
let distanceFromDestinationStop = await getWalkingDistance(destinationStop.coordinates, destination);
let totalDistance = distanceToOriginStop + distanceFromDestinationStop;
console.log('fetched from Google Maps API, total distance: '+totalDistance);
let distanceToOriginStop
let distanceFromDestinationStop
let totalDistance
let totalTime
console.log('TOTAL REACHABLE STOPS2: '+reachableFromOrigin.length + reachableFromDestination.length)
if (Number(reachableFromOrigin.length + reachableFromDestination.length) < 20){

distanceToOriginStop = await getWalkingDistance(origin, originStop.coordinates);
distanceFromDestinationStop = await getWalkingDistance(destinationStop.coordinates, destination);
totalDistance = distanceToOriginStop.distance + distanceFromDestinationStop.distance;
console.log('fetched from Google Maps API, total distance: '+totalDistance);
totalTime = Number(distanceToOriginStop.time.split(' ')[0]) + Number(distanceFromDestinationStop.time.split(' ')[0]);
console.log('fetched from Google Maps API, total time: '+totalTime);
}
else{
distanceToOriginStop = await getEuclideanDistance(origin, originStop.coordinates);
distanceFromDestinationStop = await getEuclideanDistance(destinationStop.coordinates, destination);
totalDistance = distanceToOriginStop + distanceFromDestinationStop;
console.log('getting euclidean distance, total distance: '+totalDistance);

}

// Create an object with route names as keys and {distance, time} as values
let routeDetails = {};
for (let route of onSameRoute) {
routeDetails[route] = {distance: totalDistance, time: totalTime};
}

// Check if this route is better than the current best
if (totalDistance < minTotalDistance) {
console.log('new optimal route found')
minTotalDistance = totalDistance;
optimalRoute = { originStop, destinationStop, onSameRoute };
optimalRoute = { origin, originStop, destination, destinationStop, onSameRoute: routeDetails };
}
}

}
}

Expand All @@ -117,6 +165,12 @@ async function findOptimalRoute(graph, routes, busstops, origin_lat, origin_lng,
}
console.log('-------------------------------------')
console.log('optimal route: '+optimalRoute.onSameRoute);
for (let route in optimalRoute.onSameRoute) {

console.log('<-----------Route Name: '+route+'--------------->');
console.log('Total Distance: '+optimalRoute.onSameRoute[route].distance);
console.log('Total Time: '+optimalRoute.onSameRoute[route].time);
}
console.log('origin stop location: '+optimalRoute.originStop.coordinates);
console.log('destination stop location: '+optimalRoute.destinationStop.coordinates);

Expand Down
30 changes: 30 additions & 0 deletions front-end/src/components/RouteButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useState, useEffect } from 'react';
function RouteButton({ route }) {
const [color, setColor] = useState('rgba(0, 0, 0, 0)');
function hexToRGBA(hex, alpha) {
if (hex === undefined) {
return 'rgba(0, 0, 0, 0)'; // return transparent color if hex is undefined
}
let r = parseInt(hex.slice(1, 3), 16),
g = parseInt(hex.slice(3, 5), 16),
b = parseInt(hex.slice(5, 7), 16);

if (alpha) {
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
} else {
return `rgb(${r}, ${g}, ${b})`;
}
}

useEffect(() => {
setColor(hexToRGBA(route.color, 0.5));
}, [route.color]);

return (
<button className="w-20 h-20" style={{ backgroundColor: color }}>
{route.name}
</button>
);
}

export default RouteButton;
9 changes: 8 additions & 1 deletion front-end/src/components/RouteMap.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useEffect, useRef } from "react";
import "../css/routeMap.css";
import SaveRouteButton from "./SaveRouteButton";

function RouteMap({ location1, location2 }) {
const mapRef = useRef(null);
Expand Down Expand Up @@ -54,7 +55,13 @@ function RouteMap({ location1, location2 }) {
});
}, [location1, location2]);

return <div ref={mapRef} className="route_map"></div>;
return (
<div className="relative w-full">
<SaveRouteButton />
<div ref={mapRef} className="route_map"> </div>
</div>
)

}

export default RouteMap;
19 changes: 14 additions & 5 deletions front-end/src/components/RoutesPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,16 @@ function RoutesPage() {
console.log('awaiting data');
return }
if (fromLocation.name === lastLocation1.current.name && toLocation.name === lastLocation2.current.name) {
awaitingData.current = false;
return;
}
}
catch(typeError){
console.log('same input, no need to fetch new route');
}
if (fromLocation.name && toLocation.name) {
if (fromLocation.name && toLocation.name && fromLocation.name !== toLocation.name) {
awaitingData.current = true;
let reachableRoutes = fetch(`http://localhost:4000/getRoute`, {
let reachableRoutes = fetch(`/getRoute`, {
method: "POST",
headers: {
'Content-Type': 'application/json'
Expand All @@ -97,13 +98,21 @@ function RoutesPage() {
.then((data) => {
lastLocation1.current = fromLocation;
lastLocation2.current = toLocation;
if (Object.keys(data).length === 0){
if (Object.keys(data.onSameRoute).length === 0){
alert('No route found');
return;
}
setRoutes(data);

console.log(Object.keys(data.onSameRoute).length)
if (Object.keys(data.onSameRoute).length === 7){
setRoutes(['You should walk instead!']);
}
else{
setRoutes(data.onSameRoute);
window.nyushuttle.startStopLocation = data.originStop.stopId;
window.nyushuttle.endStopLocation = data.destinationStop.stopId;
}
setShowSubpage(true);
alert('Starting' + JSON.stringify(data[0]));
return data;
})
.catch((error) => {
Expand Down
Loading