add preliminary nta support

This commit is contained in:
Nicholas Orlowsky 2026-01-12 22:46:53 -05:00
parent a7d323056a
commit 1d66553398
No known key found for this signature in database
GPG key ID: A9F3BA4C0AA7A70B
21 changed files with 3318 additions and 257 deletions

View file

@ -1,11 +1,12 @@
use actix_web::{get, web::{self, Data}, HttpRequest, HttpResponse, Responder};
use anyhow::anyhow;
use log::info;
use std::{collections::{HashMap, HashSet}, sync::Arc, time::Instant};
use libseptastic::{direction, route::RouteType, stop_schedule::Trip};
use serde::{Serialize, Deserialize};
use crate::AppState;
use crate::AppState;
//use crate::{routing::{bfs_rts, construct_graph, get_stops_near}, AppState};
//use crate::routing;
#[get("/routes")]
async fn get_routes_html(req: HttpRequest, state: Data<Arc<AppState>>) -> impl Responder {
@ -21,7 +22,7 @@ async fn get_routes_html(req: HttpRequest, state: Data<Arc<AppState>>) -> impl R
let bus_routes = all_routes.into_iter().filter(|x| x.route_type == RouteType::TracklessTrolley || x.route_type == RouteType::Bus).collect();
Ok(crate::templates::ContentTemplate {
page_title: Some(String::from("SEPTASTIC | Routes")),
page_title: Some(String::from("Routes")),
page_desc: Some(String::from("All SEPTA routes.")),
widescreen: false,
content: crate::templates::RoutesTemplate {
@ -75,106 +76,37 @@ async fn get_route_info(route_id: String, state: Data<Arc<AppState>>) -> ::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<String, HashSet<RoutingNodePointer>>,
pub visited: bool
}
struct TripState {
pub used_lines: HashSet<String>
}
struct Coordinates {
pub lat: f64,
pub lng: f64,
}
#[get("/directions")]
async fn get_directions(state: Data<Arc<AppState>>) -> 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<String> = HashSet::new();
let mut dest_stops: HashSet<String> = HashSet::new();
let mut graph = HashMap::<String, RoutingNode>::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("/directions")]
//async fn get_directions(state: Data<Arc<AppState>>) -> impl Responder {
// let near_thresh = 0.45;
//
// let sig_cds = routing::Coordinates {
// lat: 40.008420,
// lng: -75.213439
// };
//
// let home_cds = routing::Coordinates {
// lat: 39.957210,
// lng: -75.166214
// };
//
// let all_stops = state.gtfs_service.get_all_stops();
//
//
//
// let origin_stops: HashSet<String> = get_stops_near(home_cds, &all_stops);
// let dest_stops: HashSet<String> = get_stops_near(sig_cds.clone(), &all_stops);
//
// let mut graph = construct_graph(sig_cds, &all_stops, &state.gtfs_service);
//
// let mut response = String::new();
// for stop in &origin_stops {
// response += bfs_rts(&stop, &mut graph, &dest_stops).as_str();
// }
//
// return response;
//}
#[get("/route/{route_id}")]
async fn get_route(state: Data<Arc<AppState>>, req: HttpRequest, info: web::Query<RouteQueryParams>, path: web::Path<String>) -> impl Responder {
@ -183,12 +115,12 @@ async fn get_route(state: Data<Arc<AppState>>, req: HttpRequest, info: web::Quer
let infox = info.clone();
let statex = state.clone();
async move {
let mut filters: Option<Vec<i64>> = None;
let mut filters: Option<Vec<String>> = None;
if let Some (stops_v) = infox.stops.clone() {
let mut items = Vec::new();
for sid in stops_v.split(",") {
items.push(sid.parse::<i64>().unwrap());
items.push(String::from(sid));
}
filters = Some(items);
}
@ -201,7 +133,7 @@ async fn get_route(state: Data<Arc<AppState>>, req: HttpRequest, info: web::Quer
Ok(crate::templates::ContentTemplate {
widescreen: false,
page_title: Some(format!("SEPTASTIC | Schedules for {}", route_id.clone())),
page_title: Some(format!("Schedules for {}", route_id.clone())),
page_desc: Some(format!("Schedule information for {}", route_id.clone())),
content: crate::templates::RouteTemplate {
route: route_info.route,