advanced_spatial/
advanced_spatial.rs

1//! Advanced Spatial Operations Example
2//!
3//! This example demonstrates the advanced spatial capabilities of Spatio,
4//! including distance calculations, K-nearest-neighbors, polygon queries,
5//! and bounding box operations using the geo crate.
6
7use spatio::{
8    Point, Polygon, Spatio,
9    compute::spatial::{
10        DistanceMetric, bounding_box, bounding_rect_for_points, convex_hull, distance_between,
11    },
12};
13
14fn main() -> Result<(), Box<dyn std::error::Error>> {
15    println!("=== Advanced Spatial Operations Demo ===\n");
16
17    // Create an in-memory database
18    let mut db = Spatio::memory()?;
19
20    // ========================================
21    // 1. Distance Calculations
22    // ========================================
23    println!("1. Distance Calculations");
24    println!("{}", "=".repeat(50));
25
26    let new_york = Point::new(-74.0060, 40.7128);
27    let los_angeles = Point::new(-118.2437, 34.0522);
28    let london = Point::new(-0.1278, 51.5074);
29
30    // Calculate distances using different metrics
31    let dist_haversine = distance_between(&new_york, &los_angeles, DistanceMetric::Haversine);
32    let dist_geodesic = distance_between(&new_york, &los_angeles, DistanceMetric::Geodesic);
33    let dist_rhumb = distance_between(&new_york, &los_angeles, DistanceMetric::Rhumb);
34
35    println!("NYC to LA:");
36    println!("  Haversine:  {:.2} km", dist_haversine / 1000.0);
37    println!("  Geodesic:   {:.2} km", dist_geodesic / 1000.0);
38    println!("  Rhumb:      {:.2} km", dist_rhumb / 1000.0);
39
40    // Using the database method
41    let db_distance = db.distance_between(&new_york, &london, DistanceMetric::Haversine)?;
42    println!("\nNYC to London: {:.2} km", db_distance / 1000.0);
43
44    // ========================================
45    // 2. K-Nearest-Neighbors (KNN)
46    // ========================================
47    println!("\n2. K-Nearest-Neighbors Query");
48    println!("{}", "=".repeat(50));
49
50    // Insert major cities
51    let cities = vec![
52        (Point::new(-74.0060, 40.7128), "New York", "USA"),
53        (Point::new(-118.2437, 34.0522), "Los Angeles", "USA"),
54        (Point::new(-87.6298, 41.8781), "Chicago", "USA"),
55        (Point::new(-95.3698, 29.7604), "Houston", "USA"),
56        (Point::new(-75.1652, 39.9526), "Philadelphia", "USA"),
57        (Point::new(-122.4194, 37.7749), "San Francisco", "USA"),
58        (Point::new(-0.1278, 51.5074), "London", "UK"),
59        (Point::new(2.3522, 48.8566), "Paris", "France"),
60        (Point::new(139.6917, 35.6895), "Tokyo", "Japan"),
61    ];
62
63    for (point, name, country) in &cities {
64        let data = format!("{},{}", name, country);
65        db.insert_point("world_cities", point, data.as_bytes(), None)?;
66    }
67
68    // Find 3 nearest cities to a query point (somewhere in New Jersey)
69    let query_point = Point::new(-74.1719, 40.7357);
70    let nearest = db.knn(
71        "world_cities",
72        &query_point,
73        3,
74        500_000.0, // Search within 500km
75        DistanceMetric::Haversine,
76    )?;
77
78    println!("3 nearest cities to query point:");
79    for (i, (_point, data, distance)) in nearest.iter().enumerate() {
80        let city_info = String::from_utf8_lossy(data);
81        println!(
82            "  {}. {} ({:.2} km away)",
83            i + 1,
84            city_info,
85            distance / 1000.0
86        );
87    }
88
89    // ========================================
90    // 3. Polygon Queries
91    // ========================================
92    println!("\n3. Polygon Queries");
93    println!("{}", "=".repeat(50));
94
95    // Define a polygon covering parts of the eastern US
96    use geo::polygon;
97    let east_coast_polygon = polygon![
98        (x: -80.0, y: 35.0),  // South
99        (x: -70.0, y: 35.0),  // Southeast
100        (x: -70.0, y: 45.0),  // Northeast
101        (x: -80.0, y: 45.0),  // Northwest
102        (x: -80.0, y: 35.0),  // Close the polygon
103    ];
104    let east_coast_polygon: Polygon = east_coast_polygon.into();
105
106    let cities_in_polygon = db.query_within_polygon("world_cities", &east_coast_polygon, 100)?;
107
108    println!("Cities within East Coast polygon:");
109    for (point, data) in &cities_in_polygon {
110        let city_info = String::from_utf8_lossy(data);
111        println!("  - {} at ({:.4}, {:.4})", city_info, point.x(), point.y());
112    }
113
114    // ========================================
115    // 4. Bounding Box Operations
116    // ========================================
117    println!("\n4. Bounding Box Operations");
118    println!("{}", "=".repeat(50));
119
120    // Create a bounding box around the New York area
121    let ny_bbox = bounding_box(-74.5, 40.5, -73.5, 41.0)?;
122    println!("NY Area Bounding Box: {:?}", ny_bbox);
123
124    // Find cities in bounding box
125    let cities_in_bbox = db.find_within_bounds("world_cities", 40.5, -74.5, 41.0, -73.5, 100)?;
126    println!("\nCities in NY area bounding box:");
127    for (_point, data) in &cities_in_bbox {
128        let city_info = String::from_utf8_lossy(data);
129        println!("  - {}", city_info);
130    }
131
132    // ========================================
133    // 5. Convex Hull
134    // ========================================
135    println!("\n5. Convex Hull Calculation");
136    println!("{}", "=".repeat(50));
137
138    // Get all city points
139    let city_points: Vec<Point> = cities.iter().map(|(p, _, _)| *p).collect();
140
141    // Calculate convex hull
142    if let Some(hull) = convex_hull(&city_points) {
143        println!("Convex hull of all cities:");
144        println!("  Exterior points: {}", hull.exterior().0.len() - 1);
145        for coord in hull.exterior().0.iter().take(5) {
146            println!("    ({:.4}, {:.4})", coord.x, coord.y);
147        }
148    }
149
150    // ========================================
151    // 6. Bounding Rectangle
152    // ========================================
153    println!("\n6. Bounding Rectangle");
154    println!("{}", "=".repeat(50));
155
156    if let Some(bbox) = bounding_rect_for_points(&city_points) {
157        println!("Bounding rectangle of all cities:");
158        println!("  Min: ({:.4}, {:.4})", bbox.min().x, bbox.min().y);
159        println!("  Max: ({:.4}, {:.4})", bbox.max().x, bbox.max().y);
160        println!("  Width:  {:.2}°", bbox.max().x - bbox.min().x);
161        println!("  Height: {:.2}°", bbox.max().y - bbox.min().y);
162    }
163
164    // ========================================
165    // 7. Advanced Radius Queries
166    // ========================================
167    println!("\n7. Advanced Radius Queries");
168    println!("{}", "=".repeat(50));
169
170    // Count cities within 1000km of NYC
171    let count = db.count_within_radius("world_cities", &new_york, 1_000_000.0)?;
172    println!("Cities within 1000km of NYC: {}", count);
173
174    // Check if any cities exist within 100km
175    let has_nearby = db.intersects_radius("world_cities", &new_york, 100_000.0)?;
176    println!("Has cities within 100km of NYC: {}", has_nearby);
177
178    // Query with radius
179    let nearby = db.query_within_radius("world_cities", &new_york, 200_000.0, 10)?;
180    println!("\nCities within 200km of NYC:");
181    for (point, data, _distance) in &nearby {
182        let city_info = String::from_utf8_lossy(data);
183        let dist = distance_between(&new_york, point, DistanceMetric::Haversine);
184        println!("  - {} ({:.2} km)", city_info, dist / 1000.0);
185    }
186
187    // ========================================
188    // 8. Spatial Analytics
189    // ========================================
190    println!("\n8. Spatial Analytics");
191    println!("{}", "=".repeat(50));
192
193    // Find the two most distant cities (brute force)
194    let mut max_distance = 0.0;
195    let mut furthest_pair = ("", "");
196
197    for (i, (p1, n1, _)) in cities.iter().enumerate() {
198        for (p2, n2, _) in cities.iter().skip(i + 1) {
199            let dist = distance_between(p1, p2, DistanceMetric::Geodesic);
200            if dist > max_distance {
201                max_distance = dist;
202                furthest_pair = (n1, n2);
203            }
204        }
205    }
206
207    println!(
208        "Most distant city pair: {} ↔ {}",
209        furthest_pair.0, furthest_pair.1
210    );
211    println!("Distance: {:.2} km", max_distance / 1000.0);
212
213    // Calculate average distance between all US cities
214    let us_cities: Vec<_> = cities
215        .iter()
216        .filter(|(_, _, country)| *country == "USA")
217        .collect();
218
219    if us_cities.len() > 1 {
220        let mut total_distance = 0.0;
221        let mut count = 0;
222
223        for (i, (p1, _, _)) in us_cities.iter().enumerate() {
224            for (p2, _, _) in us_cities.iter().skip(i + 1) {
225                total_distance += distance_between(p1, p2, DistanceMetric::Haversine);
226                count += 1;
227            }
228        }
229
230        let avg_distance = total_distance / count as f64;
231        println!(
232            "\nAverage distance between US cities: {:.2} km",
233            avg_distance / 1000.0
234        );
235    }
236
237    // ========================================
238    // Summary
239    // ========================================
240    println!("\n{}", "=".repeat(50));
241    println!("Summary:");
242    println!("  - Demonstrated multiple distance metrics");
243    println!("  - Performed K-nearest-neighbor searches");
244    println!("  - Queried points within polygons");
245    println!("  - Used bounding box operations");
246    println!("  - Calculated convex hulls");
247    println!("  - Performed spatial analytics");
248    println!("\nAll spatial operations leverage the geo crate!");
249
250    Ok(())
251}