93 lines
2.7 KiB
Rust
93 lines
2.7 KiB
Rust
//! 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<GeoScene> {
|
|
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],
|
|
)
|
|
}
|