use spatio::{Point3d, Spatio};
use std::time::Instant;
#[test]
fn test_3d_sphere_query_scales_sublinearly() {
let dataset_sizes = [1000, 5000, 10000];
let mut query_times_ms = Vec::new();
for &size in &dataset_sizes {
let db = Spatio::memory().unwrap();
for i in 0..size {
let lat = 40.0 + ((i % 100) as f64 * 0.001);
let lon = -74.0 + ((i / 100) as f64 * 0.001);
let alt = (i as f64 * 10.0) % 10000.0;
let point = Point3d::new(lon, lat, alt);
db.upsert(
"aircraft",
&format!("data_{}", i),
point,
serde_json::json!({}),
None,
)
.unwrap();
}
let center = Point3d::new(-74.05, 40.05, 5000.0);
let radius = 10000.0;
let start = Instant::now();
let results = db.query_radius("aircraft", ¢er, radius, 100).unwrap();
let elapsed = start.elapsed();
query_times_ms.push(elapsed.as_secs_f64() * 1000.0);
assert!(
!results.is_empty(),
"Should find results for dataset size {}",
size
);
}
let ratio_10x = query_times_ms[2] / query_times_ms[0];
println!("3D Sphere Query Performance:");
println!(" 1,000 points: {:.2}ms", query_times_ms[0]);
println!(" 5,000 points: {:.2}ms", query_times_ms[1]);
println!(" 10,000 points: {:.2}ms", query_times_ms[2]);
println!(" 10x data ratio: {:.2}x time", ratio_10x);
assert!(
ratio_10x < 50.0,
"Query time should scale sublinearly with envelope pruning (got {:.2}x for 10x data)",
ratio_10x
);
}
#[test]
fn test_3d_cylinder_query_altitude_pruning() {
let db = Spatio::memory().unwrap();
for i in 0..10000 {
let lat = 40.0 + ((i % 100) as f64 * 0.001);
let lon = -74.0 + ((i / 100) as f64 * 0.001);
let alt = (i as f64 / 10000.0) * 20000.0; let point = Point3d::new(lon, lat, alt);
db.upsert(
"aircraft",
&format!("data_{}", i),
point,
serde_json::json!({}),
None,
)
.unwrap();
}
let center_2d = spatio::Point::new(-74.0, 40.0);
let start1 = Instant::now();
let narrow_results = db
.query_within_cylinder("aircraft", center_2d, 2000.0, 3000.0, 10000.0, 1000)
.unwrap();
let narrow_time = start1.elapsed();
let start2 = Instant::now();
let wide_results = db
.query_within_cylinder("aircraft", center_2d, 0.0, 20000.0, 10000.0, 1000)
.unwrap();
let wide_time = start2.elapsed();
println!("3D Cylinder Query Altitude Pruning:");
println!(
" Narrow range (2000-3000m): {} results in {:.2}ms",
narrow_results.len(),
narrow_time.as_secs_f64() * 1000.0
);
println!(
" Wide range (0-20000m): {} results in {:.2}ms",
wide_results.len(),
wide_time.as_secs_f64() * 1000.0
);
if !wide_results.is_empty() {
assert!(
narrow_results.len() <= wide_results.len(),
"Narrow altitude range should return fewer or equal results"
);
}
for (loc, _) in &narrow_results {
assert!(
loc.position.z() >= 2000.0 && loc.position.z() <= 3000.0,
"Point altitude {} outside range [2000, 3000]",
loc.position.z()
);
}
}
#[test]
fn test_3d_knn_with_large_dataset() {
let db = Spatio::memory().unwrap();
for i in 0..5000 {
let lat = 40.0 + ((i % 50) as f64 * 0.002);
let lon = -74.0 + ((i / 50) as f64 * 0.002);
let alt = (i as f64 * 5.0) % 8000.0;
let point = Point3d::new(lon, lat, alt);
db.upsert(
"points",
&format!("data_{}", i),
point,
serde_json::json!({}),
None,
)
.unwrap();
}
let query_point = Point3d::new(-74.0, 40.0, 4000.0);
let start = Instant::now();
let neighbors = db.knn("points", &query_point, 10).unwrap();
let elapsed = start.elapsed();
println!(
"3D KNN Query (k=10 from 5000 points): {:.2}ms",
elapsed.as_secs_f64() * 1000.0
);
assert_eq!(neighbors.len(), 10, "Should return exactly 10 neighbors");
assert!(
elapsed.as_millis() < 100,
"KNN query should be fast with R*-tree indexing (took {}ms)",
elapsed.as_millis()
);
}
#[test]
fn test_3d_sphere_query_correctness() {
let db = Spatio::memory().unwrap();
let test_points = [
(-74.0, 40.0, 1000.0),
(-74.001, 40.001, 1100.0),
(-74.002, 40.002, 2000.0),
(-74.01, 40.01, 5000.0),
(-74.1, 40.1, 10000.0),
];
for (i, &(lon, lat, alt)) in test_points.iter().enumerate() {
let point = Point3d::new(lon, lat, alt);
db.upsert(
"test",
&format!("point_{}", i),
point,
serde_json::json!({}),
None,
)
.unwrap();
}
let center = Point3d::new(-74.0, 40.0, 1000.0);
let radius = 2000.0;
let results = db.query_radius("test", ¢er, radius, 10).unwrap();
assert!(
results.len() >= 2 && results.len() <= 4,
"Should find 2-4 nearby points"
);
}