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

Added route-finding functionalities #110

Merged
merged 3 commits into from
Dec 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 67 additions & 57 deletions back-end/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -28,65 +29,74 @@ 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) => {
app.get("/getRoute", async (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 = await routeFinding.findOptimalRoute(
graph, routes, busStops,
req.query.origin_lat,
req.query.origin_lng,
req.query.destination_lat,
req.query.destination_lng,
);

console.log(optimalRoute.onSameRoute);
res.send(optimalRoute.onSameRoute);
}
);

module.exports = app;
103 changes: 69 additions & 34 deletions back-end/getOptimizedRoute.js
Original file line number Diff line number Diff line change
@@ -1,93 +1,128 @@
//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));
if(sharedRoutes.length == 0){
return false;
}
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);

let reachableFromDestination = await findAllReachableStops(graph, destination);
// 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) {
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);
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('origin stop: '+optimalRoute.originStop.coordinates);
console.log('destination stop: '+optimalRoute.destinationStop.coordinates);


if (optimalRoute === null) {
console.log('no optimal route found')
return null;
}
console.log('-------------------------------------')
console.log('optimal route: '+optimalRoute.onSameRoute);
console.log('origin stop location: '+optimalRoute.originStop.coordinates);
console.log('destination stop location: '+optimalRoute.destinationStop.coordinates);

return optimalRoute;
}

Expand Down
9 changes: 8 additions & 1 deletion front-end/src/components/LocationFilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
});
Expand Down
5 changes: 4 additions & 1 deletion front-end/src/components/RoutesPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ function RoutesPage() {
setFromLocation({
name: location.name,
address: location.address,
place_id: location.place_id,
lat: location.lat,
lng: location.lng,
});
}}
/>
Expand All @@ -86,7 +89,7 @@ function RoutesPage() {
<LocationFilter
initialLocation={toLocation}
onLocationChange={(location) => {
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 });
}}
/>
</div>
Expand Down
26 changes: 22 additions & 4 deletions front-end/src/components/RoutesSubpage.js
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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)) {
Expand Down Expand Up @@ -64,8 +64,26 @@ function RoutesSubpage({ location1, location2 }) {
};

const startNavigation = () => {
alert("Navigation started!")
};
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) => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then((data) => {
console.log('Response from server:');
console.log(data);
alert('Starting' + JSON.stringify(data[0]));

})
.catch((error) => {
console.error('Fetch error:', error);
});
};

const shuttle = "X";
const shuttleSchedule = "HH:MM";
Expand Down