// #![recursion_limit="2048"] #[macro_use] extern crate gotham_derive; use core::f64::consts::PI; use std::sync::{Arc, atomic::{AtomicUsize, Ordering}, Mutex}; use gotham::{ handler::assets::FileOptions, router::builder::{DefineSingleRoute, DrawRoutes}, middleware::state::StateMiddleware, pipeline::single::single_pipeline, pipeline::single_middleware, router::builder::*, }; use geo::{Rect, Point}; use serde::Deserialize; mod area; mod trees; mod tile_style; mod tiles; #[derive(Clone, StateData)] pub struct AppState { pub db_pool: Vec>>, pub db_pool_index: Arc, } impl AppState { pub fn with_db R, R>(&self, f: F) -> R { let db_pool_len = self.db_pool.len(); let db_pool_index = self.db_pool_index.fetch_update( Ordering::SeqCst, Ordering::SeqCst, |mut db_pool_index| { db_pool_index += 1; if db_pool_index >= db_pool_len { db_pool_index = 0; } Some(db_pool_index) }).unwrap(); let mut db = self.db_pool[db_pool_index].lock().unwrap(); f(&mut db) } } #[derive(Debug, Deserialize, StateData, StaticResponseExtender)] pub struct IdExtractor { pub id: String, } #[derive(Debug, Deserialize, StateData, StaticResponseExtender)] pub struct AreaExtractor { x1: f64, y1: f64, x2: f64, y2: f64, } impl AreaExtractor { fn grow(&self, a: f64) -> Self { AreaExtractor { x1: self.x1 - a * self.w(), y1: self.y1 - a * self.h(), x2: self.x2 + a * self.w(), y2: self.y2 + a * self.h(), } } fn to_rect(&self) -> Rect { Rect::new((self.x1, self.y1), (self.x2, self.y2)) } fn w(&self) -> f64 { self.x2 - self.x1 } fn h(&self) -> f64 { self.y2 - self.y1 } } #[derive(Debug, Deserialize, StateData, StaticResponseExtender)] pub struct PointExtractor { x: f64, y: f64, } impl PointExtractor { fn to_point(&self) -> Point { Point::new(self.x, self.y) } } #[derive(Debug, Deserialize, StateData, StaticResponseExtender)] pub struct TileExtractor { zoom: i32, x: u32, y: u32, } impl TileExtractor { fn lon(&self) -> f64 { 360. * self.x as f64 / 2f64.powi(self.zoom) - 180. } fn lat(&self) -> f64 { (PI * (1f64 - 2f64 * self.y as f64 / 2f64.powi(self.zoom))).sinh().atan() * 180. / PI } pub fn south_west(&self) -> Self { TileExtractor { zoom: self.zoom, x: self.x + 1, y: self.y + 1, } } pub fn to_point(&self) -> Point { Point::new(self.lon(), self.lat()) } pub fn to_rect(&self) -> Rect { let sw = self.south_west(); Rect::new(self.to_point(), sw.to_point()) } } fn main() { const DB_URL: &str = "host=10.233.1.2 dbname=treeadvisor user=treeadvisor password=123"; let cpus = num_cpus::get(); let db_pool = (0..cpus).map(|_| Arc::new(Mutex::new( postgres::Client::connect(DB_URL, postgres::NoTls) .expect("DB") ))).collect(); let state = AppState { db_pool, db_pool_index: Arc::new(AtomicUsize::new(0)), }; let (chain, pipelines) = single_pipeline( single_middleware( StateMiddleware::new(state) ) ); let router = build_router(chain, pipelines, |route| { route.get("/tiles/:zoom/:x/:y/tile.png") .with_path_extractor::() .to(tiles::get_tile); route.get("/heatmap/:zoom/:x/:y/tile.png") .with_path_extractor::() .to(trees::get_heatmap); route.get("/area/:x/:y") .with_path_extractor::() .to(area::get_details); route.get("/trees/:x1/:y1/:x2/:y2") .with_path_extractor::() .to(trees::get_trees); route.get("/tree/:id") .with_path_extractor::() .to(trees::get_tree); route.get("static/*").to_dir( FileOptions::new(&"static") .with_cache_control("no-cache") .with_gzip(true) .build() ); }); gotham::start_with_num_threads("0.0.0.0:8400", router, cpus); }