Spatiotemporal Index
The SpatiotemporalIndex combines spatial and temporal indexing for efficient queries on both dimensions.
Creating an Index
use spatial_narrative::index::SpatiotemporalIndex;
use spatial_narrative::core::{Event, Location, Timestamp};
// Empty index
let mut index: SpatiotemporalIndex<Event> = SpatiotemporalIndex::new();
// Insert items
let event = Event::new(
Location::new(40.7128, -74.0060),
Timestamp::parse("2024-01-15T10:00:00Z").unwrap(),
"NYC Event"
);
index.insert(event,
&Location::new(40.7128, -74.0060),
&Timestamp::parse("2024-01-15T10:00:00Z").unwrap()
);
From Iterator
let index = SpatiotemporalIndex::from_iter(
events.iter().cloned(),
|e| &e.location,
|e| &e.timestamp
);
Combined Queries
Query by both space and time:
use spatial_narrative::core::{GeoBounds, TimeRange};
let bounds = GeoBounds::new(40.0, -75.0, 41.0, -73.0); // NYC area
let range = TimeRange::month(2024, 1); // January 2024
// Events in NYC during January
let results = index.query(&bounds, &range);
println!("Found {} events", results.len());
Individual Dimension Queries
Spatial Only
let bounds = GeoBounds::new(40.0, -75.0, 41.0, -73.0);
let in_area = index.query_spatial(&bounds);
Temporal Only
let range = TimeRange::year(2024);
let in_2024 = index.query_temporal(&range);
Nearest in Time Range
Find spatially nearest items within a time range:
let range = TimeRange::day(2024, 1, 15);
// 5 nearest events on January 15th
let nearest = index.nearest_in_range(40.7128, -74.0060, 5, &range);
for event in nearest {
println!("{}: {}", event.timestamp.to_rfc3339(), event.text);
}
Index Properties
// Number of items
println!("Items: {}", index.len());
// Check if empty
if !index.is_empty() {
// Get bounds
if let Some(bounds) = index.bounds() {
println!("Geographic extent: {:.2}° x {:.2}°",
bounds.lat_span(), bounds.lon_span());
}
// Get time range
if let Some(range) = index.time_range() {
println!("Time span: {} to {}",
range.start.to_rfc3339(),
range.end.to_rfc3339());
}
}
// Access all items
for item in index.items() {
println!("{}", item.text);
}
Methods Reference
| Method | Description |
|---|---|
new() | Create empty index |
from_iter() | Build from iterator |
insert(item, location, timestamp) | Add an item |
query(bounds, range) | Query by space AND time |
query_spatial(bounds) | Query by space only |
query_temporal(range) | Query by time only |
nearest_in_range(lat, lon, k, range) | K-nearest in time window |
bounds() | Geographic extent |
time_range() | Temporal extent |
len() | Number of items |
is_empty() | Check if empty |
items() | Access all items |
Use Cases
Incident Analysis
// Find incidents in downtown during business hours
let downtown = GeoBounds::new(40.70, -74.02, 40.75, -73.98);
let business_hours = TimeRange::new(
Timestamp::parse("2024-01-15T09:00:00Z").unwrap(),
Timestamp::parse("2024-01-15T17:00:00Z").unwrap(),
);
let incidents = index.query(&downtown, &business_hours);
println!("Found {} downtown incidents during business hours", incidents.len());
Event Correlation
// Find events near a location around a specific time
let location = (40.7580, -73.9855); // Times Square
let time = Timestamp::parse("2024-01-01T00:00:00Z").unwrap();
// 2 hours around midnight on NYE
let window = TimeRange::new(
time.subtract(std::time::Duration::from_secs(3600)),
time.add(std::time::Duration::from_secs(3600)),
);
let times_square = GeoBounds::new(40.755, -73.990, 40.760, -73.982);
let nye_events = index.query(×_square, &window);
Coverage Analysis
// Check coverage across regions and time periods
let regions = vec![
("North", GeoBounds::new(41.0, -74.5, 42.0, -73.5)),
("Central", GeoBounds::new(40.5, -74.5, 41.0, -73.5)),
("South", GeoBounds::new(40.0, -74.5, 40.5, -73.5)),
];
let months = (1..=12).map(|m| TimeRange::month(2024, m));
println!("Coverage by region and month:");
for (name, bounds) in ®ions {
print!("{}: ", name);
for month in months.clone() {
let count = index.query(bounds, &month).len();
print!("{} ", count);
}
println!();
}
Anomaly Detection
// Find unusual activity spikes
let normal_bounds = GeoBounds::new(40.0, -75.0, 42.0, -73.0);
// Compare weekday vs weekend activity
let weekday = TimeRange::new(
Timestamp::parse("2024-01-15T00:00:00Z").unwrap(), // Monday
Timestamp::parse("2024-01-15T23:59:59Z").unwrap(),
);
let weekend = TimeRange::new(
Timestamp::parse("2024-01-13T00:00:00Z").unwrap(), // Saturday
Timestamp::parse("2024-01-13T23:59:59Z").unwrap(),
);
let weekday_count = index.query(&normal_bounds, &weekday).len();
let weekend_count = index.query(&normal_bounds, &weekend).len();
println!("Weekday: {} events, Weekend: {} events", weekday_count, weekend_count);