seemingly finished state
This commit is contained in:
parent
eab76d8787
commit
ab4e669f55
9 changed files with 111 additions and 28 deletions
|
@ -7,14 +7,6 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- google maps api-->
|
<!-- google maps api-->
|
||||||
<script>
|
|
||||||
(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
|
|
||||||
key: "AIzaSyBa1wDqAMfKv30BSzSdXB-AEsk4BRD9xTw",
|
|
||||||
v: "weekly",
|
|
||||||
// Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
|
|
||||||
// Add other bootstrap parameters as needed, using camel case.
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div id="map"></div>
|
<div id="map"></div>
|
||||||
<script type="module" src="/main.js"></script>
|
<script type="module" src="/main.js"></script>
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
import './style.css'
|
import './style.css'
|
||||||
|
|
||||||
|
window.main = main;
|
||||||
|
|
||||||
|
var script = document.createElement('script');
|
||||||
|
script.src = 'https://maps.googleapis.com/maps/api/js?key=AIzaSyBa1wDqAMfKv30BSzSdXB-AEsk4BRD9xTw&callback=main&libraries=geometry';
|
||||||
|
script.async = true;
|
||||||
|
|
||||||
|
document.head.appendChild(script);
|
||||||
|
|
||||||
async function main(){
|
async function main(){
|
||||||
const {Map} = await google.maps.importLibrary("maps");
|
const {Map} = await google.maps.importLibrary("maps");
|
||||||
const map = new Map(document.getElementById("map"),{
|
const map = new Map(document.getElementById("map"),{
|
||||||
|
@ -34,11 +42,13 @@ async function main(){
|
||||||
const to_lattitude_input = document.createElement("input");
|
const to_lattitude_input = document.createElement("input");
|
||||||
to_lattitude_input.placeholder = "42.10125081757972";
|
to_lattitude_input.placeholder = "42.10125081757972";
|
||||||
to_lattitude_input.type = "number";
|
to_lattitude_input.type = "number";
|
||||||
|
to_lattitude_input.id = "to_lattitude_input";
|
||||||
UI.appendChild(to_lattitude_input);
|
UI.appendChild(to_lattitude_input);
|
||||||
|
|
||||||
const to_longitude_input = document.createElement("input");
|
const to_longitude_input = document.createElement("input");
|
||||||
to_longitude_input.type = "number";
|
to_longitude_input.type = "number";
|
||||||
to_longitude_input.placeholder = "-75.94181323552698";
|
to_longitude_input.placeholder = "-75.94181323552698";
|
||||||
|
to_longitude_input.id = "to_longitude_input";
|
||||||
UI.appendChild(to_longitude_input);
|
UI.appendChild(to_longitude_input);
|
||||||
|
|
||||||
const to_pos_button = document.createElement("button");
|
const to_pos_button = document.createElement("button");
|
||||||
|
@ -57,6 +67,13 @@ async function main(){
|
||||||
sub_button.innerText = "Submit";
|
sub_button.innerText = "Submit";
|
||||||
UI.appendChild(sub_button);
|
UI.appendChild(sub_button);
|
||||||
|
|
||||||
|
window.paths = [];
|
||||||
|
let clear_button = document.createElement("button");
|
||||||
|
clear_button.addEventListener("click", clear_lines);
|
||||||
|
clear_button.id = "clear_button";
|
||||||
|
clear_button.innerText = "Clear";
|
||||||
|
UI.appendChild(clear_button)
|
||||||
|
|
||||||
map.addListener("click", e=>{
|
map.addListener("click", e=>{
|
||||||
const {lat, lng} = e.latLng;
|
const {lat, lng} = e.latLng;
|
||||||
if(from_pos_toggle){
|
if(from_pos_toggle){
|
||||||
|
@ -75,10 +92,46 @@ async function main(){
|
||||||
function submit_func(map){
|
function submit_func(map){
|
||||||
//TODO: grab the path and show the poly lines and the bus route name
|
//TODO: grab the path and show the poly lines and the bus route name
|
||||||
async function submit(clickEvent){
|
async function submit(clickEvent){
|
||||||
let from_lat = document.getElementById("from_lattitude_input");
|
let from_lat = document.getElementById("from_lattitude_input").value;
|
||||||
let from_lon = document.getElementById("from_longitude_input");
|
let from_lon = document.getElementById("from_longitude_input").value;
|
||||||
}
|
let to_lat = document.getElementById("to_lattitude_input").value;
|
||||||
|
let to_lon = document.getElementById("to_longitude_input").value;
|
||||||
|
let path = await fetch(`http://localhost:8080/path?time=e&from_lat=${from_lat}&from_lon=${from_lon}&to_lat=${to_lat}&to_lon=${to_lon}`).then(r=>r.json());
|
||||||
|
let secs = [];
|
||||||
|
for(let route of path.path){
|
||||||
|
console.log(route.route_name);
|
||||||
|
secs.push(fetch(`http://localhost:8080/time_to_arrive?service=${route.service}&from=${route.enter_stop_id}&to=${route.exit_stop_id}&route=${route.route_id}`).then(r=>r.json()));
|
||||||
|
fetch(`http://localhost:8080/route_draw?service=${route.service}&id=${route.route_id}`).then(r=>r.json()).then(v=>{
|
||||||
|
return {path:google.maps.geometry.encoding.decodePath(v.poly_line), color:v.color}
|
||||||
|
})
|
||||||
|
.then(o=>{
|
||||||
|
let line = new google.maps.Polyline({
|
||||||
|
path:o.path,
|
||||||
|
strokeColor: o.color,
|
||||||
|
strokeOpacity: 1.0,
|
||||||
|
strokeWeight: 3,
|
||||||
|
});
|
||||||
|
line.setMap(map);
|
||||||
|
window.paths.push(line);
|
||||||
|
//setTimeout(()=>line.setVisible(false), 5000);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Promise.all(secs).then(l=>l.reduce((a,b)=>a+b)).then(v=>alert(`bus ride will take ${display_time(v)}`));
|
||||||
|
}
|
||||||
return submit;
|
return submit;
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
function display_time(secs){
|
||||||
|
if(secs > 60){
|
||||||
|
return `${Math.floor(secs/60)} minutes and ${secs%60} seconds`;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return `${secs%60} seconds`;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function clear_lines(){
|
||||||
|
for (let path of window.paths) path.setMap(null);
|
||||||
|
window.paths = [];
|
||||||
|
console.clear();
|
||||||
|
}
|
||||||
|
|
16
BBB_frontend/package-lock.json
generated
16
BBB_frontend/package-lock.json
generated
|
@ -7,6 +7,9 @@
|
||||||
"": {
|
"": {
|
||||||
"name": "bbb-frontend",
|
"name": "bbb-frontend",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@googlemaps/js-api-loader": "^1.16.6"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^5.1.0"
|
"vite": "^5.1.0"
|
||||||
}
|
}
|
||||||
|
@ -379,6 +382,14 @@
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@googlemaps/js-api-loader": {
|
||||||
|
"version": "1.16.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.16.6.tgz",
|
||||||
|
"integrity": "sha512-V8p5W9DbPQx74jWUmyYJOerhiB4C+MHekaO0ZRmc6lrOYrvY7+syLhzOWpp55kqSPeNb+qbC2h8i69aLIX6krQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-deep-equal": "^3.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.12.0",
|
"version": "4.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz",
|
||||||
|
@ -592,6 +603,11 @@
|
||||||
"@esbuild/win32-x64": "0.19.12"
|
"@esbuild/win32-x64": "0.19.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-deep-equal": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||||
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
|
|
|
@ -10,5 +10,8 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^5.1.0"
|
"vite": "^5.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@googlemaps/js-api-loader": "^1.16.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,13 @@ to run you need a file called GOOGLE_API_KEY in the dir the app is run in which
|
||||||
|
|
||||||
## TODO:
|
## TODO:
|
||||||
|
|
||||||
- test with OCCT buses
|
- test with BC buses
|
||||||
|
- Make buttons change color to indicate toggle
|
||||||
|
- Prevent buses from going backwards
|
||||||
|
- make time estimate accurate (beyond hack divide by 3)
|
||||||
- Change api to give a not shitty poly line for Broome county buses
|
- Change api to give a not shitty poly line for Broome county buses
|
||||||
|
- Trim poly lines down to the bit between bus stops
|
||||||
|
- Fix the UI so console and alert aren't needed
|
||||||
- refactor to work within docker and setup within docker-compose
|
- refactor to work within docker and setup within docker-compose
|
||||||
- make route checking more advanced with better walking heuristic
|
- make route checking more advanced with better walking heuristic
|
||||||
- make route checking more advanced by allowing multiple factors with multiple weights including
|
- make route checking more advanced by allowing multiple factors with multiple weights including
|
||||||
|
@ -14,6 +19,5 @@ to run you need a file called GOOGLE_API_KEY in the dir the app is run in which
|
||||||
- bus travel time
|
- bus travel time
|
||||||
- layover time
|
- layover time
|
||||||
- ultimate arrival time
|
- ultimate arrival time
|
||||||
- Test for Broome county buses (can't at night due to being too late)
|
|
||||||
- use a DB via SeaORM to avoid spaming APIs
|
- use a DB via SeaORM to avoid spaming APIs
|
||||||
- use info in db to try and predict bus schedules in future
|
- use info in db to try and predict bus schedules in future
|
||||||
|
|
|
@ -59,7 +59,7 @@ struct LocIn{
|
||||||
}
|
}
|
||||||
fn build_route_body_pain(path:&[crate::types::Coords])->serde_json::Value{
|
fn build_route_body_pain(path:&[crate::types::Coords])->serde_json::Value{
|
||||||
let inter = if path.len() >= 2 {
|
let inter = if path.len() >= 2 {
|
||||||
&path[1..25]//path.len()-1]
|
&path[1..25.min(path.len()-1)]//]
|
||||||
}else {[].as_slice()};
|
}else {[].as_slice()};
|
||||||
let inter = inter.iter().map(|v|{
|
let inter = inter.iter().map(|v|{
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
|
|
19
src/main.rs
19
src/main.rs
|
@ -21,7 +21,11 @@ async fn main() -> anyhow::Result<()> {
|
||||||
app.at("/route_draw").get(bus_route_draw);
|
app.at("/route_draw").get(bus_route_draw);
|
||||||
app.at("/").get(|_|async {
|
app.at("/").get(|_|async {
|
||||||
println!("get");
|
println!("get");
|
||||||
Ok(tide::Response::from("OK"))
|
let resp = tide::Response::builder(200)
|
||||||
|
.body("OK")
|
||||||
|
.header("Access-Control-Allow-Origin", "*")
|
||||||
|
.build();
|
||||||
|
Ok(resp)
|
||||||
});
|
});
|
||||||
app.at("/*").get(|_|async{
|
app.at("/*").get(|_|async{
|
||||||
Ok(tide::Response::from("???"))
|
Ok(tide::Response::from("???"))
|
||||||
|
@ -61,7 +65,8 @@ async fn transit_path(req: tide::Request<state::State>)->tide::Result{
|
||||||
route_name:route.name,
|
route_name:route.name,
|
||||||
route_id: route.id,
|
route_id: route.id,
|
||||||
enter_stop_id:enter,
|
enter_stop_id:enter,
|
||||||
exit_stop_id:exit
|
exit_stop_id:exit,
|
||||||
|
service: route.service.into()
|
||||||
}
|
}
|
||||||
}).collect();
|
}).collect();
|
||||||
let ret = TransitPathResult{
|
let ret = TransitPathResult{
|
||||||
|
@ -71,6 +76,7 @@ async fn transit_path(req: tide::Request<state::State>)->tide::Result{
|
||||||
let resp = tide::Response::builder(200)
|
let resp = tide::Response::builder(200)
|
||||||
.body(resp_body)
|
.body(resp_body)
|
||||||
.header("Content-Type","application/json")
|
.header("Content-Type","application/json")
|
||||||
|
.header("Access-Control-Allow-Origin", "*")
|
||||||
.build();
|
.build();
|
||||||
Ok(resp)
|
Ok(resp)
|
||||||
}
|
}
|
||||||
|
@ -85,14 +91,15 @@ struct TransitPathQuery{
|
||||||
}
|
}
|
||||||
#[derive(serde::Serialize)]
|
#[derive(serde::Serialize)]
|
||||||
struct TransitPathResult{
|
struct TransitPathResult{
|
||||||
path:Vec<RetRoute>,
|
path:Vec<RetRoute<&'static str>>,
|
||||||
}
|
}
|
||||||
#[derive(serde::Serialize)]
|
#[derive(serde::Serialize)]
|
||||||
struct RetRoute{
|
struct RetRoute<S:AsRef<str>>{
|
||||||
route_name:String,
|
route_name:String,
|
||||||
route_id: u32,
|
route_id: u32,
|
||||||
enter_stop_id:u32,
|
enter_stop_id:u32,
|
||||||
exit_stop_id:u32,
|
exit_stop_id:u32,
|
||||||
|
service: S
|
||||||
//enter_stop_coords: types::Coords
|
//enter_stop_coords: types::Coords
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,10 +129,11 @@ async fn transit_estimate(req: tide::Request<state::State>)->tide::Result{
|
||||||
let Some((to_idx,_)) = route.stops.iter().enumerate().find(|(_,stop)|stop.id == query.to)
|
let Some((to_idx,_)) = route.stops.iter().enumerate().find(|(_,stop)|stop.id == query.to)
|
||||||
else {return Ok(tide::Response::builder(400).body("Error: invalid id").into())};
|
else {return Ok(tide::Response::builder(400).body("Error: invalid id").into())};
|
||||||
|
|
||||||
let resp_body = format!("{}", path_calc::bus_travel_time(route, from_idx, to_idx).await?.num_seconds());
|
let resp_body = format!("{}", path_calc::bus_travel_time(route, from_idx, to_idx).await?.num_seconds()/3);
|
||||||
let resp = tide::Response::builder(200)
|
let resp = tide::Response::builder(200)
|
||||||
.body(resp_body)
|
.body(resp_body)
|
||||||
.header("Content-Type","application/json")
|
.header("Content-Type","application/json")
|
||||||
|
.header("Access-Control-Allow-Origin", "*")
|
||||||
.build();
|
.build();
|
||||||
Ok(resp)
|
Ok(resp)
|
||||||
}
|
}
|
||||||
|
@ -166,6 +174,7 @@ async fn bus_route_draw(req: tide::Request<state::State>)->tide::Result{
|
||||||
let resp = tide::Response::builder(200)
|
let resp = tide::Response::builder(200)
|
||||||
.body(resp_body)
|
.body(resp_body)
|
||||||
.header("Content-Type","application/json")
|
.header("Content-Type","application/json")
|
||||||
|
.header("Access-Control-Allow-Origin", "*")
|
||||||
.build();
|
.build();
|
||||||
Ok(resp)
|
Ok(resp)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
pub async fn bus_travel_time(route:crate::route::Route, from_idx:usize, to_idx:usize)->anyhow::Result<chrono::Duration>{
|
pub async fn bus_travel_time(route:crate::route::Route, from_idx:usize, to_idx:usize)->anyhow::Result<chrono::Duration>{
|
||||||
let coords:Vec<crate::types::Coords> = route.stops[from_idx..=to_idx].iter()
|
// TODO: taking the min allows the bus to go backwards for travel time which is illegal
|
||||||
|
let coords:Vec<crate::types::Coords> = route.stops[from_idx.min(to_idx)..=to_idx.max(from_idx)].iter()
|
||||||
.map(|stop|crate::types::Coords{lat:stop.lat, lon:stop.lon})
|
.map(|stop|crate::types::Coords{lat:stop.lat, lon:stop.lon})
|
||||||
.collect();
|
.collect();
|
||||||
let travel_time = crate::google::calc_travel_path(&coords).await?;
|
let travel_time = crate::google::calc_travel_path(&coords).await?;
|
||||||
|
@ -62,10 +63,10 @@ pub async fn calc_route(routes:&mut [crate::route::Route], from:crate::types::Co
|
||||||
let bc_route_cost = u64::MAX;
|
let bc_route_cost = u64::MAX;
|
||||||
|
|
||||||
let min = *[direct_route_cost,bu_route_cost,bc_route_cost].iter().min().unwrap();
|
let min = *[direct_route_cost,bu_route_cost,bc_route_cost].iter().min().unwrap();
|
||||||
if min == direct_route_cost{
|
if direct_route_cost <= min{
|
||||||
Ok(vec![(min_direct_route.clone(), min_direct_route.stops.iter().min_by_key(measure_distance_from).unwrap().id, min_direct_route.stops.iter().min_by_key(measure_distance_to).unwrap().id)])
|
Ok(vec![(min_direct_route.clone(), min_direct_route.stops.iter().min_by_key(measure_distance_from).unwrap().id, min_direct_route.stops.iter().min_by_key(measure_distance_to).unwrap().id)])
|
||||||
}
|
}
|
||||||
else if min == bu_route_cost{
|
else if min >= bu_route_cost{
|
||||||
Ok(vec![(min_to_bu_route.clone(), min_to_bu_route.stops.iter().min_by_key(measure_distance_from).unwrap().id, min_to_bu_route.stops.iter().min_by_key(measure_distance_bu.clone()).unwrap().id),(min_from_bu_route.clone(), min_from_bu_route.stops.iter().min_by_key(measure_distance_bu).unwrap().id, min_from_bu_route.stops.iter().min_by_key(measure_distance_to).unwrap().id)])
|
Ok(vec![(min_to_bu_route.clone(), min_to_bu_route.stops.iter().min_by_key(measure_distance_from).unwrap().id, min_to_bu_route.stops.iter().min_by_key(measure_distance_bu.clone()).unwrap().id),(min_from_bu_route.clone(), min_from_bu_route.stops.iter().min_by_key(measure_distance_bu).unwrap().id, min_from_bu_route.stops.iter().min_by_key(measure_distance_to).unwrap().id)])
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
15
src/types.rs
15
src/types.rs
|
@ -5,11 +5,16 @@ pub struct Coords{
|
||||||
pub lon:f64
|
pub lon:f64
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Hash)]
|
#[derive(Clone, Copy, Debug, Hash, Default)]
|
||||||
pub enum Service{OCCT, BC}
|
pub enum Service{OCCT, #[default]BC}
|
||||||
|
|
||||||
impl Default for Service{
|
|
||||||
fn default() -> Self {
|
impl From<Service> for &'static str{
|
||||||
Service::BC
|
fn from(value: Service) -> Self {
|
||||||
|
match value {
|
||||||
|
Service::OCCT => "OCCT",
|
||||||
|
Service::BC => "BC"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue