etiles_core/
tileset.rs

1use crate::error::Error;
2use crate::reproject::reproject_point_cloud;
3use ecoord::HasAabb;
4use ecoord::octree::Octree;
5use epoint::transform::apply_isometry;
6use eproj::{Projector, SpatialReferenceIdentifier};
7use nalgebra::{Isometry3, Point3, UnitQuaternion};
8use palette::Srgb;
9use std::f64;
10use std::iter::zip;
11
12#[derive(Copy, Clone, Debug)]
13pub struct Vertex {
14    pub position: Point3<f64>,
15    pub color: Srgb<f32>,
16}
17
18impl HasAabb for Vertex {
19    fn center(&self) -> Point3<f64> {
20        self.position
21    }
22
23    fn min(&self) -> Point3<f64> {
24        self.position
25    }
26
27    fn max(&self) -> Point3<f64> {
28        self.position
29    }
30}
31
32pub struct Tileset {
33    pub tiled_content: Octree<Vertex>,
34    pub root_transform: Isometry3<f64>,
35    pub root_geometric_error: f64,
36    pub geometric_error: f64,
37}
38
39impl Tileset {
40    pub fn from_point_cloud(
41        point_cloud: epoint::PointCloud,
42        source_srs: SpatialReferenceIdentifier,
43        maximum_points_per_octant: u64,
44        seed_number: Option<u64>,
45    ) -> Result<Self, Error> {
46        let number_of_points = point_cloud.point_data.height();
47        let projector = Projector::new(source_srs, SpatialReferenceIdentifier::Epsg4978)?;
48        let isometry = Isometry3::from_parts(
49            point_cloud.point_data.get_local_center().into(),
50            UnitQuaternion::default(),
51        );
52        let converted_isometry = projector.convert_isometry(isometry)?;
53        //info!("Derived isometry: {:?}", &converted_isometry);
54
55        // info!("Start reprojecting");
56        let reprojected_point_cloud = reproject_point_cloud(
57            point_cloud,
58            source_srs,
59            SpatialReferenceIdentifier::Epsg4978,
60        )?;
61
62        //info!("Start applying isometry");
63        let geodetic_transform_isometry = converted_isometry.inverse();
64        let local_point_cloud =
65            apply_isometry(&reprojected_point_cloud, geodetic_transform_isometry).unwrap();
66
67        let point_cloud_positions = local_point_cloud.point_data.get_all_points();
68        let point_cloud_colors: Vec<Srgb<f32>> =
69            match local_point_cloud.point_data.get_all_colors().ok() {
70                Some(colors) => colors.into_iter().map(|c| c.into_format()).collect(),
71                None => {
72                    vec![
73                        Srgb::<f32>::new(0.83144885, 0.83144885, 0.83144885);
74                        local_point_cloud.point_data.height()
75                    ]
76                }
77            };
78        let point_cloud_vertices: Vec<Vertex> = zip(point_cloud_positions, point_cloud_colors)
79            .map(|(p, c)| Vertex {
80                position: p,
81                color: c,
82            })
83            .collect();
84
85        //info!("Start building octree");
86        let point_cloud_octree = Octree::new(
87            point_cloud_vertices,
88            maximum_points_per_octant as usize,
89            seed_number,
90        )?;
91
92        let root_geometric_error = point_cloud_octree.bounds().bounding_box().diagonal().norm();
93        let geometric_error = {
94            let bounding_box_volume = point_cloud_octree.bounds().bounding_box().volume();
95            let average_spacing = (bounding_box_volume / number_of_points as f64).cbrt();
96            let base_scaling = 7.0;
97            average_spacing * 2.0f64.sqrt() * base_scaling
98        };
99        //info!("geometric_error: {geometric_error}");
100
101        Ok(Self {
102            tiled_content: point_cloud_octree,
103            root_transform: converted_isometry,
104            root_geometric_error,
105            geometric_error,
106        })
107    }
108}