From 4ec13dca070c13f79c456af2a5c2df481e4826b4 Mon Sep 17 00:00:00 2001 From: unfiltered-syrup Date: Fri, 1 Dec 2023 20:48:06 -0500 Subject: [PATCH 1/3] Completed single route finding algorithm --- back-end/app.js | 117 +++++++++++---------- back-end/getOptimizedRoute.js | 99 ++++++++++++----- front-end/src/components/LocationFilter.js | 9 +- front-end/src/components/RoutesPage.js | 5 +- front-end/src/components/RoutesSubpage.js | 16 ++- 5 files changed, 158 insertions(+), 88 deletions(-) diff --git a/back-end/app.js b/back-end/app.js index e4c991d..7560303 100644 --- a/back-end/app.js +++ b/back-end/app.js @@ -8,6 +8,7 @@ require("dotenv").config({ silent: true }); const mongoose = require("mongoose"); const Feedback = require("./models/Feedback.js"); + // connect to the database try { mongoose.connect(process.env.MONGODB_URI); @@ -30,63 +31,67 @@ app.use("/feedback", feedbackRoutes()); app.get("/getRoute", (req, res) => { + const routeFinding = require("./getOptimizedRoute.js"); const busStops = { - unionsquare: [40.73498551788369, -73.990696048825], - tompkins: [40.7251277537963, -73.98121749968648], - hamilton: [40.720213039417494, -73.980207088181], - eastbroadway: [40.7141707879376, -73.9901227463216], - chinatown: [40.71559782189394, -73.99850504010124], - financial: [40.70811320415039, -74.00798229139403], - tribeca: [40.716765448621125, -74.00913568860388], - canal: [40.721857387647354, -74.00518353611693], - soho: [40.72488113646396, -74.00162848801607], - greenwich: [40.733368703161425, -74.00412600650982], - washingtonsquare: [40.731522867853634, -73.9971283464239], - jayst: [40.69225251854892, -73.98648974152808], - dumbo: [40.70372584858276, -73.98861865993923], - ikea: [40.672611811522145, -74.01009335210519], - }; - const routes = { - route1: [ - busStops.washingtonsquare, - busStops.unionsquare, - busStops.tompkins, - busStops.hamilton, - busStops.eastbroadway, - busStops.chinatown, - busStops.financial, - busStops.tribeca, - busStops.canal, - busStops.soho, - busStops.greenwich, - busStops.washingtonsquare, - ], - route2: [busStops.jayst, busStops.dumbo, busStops.ikea], - route3: [ - busStops.jayst, - busStops.eastbroadway, - busStops.washingtonsquare, - busStops.chinatown, - busStops.financial, - ], - route4: [ - busStops.eastbroadway, - busStops.washingtonsquare, - busStops.unionsquare, - busStops.tompkins, - busStops.hamilton, - busStops.eastbroadway, - ], + unionsquare: [40.73498551788369, -73.990696048825], + tompkins: [40.7251277537963, -73.98121749968648], + hamilton: [40.720213039417494, -73.980207088181], + eastbroadway: [40.7141707879376, -73.9901227463216], + chinatown: [40.71559782189394, -73.99850504010124], + financial: [40.70811320415039, -74.00798229139403], + tribeca: [40.716765448621125, -74.00913568860388], + canal: [40.721857387647354, -74.00518353611693], + soho: [40.72488113646396, -74.00162848801607], + greenwich: [40.733368703161425, -74.00412600650982], + washingtonsquare: [40.731522867853634, -73.9971283464239], + jayst: [40.69225251854892, -73.98648974152808], + dumbo: [40.70372584858276, -73.98861865993923], + ikea: [40.672611811522145, -74.01009335210519], }; - const graph = createGraph(routes); - let optimalRoute = findOptimalRoute( - graph, - req.query.origin, - req.query.destination - ); - console.log(optimalRoute); - res.send("Hello World!"); - -}); + const routes = { + route1: [ + busStops.washingtonsquare, + busStops.unionsquare, + busStops.tompkins, + busStops.hamilton, + busStops.eastbroadway, + busStops.chinatown, + busStops.financial, + busStops.tribeca, + busStops.canal, + busStops.soho, + busStops.greenwich, + busStops.washingtonsquare, + ], + route2: [busStops.jayst, busStops.dumbo, busStops.ikea], + route3: [ + busStops.jayst, + busStops.eastbroadway, + busStops.washingtonsquare, + busStops.chinatown, + busStops.financial, + ], + route4: [ + busStops.eastbroadway, + busStops.washingtonsquare, + busStops.unionsquare, + busStops.tompkins, + busStops.hamilton, + busStops.eastbroadway, + ], + }; + const graph = routeFinding.createGraph(routes, busStops); + console.log('graph created') + let optimalRoute = routeFinding.findOptimalRoute( + graph, routes, busStops, + req.query.origin_lat, + req.query.origin_lng, + req.query.destination_lat, + req.query.destination_lng, + ); + console.log(optimalRoute); + res.send(optimalRoute); + } +); module.exports = app; diff --git a/back-end/getOptimizedRoute.js b/back-end/getOptimizedRoute.js index c5b9dec..6f77ee4 100644 --- a/back-end/getOptimizedRoute.js +++ b/back-end/getOptimizedRoute.js @@ -1,90 +1,131 @@ //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.*/ -function createGraph(routes) { +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 routeId in routes) { let stops = routes[routeId]; - for ( let i = 0; i < stops.length; i++) { + for (let i = 0; i < stops.length; i++) { let currentStop = stops[i]; let nextStop = stops[(i + 1) % stops.length]; let currentKey = currentStop.toString(); - if (!graph[currentKey]) { - graph[currentKey] = []; + // Add the current route to the list of routes for this stop + if (!graph[currentKey].routes.includes(routeId)) { + graph[currentKey].routes.push(routeId); } - graph[currentKey].push({ coordinates: nextStop, route: routeId }); + // Add the connection information + graph[currentKey].connections.push({ coordinates: nextStop, route: graph[currentKey].routes }); } } return graph; } -function getEuclideanDistance (origin, destination) { - let x1 = origin.split(',')[0]; - let y1 = origin.split(',')[1]; - let x2 = destination.split(',')[0]; - let y2 = destination.split(',')[1]; +function getEuclideanDistance (a, b) { + let x1 = a[0]; + let y1 = a[1]; + let x2 = b.split(',')[0]; + let y2 = b.split(',')[1]; 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 -function findAllReachableStops(graph, origin, threshold=0.013) { +async function findAllReachableStops(graph, origin, threshold=0.01) { + + let resolvedGraph = await graph; // Waits for the graph Promise to resolve + let reachableStops = []; - for (busstop in graph){ + for (let busstop in resolvedGraph) { busstop = busstop.toString(); - let distance = getEuclideanDistance(busstop, origin); + let distance = getEuclideanDistance(origin, busstop); if (distance < threshold) { - console.log('reachable stop found: '+graph[busstop][0].route); - reachableStops.push({coordinates: busstop, route: graph[busstop][0].route}); + console.log('reachable stop found: ' + resolvedGraph[busstop].routes); + reachableStops.push({ coordinates: busstop, route: resolvedGraph[busstop].route }); } } - return reachableStops; - + return reachableStops; } + + // return walking distance from point A to point B async function getWalkingDistance(origin, destination) { - origin = origin.split(',').join('%2C'); - destination = destination.split(',').join('%2C'); + try{ + origin = origin.join('%2C'); + } + catch(err){ + origin = origin.split(',').join('%2C'); + } + + try{ + destination = destination.split(',').join('%2C'); + } + catch(err){ + 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 data = await res.json(); res = data.rows[0].elements[0].duration.value; return res; } -function isOnSameRoute(stop1, stop2) { - console.log(stop1.route); - return stop1.route === stop2.route; +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('shared routes: '+sharedRoutes) + if(sharedRoutes.length == 0){ + return false; + } + console.log('returned true') + console.log('shared routes length: '+sharedRoutes.length) + return sharedRoutes; } -async function findOptimalRoute(graph, origin, destination) { +async function findOptimalRoute(graph, routes, busstops, origin_lat, origin_lng, destination_lat, destination_lng) { + let origin = [origin_lat, origin_lng]; + let destination = [destination_lat, destination_lng]; let minTotalDistance = Number.MAX_VALUE; let optimalRoute = null; - // Find all reachable stops from origin and destination - let reachableFromOrigin = findAllReachableStops(graph, origin); - let reachableFromDestination = findAllReachableStops(graph, destination); + let reachableFromOrigin = await findAllReachableStops(graph, origin); + console.log('reachable from origin: '+reachableFromOrigin, reachableFromOrigin); + let reachableFromDestination = await findAllReachableStops(graph, destination); + console.log('reachable from desination: '+reachableFromDestination[0].coordinates, reachableFromDestination[0].coordinates); + for (let stop of reachableFromOrigin) { + console.log(stop.coordinates) + } // Calculate route distances for (let originStop of reachableFromOrigin) { for (let destinationStop of reachableFromDestination) { - if (isOnSameRoute(originStop, destinationStop)) { + let onSameRoute = await isOnSameRoute(graph, originStop, destinationStop, routes, busstops); + console.log('on same route result: '+onSameRoute) + if (onSameRoute.length > 0) { + console.log('----------------------------------') let distanceToOriginStop = await getWalkingDistance(origin, originStop.coordinates); let distanceFromDestinationStop = await getWalkingDistance(destinationStop.coordinates, destination); let totalDistance = distanceToOriginStop + distanceFromDestinationStop; + console.log('total', totalDistance) console.log('fetched from Google Maps API, total distance: '+totalDistance); console.log('----------------------------------') // Check if this route is better than the current best if (totalDistance < minTotalDistance) { + console.log('new optimal route found') minTotalDistance = totalDistance; - optimalRoute = { originStop, destinationStop }; + optimalRoute = { originStop, destinationStop, onSameRoute }; } } } } - console.log('optimal route: '+optimalRoute.originStop.route); + console.log('optimal route: '+optimalRoute.onSameRoute); console.log('origin stop: '+optimalRoute.originStop.coordinates); console.log('destination stop: '+optimalRoute.destinationStop.coordinates); diff --git a/front-end/src/components/LocationFilter.js b/front-end/src/components/LocationFilter.js index 6fcc023..fcb0df8 100644 --- a/front-end/src/components/LocationFilter.js +++ b/front-end/src/components/LocationFilter.js @@ -22,13 +22,20 @@ const LocationDropdown = ({initialLocation, onLocationChange }) => { query: inputValue, }; placesService.textSearch(request, (results, status) => { + console.log(results[0]) if (status === google.maps.places.PlacesServiceStatus.OK) { const nycPlaces = results.filter((result) => result.formatted_address); const limitedPlaces = nycPlaces.slice(0, 5); const places = limitedPlaces.map((result) => ({ name: result.name, address: result.formatted_address, - })); + place_id: result.place_id, + lat: result.geometry.location.lat(), + lng: result.geometry.location.lng(), + } + + )); + console.log(results[0].geometry.location.lat, results[0].geometry.location.lng); setOptions(places); } }); diff --git a/front-end/src/components/RoutesPage.js b/front-end/src/components/RoutesPage.js index e37e792..86073ce 100644 --- a/front-end/src/components/RoutesPage.js +++ b/front-end/src/components/RoutesPage.js @@ -76,6 +76,9 @@ function RoutesPage() { setFromLocation({ name: location.name, address: location.address, + place_id: location.place_id, + lat: location.lat, + lng: location.lng, }); }} /> @@ -86,7 +89,7 @@ function RoutesPage() { { - setToLocation({ name: location.name, address: location.address }); + setToLocation({ name: location.name, address: location.address, place_id: location.place_id, lat: location.lat, lng: location.lng }); }} /> diff --git a/front-end/src/components/RoutesSubpage.js b/front-end/src/components/RoutesSubpage.js index cd64b9e..85d9704 100644 --- a/front-end/src/components/RoutesSubpage.js +++ b/front-end/src/components/RoutesSubpage.js @@ -64,7 +64,21 @@ function RoutesSubpage({ location1, location2 }) { }; const startNavigation = () => { - alert("Navigation started!") + console.log(encodeURIComponent(location1.lat)) + fetch(`http://localhost:4000/getRoute?origin_lat=${location1.lat}&origin_lng=${location1.lng} &destination_lat=${location2.lat}&destination_lng=${location2.lng}`, { + method: "GET", + headers: { "Content-Type": "application/json"}, + }) + .then((response) => response.json()) + .then((response) => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .catch((error) => { + console.error('Fetch error:', error); + }); }; const shuttle = "X"; From c7da0078a14a515fa6178c27ecfe60867425411f Mon Sep 17 00:00:00 2001 From: unfiltered-syrup Date: Fri, 1 Dec 2023 21:17:38 -0500 Subject: [PATCH 2/3] small fixes --- back-end/app.js | 5 +++++ back-end/getOptimizedRoute.js | 24 +++++++++-------------- front-end/src/components/RoutesSubpage.js | 3 ++- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/back-end/app.js b/back-end/app.js index 7560303..7ac9ca8 100644 --- a/back-end/app.js +++ b/back-end/app.js @@ -29,6 +29,10 @@ const feedbackRoutes = require("./routes/feedback-routes.js"); app.use("/feedback", feedbackRoutes()); +app.get("/test", (req, res) => { + console.log(window.nyushuttle) +}); + app.get("/getRoute", (req, res) => { const routeFinding = require("./getOptimizedRoute.js"); @@ -89,6 +93,7 @@ app.get("/getRoute", (req, res) => { req.query.destination_lat, req.query.destination_lng, ); + console.log(optimalRoute); res.send(optimalRoute); } diff --git a/back-end/getOptimizedRoute.js b/back-end/getOptimizedRoute.js index 6f77ee4..8ffa213 100644 --- a/back-end/getOptimizedRoute.js +++ b/back-end/getOptimizedRoute.js @@ -79,12 +79,9 @@ async function getWalkingDistance(origin, destination) { 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('shared routes: '+sharedRoutes) if(sharedRoutes.length == 0){ return false; } - console.log('returned true') - console.log('shared routes length: '+sharedRoutes.length) return sharedRoutes; } @@ -95,27 +92,18 @@ async function findOptimalRoute(graph, routes, busstops, origin_lat, origin_lng, let optimalRoute = null; // Find all reachable stops from origin and destination let reachableFromOrigin = await findAllReachableStops(graph, origin); - console.log('reachable from origin: '+reachableFromOrigin, reachableFromOrigin); let reachableFromDestination = await findAllReachableStops(graph, destination); - console.log('reachable from desination: '+reachableFromDestination[0].coordinates, reachableFromDestination[0].coordinates); - for (let stop of reachableFromOrigin) { - console.log(stop.coordinates) - } // 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) { - console.log('----------------------------------') let distanceToOriginStop = await getWalkingDistance(origin, originStop.coordinates); let distanceFromDestinationStop = await getWalkingDistance(destinationStop.coordinates, destination); - let totalDistance = distanceToOriginStop + distanceFromDestinationStop; - console.log('total', totalDistance) console.log('fetched from Google Maps API, total distance: '+totalDistance); - console.log('----------------------------------') // Check if this route is better than the current best if (totalDistance < minTotalDistance) { console.log('new optimal route found') @@ -125,10 +113,16 @@ async function findOptimalRoute(graph, routes, busstops, origin_lat, origin_lng, } } } + + if (optimalRoute === null) { + console.log('no optimal route found') + return null; + } + console.log('-------------------------------------') console.log('optimal route: '+optimalRoute.onSameRoute); - console.log('origin stop: '+optimalRoute.originStop.coordinates); - console.log('destination stop: '+optimalRoute.destinationStop.coordinates); - + console.log('origin stop location: '+optimalRoute.originStop.coordinates); + console.log('destination stop location: '+optimalRoute.destinationStop.coordinates); + return optimalRoute; } diff --git a/front-end/src/components/RoutesSubpage.js b/front-end/src/components/RoutesSubpage.js index 85d9704..c07154b 100644 --- a/front-end/src/components/RoutesSubpage.js +++ b/front-end/src/components/RoutesSubpage.js @@ -64,7 +64,6 @@ function RoutesSubpage({ location1, location2 }) { }; const startNavigation = () => { - console.log(encodeURIComponent(location1.lat)) fetch(`http://localhost:4000/getRoute?origin_lat=${location1.lat}&origin_lng=${location1.lng} &destination_lat=${location2.lat}&destination_lng=${location2.lng}`, { method: "GET", headers: { "Content-Type": "application/json"}, @@ -74,6 +73,8 @@ function RoutesSubpage({ location1, location2 }) { if (!response.ok) { throw new Error('Network response was not ok'); } + console.log('Response from server:'); + console.log(response); return response.json(); }) .catch((error) => { From 94b5bb1d662dfaa1df101eb27e4b2ba5a5135879 Mon Sep 17 00:00:00 2001 From: unfiltered-syrup Date: Fri, 1 Dec 2023 22:40:24 -0500 Subject: [PATCH 3/3] connected route finding to frontend --- back-end/app.js | 8 +++--- front-end/src/components/RoutesSubpage.js | 31 +++++++++++++---------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/back-end/app.js b/back-end/app.js index 7ac9ca8..5a2cf72 100644 --- a/back-end/app.js +++ b/back-end/app.js @@ -34,7 +34,7 @@ app.get("/test", (req, res) => { }); -app.get("/getRoute", (req, res) => { +app.get("/getRoute", async (req, res) => { const routeFinding = require("./getOptimizedRoute.js"); const busStops = { unionsquare: [40.73498551788369, -73.990696048825], @@ -86,7 +86,7 @@ app.get("/getRoute", (req, res) => { }; const graph = routeFinding.createGraph(routes, busStops); console.log('graph created') - let optimalRoute = routeFinding.findOptimalRoute( + let optimalRoute = await routeFinding.findOptimalRoute( graph, routes, busStops, req.query.origin_lat, req.query.origin_lng, @@ -94,8 +94,8 @@ app.get("/getRoute", (req, res) => { req.query.destination_lng, ); - console.log(optimalRoute); - res.send(optimalRoute); + console.log(optimalRoute.onSameRoute); + res.send(optimalRoute.onSameRoute); } ); diff --git a/front-end/src/components/RoutesSubpage.js b/front-end/src/components/RoutesSubpage.js index c07154b..3770e04 100644 --- a/front-end/src/components/RoutesSubpage.js +++ b/front-end/src/components/RoutesSubpage.js @@ -1,5 +1,5 @@ import { React, useState, useEffect } from "react"; -import { Link } from "react-router-dom"; +import { Link , useNavigate } from "react-router-dom"; import "../css/routesSubpage.css"; import "../css/basicUI.css"; import HeartIcon from "../images/heart-svg.svg"; @@ -12,7 +12,7 @@ function RoutesSubpage({ location1, location2 }) { const [isSaveDialogOpen, setSaveDialogOpen] = useState(false); const [isRouteSaved, setIsRouteSaved] = useState(false); const [displayText, setDisplayText] = useState("Click to save route ->"); - + const navigate = useNavigate(); useEffect(() => { const loadedRoutes = localStorageLoad('routes'); if (loadedRoutes && loadedRoutes.some((route) => route.from.name === location1.name && route.to === location2.name)) { @@ -64,23 +64,26 @@ function RoutesSubpage({ location1, location2 }) { }; const startNavigation = () => { - fetch(`http://localhost:4000/getRoute?origin_lat=${location1.lat}&origin_lng=${location1.lng} &destination_lat=${location2.lat}&destination_lng=${location2.lng}`, { - method: "GET", - headers: { "Content-Type": "application/json"}, + fetch(`http://localhost:4000/getRoute?origin_lat=${location1.lat}&origin_lng=${location1.lng}&destination_lat=${location2.lat}&destination_lng=${location2.lng}`, { + method: "GET", + headers: { "Content-Type": "application/json"}, }) - .then((response) => response.json()) - .then((response) => { + .then((response) => { if (!response.ok) { - throw new Error('Network response was not ok'); + throw new Error('Network response was not ok'); } + return response.json(); + }) + .then((data) => { console.log('Response from server:'); - console.log(response); - return response.json(); - }) - .catch((error) => { + console.log(data); + alert('Starting' + JSON.stringify(data[0])); + + }) + .catch((error) => { console.error('Fetch error:', error); - }); - }; + }); +}; const shuttle = "X"; const shuttleSchedule = "HH:MM";