use crate::rdf::physics_rdf_mapper::{extract_double_literal, sanitize_iri_fragment, PhysicsToRdf};
use crate::rdf::physics_rdf_types::{Triple, NS_PHYS, NS_RDF, NS_SOSA, NS_SSN};
use crate::simulation::result_injection::SimulationResult;
use std::collections::HashMap;
pub struct SparqlPhysicsQuery {
store: HashMap<String, Vec<(String, String)>>,
triples: Vec<Triple>,
}
impl SparqlPhysicsQuery {
pub fn new(triples: &[Triple]) -> Self {
let mut store: HashMap<String, Vec<(String, String)>> = HashMap::new();
for t in triples {
store
.entry(t.subject.clone())
.or_default()
.push((t.predicate.clone(), t.object.clone()));
}
Self {
store,
triples: triples.to_vec(),
}
}
pub fn from_result(result: &SimulationResult) -> Self {
let converter = PhysicsToRdf::new();
let triples = converter.convert(result);
Self::new(&triples)
}
pub fn get_max_temperature(&self) -> Option<f64> {
self.get_max_for_property("temperature")
}
pub fn get_max_for_property(&self, property: &str) -> Option<f64> {
let prop_iri = format!("<{}{}>", NS_PHYS, sanitize_iri_fragment(property));
let observed_prop_pred = format!("<{}observedProperty>", NS_SOSA);
let simple_result_pred = format!("<{}hasSimpleResult>", NS_SOSA);
let obs_subjects: Vec<&String> = self
.triples
.iter()
.filter(|t| t.predicate == observed_prop_pred && t.object == prop_iri)
.map(|t| &t.subject)
.collect();
let mut max_val: Option<f64> = None;
for subj in obs_subjects {
if let Some(preds) = self.store.get(subj) {
for (pred, obj) in preds {
if pred == &simple_result_pred {
if let Some(v) = extract_double_literal(obj) {
max_val = Some(match max_val {
None => v,
Some(cur) => cur.max(v),
});
}
}
}
}
}
max_val
}
pub fn get_observations_in_range(&self, t_start: f64, t_end: f64) -> Vec<(String, f64)> {
let sim_time_pred = format!("<{}simTime>", NS_PHYS);
let has_obs_pred = format!("<{}hasObservation>", NS_SSN);
let simple_result_pred = format!("<{}hasSimpleResult>", NS_SOSA);
let observed_prop_pred = format!("<{}observedProperty>", NS_SOSA);
let in_range_states: Vec<String> = self
.triples
.iter()
.filter(|t| t.predicate == sim_time_pred)
.filter_map(|t| {
extract_double_literal(&t.object).and_then(|sim_t| {
if sim_t >= t_start && sim_t <= t_end {
Some(t.subject.clone())
} else {
None
}
})
})
.collect();
let mut results = Vec::new();
for state_subj in &in_range_states {
if let Some(preds) = self.store.get(state_subj) {
let obs_iris: Vec<String> = preds
.iter()
.filter(|(p, _)| p == &has_obs_pred)
.map(|(_, o)| o.clone())
.collect();
for obs_iri in obs_iris {
if let Some(obs_preds) = self.store.get(&obs_iri) {
let prop = obs_preds
.iter()
.find(|(p, _)| p == &observed_prop_pred)
.map(|(_, o)| o.clone())
.unwrap_or_default();
let value = obs_preds
.iter()
.find(|(p, _)| p == &simple_result_pred)
.and_then(|(_, o)| extract_double_literal(o));
if let Some(v) = value {
results.push((prop, v));
}
}
}
}
}
results
}
pub fn get_min_for_property(&self, property: &str) -> Option<f64> {
let prop_iri = format!("<{}{}>", NS_PHYS, sanitize_iri_fragment(property));
let observed_prop_pred = format!("<{}observedProperty>", NS_SOSA);
let simple_result_pred = format!("<{}hasSimpleResult>", NS_SOSA);
let obs_subjects: Vec<&String> = self
.triples
.iter()
.filter(|t| t.predicate == observed_prop_pred && t.object == prop_iri)
.map(|t| &t.subject)
.collect();
let mut min_val: Option<f64> = None;
for subj in obs_subjects {
if let Some(preds) = self.store.get(subj) {
for (pred, obj) in preds {
if pred == &simple_result_pred {
if let Some(v) = extract_double_literal(obj) {
min_val = Some(match min_val {
None => v,
Some(cur) => cur.min(v),
});
}
}
}
}
}
min_val
}
pub fn get_mean_for_property(&self, property: &str) -> Option<f64> {
let prop_iri = format!("<{}{}>", NS_PHYS, sanitize_iri_fragment(property));
let observed_prop_pred = format!("<{}observedProperty>", NS_SOSA);
let simple_result_pred = format!("<{}hasSimpleResult>", NS_SOSA);
let obs_subjects: Vec<&String> = self
.triples
.iter()
.filter(|t| t.predicate == observed_prop_pred && t.object == prop_iri)
.map(|t| &t.subject)
.collect();
let mut sum = 0.0_f64;
let mut count = 0usize;
for subj in obs_subjects {
if let Some(preds) = self.store.get(subj) {
for (pred, obj) in preds {
if pred == &simple_result_pred {
if let Some(v) = extract_double_literal(obj) {
sum += v;
count += 1;
}
}
}
}
}
if count == 0 {
None
} else {
Some(sum / count as f64)
}
}
pub fn count_observations(&self) -> usize {
let obs_type = format!("<{}Observation>", NS_SOSA);
let rdf_type = format!("<{}type>", NS_RDF);
self.triples
.iter()
.filter(|t| t.predicate == rdf_type && t.object == obs_type)
.count()
}
pub fn list_observed_properties(&self) -> Vec<String> {
let observed_prop_pred = format!("<{}observedProperty>", NS_SOSA);
let mut seen = std::collections::HashSet::new();
self.triples
.iter()
.filter(|t| t.predicate == observed_prop_pred)
.map(|t| t.object.trim().to_string())
.filter(|o| seen.insert(o.clone()))
.collect()
}
pub fn all_values_for_property(&self, property: &str) -> Vec<f64> {
let prop_iri = format!("<{}{}>", NS_PHYS, sanitize_iri_fragment(property));
let observed_prop_pred = format!("<{}observedProperty>", NS_SOSA);
let simple_result_pred = format!("<{}hasSimpleResult>", NS_SOSA);
let obs_subjects: Vec<&String> = self
.triples
.iter()
.filter(|t| t.predicate == observed_prop_pred && t.object == prop_iri)
.map(|t| &t.subject)
.collect();
let mut values = Vec::new();
for subj in obs_subjects {
if let Some(preds) = self.store.get(subj) {
for (pred, obj) in preds {
if pred == &simple_result_pred {
if let Some(v) = extract_double_literal(obj) {
values.push(v);
}
}
}
}
}
values
}
}