spatial_queries/
spatial_queries.rs

1use spatio::{Point3d, Spatio};
2
3fn main() -> Result<(), Box<dyn std::error::Error>> {
4    println!("=== Spatio - Spatial Queries ===\n");
5
6    let db = Spatio::memory()?;
7    println!("✓ Created in-memory database\n");
8
9    // === SETUP: Insert World Cities ===
10    println!("Setting up test data...");
11
12    let cities = vec![
13        ("New York", Point3d::new(-74.0060, 40.7128, 0.0)),
14        ("London", Point3d::new(-0.1278, 51.5074, 0.0)),
15        ("Paris", Point3d::new(2.3522, 48.8566, 0.0)),
16        ("Berlin", Point3d::new(13.4050, 52.5200, 0.0)),
17        ("Madrid", Point3d::new(-3.7038, 40.4168, 0.0)),
18        ("Rome", Point3d::new(12.4964, 41.9028, 0.0)),
19        ("Tokyo", Point3d::new(139.6503, 35.6762, 0.0)),
20        ("Sydney", Point3d::new(151.2093, -33.8688, 0.0)),
21        ("Mumbai", Point3d::new(72.8777, 19.0760, 0.0)),
22        ("Cairo", Point3d::new(31.2357, 30.0444, 0.0)),
23        ("São Paulo", Point3d::new(-46.6333, -23.5505, 0.0)),
24        ("Mexico City", Point3d::new(-99.1332, 19.4326, 0.0)),
25    ];
26
27    for (name, point) in &cities {
28        // Using name as object_id for simplicity
29        let object_id = name.to_lowercase().replace(" ", "_");
30        db.upsert(
31            "world_cities",
32            &object_id,
33            point.clone(),
34            serde_json::json!({"name": name}),
35            None,
36        )?;
37    }
38    println!("   Added {} cities to spatial index\n", cities.len());
39
40    // 4. Populate with sample data (using upsert)
41    // Create a grid of points
42    for i in 0..10 {
43        for j in 0..10 {
44            let id = format!("point-{}-{}", i, j);
45            let lon = -74.0 + (i as f64) * 0.01;
46            let lat = 40.7 + (j as f64) * 0.01;
47            let pos = Point3d::new(lon, lat, 0.0);
48
49            db.upsert(
50                "nyc_grid",
51                &id,
52                pos,
53                serde_json::json!({"category": "grid_point"}),
54                None,
55            )?;
56        }
57    }
58    println!("   Populated database with 100 points in 'nyc_grid' namespace\n");
59
60    // === 1. RADIUS QUERIES ===
61    println!("1. Radius-Based Queries (query_radius)");
62    println!("-----------------------------------------------------");
63
64    let london = Point3d::new(-0.1278, 51.5074, 0.0);
65
66    // Find cities within 500km of London
67    let nearby_500km = db.query_radius("world_cities", &london, 500_000.0, 10)?;
68    println!("   Cities within 500km of London: {}", nearby_500km.len());
69    for (loc, dist) in &nearby_500km {
70        println!("     - {} ({:.1}m)", loc.metadata, dist);
71    }
72
73    // Find cities within 2000km of London
74    let nearby_2000km = db.query_radius("world_cities", &london, 2_000_000.0, 10)?;
75    println!(
76        "\n   Cities within 2000km of London: {}",
77        nearby_2000km.len()
78    );
79    for (loc, dist) in &nearby_2000km {
80        println!("     - {} ({:.1}m)", loc.metadata, dist);
81    }
82
83    // Use limit to get only closest N cities
84    let closest_3 = db.query_radius("world_cities", &london, f64::INFINITY, 3)?;
85    println!("\n   Closest 3 cities to London:");
86    for (i, (loc, dist)) in closest_3.iter().enumerate() {
87        println!("     {}. {} ({:.1}m)", i + 1, loc.metadata, dist);
88    }
89    println!();
90
91    // === 2. EXISTENCE CHECKS ===
92    println!("2. Existence Checks (simulated via radius query)");
93    println!("------------------------------------------------");
94
95    let has_nearby_500km = !db
96        .query_radius("world_cities", &london, 500_000.0, 1)?
97        .is_empty();
98    let has_nearby_100km = !db
99        .query_radius("world_cities", &london, 100_000.0, 1)?
100        .is_empty();
101
102    println!("   Any cities within 500km of London? {}", has_nearby_500km);
103    println!("   Any cities within 100km of London? {}", has_nearby_100km);
104    println!();
105
106    // === 3. BOUNDING BOX QUERIES ===
107    println!("3. Bounding Box Queries (query_bbox)");
108    println!("---------------------------------------------------");
109
110    // European bounding box (min_lon, min_lat, max_lon, max_lat)
111    println!("   European region (lon: -10 to 20, lat: 40 to 60):");
112    let europe_cities = db.query_bbox("world_cities", -10.0, 40.0, 20.0, 60.0, 20)?;
113    println!("     Found {} cities:", europe_cities.len());
114    for loc in &europe_cities {
115        println!(
116            "       - {} at ({:.2}°, {:.2}°)",
117            loc.metadata,
118            loc.position.x(),
119            loc.position.y()
120        );
121    }
122
123    // 6. Perform Bounding Box Query (query_bbox)
124    let min_x = -74.06;
125    let min_y = 40.74;
126    let max_x = -74.04;
127    let max_y = 40.76;
128
129    println!("\n6. Bounding Box Query (query_bbox)");
130    println!("   BBox: [{}, {}] to [{}, {}]", min_x, min_y, max_x, max_y);
131
132    let bbox_results = db.query_bbox("nyc_grid", min_x, min_y, max_x, max_y, 100)?;
133    println!("   Found {} points in bounding box", bbox_results.len());
134    for loc in &bbox_results {
135        println!(
136            "       - {} at ({:.4}, {:.4})",
137            loc.object_id,
138            loc.position.x(),
139            loc.position.y()
140        );
141    }
142
143    // Asia-Pacific region
144    println!("\n   Asia-Pacific region (lon: 70 to 180, lat: -40 to 40):");
145    let asia_cities = db.query_bbox("world_cities", 70.0, -40.0, 180.0, 40.0, 20)?;
146    println!("     Found {} cities:", asia_cities.len());
147    for loc in &asia_cities {
148        println!("       - {}", loc.metadata);
149    }
150
151    // Americas region
152    println!("\n   Americas region (lon: -130 to -30, lat: -60 to 60):");
153    let americas_cities = db.query_bbox("world_cities", -130.0, -60.0, -30.0, 60.0, 20)?;
154    println!("     Found {} cities:", americas_cities.len());
155    for loc in &americas_cities {
156        println!("       - {}", loc.metadata);
157    }
158    println!();
159
160    // === 4. BOUNDING BOX INTERSECTION ===
161    println!("4. Bounding Box Intersection (simulated)");
162    println!("----------------------------------------");
163
164    let has_european = !db
165        .query_bbox("world_cities", -10.0, 40.0, 20.0, 60.0, 1)?
166        .is_empty();
167    let has_antarctica = !db
168        .query_bbox("world_cities", -180.0, -90.0, 180.0, -60.0, 1)?
169        .is_empty();
170
171    println!("   European region has cities? {}", has_european);
172    println!("   Antarctica region has cities? {}", has_antarctica);
173    println!();
174
175    // === 5. MULTIPLE NAMESPACES ===
176    println!("5. Multiple Namespaces");
177    println!("----------------------");
178
179    // Add some landmarks in London
180    let london_landmarks = vec![
181        ("Big Ben", Point3d::new(-0.1245, 51.4994, 0.0)),
182        ("Tower Bridge", Point3d::new(-0.0754, 51.5055, 0.0)),
183        ("London Eye", Point3d::new(-0.1195, 51.5033, 0.0)),
184        ("Buckingham Palace", Point3d::new(-0.1419, 51.5014, 0.0)),
185    ];
186
187    for (name, point) in &london_landmarks {
188        let object_id = name.to_lowercase().replace(" ", "_");
189        db.upsert(
190            "landmarks",
191            &object_id,
192            point.clone(),
193            serde_json::json!({"name": name}),
194            None,
195        )?;
196    }
197
198    println!("   Added {} London landmarks", london_landmarks.len());
199
200    // 5. Perform Radius Query (using query_radius)
201    // Note: query_radius replaces query_current_within_radius and always returns distance
202    let center = Point3d::new(-74.05, 40.75, 0.0);
203    let radius = 3000.0; // 3km
204    println!("\n5. Radius Query (query_radius)");
205    println!("   Center: ({:.4}, {:.4})", center.x(), center.y());
206    println!("   Radius: {:.0}m", radius);
207
208    let results = db.query_radius("nyc_grid", &center, radius, 100)?;
209    println!("   Found {} points within radius:", results.len());
210
211    // Verify results
212    if let Some((loc, dist)) = results.first() {
213        println!("     - Sample: {} at {:.1}m", loc.object_id, dist);
214    }
215
216    // Performance check (optional, run many queries)
217    let start = std::time::Instant::now();
218    for _ in 0..1000 {
219        db.query_radius("nyc_grid", &center, radius, 100)?;
220    }
221    println!("   Perf: 1000 radius queries in {:?}", start.elapsed());
222
223    // Query different namespaces
224    let center_london = Point3d::new(-0.1278, 51.5074, 0.0);
225
226    let nearby_cities = db.query_radius("world_cities", &center_london, 10_000.0, 10)?;
227    let nearby_landmarks = db.query_radius("landmarks", &center_london, 10_000.0, 10)?;
228
229    println!("   Within 10km of center London:");
230    println!("     Cities: {}", nearby_cities.len());
231    println!("     Landmarks: {}", nearby_landmarks.len());
232
233    println!("\n   Landmarks within 2km:");
234    let close_landmarks = db.query_radius("landmarks", &center_london, 2_000.0, 10)?;
235    for (loc, dist) in &close_landmarks {
236        println!("     - {} ({:.1}m)", loc.metadata, dist);
237    }
238    println!();
239
240    // === 6. QUERY LIMITS ===
241    println!("6. Query Result Limiting");
242    println!("------------------------");
243
244    let all_cities = db.query_radius("world_cities", &london, f64::INFINITY, 100)?;
245    let top_5 = db.query_radius("world_cities", &london, f64::INFINITY, 5)?;
246    let top_3 = db.query_radius("world_cities", &london, f64::INFINITY, 3)?;
247
248    println!("   Total cities in database: {}", all_cities.len());
249    println!("   With limit=5: {} cities", top_5.len());
250    println!("   With limit=3: {} cities", top_3.len());
251    println!();
252
253    // === SUMMARY ===
254    let stats = db.stats();
255    println!("=== Query Summary ===");
256    println!("Database statistics:");
257    println!("  Operations: {}", stats.operations_count);
258
259    println!("\nSpatial query methods demonstrated:");
260    println!("  • query_radius - Find points within distance");
261    println!("  • query_bbox - Rectangular region queries");
262    println!("  • Multiple namespaces - Organize different point types");
263    println!("  • Result limiting - Control query result size");
264
265    Ok(())
266}