diff --git a/frontend/src/lib.rs b/frontend/src/lib.rs index 4d7de0a..3666223 100644 --- a/frontend/src/lib.rs +++ b/frontend/src/lib.rs @@ -222,7 +222,20 @@ async fn fetch_cached(url: &str) -> Option { } fn get_visible_tiles(camera: &Camera) -> Vec<(i32, i32, i32)> { - let z = 10; // Fixed zoom level for data + // Select zoom level based on camera zoom + // Zoom 6: World/Country view + // Zoom 9: Region view + // Zoom 12: City view + // Zoom 14: Street view + let z = if camera.zoom < 500.0 { + 6 + } else if camera.zoom < 2000.0 { + 9 + } else if camera.zoom < 8000.0 { + 12 + } else { + 14 + }; let n = 2.0f64.powi(z); let half_width = 1.0 * camera.aspect / camera.zoom; @@ -360,8 +373,8 @@ pub async fn run() { if let Some(btn) = btn_zoom_in { let closure = wasm_bindgen::closure::Closure::::new(move || { let mut cam = camera_clone.lock().unwrap(); - cam.zoom *= 1.2; - cam.zoom = cam.zoom.max(100.0).min(50000.0); + cam.zoom *= 1.5; + cam.zoom = cam.zoom.max(20.0).min(50000.0); window_clone.request_redraw(); }); btn.set_onclick(Some(closure.as_ref().unchecked_ref())); @@ -376,8 +389,8 @@ pub async fn run() { if let Some(btn) = btn_zoom_out { let closure = wasm_bindgen::closure::Closure::::new(move || { let mut cam = camera_clone.lock().unwrap(); - cam.zoom /= 1.2; - cam.zoom = cam.zoom.max(100.0).min(50000.0); + cam.zoom /= 1.5; + cam.zoom = cam.zoom.max(20.0).min(50000.0); window_clone.request_redraw(); }); btn.set_onclick(Some(closure.as_ref().unchecked_ref())); @@ -505,7 +518,7 @@ pub async fn run() { }; let mut cam = camera.lock().unwrap(); - let zoom_factor = 1.1f32; + let zoom_factor = 1.5f32; if scroll_y > 0.0 { cam.zoom *= zoom_factor; } else if scroll_y < 0.0 { @@ -513,7 +526,7 @@ pub async fn run() { } // Clamp zoom - cam.zoom = cam.zoom.max(100.0).min(50000.0); + cam.zoom = cam.zoom.max(20.0).min(50000.0); window.request_redraw(); } @@ -713,7 +726,7 @@ pub async fn run() { contents: bytemuck::cast_slice(&landuse_vertex_data), usage: wgpu::BufferUsages::VERTEX, }); - web_sys::console::log_1(&format!("Created landuse buffer with {} vertices", landuse_vertex_data.len()).into()); + let water_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Tile Water Buffer"), @@ -826,23 +839,17 @@ pub async fn run() { } if let Some(landuse) = landuse_data { - if !landuse.is_empty() { - web_sys::console::log_1(&format!("Fetched {} landuse items for tile {}/{}/{}", landuse.len(), z, x, y).into()); - } + guard.landuse.insert((z, x, y), landuse); } if let Some(water) = water_data { - if !water.is_empty() { - web_sys::console::log_1(&format!("Fetched {} water items for tile {}/{}/{}", water.len(), z, x, y).into()); - } + guard.water.insert((z, x, y), water); } if let Some(railways) = railways_data { - if !railways.is_empty() { - web_sys::console::log_1(&format!("Fetched {} railway items for tile {}/{}/{}", railways.len(), z, x, y).into()); - } + guard.railways.insert((z, x, y), railways); } diff --git a/importer/src/main.rs b/importer/src/main.rs index 3bd002e..234b712 100644 --- a/importer/src/main.rs +++ b/importer/src/main.rs @@ -4,6 +4,49 @@ use scylla::{Session, SessionBuilder}; use std::collections::HashMap; use tokio::task::JoinSet; + +const ZOOM_LEVELS: [u32; 4] = [6, 9, 12, 14]; + +fn should_include(tags: &HashMap, zoom: u32) -> bool { + if zoom >= 14 { return true; } + + let highway = tags.get("highway").map(|s| s.as_str()); + let place = tags.get("place").map(|s| s.as_str()); + let natural = tags.get("natural").map(|s| s.as_str()); + let railway = tags.get("railway").map(|s| s.as_str()); + let waterway = tags.get("waterway").map(|s| s.as_str()); + + match zoom { + 6 => { + matches!(highway, Some("motorway" | "trunk" | "primary" | "secondary")) || + matches!(place, Some("city")) || + matches!(natural, Some("water" | "wood" | "scrub" | "heath" | "wetland")) || + matches!(tags.get("landuse").map(|s| s.as_str()), Some("forest" | "meadow" | "grass" | "recreation_ground" | "farmland")) || + tags.contains_key("leisure") || // Parks, nature reserves, golf courses + matches!(waterway, Some("river" | "riverbank")) + }, + 9 => { + matches!(highway, Some("motorway" | "trunk" | "primary" | "secondary")) || + matches!(place, Some("city" | "town")) || + matches!(railway, Some("rail")) || + matches!(natural, Some("water" | "wood" | "scrub" | "heath" | "wetland")) || + tags.contains_key("landuse") || + tags.contains_key("leisure") || + matches!(waterway, Some("river" | "riverbank")) + }, + 12 => { + matches!(highway, Some("motorway" | "trunk" | "primary" | "secondary" | "tertiary")) || + matches!(place, Some("city" | "town" | "village")) || + matches!(railway, Some("rail")) || + tags.contains_key("landuse") || + tags.contains_key("leisure") || + matches!(natural, Some("water" | "wood" | "scrub" | "wetland" | "heath")) || + matches!(waterway, Some("river" | "riverbank" | "stream")) + }, + _ => false + } +} + #[tokio::main] async fn main() -> Result<()> { // Connect to ScyllaDB @@ -46,8 +89,8 @@ async fn main() -> Result<()> { // Channel for backpressure // Producer (reader) -> Consumer (writer) enum DbTask { - Node { id: i64, lat: f64, lon: f64, tags: HashMap, x: i32, y: i32 }, - Way { table: &'static str, id: i64, tags: HashMap, points: Vec, x: i32, y: i32 }, + Node { zoom: i32, id: i64, lat: f64, lon: f64, tags: HashMap, x: i32, y: i32 }, + Way { zoom: i32, table: &'static str, id: i64, tags: HashMap, points: Vec, x: i32, y: i32 }, } let (tx, mut rx) = tokio::sync::mpsc::channel::(10_000); @@ -72,20 +115,20 @@ async fn main() -> Result<()> { } match task { - DbTask::Node { id, lat, lon, tags, x, y } => { + DbTask::Node { zoom, id, lat, lon, tags, x, y } => { join_set.spawn(async move { let _ = session.query( "INSERT INTO map_data.nodes (zoom, tile_x, tile_y, id, lat, lon, tags) VALUES (?, ?, ?, ?, ?, ?, ?)", - (10, x, y, id, lat, lon, tags), + (zoom, x, y, id, lat, lon, tags), ).await; }); } - DbTask::Way { table, id, tags, points, x, y } => { + DbTask::Way { zoom, table, id, tags, points, x, y } => { let query = format!("INSERT INTO map_data.{} (zoom, tile_x, tile_y, id, tags, points) VALUES (?, ?, ?, ?, ?, ?)", table); join_set.spawn(async move { let _ = session.query( query, - (10, x, y, id, tags, points), + (zoom, x, y, id, tags, points), ).await; }); } @@ -123,10 +166,14 @@ async fn main() -> Result<()> { let lat = node.lat(); let lon = node.lon(); let tags: HashMap = node.tags().map(|(k, v)| (k.to_string(), v.to_string())).collect(); - let (x, y) = lat_lon_to_tile(lat, lon, 10); - - let task = DbTask::Node { id, lat, lon, tags, x, y }; - let _ = tx.blocking_send(task); + + for &zoom in &ZOOM_LEVELS { + if should_include(&tags, zoom) { + let (x, y) = lat_lon_to_tile(lat, lon, zoom); + let task = DbTask::Node { zoom: zoom as i32, id, lat, lon, tags: tags.clone(), x, y }; + let _ = tx.blocking_send(task); + } + } } } Element::DenseNode(node) => { @@ -144,10 +191,14 @@ async fn main() -> Result<()> { let lat = node.lat(); let lon = node.lon(); let tags: HashMap = node.tags().map(|(k, v)| (k.to_string(), v.to_string())).collect(); - let (x, y) = lat_lon_to_tile(lat, lon, 10); - - let task = DbTask::Node { id, lat, lon, tags, x, y }; - let _ = tx.blocking_send(task); + + for &zoom in &ZOOM_LEVELS { + if should_include(&tags, zoom) { + let (x, y) = lat_lon_to_tile(lat, lon, zoom); + let task = DbTask::Node { zoom: zoom as i32, id, lat, lon, tags: tags.clone(), x, y }; + let _ = tx.blocking_send(task); + } + } } } Element::Way(way) => { @@ -157,12 +208,12 @@ async fn main() -> Result<()> { // Filter for highways/roads OR buildings OR landuse OR water OR railways let is_highway = tags.contains_key("highway"); let is_building = tags.contains_key("building"); - let is_water = tags.get("natural").map(|v| v == "water").unwrap_or(false) || - tags.get("waterway").map(|v| v == "riverbank").unwrap_or(false) || - tags.get("landuse").map(|v| v == "basin").unwrap_or(false); - let is_landuse = tags.get("leisure").map(|v| v == "park" || v == "garden").unwrap_or(false) || - tags.get("landuse").map(|v| v == "grass" || v == "forest" || v == "meadow").unwrap_or(false) || - tags.get("natural").map(|v| v == "wood" || v == "scrub").unwrap_or(false); + let is_water = tags.get("natural").map(|v| v == "water" || v == "wetland").unwrap_or(false) || + tags.get("waterway").map(|v| v == "riverbank" || v == "stream" || v == "river").unwrap_or(false) || + tags.get("landuse").map(|v| v == "basin" || v == "reservoir").unwrap_or(false); + let is_landuse = tags.contains_key("leisure") || + tags.contains_key("landuse") || + tags.get("natural").map(|v| v == "wood" || v == "scrub" || v == "heath" || v == "wetland").unwrap_or(false); let is_railway = tags.contains_key("railway"); if is_highway || is_building || is_water || is_landuse || is_railway { @@ -183,7 +234,6 @@ async fn main() -> Result<()> { // Insert into the tile of the first point let (first_lat, first_lon) = points[0]; - let (x, y) = lat_lon_to_tile(first_lat, first_lon, 10); // Serialize points to blob (f64, f64) pairs let mut blob = Vec::with_capacity(points.len() * 16); @@ -192,29 +242,36 @@ async fn main() -> Result<()> { blob.extend_from_slice(&lon.to_be_bytes()); } - if is_highway { - let task = DbTask::Way { table: "ways", id, tags: tags.clone(), points: blob.clone(), x, y }; - let _ = tx.blocking_send(task); - } + for &zoom in &ZOOM_LEVELS { + if !should_include(&tags, zoom) { continue; } + + let (x, y) = lat_lon_to_tile(first_lat, first_lon, zoom); + let zoom_i32 = zoom as i32; - if is_building { - let task = DbTask::Way { table: "buildings", id, tags: tags.clone(), points: blob.clone(), x, y }; - let _ = tx.blocking_send(task); - } + if is_highway { + let task = DbTask::Way { zoom: zoom_i32, table: "ways", id, tags: tags.clone(), points: blob.clone(), x, y }; + let _ = tx.blocking_send(task); + } - if is_water { - let task = DbTask::Way { table: "water", id, tags: tags.clone(), points: blob.clone(), x, y }; - let _ = tx.blocking_send(task); - } + if is_building { + let task = DbTask::Way { zoom: zoom_i32, table: "buildings", id, tags: tags.clone(), points: blob.clone(), x, y }; + let _ = tx.blocking_send(task); + } - if is_landuse { - let task = DbTask::Way { table: "landuse", id, tags: tags.clone(), points: blob.clone(), x, y }; - let _ = tx.blocking_send(task); - } + if is_water { + let task = DbTask::Way { zoom: zoom_i32, table: "water", id, tags: tags.clone(), points: blob.clone(), x, y }; + let _ = tx.blocking_send(task); + } - if is_railway { - let task = DbTask::Way { table: "railways", id, tags: tags.clone(), points: blob.clone(), x, y }; - let _ = tx.blocking_send(task); + if is_landuse { + let task = DbTask::Way { zoom: zoom_i32, table: "landuse", id, tags: tags.clone(), points: blob.clone(), x, y }; + let _ = tx.blocking_send(task); + } + + if is_railway { + let task = DbTask::Way { zoom: zoom_i32, table: "railways", id, tags: tags.clone(), points: blob.clone(), x, y }; + let _ = tx.blocking_send(task); + } } } }