diff --git a/api/src/controllers/route.rs b/api/src/controllers/route.rs index 011031d..fcc67fb 100644 --- a/api/src/controllers/route.rs +++ b/api/src/controllers/route.rs @@ -1,6 +1,7 @@ use actix_web::{get, web::{self, Data}, HttpRequest, HttpResponse, Responder}; use anyhow::anyhow; -use std::{collections::HashSet, sync::Arc, time::Instant}; +use log::info; +use std::{collections::{HashMap, HashSet}, sync::Arc, time::Instant}; use libseptastic::{direction, route::RouteType, stop_schedule::Trip}; use serde::{Serialize, Deserialize}; @@ -35,6 +36,7 @@ async fn get_routes_html(req: HttpRequest, state: Data>) -> impl R }).await } + #[get("/routes.json")] async fn get_routes_json(state: Data>) -> impl Responder { let all_routes: Vec = state.gtfs_service.get_routes(); @@ -73,6 +75,106 @@ async fn get_route_info(route_id: String, state: Data>) -> ::anyho schedule: trips }) } +pub fn haversine_distance(lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> f64 { + let r = 6371.0; // Earth's radius in kilometers + + let d_lat = (lat2 - lat1).to_radians(); + let d_lon = (lon2 - lon1).to_radians(); + + let lat1_rad = lat1.to_radians(); + let lat2_rad = lat2.to_radians(); + + let a = (d_lat / 2.0).sin().powi(2) + + lat1_rad.cos() * lat2_rad.cos() * (d_lon / 2.0).sin().powi(2); + + let c = 2.0 * a.sqrt().asin(); + + r * c +} + +enum RoutingNodeType { + Origin, + Destination, + Midpoint +} + +struct RoutingNodePointer { + pub stop_id: String, + pub route_id: String, + pub stop_sequence: u64, + pub direction: u64 +} + +struct RoutingNode { + pub node_type: RoutingNodeType, + pub stop_id: String, + pub next_stops_per_routes: HashMap>, + pub visited: bool +} + +struct TripState { + pub used_lines: HashSet +} + +struct Coordinates { + pub lat: f64, + pub lng: f64, +} + +#[get("/directions")] +async fn get_directions(state: Data>) -> impl Responder { + let near_thresh = 0.45; + + let sig_cds = Coordinates { + lat: 40.008420, + lng: -75.213439 + }; + + let home_cds = Coordinates { + lat: 39.957210, + lng: -75.166214 + }; + + let all_stops = state.gtfs_service.get_all_stops(); + + let mut response = String::new(); + + + let mut origin_stops: HashSet = HashSet::new(); + let mut dest_stops: HashSet = HashSet::new(); + + let mut graph = HashMap::::new(); + + for stop_p in &all_stops { + let stop = stop_p.1; + + let dist = haversine_distance(sig_cds.lat, sig_cds.lng, stop.lat, stop.lng); + + if dist.abs() < near_thresh { + origin_stops.insert(stop.id.clone()); + graph.insert(stop.id.clone(), RoutingNode { + node_type: RoutingNodeType::Origin, + stop_id: stop.id.clone(), + next_stops_per_routes: , + visited: false + }); + } + } + + for stop_p in &all_stops { + let stop = stop_p.1; + + let dist = haversine_distance(home_cds.lat, home_cds.lng, stop.lat, stop.lng); + + if dist.abs() < near_thresh { + dest_stops.insert(stop.id.clone()); + } + } + + + + return response; +} #[get("/route/{route_id}")] async fn get_route(state: Data>, req: HttpRequest, info: web::Query, path: web::Path) -> impl Responder { diff --git a/api/src/main.rs b/api/src/main.rs index 0cbe218..833f99c 100644 --- a/api/src/main.rs +++ b/api/src/main.rs @@ -105,6 +105,7 @@ async fn main() -> ::anyhow::Result<()> { .service(controllers::route::get_route) .service(controllers::route::get_routes_json) .service(controllers::route::get_routes_html) + .service(controllers::route::get_directions) .service(get_index) .service(actix_files::Files::new("/assets", "./assets")) }) diff --git a/api/src/services/gtfs_pull.rs b/api/src/services/gtfs_pull.rs index 47e1794..68b5631 100644 --- a/api/src/services/gtfs_pull.rs +++ b/api/src/services/gtfs_pull.rs @@ -1,8 +1,6 @@ -use std::{collections::HashMap, env, hash::Hash, io::Cursor, path::PathBuf, sync::{Arc, Mutex}, thread, time::Duration}; +use std::{collections::{HashMap, HashSet}, env, hash::Hash, io::Cursor, path::PathBuf, sync::{Arc, Mutex}, thread, time::Duration}; use anyhow::anyhow; -use gtfs_structures::Trip; -use libseptastic::agency; use log::{info, error}; use serde::{Deserialize, Serialize}; use zip::ZipArchive; @@ -27,7 +25,9 @@ struct GtfsFile { struct TransitData { pub routes: HashMap, pub agencies: HashMap, - pub trips: HashMap> + pub trips: HashMap>, + pub stops: HashMap, + pub route_id_by_stops: HashMap> } struct GtfsPullServiceState { @@ -43,7 +43,7 @@ pub struct GtfsPullService { impl TransitData { pub fn new() -> Self { - return TransitData { routes: HashMap::new(), agencies: HashMap::new(), trips: HashMap::new() } + return TransitData { routes: HashMap::new(), agencies: HashMap::new(), trips: HashMap::new(), stops: HashMap::new(), route_id_by_stops: HashMap::new() } } } @@ -102,6 +102,26 @@ impl GtfsPullService { } } + pub fn get_all_routes(&self) -> HashMap { + let l_state = self.state.lock().unwrap(); + l_state.transit_data.routes.clone() + } + + pub fn get_all_stops(&self) -> HashMap { + let l_state = self.state.lock().unwrap(); + l_state.transit_data.stops.clone() + } + + pub fn get_all_trips(&self) -> HashMap> { + let l_state = self.state.lock().unwrap(); + l_state.transit_data.trips.clone() + } + + pub fn get_routes_at_stop(&self, id: String) -> HashSet { + let l_state = self.state.lock().unwrap(); + l_state.transit_data.route_id_by_stops.get(&id).unwrap_or(&HashSet::new()).clone() + } + pub fn get_schedule(&self, route_id: String) -> anyhow::Result> { let l_state = self.state.lock().unwrap(); if let Some(trips) = l_state.transit_data.trips.get(&route_id) { @@ -189,12 +209,25 @@ impl GtfsPullService { }); } + for stop in >fs.stops { + l_state.transit_data.stops.insert(stop.1.id.clone(), libseptastic::stop::Stop { + id: stop.1.id.clone(), + name: stop.1.name.clone().unwrap(), + lat: stop.1.latitude.unwrap(), + lng: stop.1.longitude.unwrap(), + stop_type: libseptastic::stop::StopType::Normal + }); + } + for trip in >fs.trips { let global_rt_id = match &hack_agency { Some(a) => format!("{}_{}", a.id, trip.1.route_id.clone()), None => format!("{}", trip.1.route_id.clone()) }; - let sched = trip.1.stop_times.iter().map(|s| libseptastic::stop_schedule::StopSchedule{ + let sched = trip.1.stop_times.iter().map(|s| { + + l_state.transit_data.route_id_by_stops.entry(s.stop.id.clone()).or_insert(HashSet::new()).insert(trip.1.route_id.clone()); + libseptastic::stop_schedule::StopSchedule{ arrival_time: i64::from(s.arrival_time.unwrap()), stop_sequence: i64::from(s.stop_sequence), stop: libseptastic::stop::Stop { @@ -203,7 +236,7 @@ impl GtfsPullService { lng: s.stop.longitude.unwrap(), id: s.stop.id.parse().unwrap(), stop_type: libseptastic::stop::StopType::Normal - } + }} }).collect(); let trip = libseptastic::stop_schedule::Trip{ diff --git a/api/src/services/trip_tracking.rs b/api/src/services/trip_tracking.rs index a1cc2b1..832f8e3 100644 --- a/api/src/services/trip_tracking.rs +++ b/api/src/services/trip_tracking.rs @@ -1,10 +1,9 @@ use chrono::Utc; use serde_json::Value; use serde::de; -use sqlx::{Execute, Postgres, QueryBuilder, Transaction}; +use sqlx::{Postgres, QueryBuilder, Transaction}; use std::sync::{Arc}; use futures::lock::Mutex; -use std::thread; use std::collections::HashMap; use std::time::Duration; use log::{error, info}; @@ -96,7 +95,6 @@ impl TripTrackingService { } let query = query_builder.build(); - //info!("{}", query.sql()); query.execute(&mut **transaction).await?; Ok(()) @@ -117,7 +115,7 @@ impl TripTrackingService { } pub fn start(&self) { - info!("Starting live service"); + info!("Starting live tracking service"); let cloned_state = Arc::clone(&self.state); tokio::spawn( async move { loop { @@ -200,11 +198,8 @@ impl TripTrackingService { ); } - info!("Logged live data"); let mut svc = service.lock().await; - info!("Logged live data"); - let mut tx = svc.database.begin().await?; Self::log_delay(&mut tx, &new_map, Utc::now().timestamp_nanos_opt().unwrap()).await?; tx.commit().await?; diff --git a/api/src/templates.rs b/api/src/templates.rs index fb28d1d..3e32d40 100644 --- a/api/src/templates.rs +++ b/api/src/templates.rs @@ -39,7 +39,7 @@ pub struct IndexTemplate { #[derive(Debug, Serialize)] pub struct TimetableStopRow { - pub stop_id: i64, + pub stop_id: i64, pub stop_name: String, pub stop_sequence: i64, pub times: Vec> @@ -102,12 +102,12 @@ pub fn build_timetables( .collect(); - let mut stop_map: BTreeMap>)> = BTreeMap::new(); + let mut stop_map: BTreeMap>)> = BTreeMap::new(); for (trip_index, trip) in direction_trips.iter().enumerate() { for stop in &trip.schedule { let entry = stop_map - .entry(stop.stop.id) + .entry(stop.stop.id.clone()) .or_insert((stop.stop_sequence, stop.stop.name.clone(), vec![None; direction_trips.len()])); // If this stop_id appears in multiple trips with different sequences, keep the lowest @@ -120,7 +120,7 @@ pub fn build_timetables( let mut rows: Vec = stop_map .into_iter() .map(|(stop_id, (stop_sequence, stop_name, times))| TimetableStopRow { - stop_id, + stop_id: stop_id.parse().unwrap(), stop_sequence, stop_name, times, diff --git a/libseptastic/src/stop.rs b/libseptastic/src/stop.rs index 911fa49..47c8afa 100644 --- a/libseptastic/src/stop.rs +++ b/libseptastic/src/stop.rs @@ -10,7 +10,7 @@ pub enum StopType { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Stop { - pub id: i64, + pub id: String, pub name: String, pub lat: f64, pub lng: f64,