Skip to content

Commit

Permalink
added optimize route finding algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
unfiltered-syrup committed Nov 26, 2023
1 parent ce41a67 commit 8ad37c2
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 4 deletions.
142 changes: 142 additions & 0 deletions back-end/app.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,151 @@
// import and instantiate express
const express = require('express'); // CommonJS import style!
const app = express(); // instantiate an Express object
require('dotenv').config();
// we will put some server logic here later...

const cors = require('cors');
app.use(cors());

module.exports = app;

//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) {
let graph = {};
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();

if (!graph[currentKey]) {
graph[currentKey] = [];
}
graph[currentKey].push({ coordinates: nextStop, route: routeId });

}
}
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];
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) {
let reachableStops = [];
for (busstop in graph){
busstop = busstop.toString();
let distance = getEuclideanDistance(busstop, origin);
if (distance < threshold) {
reachableStops.push({coordinates: busstop});
}
}

return reachableStops;

}

async function getWalkingDistance(origin, destination) {
origin = origin.split(',').join('%2C');
destination = destination.split(',').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;
let originName = data.origin_addresses[0];
let destinationName = data.destination_addresses[0];
return res;
}

function isOnSameRoute(stop1, stop2) {
return stop1.route === stop2.route;
}

async function findOptimalRoute(graph, origin, destination) {
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);

// Calculate route distances
for (let originStop of reachableFromOrigin) {
for (let destinationStop of reachableFromDestination) {
if (isOnSameRoute(originStop, destinationStop)) {
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) {
minTotalDistance = totalDistance;
optimalRoute = { originStop, destinationStop, route: originStop.route };
}
}
}
}
console.log('optimal route: '+optimalRoute.route);
console.log('origin stop: '+optimalRoute.originStop.coordinates);
console.log('destination stop: '+optimalRoute.destinationStop.coordinates);

return optimalRoute;
}

function findRoute (graph, origin, destination) {
const minTotalWalkingDistance = Number.MAX_VALUE;
const minRoute = null;
let closestStop = null;
let walkdist = Number.MAX_VALUE;
//find closest starting point
for (let stop in graph) {
let currentStop = stop.toString();
let currentRoute = graph[stop];
let currentDistance = getEuclideanDistance(currentStop, origin);
if (currentDistance < walkdist) {
walkdist = currentDistance;
closestStop = currentStop;
}
}
console.log('closest to starting point: '+closestStop);
}


app.get('/getRoute', (req, res) => {
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],
}
const graph = createGraph(routes);
let optimalRoute = findOptimalRoute(graph, req.query.origin, req.query.destination);
console.log(optimalRoute);
res.send('Hello World!');
})
11 changes: 7 additions & 4 deletions front-end/src/utils/transportMarker.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ function createTransportMarker(position, transportInfo, map) {
position,
map,
title: String(transportInfo.busId),
icon: generateTransportMarkerIcon(transportInfo.color || '#000', transportInfo.calculatedCourse || 0),
icon: generateTransportMarkerIcon(transportInfo.color || '#000', transportInfo.calculatedCourse || 0, transportInfo.route || 0),
});

let infowindow = new window.google.maps.InfoWindow({
content: `<div><strong>No.${transportInfo.busId}</strong><br>Line: ${transportInfo.route}</div>`,
content: `<div><strong>No.${transportInfo.busId}</strong><br>Line: ${transportInfo.route}</div>Passenger Count: ${transportInfo.paxLoad}</div>`,
});

transportMarker.direction = transportInfo.calculatedCourse;
Expand Down Expand Up @@ -108,7 +108,7 @@ function animateMarker(marker, startPosition, endPosition, duration) {

// Generate custom transport marker icon (currently only support buses)
// Original source of bus svg path: www.svgrepo.com
function generateTransportMarkerIcon(color, direction) {
function generateTransportMarkerIcon(color, direction, route) {
const svg = `<?xml version="1.0" encoding="utf-8"?>
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg">
<g class="layer" transform="rotate(${direction}, 12, 12)">
Expand All @@ -122,8 +122,11 @@ function generateTransportMarkerIcon(color, direction) {
</g>
</g>
</svg>`;
let iconImg;
route !== 'Ferry Route' ? (iconImg = 'data:image/svg+xml;charset=UTF-8;base64,' + btoa(svg)) : (iconImg = 'busIcons/busIcon_routeFerry_Route.png');

const icon = {
url: 'data:image/svg+xml;charset=UTF-8;base64,' + btoa(svg),
url: iconImg,
scaledSize: new window.google.maps.Size(40, 40),
};
return icon;
Expand Down

0 comments on commit 8ad37c2

Please sign in to comment.