summaryrefslogtreecommitdiff
path: root/dashboard_website
diff options
context:
space:
mode:
Diffstat (limited to 'dashboard_website')
-rw-r--r--dashboard_website/dashboard.py2
-rw-r--r--dashboard_website/datastructs.py75
-rw-r--r--dashboard_website/router.py137
3 files changed, 137 insertions, 77 deletions
diff --git a/dashboard_website/dashboard.py b/dashboard_website/dashboard.py
index 90bd718..ba29799 100644
--- a/dashboard_website/dashboard.py
+++ b/dashboard_website/dashboard.py
@@ -3,7 +3,7 @@
# dashboard.py contains web interface to clue DB + router
#
-from flask import Flask, flash, request, redirect, render_template, send_from_directory, jsonify
+from flask import Flask, request, render_template, send_from_directory, jsonify
import db
import router
diff --git a/dashboard_website/datastructs.py b/dashboard_website/datastructs.py
index 997d3e7..f150825 100644
--- a/dashboard_website/datastructs.py
+++ b/dashboard_website/datastructs.py
@@ -2,20 +2,20 @@ import math
import time
# time since last ping before deactivating/deleting
-BIKE_TIMEOUT = 60000 # 3 minutes
-BIKE_DELETE = 360000 # time before bike deletes itself
+BIKE_TIMEOUT = 60000 # 3 minutes
+BIKE_DELETE = 360000 # time before bike deletes itself
+
# data structures
class Point:
def __init__(self, lat, long):
self.longitude = long
self.latitude = lat
-
+
def toJSON(self):
- json_dict = {'longitude' : self.longitude,
- 'latitude' : self.latitude}
+ json_dict = {"longitude": self.longitude, "latitude": self.latitude}
return json_dict
-
+
def setCoords(self, lat, long):
self.longitude = long
self.latitude = lat
@@ -26,19 +26,24 @@ class Point:
def __str__(self):
return f"{self.longitude},{self.latitude}"
-
+
def __repr__(self):
return f"{self.longitude},{self.latitude}"
-
- def distanceTo(self, pt): # distance between points in miles
- lat1 = self.latitude; lon1 = self.longitude;
- lat2 = pt.latitude; lon2 = pt.longitude;
- R = 3958.8 # Radius of the earth
- lat_d = math.radians(lat2-lat1);
- lon_d = math.radians(lon2-lon1);
- a = math.sin(lat_d/2) * math.sin(lat_d/2) + math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) * math.sin(lon_d/2) * math.sin(lon_d/2)
- c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a));
- d = R * c; # Distance in mi
+
+ def distanceTo(self, pt): # distance between points in miles
+ lat1 = self.latitude
+ lon1 = self.longitude
+ lat2 = pt.latitude
+ lon2 = pt.longitude
+ R = 3958.8 # Radius of the earth
+ lat_d = math.radians(lat2 - lat1)
+ lon_d = math.radians(lon2 - lon1)
+ a = math.sin(lat_d / 2) * math.sin(lat_d / 2) + math.cos(
+ math.radians(lat1)
+ ) * math.cos(math.radians(lat2)) * math.sin(lon_d / 2) * math.sin(lon_d / 2)
+ c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
+ d = R * c
+ # Distance in mi
return d
@@ -52,6 +57,7 @@ class Clue(Point):
self.pool_inbex = pool_index
self.required = required
+
def visit(self):
self.status = "VISITED"
@@ -68,8 +74,10 @@ class Clue(Point):
'clue_info' : self.info.replace('"', "'"),
'clue_status' : self.status,
'clue_required' : self.required}
+
return json_dict
+
class Bike(Point):
def __init__(self, lat, long, name, status):
self.longitude = long
@@ -77,35 +85,34 @@ class Bike(Point):
self.name = name
self.last_contact = time.time()
self.target_name = "N/A"
- self.cluster = [] # list of clues this bike team is responsible for
- self.status = status # ACTIVE | INACTIVE
-
+ self.cluster = [] # list of clues this bike team is responsible for
+ self.status = status # ACTIVE | INACTIVE
+
def setTarget(self, clue_name):
self.target_name = clue_name
-
+
def setCluster(self, clue_cluster):
self.cluster = clue_cluster
self.updateTarget()
-
+
def updateTarget(self):
if len(self.cluster) <= 0:
self.target_name = "N/A"
else:
self.target_name = self.cluster[0].name
-
+
def visitTarget(self):
self.cluster[0].visit()
self.cluster.pop(0)
self.updateTarget()
while len(self.cluster) > 0 and self.cluster[0].status == "VISITED":
- self.cluster.pop(0) # skip next node if it has been somehow visited
+ self.cluster.pop(0) # skip next node if it has been somehow visited
self.updateTarget()
-
def ping(self):
self.status = "ACTIVE"
self.last_contact = time.time()
-
+
def disable(self):
self.status = "INACTIVE"
self.target = "N/A"
@@ -120,10 +127,12 @@ class Bike(Point):
return 0
def toJSON(self):
- json_dict = {'longitude' : self.longitude,
- 'latitude' : self.latitude,
- 'time_since_last_contact' : time.time()-self.last_contact,
- 'team_name' : self.name,
- 'team_status' : self.status,
- 'target_clue' : self.target_name}
- return json_dict \ No newline at end of file
+ json_dict = {
+ "longitude": self.longitude,
+ "latitude": self.latitude,
+ "time_since_last_contact": time.time() - self.last_contact,
+ "team_name": self.name,
+ "team_status": self.status,
+ "target_clue": self.target_name,
+ }
+ return json_dict
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