ecitygml-transform 0.0.2-alpha.4

Supplementary operations for CityGML data.
Documentation
use bvh::aabb::{Aabb, Bounded};
use bvh::bounding_hierarchy::BHShape;
use bvh::bvh::Bvh;
use ecitygml_core::operations::{CityModelGeometryStore, CityObjectGeometryEntry};
use egml::model::geometry::Envelope;
use nalgebra::Point3;

struct SpatialEntry {
    index: usize,
    aabb: Aabb<f64, 3>,
    node_index: usize,
}

impl SpatialEntry {
    fn from_object(index: usize, obj: &CityObjectGeometryEntry) -> Option<Self> {
        let env = obj.envelope()?;
        Some(Self {
            index,
            aabb: envelope_to_aabb(env),
            node_index: 0,
        })
    }
}

impl Bounded<f64, 3> for SpatialEntry {
    fn aabb(&self) -> Aabb<f64, 3> {
        self.aabb
    }
}

impl BHShape<f64, 3> for SpatialEntry {
    fn bh_node_index(&self) -> usize {
        self.node_index
    }

    fn set_bh_node_index(&mut self, index: usize) {
        self.node_index = index;
    }
}

pub struct BvhIndex<'a> {
    bvh: Bvh<f64, 3>,
    entries: Vec<SpatialEntry>,
    geometry_store: &'a CityModelGeometryStore,
}

impl<'a> BvhIndex<'a> {
    pub fn from_geometry_store(geometry_store: &'a CityModelGeometryStore) -> Self {
        let mut entries: Vec<SpatialEntry> = geometry_store
            .objects
            .iter()
            .enumerate()
            .filter_map(|(i, obj)| SpatialEntry::from_object(i, obj))
            .collect();

        let bvh = Bvh::build(&mut entries);

        Self {
            bvh,
            entries,
            geometry_store,
        }
    }

    pub fn query_envelope(&self, envelope: &Envelope) -> Vec<&CityObjectGeometryEntry> {
        let query_aabb = envelope_to_aabb(envelope);

        self.bvh
            .traverse_iterator(&query_aabb, &self.entries)
            .filter_map(|entry| self.geometry_store.get_by_index(entry.index))
            .collect()
    }
}

fn envelope_to_aabb(envelope: &Envelope) -> Aabb<f64, 3> {
    let min = Point3::new(
        envelope.lower_corner().x(),
        envelope.lower_corner().y(),
        envelope.lower_corner().z(),
    );
    let max = Point3::new(
        envelope.upper_corner().x(),
        envelope.upper_corner().y(),
        envelope.upper_corner().z(),
    );
    Aabb::with_bounds(min, max)
}