diff --git a/back-end/app.js b/back-end/app.js index 57a7cab..478c09d 100644 --- a/back-end/app.js +++ b/back-end/app.js @@ -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 = {}; @@ -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); diff --git a/back-end/getOptimizedRoute.js b/back-end/getOptimizedRoute.js index 82a4895..1be788b 100644 --- a/back-end/getOptimizedRoute.js +++ b/back-end/getOptimizedRoute.js @@ -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; @@ -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; } @@ -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; } @@ -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 }; } } + } } @@ -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); diff --git a/front-end/src/components/RouteButton.js b/front-end/src/components/RouteButton.js new file mode 100644 index 0000000..e5a6ed6 --- /dev/null +++ b/front-end/src/components/RouteButton.js @@ -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 ( + + ); + } + +export default RouteButton; \ No newline at end of file diff --git a/front-end/src/components/RouteMap.js b/front-end/src/components/RouteMap.js index f1942ab..556b609 100644 --- a/front-end/src/components/RouteMap.js +++ b/front-end/src/components/RouteMap.js @@ -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); @@ -54,7 +55,13 @@ function RouteMap({ location1, location2 }) { }); }, [location1, location2]); - return
; + return ( +
+ +
+
+ ) + } export default RouteMap; diff --git a/front-end/src/components/RoutesPage.js b/front-end/src/components/RoutesPage.js index 8687e99..93745ad 100644 --- a/front-end/src/components/RoutesPage.js +++ b/front-end/src/components/RoutesPage.js @@ -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' @@ -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) => { diff --git a/front-end/src/components/RoutesSubpage.js b/front-end/src/components/RoutesSubpage.js index 1d24d8b..733c153 100644 --- a/front-end/src/components/RoutesSubpage.js +++ b/front-end/src/components/RoutesSubpage.js @@ -6,6 +6,8 @@ import HeartIcon from "../images/heart-svg.svg"; import HeartIconLoaded from "../images/heart-svg-loaded.svg"; import SaveRouteDialog from "./SaveRouteDialog"; import RouteMap from "./RouteMap"; +import SaveRouteButton from "./SaveRouteButton"; +import ViewRouteButton from "./ViewRouteButton"; import { localStorageSave, localStorageLoad } from '../utils/localStorageSaveLoad'; function RoutesSubpage({ location1, location2, routes }) { @@ -13,17 +15,9 @@ function RoutesSubpage({ location1, location2, routes }) { 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)) { - setIsRouteSaved(true); - setDisplayText("View saved routes here"); - } else { - setIsRouteSaved(false); - } - }, [location1, location2]); const openSaveDialog = () => { + console.log('clicked') if (!isRouteSaved) { setSaveDialogOpen(true); } else { @@ -32,40 +26,48 @@ function RoutesSubpage({ location1, location2, routes }) { localStorageSave('routes', updatedRoutes); } }; + useEffect(() => { + const loadedRoutes = localStorageLoad('routes'); + if (loadedRoutes && loadedRoutes.some((route) => route.from.name === location1.name && route.to === location2.name)) { + setIsRouteSaved(true); + setDisplayText("View saved routes here"); + } else { + setIsRouteSaved(false); + } + }, [location1, location2]); - const closeSaveDialog = () => { - setSaveDialogOpen(false); - }; - const toggleRouteSaved = (name) => { - const loadedRoutes = localStorageLoad('routes') || []; - const maxId = loadedRoutes.reduce((max, route) => (route._id > max ? route._id : max), 0); - const newId = maxId + 1; - const newRoute = { - _id: newId, - name: name, - timestamp: Date.now(), - from: { - name: location1.name, - address: location1.address, - }, - to: { - name: location2.name, - address: location2.address, - }, - }; - loadedRoutes.push(newRoute); - localStorageSave('routes', loadedRoutes); - setIsRouteSaved(true); - setDisplayText("Saved"); - setTimeout (() => { - setDisplayText("View saved routes here"); - }, 3000); - }; - - const startNavigation = () => { + useEffect(() => { + if (window.nyushuttle.routes){ + for ( let route in routes) { + for (let routename in window.nyushuttle.routes) { + console.log(routename) + if (window.nyushuttle.routes[routename][0] === route) { + routes[route].color = window.nyushuttle.routes[routename][1]; + console.log(window.nyushuttle.routes[routename]) + } + } + } + if(routes[Object.keys(routes)[0]].time){ + console.log('time calculated') + } + else{ + console.log('time not calculated') + } + } -}; + }, [window.nyushuttle.routes]); + + const startNavigation = value => () => { + for (let [key, name] of Object.entries(window.nyushuttle.routes)) { + if (name[0] === value) { + window.nyushuttle.routesSelected = key; + window.nyushuttle.navigating = true; + break; + } + } + navigate('/map'); + }; const shuttle = "X"; const shuttleSchedule = "HH:MM"; @@ -77,46 +79,36 @@ function RoutesSubpage({ location1, location2, routes }) { return (
- - {displayText} - - {!isRouteSaved && - Saved Icon - } +
- {routes.map((route, index) => ( -
+
+ {Object.keys(routes).map((route, index) => ( +

{route}

-

- Shuttle {route} scheduled at {shuttleSchedule} +

+ Total Walking Time:{" "} + {routes[route].time} min

- {location1.name} to shuttle:{" "} - {timeToShuttle} min -

-

- Shuttle to {location2.name}:{" "} - {timeToDestination2} min + Shuttle {route} scheduled at {shuttleSchedule}

+
-
))} +
- {isSaveDialogOpen && ( - - )}
); } diff --git a/front-end/src/components/SaveRouteButton.js b/front-end/src/components/SaveRouteButton.js new file mode 100644 index 0000000..2d55aec --- /dev/null +++ b/front-end/src/components/SaveRouteButton.js @@ -0,0 +1,92 @@ +import '../css/routesSubpage.css'; +import { Link } from 'react-router-dom'; +import React, { useState, useEffect } from 'react'; +import HeartIcon from '../images/heart-svg.svg'; +import HeartIconLoaded from '../images/heart-svg-loaded.svg'; +import { localStorageSave, localStorageLoad } from '../utils/localStorageSaveLoad'; +import SaveRouteDialog from "./SaveRouteDialog"; + + +function SaveRouteButton (prop) { + const [isRouteSaved, setIsRouteSaved] = useState(false); + const [isSaveDialogOpen, setSaveDialogOpen] = useState(false); + + const toggleButton = () => { + + console.log(window.nyushuttle.fromLocation.name) + } + useEffect(() => { + setIsRouteSaved(prop.saved); + },[prop.saved]); + + const openSaveDialog = () => { + setIsRouteSaved(!isRouteSaved); + const location1 = window.nyushuttle.fromLocation; + const location2 = window.nyushuttle.toLocation; + if (!isRouteSaved) { + setSaveDialogOpen(true); + } else { + const loadedRoutes = localStorageLoad('routes') || []; + const updatedRoutes = loadedRoutes.filter((route) => route.from.name !== location1.name || route.to.name !== location2.name); + localStorageSave('routes', updatedRoutes); + } + }; + + const closeSaveDialog = () => { + setSaveDialogOpen(false); + }; + + const toggleRouteSaved = (name) => { + const location1 = window.nyushuttle.fromLocation; + const location2 = window.nyushuttle.toLocation; + const loadedRoutes = localStorageLoad('routes') || []; + const maxId = loadedRoutes.reduce((max, route) => (route._id > max ? route._id : max), 0); + const newId = maxId + 1; + const newRoute = { + _id: newId, + name: name, + timestamp: Date.now(), + from: { + name: location1.name, + address: location1.address, + }, + to: { + name: location2.name, + address: location2.address, + }, + }; + loadedRoutes.push(newRoute); + localStorageSave('routes', loadedRoutes); + setIsRouteSaved(true); + }; + + return ( + <> + + {isSaveDialogOpen && ( + + )} + + ) +} + +export default SaveRouteButton; +/* + +{displayText} + +{!isRouteSaved && +Saved Icon +}*/ \ No newline at end of file diff --git a/front-end/src/components/ViewRouteButton.js b/front-end/src/components/ViewRouteButton.js new file mode 100644 index 0000000..5852bc0 --- /dev/null +++ b/front-end/src/components/ViewRouteButton.js @@ -0,0 +1,18 @@ +import '../css/routesSubpage.css'; +import { Link } from 'react-router-dom'; +function ViewRouteButton () { + return ( + <> +
+ + + +
+ + + + + ) +} + +export default ViewRouteButton; \ No newline at end of file diff --git a/front-end/src/css/routeMap.css b/front-end/src/css/routeMap.css index d7ff180..4a1b4d5 100644 --- a/front-end/src/css/routeMap.css +++ b/front-end/src/css/routeMap.css @@ -1,13 +1,6 @@ -.route_map { - width: 85%; - height: 50%; - border-radius: 20px; - overflow: hidden; - filter: grayscale(20%); - } .route_map{ - @apply bg-lightTone w-80 opacity-60 border-solid drop-shadow-2xl border-2 border-darkTone rounded-3xl; + @apply bg-lightTone h-52 w-full border-solid drop-shadow-2xl border-2 border-darkTone rounded-3xl; } .dark .rooute_map{ diff --git a/front-end/src/css/routesPage.css b/front-end/src/css/routesPage.css index 5c1a8d9..c16145b 100644 --- a/front-end/src/css/routesPage.css +++ b/front-end/src/css/routesPage.css @@ -1,5 +1,5 @@ .route-container { - @apply p-2 pl-4 pr-6 pt-4 pb-5 h-screen bg-lightTone flex flex-col overflow-y-scroll; + @apply p-2 pl-4 pr-4 pt-4 pb-5 h-screen bg-lightTone flex flex-col overflow-y-scroll; } .dark .route-container{ @@ -7,11 +7,11 @@ } .input-wrapper{ - @apply flex flex-col items-center; + @apply flex flex-col items-center px-4; } .location-input { - @apply my-0.5 w-80; + @apply my-0.5 w-full; } .location-input label { diff --git a/front-end/src/css/routesSubpage.css b/front-end/src/css/routesSubpage.css index a92f0e3..ff1df62 100644 --- a/front-end/src/css/routesSubpage.css +++ b/front-end/src/css/routesSubpage.css @@ -1,7 +1,7 @@ /* routesSubpage.css */ .routes-subpage-container{ - @apply h-screen bg-lightTone w-full overflow-y-auto flex flex-col items-center pb-20; + @apply h-screen bg-lightTone px-4 w-full pt-3 overflow-y-auto flex flex-col items-center pb-20 my-0; } .dark .routes-subpage-container{ @@ -13,11 +13,19 @@ img { } .title-container { - @apply flex justify-items-start w-80; + @apply flex justify-items-start w-full; +} + +.save-route-button{ + @apply text-darkTone text-center w-1/4 text-base mx-0 h-full p-0 rounded-xl; +} + +.view-route-button{ + @apply bg-darkTone text-lightTone w-full text-base mx-0 h-11 py-2 text-center rounded-xl px-6; } .saved-icon { - @apply w-16 h-16 mb-0 grayscale-0 ; + @apply absolute z-50 w-8 h-12 mb-0 top-8 right-5 pb-2 grayscale-0 p-0; } .save{ @@ -32,6 +40,10 @@ img { @apply w-full text-right; } +.route-info-container{ + @apply flex flex-col items-center w-full pt-3; +} + .red{ filter: invert(72%) sepia(45%) saturate(5044%) hue-rotate(319deg) brightness(105%) contrast(82%); } @@ -42,12 +54,12 @@ img { align-items: center; justify-content: space-between; padding: 0.5rem; - width: 90%; - margin-top: 10px; - background-color: #fff; - border: 0.5px solid #4e4a4a; - border-radius: 8px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + width: 100%; + border-radius: 10px; + border-style: solid; + margin-bottom: 10px; + background-color: white; + box-shadow: 0 2px 4px rgba(103, 58, 163, 0.5); } .dark .route-info { @@ -59,6 +71,7 @@ img { flex-direction: column; justify-content: space-between; align-items: flex-start; + margin-left: 10px; } .route-info .text-lg { @@ -70,10 +83,5 @@ img { } .nav-button { - background-color: #165516; - - padding: 1rem; - border-radius: 12px; - color: #fff; - margin: 10px; + @apply bg-darkTone text-lightTone text-base my-3 mx-3 h-14 py-2 rounded-xl px-6 w-20; } diff --git a/front-end/src/utils/stops.js b/front-end/src/utils/stops.js index 1cafef4..fd48be6 100644 --- a/front-end/src/utils/stops.js +++ b/front-end/src/utils/stops.js @@ -73,7 +73,6 @@ export async function queryStops() { window.nyushuttle.stops = data.stops; groupRoutes = data.groupRoutes; center = data.center; - console.log(window.nyushuttle.stops) return true; } catch (error) { @@ -137,20 +136,119 @@ function drawRoutes(showStopName) { if (!Object.keys(window.nyushuttle.routes).length) { return; } + let clippedRouteIndex = null; + if (window.nyushuttle.startStopLocation){ + clippedRouteIndex = getClippedRoute() + } const routes = window.nyushuttle.routes; + Object.keys(routes) .filter((routeId) => isSelectedRoute(routeId)) .forEach((routeId) => { - drawRoute(routeId, routes[routeId], showStopName); + drawRoute(routeId, routes[routeId], showStopName, clippedRouteIndex); }); } -function drawRoute(routeId, route, showStopName) { +function drawWalkingRoute(direction) { + let startLocation, endLocation; + let request; + let stops = window.nyushuttle.stops; + + if (direction === 'from') { //plot "from" route + //becuase for some reason fromLocation.lat and fronLocation.lng are corrupted, use + //fromLocation.place_id instead + startLocation = {'placeId':window.nyushuttle.fromLocation.place_id}; + endLocation = { + lat: stops['ID' + window.nyushuttle.startStopLocation].latitude, + lng: stops['ID' + window.nyushuttle.startStopLocation].longitude + }; + request = { + origin: startLocation, + destination: endLocation, + travelMode: 'WALKING', + }; + } else if (direction === 'to') { //plot "to" route + endLocation = {'placeId':window.nyushuttle.toLocation.place_id}; + startLocation = { + lat: stops['ID' + window.nyushuttle.endStopLocation].latitude, + lng: stops['ID' + window.nyushuttle.endStopLocation].longitude + }; + request = { + origin: startLocation, + destination: endLocation, + travelMode: 'WALKING', + }; + } + let directionsService = new window.google.maps.DirectionsService(); + directionsService.route(request, function (result, status) { + if (status == 'OK') { + let directionsRenderer = new window.google.maps.DirectionsRenderer({ + suppressMarkers: true // Hides the default markers + }); + directionsRenderer.setMap(window.nyushuttle.currentMap); + directionsRenderer.setDirections(result); + + // Add custom markers + if(direction === 'from') { + const startMarker = new window.google.maps.Marker({ + position: result.routes[0].legs[0].start_location, + map: window.nyushuttle.currentMap, + icon: { + path: window.google.maps.SymbolPath.CIRCLE, + scale: 7, + fillColor: '#8400a8', + fillOpacity: 1.0, + strokeColor: '#FF0000', + strokeOpacity: 0 + } + }); + } + else{ + const endMarker = new window.google.maps.Marker({ + position: result.routes[0].legs[0].end_location, + map: window.nyushuttle.currentMap, + icon: { + path: window.google.maps.SymbolPath.CIRCLE, + scale: 7, + fillColor: '#8400a8', + fillOpacity: 1, + strokeColor: '#', + strokeOpacity: 0 + } + }); + } + } + }); +} + +function drawRoute(routeId, route, showStopName, clip=null) { const routeGroupId = route[2]; const routeColor = correctColorFromARGB(route[1]); - const routePaths = getRoutePaths(routeId); + let routePaths = getRoutePaths(routeId); + if (clip) { + drawWalkingRoute('from') + drawWalkingRoute('to') + routePaths = routePaths.slice(clip[0], clip[1]) + if (routePaths.length == 0){ + if (clip[0] < 100 && clip[1] > 100){ + routePaths = routePaths.concat(routePaths.slice(100, clip[1])) + } + else if (clip[0] < 100 && clip[1] < 100){ + routePaths = routePaths[0].slice(clip[0], clip[1]) + } + else if (clip[0] >= 100 && clip[1] >= 100){ + routePaths = routePaths[1].slice(clip[0], clip[1]) + } + + } + drawRoutePath(routePaths, routeColor, routeId, routeGroupId); + } + else{ + console.log('noclip') + drawRoutePath(routePaths, routeColor, routeId, routeGroupId); - drawRoutePath(routePaths, routeColor, routeId, routeGroupId); + } + // routePaths.forEach((path) => { // updateBoundsWithPoint(path); // }); @@ -165,6 +263,49 @@ function getRoutePaths(routeId) { return points[routeId][0].map((point) => new window.google.maps.LatLng(point.lat, point.lng)); } +function getIndexofStop(r, stopPos) { + // Logic to get clipped route + let minDist = 9999 + let index = -1 + console.log('r: ',r) + for (let i=0;i destinationStopIndex){ + return [destinationStopIndex, originStopIndex] + } + return [originStopIndex, destinationStopIndex] +} + function drawRoutePath(path, routeColor, routeId, routeGroupId) { const selected = window.nyushuttle.routesSelected && window.nyushuttle.routesSelected.includes(routeId); const opacity = getOpacity(selected); @@ -219,6 +360,22 @@ function drawStopNamesForRoute(route) { // Logic to draw stop names for the given route } +function sliceRouteStops(routeStops, stop1, stop2) { + // Retain the first three elements + const initialPart = routeStops.slice(0, 3); + + let startIndex = routeStops.findIndex(subarray => subarray[1] === stop1); + let endIndex = routeStops.findIndex(subarray => subarray[1] === stop2); + if (startIndex > endIndex) { + let temp = startIndex; + startIndex = endIndex; + endIndex = temp; + } + const slicedPart = routeStops.slice(startIndex, endIndex + 1); + + return [...initialPart, ...slicedPart]; +} + function drawStops() { const stops = window.nyushuttle.stops; if (!stops) return; @@ -230,9 +387,11 @@ function drawStops() { Object.keys(routes) .filter((routeId) => isSelectedRoute(routeId)) .forEach((routeId) => { - const routestops = routes[routeId]; - const routeGroupId = routes[routeId][2]; - + let routestops = routes[routeId]; + let routeGroupId = routes[routeId][2]; + if (window.nyushuttle.startStopLocation && routestops){ + routestops = sliceRouteStops(routestops, window.nyushuttle.startStopLocation, window.nyushuttle.endStopLocation) + } addRouteMarkersOnMap(routeId, routestops, routeGroupId, showStopName); });