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 let reprojected_point_cloud = reproject_point_cloud(
57 point_cloud,
58 source_srs,
59 SpatialReferenceIdentifier::Epsg4978,
60 )?;
61
62 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 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 Ok(Self {
102 tiled_content: point_cloud_octree,
103 root_transform: converted_isometry,
104 root_geometric_error,
105 geometric_error,
106 })
107 }
108}