Skip to main content

cumulus3d_align/
lib.rs

1/*
2 * lib.rs
3 *
4 * Copyright (c) 2026 ParkSangWoo
5 *
6 * Author(s):
7 *
8 *      ParkSangWoo <dev.parksangwoo@gmail.com>
9 *
10 *
11 * This file is part of cumulus3d.
12 *
13 * Licensed under the Apache License, Version 2.0 (the "License");
14 * you may not use this file except in compliance with the License.
15 * You may obtain a copy of the License at
16 *
17 *      http://www.apache.org/licenses/LICENSE-2.0
18 *
19 * Unless required by applicable law or agreed to in writing, software
20 * distributed under the License is distributed on an "AS IS" BASIS,
21 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 * See the License for the specific language governing permissions and
23 * limitations under the License.
24 *
25 * SPDX-License-Identifier: Apache-2.0
26 */
27
28//! GPS→ENU, Sim3 alignment, shared 3D point alignment, point cloud post-processing.
29//!
30//! - [`geodesy`]: WGS84 LLA ↔ ECEF ↔ ENU.
31//! - [`umeyama`](mod@umeyama): Umeyama Sim3, LO-RANSAC robust estimation, iterative reweighting (robust Umeyama).
32//! - [`model_aligner`]: alignment of registered image projection centers to GPS ENU (robust Sim3 estimation).
33//! - [`shared`]: shared 3D point correspondences between two reconstructions and their alignment.
34//! - [`kdtree`], [`cloud`]: point cloud transform, proximity masking, thinning, snapshot composition.
35//! - [`reanchor`]: chained Sim3 that reconnects the coordinate frames of earlier zones to the new refined model.
36//!
37//! # Usage example
38//!
39//! ```
40//! use cumulus3d_align::{compose_snapshot, umeyama, EnuFrame, SnapshotOptions};
41//! use cumulus3d_core::io::PointCloud;
42//! use cumulus3d_core::{Quat, Sim3, Vec3};
43//!
44//! // GPS (WGS84) ↔ ENU: east/north/up meter coordinates relative to the origin.
45//! let enu = EnuFrame::new(37.5, 127.0, 10.0);
46//! let p = enu.lla_to_enu(37.5001, 127.0001, 10.0);
47//! let (lat, lon, _alt) = enu.enu_to_lla(&p);
48//! assert!((lat - 37.5001).abs() < 1e-9 && (lon - 127.0001).abs() < 1e-9);
49//!
50//! // Estimate the Sim3 (src → dst) between sets of corresponding points.
51//! let truth = Sim3::new(2.0, Quat::from_axis_angle(&Vec3::z(), 0.3), Vec3::new(1.0, 2.0, 3.0));
52//! let src: Vec<Vec3> = (0..10).map(|i| Vec3::new(i as f64, (i * i) as f64 * 0.1, (i as f64).sin())).collect();
53//! let dst: Vec<Vec3> = src.iter().map(|x| truth.transform_point(x)).collect();
54//! let est = umeyama(&src, &dst, true).unwrap();
55//! assert!((est.scale - 2.0).abs() < 1e-9);
56//!
57//! // Snapshot: remove preview points within 1.5 m of refined points, merge, then thin by 1/stride.
58//! let fine = PointCloud { positions: vec![[0.0, 0.0, 0.0]], ..Default::default() };
59//! let coarse = PointCloud { positions: vec![[0.5, 0.0, 0.0], [10.0, 0.0, 0.0]], ..Default::default() };
60//! let snap = compose_snapshot(&[&fine], &[&coarse], &SnapshotOptions { mask_radius: 1.5, stride: 1 });
61//! assert_eq!(snap.len(), 2);
62//! ```
63//!
64//! ```no_run
65//! use cumulus3d_align::{align_to_gps_file, ModelAlignerOptions};
66//! let mut rec = cumulus3d_core::interop::read_model("model/0").unwrap();
67//! let a = align_to_gps_file(&mut rec, "gps_ref.txt", &ModelAlignerOptions::default()).unwrap();
68//! println!("인라이어 {}/{}, 중앙 오차 {:.2} m", a.num_inliers, a.common.len(), a.median_error);
69//! ```
70#![warn(missing_docs)]
71
72pub mod cloud;
73pub mod error;
74pub mod geodesy;
75pub mod kdtree;
76pub mod model_aligner;
77pub mod reanchor;
78pub mod shared;
79pub mod umeyama;
80
81pub use cloud::{compose_snapshot, decimate, merge_clouds, remove_near, select, transform_cloud, SnapshotOptions};
82pub use error::{AlignError, Result};
83pub use geodesy::{ecef_to_lla, lla_to_ecef, EnuFrame, WGS84_A, WGS84_B, WGS84_E2, WGS84_F};
84pub use kdtree::KdTree;
85pub use model_aligner::{
86    align_to_gps, align_to_gps_file, estimate_gps_alignment, gps_to_enu, EnuOrigin, GpsAlignment, ModelAlignerOptions,
87};
88pub use reanchor::AnchorChain;
89pub use shared::{align_reconstructions, position_index_from_name, shared_point_correspondences, SharedPointOptions};
90pub use umeyama::{estimate_sim3_ransac, robust_umeyama, umeyama, RankCheck, RobustUmeyamaOptions, RobustUmeyamaResult, Sim3Estimator};