1 Commits
master ... lts

Author SHA1 Message Date
1e884a0c21 Updated dependencies and fixed linter warnings 2024-08-09 18:44:35 +02:00
7 changed files with 192 additions and 284 deletions

View File

@@ -30,13 +30,10 @@ required-features = ["bin"]
[features] [features]
bin = ["measure_time", "pretty_env_logger"] bin = ["measure_time", "pretty_env_logger"]
[profile.release]
lto = true
[dependencies] [dependencies]
ironsea_index = "0.1" ironsea_index = "^0.1"
ironsea_index_sfc_dbc = "0.1" ironsea_index_sfc_dbc = "^0.1"
ironsea_index_hashmap = "0.1" ironsea_index_hashmap = "^0.1"
arrayref = "0.3" # For Positions Objects arrayref = "0.3" # For Positions Objects
lazy_static = "1.5" lazy_static = "1.5"

View File

@@ -7,8 +7,6 @@ use super::space::Space;
use super::space_db::SpaceDB; use super::space_db::SpaceDB;
use super::space_index::SpaceSetObject; use super::space_index::SpaceSetObject;
use super::DataBase; use super::DataBase;
use super::IterObjects;
use super::IterPositions;
use super::ResultSet; use super::ResultSet;
/// Query Parameters. /// Query Parameters.
@@ -218,37 +216,31 @@ impl Core {
&self.properties &self.properties
} }
fn decode_positions<'b>( fn decode_positions(
list: IterObjects<'b>, list: &mut [(Position, &Properties)],
space: &'b Space, space: &Space,
db: &'b DataBase, db: &DataBase,
output_space: &Option<&str>, output_space: &Option<&str>,
) -> Result<IterObjects<'b>, String> { ) -> Result<(), String> {
let b: IterObjects = if let Some(unified_id) = *output_space { if let Some(unified_id) = *output_space {
let unified = db.space(unified_id)?; let unified = db.space(unified_id)?;
// Rebase the point to the requested output space before decoding. // Rebase the point to the requested output space before decoding.
Box::new(list.filter_map(move |(position, properties)| { for (position, _) in list {
match Space::change_base(&position, space, unified) { *position = unified
Err(_) => None, .decode(&Space::change_base(position, space, unified)?)?
Ok(rebased) => match unified.decode(&rebased) { .into();
Err(_) => None, }
Ok(decoded) => Some((decoded.into(), properties)),
},
}
}))
} else { } else {
// Decode the positions into f64 values, which are defined in their // Decode the positions into f64 values, which are defined in their
// respective reference space. // respective reference space.
Box::new(list.filter_map( for (position, _) in list {
move |(position, properties)| match space.decode(&position) { // Simply decode
Err(_) => None, *position = space.decode(position)?.into();
Ok(decoded) => Some((decoded.into(), properties)), }
}, }
))
};
Ok(b) Ok(())
} }
/// Retrieve everything located at specific positions. /// Retrieve everything located at specific positions.
@@ -266,57 +258,46 @@ impl Core {
/// reference space. /// reference space.
/// ///
/// [shape]: space/enum.Shape.html /// [shape]: space/enum.Shape.html
pub fn get_by_positions<'d>( pub fn get_by_positions(
&'d self, &self,
parameters: &'d CoreQueryParameters, parameters: &CoreQueryParameters,
positions: Vec<Position>, positions: &[Position],
space_id: &'d str, space_id: &str,
) -> ResultSet<'d> { ) -> ResultSet {
let CoreQueryParameters { let CoreQueryParameters {
db, output_space, .. db, output_space, ..
} = parameters; } = parameters;
let mut results = vec![]; let mut results = vec![];
let count = positions.len();
let from = db.space(space_id)?; let from = db.space(space_id)?;
// Filter positions based on the view port, if present
let filtered = match parameters.view_port(from) {
None => positions.iter().collect::<Vec<_>>(),
Some(view_port) => positions
.iter()
.filter(|&p| view_port.contains(p))
.collect::<Vec<_>>(),
};
for s in &self.space_db { for s in &self.space_db {
let to = db.space(s.name())?; let to = db.space(s.name())?;
let mut p = Vec::with_capacity(count);
// Filter positions based on the view port, if present for position in filtered.as_slice() {
// FIXME: remove clone() on positions? let position: Vec<f64> = Space::change_base(position, from, to)?.into();
let filtered: IterPositions = match parameters.view_port(from) { p.push(to.encode(&position)?);
None => Box::new(positions.clone().into_iter()), }
Some(view_port) => Box::new(
positions
.clone()
.into_iter()
.filter(move |p| view_port.contains(p)),
),
};
// Rebase the positions into the current space let mut r = s
let p = filtered.filter_map(move |position| { .get_by_positions(&p, parameters)?
match Space::change_base(&position, from, to) { .into_iter()
Err(_) => None, .map(|(position, fields)| (position, &self.properties[fields.value()]))
Ok(position) => { .collect::<Vec<_>>();
let position: Vec<f64> = position.into(); Self::decode_positions(r.as_mut_slice(), to, db, output_space)?;
match to.encode(&position) {
Err(_) => None,
Ok(position) => Some(position),
}
}
}
});
// Select the data based on the rebased viewport filter. results.push((s.name(), r));
let r = s
.get_by_positions(p, parameters)?
.map(move |(position, fields)| (position, &self.properties[fields.value()]));
results.push((
s.name(),
Self::decode_positions(Box::new(r), to, db, output_space)?,
));
} }
Ok(results) Ok(results)
@@ -337,12 +318,12 @@ impl Core {
/// reference space. /// reference space.
/// ///
/// [shape]: space/enum.Shape.html /// [shape]: space/enum.Shape.html
pub fn get_by_shape<'d>( pub fn get_by_shape(
&'d self, &self,
parameters: &'d CoreQueryParameters, parameters: &CoreQueryParameters,
shape: Shape, shape: &Shape,
space_id: &'d str, space_id: &str,
) -> ResultSet<'d> { ) -> ResultSet {
let CoreQueryParameters { let CoreQueryParameters {
db, output_space, .. db, output_space, ..
} = parameters; } = parameters;
@@ -358,14 +339,14 @@ impl Core {
// let current_shape = shape.encode(current_space)?; // let current_shape = shape.encode(current_space)?;
// println!("current shape Encoded: {:?}", current_shape); // println!("current shape Encoded: {:?}", current_shape);
let r = s let mut r = s
.get_by_shape(current_shape, parameters)? .get_by_shape(&current_shape, parameters)?
.map(move |(position, fields)| (position, &self.properties[fields.value()])); .into_iter()
.map(|(position, fields)| (position, &self.properties[fields.value()]))
.collect::<Vec<_>>();
Self::decode_positions(r.as_mut_slice(), current_space, db, output_space)?;
results.push(( results.push((s.name(), r));
s.name(),
Self::decode_positions(Box::new(r), current_space, db, output_space)?,
));
} }
Ok(results) Ok(results)
@@ -381,11 +362,11 @@ impl Core {
/// * `id`: /// * `id`:
/// Identifier for which to retrieve is positions. /// Identifier for which to retrieve is positions.
/// ///
pub fn get_by_id<'s, S>( pub fn get_by_id<S>(
&'s self, &self,
parameters: &'s CoreQueryParameters, parameters: &CoreQueryParameters,
id: S, id: S,
) -> Result<Vec<(&String, IterPositions<'s>)>, String> ) -> Result<Vec<(&String, Vec<Position>)>, String>
where where
S: Into<String>, S: Into<String>,
{ {
@@ -406,32 +387,26 @@ impl Core {
for s in &self.space_db { for s in &self.space_db {
let current_space = db.space(s.name())?; let current_space = db.space(s.name())?;
let positions_by_id = s.get_by_id(offset, parameters)?; let mut positions = s.get_by_id(offset, parameters)?;
//Self::decode_positions(r.as_mut_slice(), current_space, db, output_space)?; //Self::decode_positions(r.as_mut_slice(), current_space, db, output_space)?;
let positions: IterPositions = if let Some(unified_id) = *output_space { if let Some(unified_id) = *output_space {
let unified = db.space(unified_id)?; let unified = db.space(unified_id)?;
// Rebase the point to the requested output space before decoding. // Rebase the point to the requested output space before decoding.
Box::new(positions_by_id.filter_map(move |position| { for position in &mut positions {
match Space::change_base(&position, current_space, unified) { *position = unified
Err(_) => None, .decode(&Space::change_base(position, current_space, unified)?)?
Ok(rebased) => match unified.decode(&rebased) { .into();
Err(_) => None, }
Ok(decoded) => Some(decoded.into()),
},
}
}))
} else { } else {
// Decode the positions into f64 values, which are defined in their // Decode the positions into f64 values, which are defined in their
// respective reference space. // respective reference space.
Box::new(positions_by_id.filter_map(move |position| { for position in &mut positions {
match current_space.decode(&position) { // Simply decode
Err(_) => None, *position = current_space.decode(position)?.into();
Ok(decoded) => Some(decoded.into()), }
} }
}))
};
results.push((s.name(), positions)); results.push((s.name(), positions));
} }
@@ -451,11 +426,7 @@ impl Core {
/// * `id`: /// * `id`:
/// Identifier to use to define the search volume. /// Identifier to use to define the search volume.
/// ///
pub fn get_by_label<'d, S>( pub fn get_by_label<S>(&self, parameters: &CoreQueryParameters, id: S) -> ResultSet
&'d self,
parameters: &'d CoreQueryParameters,
id: S,
) -> ResultSet<'d>
where where
S: Into<String>, S: Into<String>,
{ {
@@ -479,7 +450,7 @@ impl Core {
let search_volume = self let search_volume = self
.space_db .space_db
.iter() .iter()
.filter_map(move |s| { .filter_map(|s| {
match db.space(s.name()) { match db.space(s.name()) {
Err(_) => None, Err(_) => None,
Ok(from) => match s.get_by_id(offset, parameters) { Ok(from) => match s.get_by_id(offset, parameters) {
@@ -502,38 +473,40 @@ impl Core {
}) })
.flatten(); .flatten();
let search_volume = if let Some(view) = view_port {
search_volume
.filter(|p| view.contains(p))
.collect::<Vec<_>>()
} else {
search_volume.collect::<Vec<_>>()
};
// Select based on the volume, and filter out the label position themselves. // Select based on the volume, and filter out the label position themselves.
for s in &self.space_db { for s in &self.space_db {
let to = db.space(s.name())?; let to = db.space(s.name())?;
let mut p = vec![];
let search_volume: IterPositions = if let Some(view) = view_port.clone() {
Box::new(search_volume.clone().filter(move |p| view.contains(p)))
} else {
Box::new(search_volume.clone())
};
// Convert the search Volume into the target space. // Convert the search Volume into the target space.
let p = search_volume.filter_map(move |position| { for position in &search_volume {
match Space::change_base(&position, Space::universe(), to) { let position = Space::change_base(position, Space::universe(), to)?;
Err(_) => None, p.push(position);
Ok(position) => Some(position), }
}
});
let r = s let mut r = s
.get_by_positions(p, parameters)? .get_by_positions(&p, parameters)?
.filter_map(move |(position, fields)| { .into_iter()
.filter_map(|(position, fields)| {
if fields.value() == offset { if fields.value() == offset {
None None
} else { } else {
Some((position, &self.properties[fields.value()])) Some((position, &self.properties[fields.value()]))
} }
}); })
.collect::<Vec<_>>();
results.push(( Self::decode_positions(r.as_mut_slice(), to, db, output_space)?;
s.name(),
Self::decode_positions(Box::new(r), to, db, output_space)?, results.push((s.name(), r));
));
} }
} }

View File

@@ -14,20 +14,13 @@ pub use db_core::Properties;
use space::Position; use space::Position;
use space::Space; use space::Space;
/// TODO doc
pub type IterPositions<'i> = Box<dyn Iterator<Item = Position> + 'i>;
/// TODO doc
pub type IterObjects<'i> = Box<dyn Iterator<Item = (Position, &'i Properties)> + 'i>;
/// TODO doc
pub type IterObjectsBySpaces<'i> = Vec<(&'i String, IterObjects<'i>)>;
/// Selected tuples matching a query. /// Selected tuples matching a query.
/// ///
/// This is either: /// This is either:
/// * `Err` with a reason stored as a `String` /// * `Err` with a reason stored as a `String`
/// * `Ok`, with a vector of tuples defined as: /// * `Ok`, with a vector of tuples defined as:
/// `(Space Name, [(Position, Properties)])` /// `(Space Name, [(Position, Properties)])`
pub type ResultSet<'r> = Result<IterObjectsBySpaces<'r>, String>; pub type ResultSet<'r> = Result<Vec<(&'r String, Vec<(Position, &'r Properties)>)>, String>;
type ReferenceSpaceIndex = ironsea_index_hashmap::Index<Space, String>; type ReferenceSpaceIndex = ironsea_index_hashmap::Index<Space, String>;
type CoreIndex = ironsea_index_hashmap::Index<Core, String>; type CoreIndex = ironsea_index_hashmap::Index<Core, String>;
@@ -135,10 +128,7 @@ impl DataBase {
if name == space::Space::universe().name() { if name == space::Space::universe().name() {
Ok(space::Space::universe()) Ok(space::Space::universe())
} else { } else {
let r = self let r = self.reference_spaces.find(&name.to_string());
.reference_spaces
.find(&name.to_string())
.collect::<Vec<_>>();
Self::check_exactly_one(&r, "spaces", name) Self::check_exactly_one(&r, "spaces", name)
} }
@@ -156,7 +146,7 @@ impl DataBase {
/// * `name`: /// * `name`:
/// The name of the dataset (core) to search for. /// The name of the dataset (core) to search for.
pub fn core(&self, name: &str) -> Result<&Core, String> { pub fn core(&self, name: &str) -> Result<&Core, String> {
let r = self.cores.find(&name.to_string()).collect::<Vec<_>>(); let r = self.cores.find(&name.to_string());
Self::check_exactly_one(&r, "cores", name) Self::check_exactly_one(&r, "cores", name)
} }

View File

@@ -60,8 +60,7 @@ impl Position {
/// Compute `||self||`. /// Compute `||self||`.
pub fn norm(&self) -> f64 { pub fn norm(&self) -> f64 {
if let Position::Position1(coordinates) = self { if let Position::Position1(coordinates) = self {
// the square root of a single number to the square is its // the square root of a single number to the square is its positive value, so ensure it is.
// positive value, so ensure it is.
coordinates.f64().abs() coordinates.f64().abs()
} else { } else {
let point: Vec<&Coordinate> = self.into(); let point: Vec<&Coordinate> = self.into();
@@ -133,7 +132,6 @@ impl Ord for Position {
self.partial_cmp(other).unwrap() self.partial_cmp(other).unwrap()
} }
} }
impl PartialOrd for Position { impl PartialOrd for Position {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
// Let's restrict for now to same-length vectors. // Let's restrict for now to same-length vectors.

View File

@@ -16,7 +16,6 @@ use super::space_index::SpaceIndex;
use super::space_index::SpaceSetIndex; use super::space_index::SpaceSetIndex;
use super::space_index::SpaceSetObject; use super::space_index::SpaceSetObject;
use super::CoreQueryParameters; use super::CoreQueryParameters;
use super::IterPositions;
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SpaceDB { pub struct SpaceDB {
@@ -279,11 +278,11 @@ impl SpaceDB {
// Search by Id, a.k.a values // Search by Id, a.k.a values
// The results are in encoded space coordinates. // The results are in encoded space coordinates.
pub fn get_by_id<'s>( pub fn get_by_id(
&'s self, &self,
id: usize, id: usize,
parameters: &CoreQueryParameters, parameters: &CoreQueryParameters,
) -> Result<IterPositions<'s>, String> { ) -> Result<Vec<Position>, String> {
// Is that ID referenced in the current space? // Is that ID referenced in the current space?
let index = self.resolution(parameters); let index = self.resolution(parameters);
@@ -292,20 +291,15 @@ impl SpaceDB {
let view_port = parameters.view_port(space); let view_port = parameters.view_port(space);
// Select the objects // Select the objects
// FIXME: How to return an iterator instead of instantiating all let objects = self.resolutions[index].find_by_value(&SpaceFields::new(self.name(), id));
// the points here? Needed because of &SpaceFields.
let objects = self.resolutions[index]
.find_by_value(&SpaceFields::new(self.name(), id))
.collect::<Vec<_>>();
let results: IterPositions<'s> = if let Some(view_port) = view_port { let results = if let Some(view_port) = view_port {
Box::new( objects
objects .into_iter()
.into_iter() .filter(|position| view_port.contains(position))
.filter(move |position| view_port.contains(position)), .collect::<Vec<_>>()
)
} else { } else {
Box::new(objects.into_iter()) objects
}; };
Ok(results) Ok(results)
@@ -313,11 +307,11 @@ impl SpaceDB {
// Search by positions defining a volume. // Search by positions defining a volume.
// The position is expressed in encoded space coordinates, and results are in encoded space coordinates. // The position is expressed in encoded space coordinates, and results are in encoded space coordinates.
pub fn get_by_positions<'s>( pub fn get_by_positions(
&'s self, &self,
positions: impl Iterator<Item = Position> + 's, positions: &[Position],
parameters: &CoreQueryParameters, parameters: &CoreQueryParameters,
) -> Result<Box<dyn Iterator<Item = (Position, &SpaceFields)> + 's>, String> { ) -> Result<Vec<(Position, &SpaceFields)>, String> {
let index = self.resolution(parameters); let index = self.resolution(parameters);
// FIXME: Should I do it here, or add the assumption this is a clean list? // FIXME: Should I do it here, or add the assumption this is a clean list?
@@ -326,13 +320,17 @@ impl SpaceDB {
//let view_port = parameters.view_port(space); //let view_port = parameters.view_port(space);
// Select the objects // Select the objects
let results = positions.flat_map(move |position| { let results = positions
self.resolutions[index] .iter()
.find(&position) .flat_map(|position| {
.map(move |fields| (position.clone(), fields)) self.resolutions[index]
}); .find(position)
.into_iter()
.map(move |fields| (position.clone(), fields))
})
.collect();
Ok(Box::new(results)) Ok(results)
} }
// Search by Shape defining a volume: // Search by Shape defining a volume:
@@ -341,11 +339,11 @@ impl SpaceDB {
// * Point (Specific position) // * Point (Specific position)
// The Shape is expressed in encoded space coordinates, and results are in encoded space coordinates. // The Shape is expressed in encoded space coordinates, and results are in encoded space coordinates.
pub fn get_by_shape<'s>( pub fn get_by_shape(
&'s self, &self,
shape: Shape, shape: &Shape,
parameters: &CoreQueryParameters, parameters: &CoreQueryParameters,
) -> Result<Box<dyn Iterator<Item = (Position, &SpaceFields)> + 's>, String> { ) -> Result<Vec<(Position, &SpaceFields)>, String> {
let index = self.resolution(parameters); let index = self.resolution(parameters);
// Convert the view port to the encoded space coordinates // Convert the view port to the encoded space coordinates

View File

@@ -7,7 +7,6 @@ use serde::Serialize;
use super::space::Coordinate; use super::space::Coordinate;
use super::space::Position; use super::space::Position;
use super::space::Shape; use super::space::Shape;
use super::IterPositions;
#[derive(Clone, Debug, Hash)] #[derive(Clone, Debug, Hash)]
pub struct SpaceSetObject { pub struct SpaceSetObject {
@@ -126,63 +125,59 @@ impl SpaceIndex {
} }
// Inputs and Results are expressed in encoded space coordinates. // Inputs and Results are expressed in encoded space coordinates.
pub fn find<'s>(&'s self, key: &Position) -> Box<dyn Iterator<Item = &SpaceFields> + 's> { pub fn find(&self, key: &Position) -> Vec<&SpaceFields> {
self.index.find(key) self.index.find(key)
} }
// Inputs and Results are expressed in encoded space coordinates. // Inputs and Results are expressed in encoded space coordinates.
fn find_range<'s>( fn find_range(&self, start: &Position, end: &Position) -> Vec<(Position, &SpaceFields)> {
&'s self,
start: &Position,
end: &Position,
) -> Box<dyn Iterator<Item = (Position, &SpaceFields)> + 's> {
self.index.find_range(start, end) self.index.find_range(start, end)
} }
// Inputs and Results are expressed in encoded space coordinates. // Inputs and Results are expressed in encoded space coordinates.
pub fn find_by_value<'s>(&'s self, id: &'s SpaceFields) -> IterPositions<'s> { pub fn find_by_value(&self, id: &SpaceFields) -> Vec<Position> {
self.index.find_by_value(id) self.index.find_by_value(id)
} }
// Inputs and Results are also in encoded space coordinates. // Inputs and Results are also in encoded space coordinates.
pub fn find_by_shape<'s>( pub fn find_by_shape(
&'s self, &self,
shape: Shape, shape: &Shape,
view_port: &Option<Shape>, view_port: &Option<Shape>,
) -> Result<Box<dyn Iterator<Item = (Position, &SpaceFields)> + 's>, String> { ) -> Result<Vec<(Position, &SpaceFields)>, String> {
match shape { match shape {
Shape::Point(position) => { Shape::Point(position) => {
if let Some(mbb) = view_port { if let Some(mbb) = view_port {
if !mbb.contains(&position) { if !mbb.contains(position) {
return Err(format!( return Err(format!(
"View port '{:?}' does not contain '{:?}'", "View port '{:?}' does not contain '{:?}'",
mbb, position mbb, position
)); ));
} }
} }
Ok(Box::new( Ok(self
self.find(&position) .find(position)
.map(move |fields| (position.clone(), fields)), .into_iter()
)) .map(|fields| (position.clone(), fields))
.collect())
} }
Shape::BoundingBox(bl, bh) => { Shape::BoundingBox(bl, bh) => {
if let Some(mbb) = view_port { if let Some(mbb) = view_port {
match mbb { match mbb {
Shape::BoundingBox(vl, vh) => { Shape::BoundingBox(vl, vh) => {
// Compute the intersection of the two boxes. // Compute the intersection of the two boxes.
let lower = (&bl).max(vl); let lower = bl.max(vl);
let higher = (&bh).min(vh); let higher = bh.min(vh);
if higher < lower { if higher < lower {
Err(format!( Err(format!(
"View port '{:?}' does not intersect '{:?}'", "View port '{:?}' does not intersect '{:?}'",
mbb, mbb, shape
Shape::BoundingBox(bl.clone(), bh.clone())
)) ))
} else { } else {
trace!( trace!(
"mbb {:?} shape {:?} lower {:?} higher {:?}", "mbb {:?} shape {:?} lower {:?} higher {:?}",
mbb, mbb,
Shape::BoundingBox(bl.clone(), bh.clone()), shape,
lower, lower,
higher higher
); );
@@ -192,11 +187,11 @@ impl SpaceIndex {
_ => Err(format!("Invalid view port shape '{:?}'", mbb)), _ => Err(format!("Invalid view port shape '{:?}'", mbb)),
} }
} else { } else {
Ok(self.find_range(&bl, &bh)) Ok(self.find_range(bl, bh))
} }
} }
Shape::HyperSphere(center, radius) => { Shape::HyperSphere(center, radius) => {
let (bl, bh) = Shape::HyperSphere(center.clone(), radius).get_mbb(); let (bl, bh) = &shape.get_mbb();
let lower; let lower;
let higher; let higher;
@@ -204,14 +199,14 @@ impl SpaceIndex {
match mbb { match mbb {
Shape::BoundingBox(vl, vh) => { Shape::BoundingBox(vl, vh) => {
// Compute the intersection of the two boxes. // Compute the intersection of the two boxes.
lower = (&bl).max(vl); lower = bl.max(vl);
higher = (&bh).min(vh); higher = bh.min(vh);
} }
_ => return Err(format!("Invalid view port shape '{:?}'", mbb)), _ => return Err(format!("Invalid view port shape '{:?}'", mbb)),
} }
} else { } else {
lower = &bl; lower = bl;
higher = &bh; higher = bh;
} }
// Filter out results using using a range query over the MBB, // Filter out results using using a range query over the MBB,
@@ -219,9 +214,11 @@ impl SpaceIndex {
// a sphere. // a sphere.
let results = self let results = self
.find_range(lower, higher) .find_range(lower, higher)
.filter(move |(position, _)| (position - &center).norm() <= radius.f64()); .into_iter()
.filter(|(position, _)| (position - center).norm() <= radius.f64())
.collect();
Ok(Box::new(results)) Ok(results)
} }
} }
} }

View File

@@ -68,8 +68,9 @@ pub mod v1 {
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use super::database; use crate::database;
use super::space; use database::space;
use super::Point; use super::Point;
use super::Properties; use super::Properties;
@@ -86,8 +87,7 @@ pub mod v1 {
/// Define a Shape, within a specific reference space. /// Define a Shape, within a specific reference space.
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Shape { pub struct Shape {
/// Type of the shape, which is used to interpret the list of /// Type of the shape, which is used to interpret the list of `vertices`.
/// `vertices`.
#[serde(rename = "type")] #[serde(rename = "type")]
pub type_name: String, pub type_name: String,
@@ -99,8 +99,8 @@ pub mod v1 {
pub vertices: Vec<Point>, pub vertices: Vec<Point>,
} }
/// Convert a list of properties grouped by space id, then positions /// Convert a list of properties grouped by space id, then positions to a
/// to a list of Spatial Objects for the rest API v1. /// list of Spatial Objects for the rest API v1.
/// ///
/// # Parameters /// # Parameters
/// ///
@@ -109,8 +109,7 @@ pub mod v1 {
pub fn to_spatial_objects( pub fn to_spatial_objects(
list: Vec<(&String, Vec<(space::Position, &database::Properties)>)>, list: Vec<(&String, Vec<(space::Position, &database::Properties)>)>,
) -> Vec<SpatialObject> { ) -> Vec<SpatialObject> {
// Filter per Properties, in order to regroup by it, then build // Filter per Properties, in order to regroup by it, then build a single SpatialObject per Properties.
// a single SpatialObject per Properties.
let mut hashmap = HashMap::new(); let mut hashmap = HashMap::new();
for (space, v) in list { for (space, v) in list {
for (position, properties) in v { for (position, properties) in v {
@@ -151,8 +150,9 @@ pub mod v2 {
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use super::database; use crate::database;
use super::space; use database::space;
use super::Point; use super::Point;
use super::Properties; use super::Properties;
@@ -208,88 +208,46 @@ pub mod v2 {
HyperSpheres(Vec<(Point, f64)>), HyperSpheres(Vec<(Point, f64)>),
} }
/// Convert a list of space id grouped by properties, then positions /// Convert a list of properties grouped by space id, then positions to a
/// to a list of Spatial Objects for the rest API v2. /// list of Spatial Objects for the rest API v2.
///
/// # Parameters
///
/// * `list`:
/// A list of (`&Properties`, [ ( **Space Id**, [ *Spatial position* ] ) ]) tuples.
#[allow(clippy::type_complexity)]
// Type alias cannot be used as Traits, so we can't use the alias to add the lifetime specifications.
pub fn from_spaces_by_properties<'o>(
objects: Box<
(dyn Iterator<
Item=(
&'o database::Properties,
Vec<(&'o String, Box<dyn Iterator<Item=space::Position> + 'o>)>,
),
> + 'o),
>,
) -> impl Iterator<Item=SpatialObject> + 'o {
objects.map(|(property, positions_by_spaces)| {
let volumes = positions_by_spaces
.into_iter()
.map(|(space, positions)| {
// We are not using vec![] as we now beforehand we
// will have only one element in the vector, so we
// optimise for space by allocating it as such.
let shapes = vec![
Shape::Points(positions.map(|position|
position.into()).collect::<Vec<_>>())
];
Volume {
space: space.clone(),
shapes,
}
})
.collect();
SpatialObject {
properties: (&property).into(),
volumes,
}
})
}
/// Convert a list of properties grouped by space id, then positions
/// to a list of Spatial Objects for the rest API v2.
/// ///
/// # Parameters /// # Parameters
/// ///
/// * `list`: /// * `list`:
/// A list of (**Space Id**, [ ( *Spatial position*, `&Properties` ) ]) tuples. /// A list of (**Space Id**, [ ( *Spatial position*, `&Properties` ) ]) tuples.
pub fn from_properties_by_spaces( pub fn to_spatial_objects(
objects: database::IterObjectsBySpaces<'_>, list: Vec<(&String, Vec<(space::Position, &database::Properties)>)>,
) -> impl Iterator<Item=SpatialObject> + '_ { ) -> Vec<SpatialObject> {
// Filter per Properties, in order to regroup by it, then build // Filter per Properties, in order to regroup by it, then build a single SpatialObject per Properties.
// a single SpatialObject per Properties.
let mut hashmap = HashMap::new(); let mut hashmap = HashMap::new();
for (space, v) in objects { for (space, v) in list {
for (position, properties) in v { for (position, properties) in v {
hashmap hashmap
.entry(properties) .entry(properties)
.or_insert_with(HashMap::new) .or_insert_with(HashMap::new)
.entry(space) .entry(space)
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.push(position); .push(position.into());
} }
} }
let results = Box::new(hashmap.into_iter().map(|(property, hm)| { let mut results = vec![];
let positions = hm for (properties, v) in hashmap.iter_mut() {
.into_iter() let volumes = v
.map(|(space, positions)| { .drain()
let positions: database::IterPositions = Box::new(positions.into_iter()); .map(|(space, positions)| Volume {
(space, positions) space: space.clone(),
shapes: vec![Shape::Points(positions)],
}) })
.collect::<Vec<_>>(); .collect();
(property, positions) results.push(SpatialObject {
})); properties: properties.into(),
volumes,
});
}
from_spaces_by_properties(results) results
} }
} }
@@ -452,9 +410,6 @@ pub fn build_index(
} }
properties.append(&mut properties_hm.drain().map(|(_, v)| v).collect::<Vec<_>>()); properties.append(&mut properties_hm.drain().map(|(_, v)| v).collect::<Vec<_>>());
// We we use sort_by_key, we get borrow checker errors.
#[allow(clippy::unnecessary_sort_by)]
properties.sort_unstable_by(|a, b| a.id().cmp(b.id())); properties.sort_unstable_by(|a, b| a.id().cmp(b.id()));
space_set_objects.iter_mut().for_each(|object| { space_set_objects.iter_mut().for_each(|object| {