//! Multi-source fusion — satellite + terrain + OSM + local sensor data. use crate::cache::TileCache; use crate::types::*; use crate::{locate, osm, terrain, tiles}; use anyhow::Result; /// Build a complete geo scene for a location. pub async fn build_scene(radius_m: f64) -> Result { let cache = TileCache::default_cache(); // 1. Locate let cache_path = cache.base_dir.join("location.json"); let location = locate::get_location(cache_path.to_str().unwrap_or("")).await?; eprintln!( " Geo: located at {:.4}N, {:.4}W", location.lat, location.lon ); // 2. Fetch satellite tiles let bbox = GeoBBox::from_center(&location, radius_m); let tile_list = tiles::fetch_area(&tiles::TileProvider::Sentinel2Cloudless, &bbox, 16, &cache).await?; eprintln!(" Geo: fetched {} satellite tiles", tile_list.len()); // 3. Fetch elevation let dem = terrain::fetch_elevation(&location, &cache).await?; let elevation = terrain::elevation_at(&dem, &location); eprintln!(" Geo: elevation {:.0}m ASL", elevation); // 4. Fetch OSM buildings + roads let buildings = osm::fetch_buildings(&location, radius_m) .await .unwrap_or_default(); let roads = osm::fetch_roads(&location, radius_m) .await .unwrap_or_default(); eprintln!( " Geo: {} buildings, {} roads", buildings.len(), roads.len() ); // 5. Build registration let mut reg_origin = location.clone(); reg_origin.alt = elevation as f64; let registration = crate::register::auto_register(®_origin); Ok(GeoScene { location: reg_origin, bbox, elevation_m: elevation, buildings, roads, tile_count: tile_list.len(), registration, last_updated: chrono::Utc::now().to_rfc3339(), }) } /// Generate a text summary of the geo scene. pub fn summarize(scene: &GeoScene) -> String { let building_count = scene.buildings.len(); let road_count = scene.roads.len(); let road_names: Vec<&str> = scene .roads .iter() .filter_map(|r| match r { OsmFeature::Road { name, .. } => name.as_deref(), _ => None, }) .take(3) .collect(); format!( "Location: {:.4}N, {:.4}W, elevation {:.0}m ASL. \ {} buildings within view. {} roads nearby{}. \ {} satellite tiles at zoom 16. Updated: {}.", scene.location.lat, scene.location.lon, scene.elevation_m, building_count, road_count, if road_names.is_empty() { String::new() } else { format!(" ({})", road_names.join(", ")) }, scene.tile_count, &scene.last_updated[..10], ) }