diff options
Diffstat (limited to 'dashboard_website/static')
| -rw-r--r-- | dashboard_website/static/css/dashboard.css | 13 | ||||
| -rw-r--r-- | dashboard_website/static/css/leaflet_mods.css | 4 | ||||
| -rw-r--r-- | dashboard_website/static/fonts/HWYGOTH.ttf | bin | 0 -> 53012 bytes | |||
| -rw-r--r-- | dashboard_website/static/img/favicon.ico | bin | 0 -> 4286 bytes | |||
| -rw-r--r-- | dashboard_website/static/img/marker-bike-active.png | bin | 0 -> 8063 bytes | |||
| -rw-r--r-- | dashboard_website/static/img/marker-bike-inactive.png (renamed from dashboard_website/static/img/marker-bike.png) | bin | 7511 -> 7511 bytes | |||
| -rw-r--r-- | dashboard_website/static/img/marker-home.png | bin | 0 -> 7622 bytes | |||
| -rw-r--r-- | dashboard_website/static/js/dashboard.js | 210 | ||||
| -rw-r--r-- | dashboard_website/static/js/map.js | 6 | ||||
| -rw-r--r-- | dashboard_website/static/js/utils.js | 18 |
10 files changed, 241 insertions, 10 deletions
diff --git a/dashboard_website/static/css/dashboard.css b/dashboard_website/static/css/dashboard.css new file mode 100644 index 0000000..b4110a7 --- /dev/null +++ b/dashboard_website/static/css/dashboard.css @@ -0,0 +1,13 @@ + +.icon-class { + transition: all 5s; /* time between GPS pings */ +} + +@font-face { + font-family: "HWYGOTH"; + src: url("/static/fonts/HWYGOTH.ttf"); +} +* { + font-family:HWYGOTH; + color: lightgray; +} diff --git a/dashboard_website/static/css/leaflet_mods.css b/dashboard_website/static/css/leaflet_mods.css deleted file mode 100644 index 5fbb907..0000000 --- a/dashboard_website/static/css/leaflet_mods.css +++ /dev/null @@ -1,4 +0,0 @@ - -.icon-class { - transition: all 5s; /* time between GPS pings */ -} diff --git a/dashboard_website/static/fonts/HWYGOTH.ttf b/dashboard_website/static/fonts/HWYGOTH.ttf Binary files differnew file mode 100644 index 0000000..20ac3e2 --- /dev/null +++ b/dashboard_website/static/fonts/HWYGOTH.ttf diff --git a/dashboard_website/static/img/favicon.ico b/dashboard_website/static/img/favicon.ico Binary files differnew file mode 100644 index 0000000..7f391c2 --- /dev/null +++ b/dashboard_website/static/img/favicon.ico diff --git a/dashboard_website/static/img/marker-bike-active.png b/dashboard_website/static/img/marker-bike-active.png Binary files differnew file mode 100644 index 0000000..f4f0ae6 --- /dev/null +++ b/dashboard_website/static/img/marker-bike-active.png diff --git a/dashboard_website/static/img/marker-bike.png b/dashboard_website/static/img/marker-bike-inactive.png Binary files differindex 48dbf96..48dbf96 100644 --- a/dashboard_website/static/img/marker-bike.png +++ b/dashboard_website/static/img/marker-bike-inactive.png diff --git a/dashboard_website/static/img/marker-home.png b/dashboard_website/static/img/marker-home.png Binary files differnew file mode 100644 index 0000000..1c63656 --- /dev/null +++ b/dashboard_website/static/img/marker-home.png diff --git a/dashboard_website/static/js/dashboard.js b/dashboard_website/static/js/dashboard.js new file mode 100644 index 0000000..b54f65f --- /dev/null +++ b/dashboard_website/static/js/dashboard.js @@ -0,0 +1,210 @@ +// uses functions in utils.js +var host = window.location.protocol + "//" + window.location.host; + +// "global" map variables +var map; +var previewmap; + +var visited_clues_m = L.layerGroup([]); // all clues +var unvisited_clues_m = L.layerGroup([]); // subset of all clues - unvisited clues +var destination_clues_m = L.layerGroup([]); // clues bikers are currently destined for +var bikes_m = L.layerGroup([]); // bike markers + +var homemarker, homebase, clues, clue_rels, bikes, previewmarker; + +var latest_timestamp = -1; // initially -1, otherwise set to value given by server in last successful info update + +var baseIcon = L.Icon.extend({ + options: { + iconSize: [25, 41], // size of the icon + iconAnchor: [12, 41], // point of the icon which will correspond to marker's location + popupAnchor: [0, -41] // point from which the popup should open relative to the iconAnchor + } +}) +var homeIcon = new baseIcon({iconUrl: 'static/img/marker-home.png'}), + activeBikeIcon = new baseIcon({iconUrl: 'static/img/marker-bike-active.png', className:"leaflet-bike-marker"}), + inactiveBikeIcon = new baseIcon({iconUrl: 'static/img/marker-bike-inactive.png'}), + visitedIcon = new baseIcon({iconUrl: 'static/img/marker-icon-grey.png'}), + unvisitedIcon = new baseIcon({iconUrl: 'static/img/marker-icon-blue.png'}), // generic, becomes colored when assigned to route + orangeIcon = new baseIcon({iconUrl: 'static/img/marker-icon-orange.png'}), + redIcon = new baseIcon({iconUrl: 'static/img/marker-icon-red.png'}), + greenIcon = new baseIcon({iconUrl: 'static/img/marker-icon-green.png'}), + yellowIcon = new baseIcon({iconUrl: 'static/img/marker-icon-yellow.png'}); +var bikeIcons = {'ACTIVE' : activeBikeIcon, 'INACTIVE' : inactiveBikeIcon} + +function zoomToBike(team_name){ + map.panTo(bikes[team_name]['marker'].getLatLng()); +} +function zoomToClue(clue_name){ + map.panTo(clue_rels[clue_name].getLatLng()); +} +function previewZoom(){ + var long = parseFloat(document.getElementById("new_clue_longitude").value); + var lat = parseFloat(document.getElementById("new_clue_latitude").value); + console.log(long); + console.log(document.getElementById("new_clue_longitude").value); + if (!isNaN(long) && !isNaN(lat)){ + previewmarker.setLatLng([lat, long]); + previewmap.panTo(previewmarker.getLatLng()); + } +} + +function drawRoute(route_coords_osrm, team_color) { + //osrm lat/long are swapped + for (var i = 0; i < route_coords_osrm.length; i++){ + var t = route_coords_osrm[i][1]; + route_coords_osrm[i][1] = route_coords_osrm[i][0]; + route_coords_osrm[i][0] = t; + } + var route = new L.polyline(route_coords_osrm, {color: team_color}).addTo(map); +} + +function updateBikeStatus(){ + var table = document.getElementById("bike-teams-table"); + table.innerHTML = ''; + for (const [key, value] of Object.entries(bikes)) { + var name = key; + var bike = value; + var row = document.createElement("tr"); + var namecell = document.createElement("td"); namecell.innerHTML = "<a href=\"#\" onclick=\"zoomToBike('"+name+"')\">"+name+"</a>"; + var statuscell = document.createElement("td"); statuscell.innerHTML = "<span "+((bike['team_status'] == "ACTIVE")? "style=\"color:lightgreen\"" : "") +">" + bike['team_status'] + " ("+parseInt(bike['time_since_last_contact']).toString()+"s)</span>"; + var targetcell = document.createElement("td"); targetcell.innerHTML = "<a href=\"#\" onclick=\"zoomToClue('"+bike['target_clue']+"')\">"+bike['target_clue']+"</a>"; + + row.appendChild(namecell); + row.appendChild(statuscell); + row.appendChild(targetcell); + table.appendChild(row); + } +} + +function updateClueStats(){ + document.getElementById("total_count").innerText = clues.length; + var visited_count = 0; + var avg_distance = 0; + for (var i =0; i < clues.length; i++){ + if(clues[i]['clue_status'] == "VISITED"){ + visited_count++; + avg_distance += getDistanceFromLatLon(homebase, clues[i]); + } + } + avg_distance /= visited_count; + document.getElementById("unvisited_count").innerText = clues.length-visited_count; + document.getElementById("visited_count").innerText = visited_count; + document.getElementById("percent_visited").innerText = (100*(visited_count/clues.length)).toFixed(2); + document.getElementById("avg_visited_distance").innerText = avg_distance.toFixed(2); +} + +function drawClues(){ + unvisited_clues_m.clearLayers(); + visited_clues_m.clearLayers(); + for (var i = 0; i < clues.length; i++) { + var tempIcon = visitedIcon; + if (clues[i]['clue_status'] == "UNVISITED") tempIcon = unvisitedIcon; + var clueMarker = L.marker([clues[i]['latitude'], clues[i]['longitude']], {icon: tempIcon}).bindPopup(clues[i]['clue_name'] + ": " + clues[i]['clue_info']); + clue_rels[clues[i]['clue_name']] = clueMarker; + if (clues[i]['clue_status'] == "UNVISITED") unvisited_clues_m.addLayer(clueMarker); + else visited_clues_m.addLayer(clueMarker); + } +} + +function addClue(){ + var long = parseFloat(document.getElementById("new_clue_longitude").value); + var lat = parseFloat(document.getElementById("new_clue_latitude").value); + if (isNaN(long) || isNaN(lat)){ + alert("Invalid coordinates."); + return; + } + if(confirm("Are you sure this is the right location?")){ + console.log("yes"); + } +} + +// run every x seconds to get latest info from server +function requestLatestInfo(){ + function handleLatestInfo(json){ + if(json['status'] != "OK") return; + latest_timestamp = json['timestamp']; + // process home base + if (json['home_changed']){ + homebase = json['homebase']; + if(homemarker)homemarker.setLatLng([homebase['latitude'], homebase['longitude']]); + else homemarker = L.marker([homebase['latitude'], homebase['longitude']], {icon: homeIcon}).addTo(map).bindPopup("Home is where the club is."); + } + // process bikes + if(true || json['bikes_changed']){ // always true since we need constant updates for bikes + var bikes_t = json['bikes']; + for (var i = 0; i < bikes_t.length; i++){ + var name = bikes_t[i]['team_name']; + if(name in bikes) { + bikes[name]['marker'].setLatLng([bikes_t[i]['latitude'],bikes_t[i]['longitude']]); + if(bikes_t[i]['team_status'] != [name]['team_status'])bikes[name]['marker'].setIcon(bikeIcons[bikes_t[i]['team_status']]); + for (const [key, value] of Object.entries(bikes_t[i])) { + bikes[name][key] = value; + } + } else { // add bike + var bikeMarker = new L.marker([bikes_t[i]['latitude'],bikes_t[i]['longitude']]).bindPopup(bikes_t[i]['team_name']); + bikes[name] = bikes_t[i]; + bikes[name]['marker'] = bikeMarker; + bikeMarker.setIcon(bikeIcons[bikes[name]['team_status']]); + bikes_m.addLayer(bikeMarker); + } + } + updateBikeStatus(); + } + // process clues + if(json['clues_changed']){ + clues = json['clues']; + clue_rels = {}; + drawClues(); + updateClueStats(); + } + } + fetch(host+'/getLatestInfo', { + method: "POST", + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ "info_age" : latest_timestamp })}) + .then((response) => response.json()) + .then((json) => handleLatestInfo(json)); +} +var intervalId = window.setInterval(function(){ + requestLatestInfo(); + }, 5000); + +var clockINterval = window.setInterval(function(){ + var d = new Date(); + document.getElementById("titletime").innerText = d.toLocaleString(); +}, 1000); + +// RUN ON PAGE LOAD +window.onload = function() { + clues = {}; bikes = {}; + + map = L.map('map').setView([42.3626081,-71.0620591], 13); + L.tileLayer('https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.{ext}', { + minZoom: 0, + maxZoom: 20, + attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', + ext: 'png' + }).addTo(map); + previewmap = L.map('previewmap').setView([42.3626081,-71.0620591], 16); + L.tileLayer('https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.{ext}', { + minZoom: 0, + maxZoom: 20, + ext: 'png' + }).addTo(previewmap); + + //homemarker = L.marker([homebase['latitude'], homebase['longitude']], {icon: homeIcon}).addTo(map).bindPopup("Home is where the club is."); + previewmarker = L.marker([0,0], {icon: greenIcon}).addTo(previewmap); + map.addLayer(visited_clues_m); + map.addLayer(unvisited_clues_m); + map.addLayer(bikes_m); + var layerControl = L.control.layers(null,null,{collapsed:false}); + layerControl.addOverlay(unvisited_clues_m, "Unvisited Clues"); + layerControl.addOverlay(visited_clues_m, "Visited Clues"); + layerControl.addOverlay(bikes_m, "Bikes"); + layerControl.addTo(map); + requestLatestInfo(); +}
\ No newline at end of file diff --git a/dashboard_website/static/js/map.js b/dashboard_website/static/js/map.js deleted file mode 100644 index 35eb70a..0000000 --- a/dashboard_website/static/js/map.js +++ /dev/null @@ -1,6 +0,0 @@ - -var all_clues = L.layerGroup([]); // all clues -var unvisited_clues = L.layerGroup([]); // subset of all clues - unvisited clues -var destination_clues = L.layerGroup([]); // clues bikers are currently destined for - -document.onload()
\ No newline at end of file diff --git a/dashboard_website/static/js/utils.js b/dashboard_website/static/js/utils.js new file mode 100644 index 0000000..83e6397 --- /dev/null +++ b/dashboard_website/static/js/utils.js @@ -0,0 +1,18 @@ +function deg2rad(deg) { + return deg * (Math.PI/180) +} +function getDistanceFromLatLon(item1, item2) { + lat1 = item1['latitude']; lon1 = item1['longitude']; + lat2 = item2['latitude']; lon2 = item2['longitude']; + var R = 3958.8; // Radius of the earth + var dLat = deg2rad(lat2-lat1); + var dLon = deg2rad(lon2-lon1); + var a = + Math.sin(dLat/2) * Math.sin(dLat/2) + + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * + Math.sin(dLon/2) * Math.sin(dLon/2) + ; + var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); + var d = R * c; // Distance in mi + return d; +}
\ No newline at end of file |
