From f528e7d156daca5af32ecaf4a5a43d0523f30b7e Mon Sep 17 00:00:00 2001 From: Erik Price Date: Wed, 11 Oct 2023 23:17:14 -0700 Subject: [PATCH] Replace hardcoded references with db::Config --- projects/hotpot/src/activity.rs | 37 +++++++++++++++++++++---------- projects/hotpot/src/db.rs | 7 ++++-- projects/hotpot/src/main.rs | 16 +++---------- projects/hotpot/src/raster.rs | 17 +++++++------- projects/hotpot/src/web.rs | 5 +++-- projects/hotpot/src/web/strava.rs | 2 +- 6 files changed, 46 insertions(+), 38 deletions(-) diff --git a/projects/hotpot/src/activity.rs b/projects/hotpot/src/activity.rs index 46ab854..23a8254 100644 --- a/projects/hotpot/src/activity.rs +++ b/projects/hotpot/src/activity.rs @@ -13,22 +13,24 @@ use geo_types::{LineString, MultiLineString, Point}; use rusqlite::params; use time::OffsetDateTime; +use crate::db; use crate::db::encode_line; use crate::simplify::simplify_line; use crate::tile::{BBox, LngLat, Tile, WebMercator}; -use crate::{DEFAULT_TILE_EXTENT, DEFAULT_ZOOM_LEVELS}; // TODO: not happy with the ergonomics of this. struct TileClipper { zoom: u8, + tile_extent: u16, current: Option<(Tile, BBox)>, tiles: HashMap>>, } impl TileClipper { - fn new(zoom: u8) -> Self { + fn new(zoom: u8, tile_extent: u16) -> Self { Self { zoom, + tile_extent, tiles: HashMap::new(), current: None, } @@ -69,14 +71,13 @@ impl TileClipper { // [start, end] is at least partially contained within the current tile. Some((a, b)) => { + let extent = self.tile_extent; let line = self.last_line(&tile); if line.0.is_empty() { - line.0 - .push(a.to_pixel(&bbox, DEFAULT_TILE_EXTENT as u16).into()); + line.0.push(a.to_pixel(&bbox, extent).into()); } - line.0 - .push(b.to_pixel(&bbox, DEFAULT_TILE_EXTENT as u16).into()); + line.0.push(b.to_pixel(&bbox, extent).into()); // If we've modified the end point, we've left the current tile. if b != end { @@ -126,10 +127,22 @@ pub struct RawActivity { impl RawActivity { /// How far apart two points can be before we consider them to be /// a separate line segment. + /// + /// TODO: move to db config? const MAX_POINT_DISTANCE: f64 = 5000.0; - pub fn clip_to_tiles(&self, zooms: &[u8], trim_dist: f64) -> ClippedTiles { - let mut clippers: Vec<_> = zooms.iter().map(|zoom| TileClipper::new(*zoom)).collect(); + pub fn clip_to_tiles( + &self, + db::Config { + ref zoom_levels, + ref trim_dist, + ref tile_extent, + }: &db::Config, + ) -> ClippedTiles { + let mut clippers: Vec<_> = zoom_levels + .iter() + .map(|z| TileClipper::new(*z, *tile_extent as u16)) + .collect(); for line in self.tracks.iter() { let points: Vec<_> = line @@ -149,14 +162,14 @@ impl RawActivity { let start_idx = points .iter() .enumerate() - .find(|(_, pt)| pt.0.euclidean_distance(first) >= trim_dist) + .find(|(_, pt)| pt.0.euclidean_distance(first) >= *trim_dist) .map(|(i, _)| i); let end_idx = points .iter() .rev() .enumerate() - .find(|(_, pt)| pt.0.euclidean_distance(last) >= trim_dist) + .find(|(_, pt)| pt.0.euclidean_distance(last) >= *trim_dist) .map(|(i, _)| points.len() - 1 - i); if let Some((i, j)) = start_idx.zip(end_idx) { @@ -386,7 +399,7 @@ pub fn upsert( conn: &mut rusqlite::Connection, name: &str, activity: &RawActivity, - trim_dist: f64, + config: &db::Config, ) -> Result { let mut insert_tile = conn.prepare_cached( "\ @@ -418,7 +431,7 @@ pub fn upsert( )?; } - let tiles = activity.clip_to_tiles(&DEFAULT_ZOOM_LEVELS, trim_dist); + let tiles = activity.clip_to_tiles(&config); for (tile, line) in tiles.iter() { let coords = encode_line(&simplify_line(&line.0, 4.0))?; insert_tile.insert(params![activity_id, tile.z, tile.x, tile.y, coords])?; diff --git a/projects/hotpot/src/db.rs b/projects/hotpot/src/db.rs index 90e447c..9508ccb 100644 --- a/projects/hotpot/src/db.rs +++ b/projects/hotpot/src/db.rs @@ -10,8 +10,6 @@ use rusqlite::{params, ToSql}; use serde::Deserialize; use time::{Date, OffsetDateTime}; -use crate::{DEFAULT_TILE_EXTENT, DEFAULT_TRIM_DIST, DEFAULT_ZOOM_LEVELS}; - const SCHEMA: &str = "\ CREATE TABLE IF NOT EXISTS config ( key TEXT NOT NULL PRIMARY KEY @@ -112,6 +110,11 @@ fn apply_schema(conn: &mut rusqlite::Connection) -> Result<()> { Ok(()) } + +const DEFAULT_TILE_EXTENT: u32 = 2048; +const DEFAULT_ZOOM_LEVELS: [u8; 5] = [2, 6, 10, 14, 16]; +const DEFAULT_TRIM_DIST: f64 = 200.0; + pub struct Config { /// Zoom levels that we store activity tiles for. pub zoom_levels: Vec, diff --git a/projects/hotpot/src/main.rs b/projects/hotpot/src/main.rs index 6a9e48b..3f70842 100644 --- a/projects/hotpot/src/main.rs +++ b/projects/hotpot/src/main.rs @@ -25,11 +25,6 @@ mod simplify; mod tile; mod web; -// TODO: Remove all direct uses of these, replace with DB config. -const DEFAULT_ZOOM_LEVELS: [u8; 5] = [2, 6, 10, 14, 16]; -const DEFAULT_TILE_EXTENT: u32 = 2048; -const DEFAULT_TRIM_DIST: f64 = 200.0; - #[derive(Subcommand)] enum Commands { /// Import activities from GPX, TCX, and FIT files. @@ -131,7 +126,7 @@ enum Commands { #[derive(Args)] struct GlobalOpts { /// Path to database - #[arg(default_value = "./hotpot.sqlite3")] + #[arg(short = 'D', long = "db", default_value = "./hotpot.sqlite3")] db_path: PathBuf, /// Enable verbose logging #[arg(short, long)] @@ -386,13 +381,8 @@ fn import_activities(p: &Path, db: &Database, prop_source: &AttributeSource) -> prop_source.enrich(&path, &mut activity); let mut conn = pool.get().expect("db connection pool timed out"); - activity::upsert( - &mut conn, - path.to_str().unwrap(), - &activity, - db.config.trim_dist, - ) - .expect("insert activity"); + activity::upsert(&mut conn, path.to_str().unwrap(), &activity, &db.config) + .expect("insert activity"); num_imported.fetch_add(1, Ordering::Relaxed); }, diff --git a/projects/hotpot/src/raster.rs b/projects/hotpot/src/raster.rs index 95563a0..43eb7ad 100644 --- a/projects/hotpot/src/raster.rs +++ b/projects/hotpot/src/raster.rs @@ -6,7 +6,6 @@ use rusqlite::{params, ToSql}; use crate::db::{decode_line, ActivityFilter, Database}; use crate::tile::{Tile, TileBounds}; -use crate::DEFAULT_TILE_EXTENT; pub static DEFAULT_GRADIENT: Lazy = Lazy::new(|| { LinearGradient::from_stops(&[ @@ -52,21 +51,23 @@ struct TileRaster { bounds: TileBounds, scale: u32, width: u32, + tile_extent: u32, pixels: Vec, } impl TileRaster { - fn new(tile: Tile, source: TileBounds, width: u32) -> Self { + fn new(tile: Tile, source: TileBounds, width: u32, tile_extent: u32) -> Self { // TODO: support upscaling - assert!(width <= DEFAULT_TILE_EXTENT, "Upscaling not supported"); + assert!(width <= tile_extent, "Upscaling not supported"); assert!(width.is_power_of_two(), "width must be power of two"); assert!(source.z >= tile.z, "source zoom must be >= target zoom"); let zoom_steps = (source.z - tile.z) as u32; - let width_steps = DEFAULT_TILE_EXTENT.ilog2() - width.ilog2(); + let width_steps = tile_extent.ilog2() - width.ilog2(); Self { width, + tile_extent, pixels: vec![0; (width * width) as usize], bounds: source, scale: zoom_steps + width_steps, @@ -77,15 +78,15 @@ impl TileRaster { debug_assert_eq!(source_tile.z, self.bounds.z); // Origin of source tile within target tile - let x_offset = DEFAULT_TILE_EXTENT * (source_tile.x - self.bounds.xmin); - let y_offset = DEFAULT_TILE_EXTENT * (source_tile.y - self.bounds.ymin); + let x_offset = self.tile_extent * (source_tile.x - self.bounds.xmin); + let y_offset = self.tile_extent * (source_tile.y - self.bounds.ymin); let mut prev = None; for Coord { x, y } in coords { // Translate (x,y) to location in target tile. // [0..(width * STORED_TILE_WIDTH)] let x = x + x_offset; - let y = (DEFAULT_TILE_EXTENT - y) + y_offset; + let y = (self.tile_extent - y) + y_offset; // Scale the coordinates back down to [0..width] let x = x >> self.scale; @@ -183,7 +184,7 @@ pub fn render_tile( .ok_or_else(|| anyhow!("no source level for tile: {:?}", tile))?; let bounds = TileBounds::from(zoom_level, &tile); - let mut raster = TileRaster::new(tile, bounds, width); + let mut raster = TileRaster::new(tile, bounds, width, db.config.tile_extent); let mut have_activity = false; diff --git a/projects/hotpot/src/web.rs b/projects/hotpot/src/web.rs index a3d3b01..659bf8b 100644 --- a/projects/hotpot/src/web.rs +++ b/projects/hotpot/src/web.rs @@ -135,7 +135,8 @@ struct RenderQueryParams { before: Option, #[serde(default, with = "crate::date::parse")] after: Option, - // TODO: parse as a `PropertyFilter` directly using `with` annotation + // TODO: support multiple filters which get AND'd together + // TODO: parse as a `PropertyFilter` directly using custom `with` annotation filter: Option, } @@ -233,7 +234,7 @@ async fn upload_activity( let mut conn = db.connection().unwrap(); let id = format!("upload:{}", file_name); - activity::upsert(&mut conn, &id, &activity, db.config.trim_dist).unwrap(); + activity::upsert(&mut conn, &id, &activity, &db.config).unwrap(); } } diff --git a/projects/hotpot/src/web/strava.rs b/projects/hotpot/src/web/strava.rs index 8f2ab7a..190ca0d 100644 --- a/projects/hotpot/src/web/strava.rs +++ b/projects/hotpot/src/web/strava.rs @@ -379,7 +379,7 @@ async fn receive_webhook( tracks: MultiLineString::from(polyline), properties: activity.properties, }, - db.config.trim_dist, + &db.config, ) .unwrap();