diff --git a/Grammars/filters.g4 b/Grammars/filters.g4 index 45597d4..2b03324 100644 --- a/Grammars/filters.g4 +++ b/Grammars/filters.g4 @@ -19,6 +19,7 @@ bag_expression // Spatial Operators | inside | outside + //| shape ; /**********************************************************************/ @@ -113,6 +114,11 @@ inside : 'inside' '(' shapes ')' ; +/* Returns the set of positions inside the shape, (face included) */ +shape + : 'shape' '(' shapes ')' + ; + /**********************************************************************/ /* SHAPES */ /**********************************************************************/ diff --git a/Grammars/queries.g4 b/Grammars/queries.g4 index 792a262..cf9e3ab 100644 --- a/Grammars/queries.g4 +++ b/Grammars/queries.g4 @@ -18,7 +18,7 @@ projection_operators * * If it is provided, it MUST resolve to a NUMBER. */ nifti_operator - : 'nifti' '(' ( STRING ',' )? ( selector ',' )? bag_expression ')' + : 'nifti' '(' ( selector ',' )? bag_expression ( ',' STRING )? ')' ; json_operator diff --git a/src/executors.rs b/src/executors.rs index 7f3cc95..ac49912 100644 --- a/src/executors.rs +++ b/src/executors.rs @@ -80,33 +80,68 @@ fn distinct<'c>( } } } +fn filter_helper<'c>( + predicate: &Predicate, + bag: &Bag, + core_id: &str, + parameters: &CoreQueryParameters<'c>, +) -> mercator_db::ResultSet<'c> { + match bag.execute(core_id, parameters) { + e @ Err(_) => e, + Ok(results) => Ok(results + .into_iter() + .filter_map(|(space, positions)| { + let filtered = positions + .into_iter() + .filter(|(position, properties)| predicate.eval((space, position, properties))) + .collect::>(); + if filtered.is_empty() { + None + } else { + Some((space, filtered)) + } + }) + .collect::>()), + } +} fn filter<'c>( core_id: &str, parameters: &CoreQueryParameters<'c>, predicate: &Option, - bag: &Bag, + bag: &Option>, ) -> mercator_db::ResultSet<'c> { match predicate { - None => bag.execute(core_id, parameters), - Some(predicate) => match bag.execute(core_id, parameters) { - e @ Err(_) => e, - Ok(results) => Ok(results - .into_iter() - .filter_map(|(space, positions)| { - let filtered = positions - .into_iter() - .filter(|(position, properties)| { - predicate.eval((space, position, properties)) - }) - .collect::>(); - if filtered.is_empty() { - None - } else { - Some((space, filtered)) - } - }) - .collect::>()), + None => { + if let Some(bag) = bag { + bag.execute(core_id, parameters) + } else { + Err("Filter without predicate nor data set.".to_string()) + } + } + Some(predicate) => match bag { + None => { + let (low, high) = space::Space::universe().bounding_box(); + let low: Vec<_> = low.into(); + let high: Vec<_> = high.into(); + let shape = Shape::HyperRectangle( + space::Space::universe().name().clone(), + vec![ + LiteralPosition( + low.into_iter() + .map(LiteralNumber::Float) + .collect::>(), + ), + LiteralPosition( + high.into_iter() + .map(LiteralNumber::Float) + .collect::>(), + ), + ], + ); + filter_helper(predicate, &Bag::Inside(shape), core_id, parameters) + } + Some(bag) => filter_helper(predicate, bag.as_ref(), core_id, parameters), }, } } @@ -248,6 +283,10 @@ fn inside<'c>( //FIXME: RADIUS IS A LENGTH, HOW TO ENCODE IT INTO THE SPACE? Ok((space_id, space::Shape::HyperSphere(position, radius))) } + Shape::Label(_, id) => { + // Not a real shape, so short circuit and return. + return core.get_by_label(parameters, id); + } Shape::Nifti(_space_id) => Err("Inside-Nifti: not yet implemented".to_string()), }; @@ -310,6 +349,7 @@ fn outside<'c>( Ok(inside) => complement_helper(core, parameters, space_id, inside), } } + Shape::Label(_, _) => Err("Label: not yet implemented".to_string()), Shape::Nifti(_space_id) => Err("Outside-nifti: not yet implemented".to_string()), } } diff --git a/src/main.rs b/src/main.rs index dd66b16..f409d98 100644 --- a/src/main.rs +++ b/src/main.rs @@ -94,7 +94,7 @@ fn main() { } if let Ok(r) = execute { - //let r = mercator_db::json::model::to_spatial_objects(&db, r); + //let r = mercator_db::json::model::to_spatial_objects(r); info!("Execution: \n{:#?}", r); info!("NB results: {:?}", r.len()); } else { diff --git a/src/predictors.rs b/src/predictors.rs index e1d0109..fd99e44 100644 --- a/src/predictors.rs +++ b/src/predictors.rs @@ -1,3 +1,4 @@ +use mercator_db::space; use mercator_db::DataBase; use super::expressions::Predictor; @@ -17,7 +18,10 @@ impl Predictor for Bag { match self { Bag::ViewPort(bag) => bag.predict(db), Bag::Distinct(bag) => bag.predict(db), - Bag::Filter(_, bag) => bag.predict(db), + Bag::Filter(_, bag) => match bag { + None => Ok(db.space(space::Space::universe().name())?.volume()), + Some(b) => b.predict(db), + }, Bag::Complement(bag) => Ok(db.space(bag.space())?.volume() - bag.predict(db)?), Bag::Intersection(lh, rh) => { let l = lh.predict(db)?; diff --git a/src/queries.lalrpop b/src/queries.lalrpop index 25fd50e..1760688 100644 --- a/src/queries.lalrpop +++ b/src/queries.lalrpop @@ -153,6 +153,8 @@ Bags: symbols::Bag = { // Spatial Operators Inside, Outside, + // returns the positions or volume of the shape, instead of the data points in or outside it. + //Shape, }; //*********************************************************************/ @@ -187,9 +189,13 @@ Union: symbols::Bag = { Filter: symbols::Bag = { // "filter" "(" "," ")" => "filter" "(" ")" => - symbols::Bag::Filter(None, Box::new(b)), + symbols::Bag::Filter(None, Some(Box::new(b))), "filter" "(" )?> ")" => - symbols::get_filter(p, b) + match b { + None => symbols::Bag::Filter(Some(p), None), + Some(b) => symbols::Bag::Filter(Some(p), Some(Box::new(b))), + } + }; Predicates: symbols::Predicate = { @@ -272,6 +278,12 @@ Inside: symbols::Bag = { symbols::Bag::Inside(<>) }; +//FIXME: ADD A SHAPE VARIANT WHICH JUST RETURNS ALL THE POSITIONS OF THAT SHAPE +//Shape: symbols::Bag = { +// => +// symbols::Bag::Shape(<>) +//} + //*********************************************************************/ // SHAPES */ //*********************************************************************/ @@ -282,6 +294,7 @@ Shapes: symbols::Shape = { Point, HyperRectangle, HyperSphere, + Label, Nifti }; @@ -333,6 +346,21 @@ Point: symbols::Shape = { } }; +// Filter by Label, a.k.a use an ID to define a volume, and use that volume to +// select data points. +Label: symbols::Shape = { + "label" "{" + + )?> + "}" => { + let space_id = match rs { + Some(id) => id, + None => Space::universe().name().clone(), + }; + symbols::Shape::Label(space_id, id) + } +}; + // Define a shape as the non-zero values in a NIfTI object, defined by // nifti{ // spaceId: string, diff --git a/src/symbols.rs b/src/symbols.rs index c09cc5f..33be326 100644 --- a/src/symbols.rs +++ b/src/symbols.rs @@ -61,7 +61,7 @@ pub enum Bag { ViewPort(Box), // Bags Distinct(Box), - Filter(Option, Box), + Filter(Option, Option>), Complement(Box), Intersection(Box, Box), Union(Box, Box), @@ -77,7 +77,10 @@ impl Bag { match self { Bag::ViewPort(bag) => bag.space(), Bag::Distinct(bag) => bag.space(), - Bag::Filter(_, bag) => bag.space(), + Bag::Filter(_, bag) => match bag { + None => space::Space::universe().name(), + Some(b) => b.space(), + }, Bag::Complement(bag) => bag.space(), Bag::Intersection(lh, _) => { // We are assuming lh and rh are in the same space. @@ -124,6 +127,7 @@ pub enum Shape { Point(String, LiteralPosition), HyperRectangle(String, Vec), HyperSphere(String, LiteralPosition, LiteralNumber), + Label(String, String), Nifti(String), } @@ -133,6 +137,7 @@ impl Shape { Shape::Point(space, _) => space, Shape::HyperRectangle(space, _) => space, Shape::HyperSphere(space, _, _) => space, + Shape::Label(space, _) => space, Shape::Nifti(space) => space, } } @@ -201,9 +206,17 @@ impl Shape { a * radius.powi(i as i32) } - Shape::Nifti(_) => unimplemented!(), + Shape::Label(_, _) => { + // FIXME: Needs to find a way to figure out the approximate volume of this specific ID, or return MAX or MIN.. + std::f64::EPSILON + } + Shape::Nifti(_) => unimplemented!("Nifti"), } } + + pub fn rasterize<'e>(&self) -> mercator_db::ResultSet<'e> { + unimplemented!("rasterize") + } } /**********************************************************************/ @@ -426,37 +439,7 @@ impl LiteralSelector { } println!("LiteralSelector.str(): {:?}", self); - unimplemented!(); - } -} - -// The logic was getting a bit too complex to be embedded directly into the -// grammar definition. -pub fn get_filter(p: Predicate, b: Option) -> Bag { - match b { - Some(b) => Bag::Filter(Some(p), Box::new(b)), - None => { - let (low, high) = space::Space::universe().bounding_box(); - let low: Vec<_> = low.into(); - let high: Vec<_> = high.into(); - let bb = Shape::HyperRectangle( - space::Space::universe().name().clone(), - vec![ - LiteralPosition( - low.into_iter() - .map(LiteralNumber::Float) - .collect::>(), - ), - LiteralPosition( - high.into_iter() - .map(LiteralNumber::Float) - .collect::>(), - ), - ], - ); - - Bag::Filter(Some(p), Box::new(Bag::Inside(bb))) - } + unimplemented!("Unknown Field"); } } diff --git a/src/validators.rs b/src/validators.rs index 3b63858..4c8f265 100644 --- a/src/validators.rs +++ b/src/validators.rs @@ -56,7 +56,10 @@ impl Validator for Bag { match self { Bag::ViewPort(bag) => bag.validate(), Bag::Distinct(bag) => bag.validate(), - Bag::Filter(_, bag) => bag.validate(), + Bag::Filter(_, bag) => match bag { + None => Ok(LiteralPosition(vec![]).get_type()), + Some(b) => b.validate(), + }, Bag::Complement(bag) => bag.validate(), Bag::Intersection(lh, rh) => compare_bag_types(lh, rh), Bag::Union(lh, rh) => compare_bag_types(lh, rh), @@ -149,6 +152,10 @@ impl Validator for Shape { } } Shape::HyperSphere(_, pos, _) => pos.validate(), + Shape::Label(_, _) => { + // FIXME: Quick Hack, we need to fix this and return the effective type of the object Id. + Ok(LiteralPosition(vec![]).get_type()) + } Shape::Nifti(_) => Err("not yet implemented".to_string()), } }