use crate::gis::Gis;
use crate::node::{Entry, M_NODE, Node, Root};
use loam::{Id, Reader, Result, Writer};
use pointy::Float;
use serde::{Serialize, de::DeserializeOwned};
use std::io::ErrorKind;
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
#[derive(Copy, Clone, Debug, PartialEq)]
enum Axis {
X,
Y,
}
impl Axis {
fn with_height(self, height: usize) -> Self {
if !height.is_multiple_of(2) {
self
} else {
match self {
Axis::X => Axis::Y,
Axis::Y => Axis::X,
}
}
}
}
enum NodeElem<F>
where
F: Float,
{
Leaf(Node<F>),
Node(Vec<usize>),
}
impl<F> NodeElem<F>
where
F: Float,
{
fn lookup(&self, node_entries: &[Entry<F>]) -> Node<F> {
match self {
NodeElem::Leaf(leaf) => leaf.clone(),
NodeElem::Node(children) => {
let mut n = Node::new();
for child in children {
let entry = &node_entries[*child];
n.push(entry.id(), entry.bbox());
}
n
}
}
}
}
pub struct BulkWriter<D, F, G>
where
F: Float + Serialize + DeserializeOwned,
G: Gis<F, Data = D> + Serialize + DeserializeOwned,
{
path: PathBuf,
writer: Writer,
reader: Reader,
elems: Vec<Entry<F>>,
nodes: Vec<NodeElem<F>>,
odd_axis: Axis,
_data: PhantomData<D>,
_float: PhantomData<F>,
_geom: PhantomData<G>,
}
fn make_writer(path: &Path) -> Result<Writer> {
match Writer::new(path) {
Err(loam::Error::Io(e)) if e.kind() == ErrorKind::AlreadyExists => {
std::fs::remove_file(path)?;
Writer::new(path)
}
w => w,
}
}
impl<D, F, G> BulkWriter<D, F, G>
where
F: Float + Serialize + DeserializeOwned,
G: Gis<F, Data = D> + Serialize + DeserializeOwned,
{
pub fn new<P>(path: P) -> Result<Self>
where
P: AsRef<Path>,
{
let mut tmp = PathBuf::new();
tmp.push(path);
let path = tmp.clone();
tmp.set_extension("tmp");
let writer = make_writer(&tmp)?;
let reader = Reader::new_empty()?;
Ok(Self {
path,
writer,
reader,
elems: Vec::new(),
nodes: Vec::new(),
odd_axis: Axis::X,
_data: PhantomData,
_float: PhantomData,
_geom: PhantomData,
})
}
pub fn push(&mut self, geom: &G) -> Result<()> {
let id = self.writer.push(geom)?;
let bbox = geom.bbox();
self.elems.push(Entry::new(id, bbox));
Ok(())
}
pub fn finish(mut self) -> Result<()> {
let mut elems = std::mem::take(&mut self.elems);
self.writer.checkpoint(Id::new(0))?;
let mut tmp = PathBuf::new();
tmp.push(&self.path);
tmp.set_extension("tmp2");
self.writer = Writer::new(&tmp)?;
tmp.set_extension("tmp");
self.reader = Reader::new(&tmp)?;
self.build_tree(&mut elems)?;
let id = self.write_nodes(elems.len())?;
self.writer.checkpoint(id)?;
let path = self.path;
drop(self.writer);
remove_tmp_file(&path)?;
rename_tree(&path)?;
Ok(())
}
pub fn cancel(self) -> Result<()> {
let path = self.path;
drop(self.writer);
remove_tmp_file(&path)
}
fn build_tree(&mut self, elems: &mut [Entry<F>]) -> Result<usize> {
let n_elems = elems.len();
log::debug!("n_elems: {}", n_elems);
let height = Node::<F>::height(n_elems);
log::debug!("height: {}", height);
self.odd_axis = Axis::Y.with_height(height);
if height > 1 {
elems.sort_unstable_by(Entry::compare_x);
let groups = Node::<F>::root_groups(n_elems);
assert!(groups > 0);
let n_group = (n_elems as f32 / groups as f32).ceil() as usize;
let v_group = M_NODE / groups;
log::debug!(
"groups: {}, n_group: {}, v_group: {}",
groups,
n_group,
v_group
);
let mut children = Vec::with_capacity(M_NODE);
for v_chunk in elems.chunks_mut(n_group) {
v_chunk.sort_unstable_by(Entry::compare_y);
let n_chunk =
(v_chunk.len() as f32 / v_group as f32).ceil() as usize;
log::debug!("n_chunk: {}", n_chunk);
for h_chunk in v_chunk.chunks_mut(n_chunk) {
let child = self.build_subtree(height - 1, h_chunk)?;
children.push(child);
}
}
Ok(self.push_node(NodeElem::Node(children)))
} else {
self.build_leaf(elems)
}
}
fn push_node(&mut self, ne: NodeElem<F>) -> usize {
let idx = self.nodes.len();
self.nodes.push(ne);
idx
}
fn build_subtree(
&mut self,
height: usize,
elems: &mut [Entry<F>],
) -> Result<usize> {
if height > 1 {
match self.odd_axis.with_height(height) {
Axis::X => elems.sort_unstable_by(Entry::compare_x),
Axis::Y => elems.sort_unstable_by(Entry::compare_y),
}
let mut children = Vec::with_capacity(M_NODE);
let n_group = Node::<F>::partition_sz(height);
for chunk in elems.chunks_mut(n_group) {
let child = self.build_subtree(height - 1, chunk)?;
children.push(child);
}
Ok(self.push_node(NodeElem::Node(children)))
} else {
self.build_leaf(elems)
}
}
fn build_leaf(&mut self, elems: &[Entry<F>]) -> Result<usize> {
let mut leaf = Node::<F>::new();
for entry in elems {
let geom: G = self.reader.lookup(entry.id())?;
let wid = self.writer.push(&geom)?;
leaf.push(wid, entry.bbox());
}
Ok(self.push_node(NodeElem::Leaf(leaf)))
}
fn write_nodes(&mut self, n_elems: usize) -> Result<Id> {
if n_elems > 0 {
let n_nodes = self.nodes.len();
let mut node_entries = Vec::with_capacity(n_nodes);
for ne in &self.nodes[..n_nodes - 1] {
let node = ne.lookup(&node_entries);
let id = self.writer.push(&node)?;
let bbox = node.bbox();
node_entries.push(Entry::new(id, bbox));
}
let ne = &self.nodes[n_nodes - 1];
let node = ne.lookup(&node_entries);
let root = Root::new(node, n_elems);
let id = self.writer.push(&root)?;
Ok(id)
} else {
let node = Node::<F>::new();
let root = Root::new(node, 0);
let id = self.writer.push(&root)?;
Ok(id)
}
}
}
fn remove_tmp_file(path: &Path) -> Result<()> {
let mut tmp = PathBuf::new();
tmp.push(path);
tmp.set_extension("tmp");
std::fs::remove_file(tmp)?;
Ok(())
}
fn rename_tree(path: &Path) -> Result<()> {
let mut tmp2 = PathBuf::new();
tmp2.push(path);
tmp2.set_extension("tmp2");
std::fs::rename(tmp2, path)?;
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn axis() {
let axis = Axis::Y.with_height(4);
assert_eq!(Axis::X, axis);
assert_eq!(Axis::X, axis.with_height(3));
assert_eq!(Axis::Y, axis.with_height(2));
assert_eq!(Axis::X, axis.with_height(1));
let axis = Axis::Y.with_height(3);
assert_eq!(Axis::Y, axis);
assert_eq!(Axis::X, axis.with_height(2));
assert_eq!(Axis::Y, axis.with_height(1));
}
}