1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//! 3D geodesy in pure `f64`, zero dependencies.
//!
//! Real 3D geometry cannot be done on geodetic `(lat, lon, height)` directly — it is not a Euclidean
//! space — so this crate provides the metric frames and the closed-form primitives on top of them:
//!
//! - [`Ellipsoid`] — a reference ellipsoid ([`WGS84`] by default) with geodetic ↔ ECEF conversion and
//! ray intersection.
//! - [`Geodetic`], [`Ecef`], [`Enu`], [`Ned`], [`Aer`] — typed coordinates, so latitude and longitude
//! or east and north cannot be swapped by accident.
//! - [`LocalFrame`] — a topocentric East-North-Up frame anchored at a reference point. It precomputes
//! its rotation once, so converting many points against one reference is a few multiplies each.
//! Look angles ([`LocalFrame::look_angles`]) and their inverse ([`LocalFrame::destination`]) live here.
//! - [`ground_strike`] / [`Ellipsoid::ray_intersect`] — where a pointing ray meets the surface.
//! - [`Volume`]s — [`AltitudeBand`], [`Sphere`], [`Cone`], [`Cylinder`]; compose with any 2D
//! footprint test.
//! - [`Track`] / [`closest_approach`] — dead reckoning and first-order conflict detection;
//! [`Course`] converts between ECEF velocities and course-over-ground/speed/climb.
//! - Great circles, radii of curvature, horizon distance and geometric line of sight on
//! [`Ellipsoid`]; [`eci`] for inertial ↔ Earth-fixed via sidereal time.
//! - [`Helmert7`] — 7-parameter datum shifts.
//!
//! Everything is `Copy`, allocation-free and `#![forbid(unsafe_code)]`. Angles in the public API are
//! **degrees**, lengths **metres**; ECEF is the frame CesiumJS renders in, so a `(lat, lon, height)`
//! track maps straight to what a globe draws.
//!
//! ```
//! use geo3d::{Geodetic, LocalFrame, Sphere, Volume, distance3d_m};
//!
//! let site = Geodetic::new(34.05, -118.24, 100.0);
//! let aircraft = Geodetic::new(34.30, -118.10, 3_000.0);
//!
//! // Straight-line (slant) distance through space, not along the surface.
//! let slant = distance3d_m(site, aircraft);
//! assert!(slant > 30_000.0 && slant < 32_000.0);
//!
//! // Where to point: azimuth from north, elevation above the horizon, range.
//! let frame = LocalFrame::new(site);
//! let aer = frame.look_angles(aircraft);
//! assert!(aer.azimuth_deg > 20.0 && aer.azimuth_deg < 30.0 && aer.elevation_deg > 4.0);
//!
//! // ...and back: the destination of that pointing is the aircraft.
//! let back = frame.destination(aer);
//! assert!((back.lat_deg - aircraft.lat_deg).abs() < 1e-9);
//!
//! // A 40 km proximity bubble around the site contains it.
//! assert!(Sphere::new(site, 40_000.0).contains(aircraft));
//! ```
/// Every Rust code block in the README is compiled and run as a doctest.
;
pub use Helmert7;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
/// WGS84 geodetic → ECEF. Shorthand for [`WGS84.to_ecef`](Ellipsoid::to_ecef).
/// WGS84 ECEF → geodetic. Shorthand for [`WGS84.to_geodetic`](Ellipsoid::to_geodetic).
/// Straight-line **slant range** between two WGS84 geodetic points, in metres — the through-space
/// distance between two things at height (the ECEF chord), unlike a 2D great-circle distance.
/// Where the line of sight from `observer`, pointing along compass `azimuth_deg` (0 = north,
/// clockwise) and `elevation_deg` (0 = horizon, positive up), first meets the WGS84 surface — the
/// nearest point ahead. An observer on the surface strikes its own point; one below the surface, or
/// a line pointing above the horizon into space, gives `None`. Shorthand for
/// [`LocalFrame::ground_strike`] on a fresh frame.