diff options
Diffstat (limited to 'dashboard_website/router.py')
| -rw-r--r-- | dashboard_website/router.py | 137 |
1 files changed, 94 insertions, 43 deletions
diff --git a/dashboard_website/router.py b/dashboard_website/router.py index e3784d3..e0e0720 100644 --- a/dashboard_website/router.py +++ b/dashboard_website/router.py @@ -1,5 +1,4 @@ import datetime -import time import numpy as np import requests @@ -9,33 +8,37 @@ from datastructs import * host = "http://acetyl.net:5000" # queries acetyl.net:5000, the OSRM engine -endtime = datetime.datetime(2023, 11, 18, hour=18, minute=45) # 11/18/2023 6:35pm +endtime = datetime.datetime(2023, 11, 18, hour=18, minute=45) # 11/18/2023 6:35pm # external facing functions + # gets single leg route between bike and clue # should be HD and GeoJSON def getRouteFullJSON(bike, clue): - bike = bike.toJSON(); clue = clue.toJSON() + bike = bike.toJSON() + clue = clue.toJSON() url = f"{host}/route/v1/bike/{bike['longitude']},{bike['latitude']};{clue['longitude']},{clue['latitude']}?steps=true&overview=full&geometries=geojson" r = requests.get(url) return r.json() def getRouteHDPolyline(bike, clue): - bike = bike.toJSON(); clue = clue.toJSON() + bike = bike.toJSON() + clue = clue.toJSON() url = f"{host}/route/v1/bike/{bike['longitude']},{bike['latitude']};{clue['longitude']},{clue['latitude']}?overview=full&geometries=geojson" r = requests.get(url) - p = r.json()['routes'][0]['geometry']['coordinates'] + p = r.json()["routes"][0]["geometry"]["coordinates"] return p def getRouteFastPolyline(bike, clue): - bike = bike.toJSON(); clue = clue.toJSON() + bike = bike.toJSON() + clue = clue.toJSON() url = f"{host}/route/v1/bike/{bike['longitude']},{bike['latitude']};{clue['longitude']},{clue['latitude']}?geometries=geojson" r = requests.get(url) - p = r.json()['routes'][0]['geometry']['coordinates'] + p = r.json()["routes"][0]["geometry"]["coordinates"] return p @@ -45,8 +48,8 @@ def getZSP(bike, home, clue_cluster): # determines clusters based on current bikes and clues def getClusters(bikes, clues, endpoint): - clusters = [[] for bike in bikes ] - route_geos = [[] for bike in bikes ] + clusters = [[] for bike in bikes] + route_geos = [[] for bike in bikes] times = {} active_indices = [i for i in range(len(bikes)) if bikes[i].status == "ACTIVE"] active_bikes = [bike for bike in bikes if bike.status == "ACTIVE"] @@ -55,10 +58,12 @@ def getClusters(bikes, clues, endpoint): active_clues = [clue for clue in clues if clue.status == "UNVISITED"] # select only active bikes # select only unvisited clues - clusters_t, route_geos_t, times_t = cluster_and_optimize(active_clues, active_bikes, endpoint) + clusters_t, route_geos_t, times_t = cluster_and_optimize( + active_clues, active_bikes, endpoint + ) for i in range(len(active_indices)): route_geos[active_indices[i]] = route_geos_t[i] - clusters[active_indices[i]] = clusters_t[i] + clusters[active_indices[i]] = clusters_t[i] bikes[active_indices[i]].setCluster(clusters_t[i]) times[bikes[active_indices[i]].name] = times_t[i] @@ -67,7 +72,9 @@ def getClusters(bikes, clues, endpoint): # utility functions (internal) -def cluster_and_optimize(clues: [Clue], bikes: [Bike], end: Point, time_diff=0.25, max_time=24, n=2): +def cluster_and_optimize( + clues: [Clue], bikes: [Bike], end: Point, time_diff=0.25, max_time=24, n=2 +): """ Takes a dataframe of gps coordinates, a list of centroids, and an end point and returns a dataframe with a cluster :param clues: a list of clues @@ -80,8 +87,10 @@ def cluster_and_optimize(clues: [Clue], bikes: [Bike], end: Point, time_diff=0.2 """ # OVERRIDE MAX TIME max_time = datetime.datetime.now() - endtime - max_time = max_time.seconds/3600 - routes = [clues] # one bike = one set of routes. only need to remove the faraway waypoints + max_time = max_time.seconds / 3600 + routes = [ + clues + ] # one bike = one set of routes. only need to remove the faraway waypoints if len(bikes) > 1: # Create a new column with normalized gps coordinates and centroids normalized_points, norm_centroids = __normalize_points(clues, bikes) @@ -95,29 +104,32 @@ def cluster_and_optimize(clues: [Clue], bikes: [Bike], end: Point, time_diff=0.2 routes[label].append(clues[i]) routes = __minimize_route_time_diff(routes, bikes, end, time_diff, n) - + # Remove waypoints from the longest route until the trip time is less than the max time for i in range(len(routes)): routes[i] = __remove_longest_waypoints(routes[i], bikes[i], end, max_time) - # Get the json of the routes route_waypoints = [] geometries = [] times = [] for i, route in enumerate(routes): - route_json = __get_json(__clues_to_string(route), __clues_to_string([bikes[i]]), __clues_to_string([end])[:-1]) - geometries.append(route_json['trips'][0]['geometry']['coordinates']) - route_waypoints.append(route_json['waypoints']) - eta = time.time() + route_json['trips'][0]['duration'] + 90 * len(route) + route_json = __get_json( + __clues_to_string(route), + __clues_to_string([bikes[i]]), + __clues_to_string([end])[:-1], + ) + geometries.append(route_json["trips"][0]["geometry"]["coordinates"]) + route_waypoints.append(route_json["waypoints"]) + eta = time.time() + route_json["trips"][0]["duration"] + 90 * len(route) eta_str = datetime.datetime.fromtimestamp(eta).strftime("%I:%M:%S%p") times.append(eta_str) # Use the waypoint_index to reorder each route for i, route in enumerate(routes): route2 = ["" for x in route] - for j,k in enumerate(route_waypoints[i][1:-1]): - route2[ k['waypoint_index']-1 ] = route[j] + for j, k in enumerate(route_waypoints[i][1:-1]): + route2[k["waypoint_index"] - 1] = route[j] routes[i] = route2 return routes, geometries, times @@ -129,9 +141,9 @@ def __clues_to_string(points: [Clue]): :param points: a list of points :return: a string of the list of points """ - string = '' + string = "" for i in points: - string += str(i.longitude) + ',' + str(i.latitude) + ';' + string += str(i.longitude) + "," + str(i.latitude) + ";" return string @@ -145,13 +157,20 @@ def __get_json(coordinate_string, start, end): :return: the json of the route """ coordinates = requests.get( - 'http://acetyl.net:5000/trip/v1/bike/' + start + coordinate_string + end + '?roundtrip=false&source=first&destination=last&geometries=geojson&overview=full') + "http://acetyl.net:5000/trip/v1/bike/" + + start + + coordinate_string + + end + + "?roundtrip=false&source=first&destination=last&geometries=geojson&overview=full" + ) coordinates = coordinates.json() return coordinates -def __get_trip_time(coordinate_string, num_waypoints, start, end, time_per_waypoint=90, seconds=False): +def __get_trip_time( + coordinate_string, num_waypoints, start, end, time_per_waypoint=90, seconds=False +): """ Takes a string of coordinates and returns the trip time in hours :param coordinate_string: a string of coordinates @@ -162,19 +181,26 @@ def __get_trip_time(coordinate_string, num_waypoints, start, end, time_per_waypo :return: the trip time in hours """ coordinates = requests.get( - 'http://acetyl.net:5000/trip/v1/bike/' + start + coordinate_string + end + '?roundtrip=false&source=first&destination=last') + "http://acetyl.net:5000/trip/v1/bike/" + + start + + coordinate_string + + end + + "?roundtrip=false&source=first&destination=last" + ) coordinates = coordinates.json() - travel_time_seconds = int(coordinates['trips'][0]['duration']) + travel_time_seconds = int(coordinates["trips"][0]["duration"]) waypoint_time_seconds = num_waypoints * time_per_waypoint if seconds: - return (travel_time_seconds + waypoint_time_seconds) + return travel_time_seconds + waypoint_time_seconds total_time_hours = (travel_time_seconds + waypoint_time_seconds) / 3600 return total_time_hours -def __minimize_route_time_diff(routes: [Clue], starts: [Point], end: Point, time_diff, n): +def __minimize_route_time_diff( + routes: [Clue], starts: [Point], end: Point, time_diff, n +): """ Takes a list of lists of coordinates, a list of start points, an end point, a time difference, and a number of routes :param routes: the list of lists of coordinates @@ -187,8 +213,14 @@ def __minimize_route_time_diff(routes: [Clue], starts: [Point], end: Point, time times = [] for i, route in enumerate(routes): - times.append(__get_trip_time(__clues_to_string(route), len(route), __clues_to_string([starts[i]]), - __clues_to_string([end])[:-1])) + times.append( + __get_trip_time( + __clues_to_string(route), + len(route), + __clues_to_string([starts[i]]), + __clues_to_string([end])[:-1], + ) + ) # Find the average trip time average_time = np.mean(times) @@ -202,8 +234,9 @@ def __minimize_route_time_diff(routes: [Clue], starts: [Point], end: Point, time # If the difference is greater than the time difference, move a coordinate from the longest route to the shortest route if time_difference > time_diff: # Move a coordinate from the longest route to the shortest route - closest_coordinate = __find_closest_coordinate(routes[sorted_indices[-1]], - __mean_center(routes[sorted_indices[0]])) + closest_coordinate = __find_closest_coordinate( + routes[sorted_indices[-1]], __mean_center(routes[sorted_indices[0]]) + ) routes[sorted_indices[0]].append(closest_coordinate) routes[sorted_indices[-1]].remove(closest_coordinate) @@ -214,7 +247,9 @@ def __minimize_route_time_diff(routes: [Clue], starts: [Point], end: Point, time return routes -def __remove_longest_waypoints(route_coordinates: [Clue], start: Bike, end: Point, max_time): +def __remove_longest_waypoints( + route_coordinates: [Clue], start: Bike, end: Point, max_time +): """ Takes a list of coordinates, a start point, an end point, and a maximum time and returns a list of coordinates :param route_coordinates: the list of coordinates @@ -224,8 +259,12 @@ def __remove_longest_waypoints(route_coordinates: [Clue], start: Bike, end: Poin :return: a list of coordinates """ # Find the trip time for the route - route_time = __get_trip_time(__clues_to_string(route_coordinates), len(route_coordinates), - __clues_to_string([start]), __clues_to_string([end])[:-1]) + route_time = __get_trip_time( + __clues_to_string(route_coordinates), + len(route_coordinates), + __clues_to_string([start]), + __clues_to_string([end])[:-1], + ) # If the trip time is greater than the max time, remove the waypoint with the longest distance from the mean if route_time > max_time: @@ -263,10 +302,18 @@ def __normalize_points(clues: [Clue], bikes: [Bike]): for i in clues: normalized_coordinates.append( - [__min_max_normalize(i.latitude, min_lat, max_lat), __min_max_normalize(i.longitude, min_lon, max_lon)]) + [ + __min_max_normalize(i.latitude, min_lat, max_lat), + __min_max_normalize(i.longitude, min_lon, max_lon), + ] + ) for i in bikes: normalized_centroids.append( - [__min_max_normalize(i.latitude, min_lat, max_lat), __min_max_normalize(i.longitude, min_lon, max_lon)]) + [ + __min_max_normalize(i.latitude, min_lat, max_lat), + __min_max_normalize(i.longitude, min_lon, max_lon), + ] + ) return normalized_coordinates, normalized_centroids @@ -325,8 +372,10 @@ def __mean_center(clues: [Clue]): :param clues: the list of coordinates :return: the mean center of the coordinates """ - return Point(np.mean([coordinate.latitude for coordinate in clues]), - np.mean([coordinate.longitude for coordinate in clues])) + return Point( + np.mean([coordinate.latitude for coordinate in clues]), + np.mean([coordinate.longitude for coordinate in clues]), + ) def __distance(coordinate1: Clue, coordinate2: Point): @@ -336,5 +385,7 @@ def __distance(coordinate1: Clue, coordinate2: Point): :param coordinate2: the second coordinate :return: the distance between the two coordinates """ - return ((coordinate1.latitude - coordinate2.latitude) ** 2 + ( - coordinate1.longitude - coordinate2.longitude) ** 2) ** 0.5 + return ( + (coordinate1.latitude - coordinate2.latitude) ** 2 + + (coordinate1.longitude - coordinate2.longitude) ** 2 + ) ** 0.5 |
