minimal routing code
This commit is contained in:
parent
2d8f131b91
commit
a7d323056a
6 changed files with 151 additions and 20 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
use actix_web::{get, web::{self, Data}, HttpRequest, HttpResponse, Responder};
|
use actix_web::{get, web::{self, Data}, HttpRequest, HttpResponse, Responder};
|
||||||
use anyhow::anyhow;
|
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 libseptastic::{direction, route::RouteType, stop_schedule::Trip};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
|
@ -35,6 +36,7 @@ async fn get_routes_html(req: HttpRequest, state: Data<Arc<AppState>>) -> impl R
|
||||||
}).await
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[get("/routes.json")]
|
#[get("/routes.json")]
|
||||||
async fn get_routes_json(state: Data<Arc<AppState>>) -> impl Responder {
|
async fn get_routes_json(state: Data<Arc<AppState>>) -> impl Responder {
|
||||||
let all_routes: Vec<libseptastic::route::Route> = state.gtfs_service.get_routes();
|
let all_routes: Vec<libseptastic::route::Route> = state.gtfs_service.get_routes();
|
||||||
|
|
@ -73,6 +75,106 @@ async fn get_route_info(route_id: String, state: Data<Arc<AppState>>) -> ::anyho
|
||||||
schedule: trips
|
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("/route/{route_id}")]
|
#[get("/route/{route_id}")]
|
||||||
async fn get_route(state: Data<Arc<AppState>>, req: HttpRequest, info: web::Query<RouteQueryParams>, path: web::Path<String>) -> impl Responder {
|
async fn get_route(state: Data<Arc<AppState>>, req: HttpRequest, info: web::Query<RouteQueryParams>, path: web::Path<String>) -> impl Responder {
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,7 @@ async fn main() -> ::anyhow::Result<()> {
|
||||||
.service(controllers::route::get_route)
|
.service(controllers::route::get_route)
|
||||||
.service(controllers::route::get_routes_json)
|
.service(controllers::route::get_routes_json)
|
||||||
.service(controllers::route::get_routes_html)
|
.service(controllers::route::get_routes_html)
|
||||||
|
.service(controllers::route::get_directions)
|
||||||
.service(get_index)
|
.service(get_index)
|
||||||
.service(actix_files::Files::new("/assets", "./assets"))
|
.service(actix_files::Files::new("/assets", "./assets"))
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -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 anyhow::anyhow;
|
||||||
use gtfs_structures::Trip;
|
|
||||||
use libseptastic::agency;
|
|
||||||
use log::{info, error};
|
use log::{info, error};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
|
|
@ -27,7 +25,9 @@ struct GtfsFile {
|
||||||
struct TransitData {
|
struct TransitData {
|
||||||
pub routes: HashMap<String, libseptastic::route::Route>,
|
pub routes: HashMap<String, libseptastic::route::Route>,
|
||||||
pub agencies: HashMap<String, libseptastic::agency::Agency>,
|
pub agencies: HashMap<String, libseptastic::agency::Agency>,
|
||||||
pub trips: HashMap<String, Vec<libseptastic::stop_schedule::Trip>>
|
pub trips: HashMap<String, Vec<libseptastic::stop_schedule::Trip>>,
|
||||||
|
pub stops: HashMap<String, libseptastic::stop::Stop>,
|
||||||
|
pub route_id_by_stops: HashMap<String, HashSet<String>>
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GtfsPullServiceState {
|
struct GtfsPullServiceState {
|
||||||
|
|
@ -43,7 +43,7 @@ pub struct GtfsPullService {
|
||||||
|
|
||||||
impl TransitData {
|
impl TransitData {
|
||||||
pub fn new() -> Self {
|
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<String, libseptastic::route::Route> {
|
||||||
|
let l_state = self.state.lock().unwrap();
|
||||||
|
l_state.transit_data.routes.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_all_stops(&self) -> HashMap<String, libseptastic::stop::Stop> {
|
||||||
|
let l_state = self.state.lock().unwrap();
|
||||||
|
l_state.transit_data.stops.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_all_trips(&self) -> HashMap<String, Vec<libseptastic::stop_schedule::Trip>> {
|
||||||
|
let l_state = self.state.lock().unwrap();
|
||||||
|
l_state.transit_data.trips.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_routes_at_stop(&self, id: String) -> HashSet<String> {
|
||||||
|
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<Vec<libseptastic::stop_schedule::Trip>> {
|
pub fn get_schedule(&self, route_id: String) -> anyhow::Result<Vec<libseptastic::stop_schedule::Trip>> {
|
||||||
let l_state = self.state.lock().unwrap();
|
let l_state = self.state.lock().unwrap();
|
||||||
if let Some(trips) = l_state.transit_data.trips.get(&route_id) {
|
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 {
|
for trip in >fs.trips {
|
||||||
let global_rt_id = match &hack_agency {
|
let global_rt_id = match &hack_agency {
|
||||||
Some(a) => format!("{}_{}", a.id, trip.1.route_id.clone()),
|
Some(a) => format!("{}_{}", a.id, trip.1.route_id.clone()),
|
||||||
None => format!("{}", 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()),
|
arrival_time: i64::from(s.arrival_time.unwrap()),
|
||||||
stop_sequence: i64::from(s.stop_sequence),
|
stop_sequence: i64::from(s.stop_sequence),
|
||||||
stop: libseptastic::stop::Stop {
|
stop: libseptastic::stop::Stop {
|
||||||
|
|
@ -203,7 +236,7 @@ impl GtfsPullService {
|
||||||
lng: s.stop.longitude.unwrap(),
|
lng: s.stop.longitude.unwrap(),
|
||||||
id: s.stop.id.parse().unwrap(),
|
id: s.stop.id.parse().unwrap(),
|
||||||
stop_type: libseptastic::stop::StopType::Normal
|
stop_type: libseptastic::stop::StopType::Normal
|
||||||
}
|
}}
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
let trip = libseptastic::stop_schedule::Trip{
|
let trip = libseptastic::stop_schedule::Trip{
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use serde::de;
|
use serde::de;
|
||||||
use sqlx::{Execute, Postgres, QueryBuilder, Transaction};
|
use sqlx::{Postgres, QueryBuilder, Transaction};
|
||||||
use std::sync::{Arc};
|
use std::sync::{Arc};
|
||||||
use futures::lock::Mutex;
|
use futures::lock::Mutex;
|
||||||
use std::thread;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
|
|
@ -96,7 +95,6 @@ impl TripTrackingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
let query = query_builder.build();
|
let query = query_builder.build();
|
||||||
//info!("{}", query.sql());
|
|
||||||
query.execute(&mut **transaction).await?;
|
query.execute(&mut **transaction).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -117,7 +115,7 @@ impl TripTrackingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&self) {
|
pub fn start(&self) {
|
||||||
info!("Starting live service");
|
info!("Starting live tracking service");
|
||||||
let cloned_state = Arc::clone(&self.state);
|
let cloned_state = Arc::clone(&self.state);
|
||||||
tokio::spawn( async move {
|
tokio::spawn( async move {
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -200,11 +198,8 @@ impl TripTrackingService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Logged live data");
|
|
||||||
let mut svc = service.lock().await;
|
let mut svc = service.lock().await;
|
||||||
|
|
||||||
info!("Logged live data");
|
|
||||||
|
|
||||||
let mut tx = svc.database.begin().await?;
|
let mut tx = svc.database.begin().await?;
|
||||||
Self::log_delay(&mut tx, &new_map, Utc::now().timestamp_nanos_opt().unwrap()).await?;
|
Self::log_delay(&mut tx, &new_map, Utc::now().timestamp_nanos_opt().unwrap()).await?;
|
||||||
tx.commit().await?;
|
tx.commit().await?;
|
||||||
|
|
|
||||||
|
|
@ -102,12 +102,12 @@ pub fn build_timetables(
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|
||||||
let mut stop_map: BTreeMap<i64, (i64, String, Vec<Option<i64>>)> = BTreeMap::new();
|
let mut stop_map: BTreeMap<String, (i64, String, Vec<Option<i64>>)> = BTreeMap::new();
|
||||||
|
|
||||||
for (trip_index, trip) in direction_trips.iter().enumerate() {
|
for (trip_index, trip) in direction_trips.iter().enumerate() {
|
||||||
for stop in &trip.schedule {
|
for stop in &trip.schedule {
|
||||||
let entry = stop_map
|
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()]));
|
.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
|
// 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<TimetableStopRow> = stop_map
|
let mut rows: Vec<TimetableStopRow> = stop_map
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(stop_id, (stop_sequence, stop_name, times))| TimetableStopRow {
|
.map(|(stop_id, (stop_sequence, stop_name, times))| TimetableStopRow {
|
||||||
stop_id,
|
stop_id: stop_id.parse().unwrap(),
|
||||||
stop_sequence,
|
stop_sequence,
|
||||||
stop_name,
|
stop_name,
|
||||||
times,
|
times,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ pub enum StopType {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Stop {
|
pub struct Stop {
|
||||||
pub id: i64,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub lat: f64,
|
pub lat: f64,
|
||||||
pub lng: f64,
|
pub lng: f64,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue