Repository navigation
Releases
geo3d 0.3.4
- 📝
Footprint::multi_polygon's doc says what its rule does (#71): a point on the boundaries of an even number of polygons is outside, unless it is inside another of them, as ingeo. The doc said it was inside. An example now pins it. The behaviour does not change.
geo3d 0.3.3
- 🗺️ A point-in-polygon test for a polygon used once (#68):
polygon_containmentandmulti_polygon_containment, behind thefootprintfeature.- It tests edge by edge with no index, and allocates nothing. A ring is any iterator of positions, so a polygon can be tested straight from where it lies, such as one row of a column of WKB.
- A small polygon takes about 13 ns a test, level with
geo::Containson a polygon already built, and 16 times faster than preparing aFootprintfor the one test. - It gives a
Footprint's answer for the same rings, valid or not: the two share one edge rule and one rule for combining rings.Footprintis as fast as before; it compiles to the same instructions.
geo3d 0.3.2
- 🎯 Exact heights on the polar axis and in the equatorial plane (#63), as
to_geodeticpromises. There the height is|z| − borr − a, rounded once. Before, it came back up to a nanometre off:(0°, 0°, 100 m)round-tripped to99.99999999969262m. Every other position gives the same answer as 0.3.1, to the bit (checked on a million). - ⚡
Footprint::containmentis inlined (#65), as geo3d's other hot functions are, so a caller's loop tests points without a call into the crate. That makes it 5% to 6% faster per point in a caller's loop, measured.
geo3d 0.3.1
-
🗺️ Fast point-in-polygon:
FootprintandPrism(#60), behind the newfootprintfeature:Footprintprepares a polygon once, with holes and several parts. Thencontainmentplaces a point as inside, on the boundary or outside.- Each ring's edges are indexed by latitude, so a point meets only the edges that cross its latitude, after a bounding-box reject. A point takes about 25 ns on a 50-vertex ring, against 55 ns for
geo::Contains. On a 5,000-vertex coastline it takes 57 ns, against 3.1 µs. - The answer is exact, with Shewchuk's orientation test (the
robustcrate, now an optional dependency), and matchesgeo'scoordinate_positionpoint for point. Prismextrudes a footprint through anAltitudeBand, as aVolume.- With
geo-types, aPolygonorMultiPolygonconverts into aFootprint. - The feature needs an allocator, not the standard library.
-
🧭
Geodetic::from_lon_latbuilds a position longitude first, the order of GeoJSON, WKT and GeoArrow, so the two cannot be swapped at those boundaries. -
🔍 Accuracy pass. A review of every formula for precision traps, with each finding checked
against exact answers. Fixed:line_of_sight_with_refractionbent the whole path, even far above the air, so it called a
geostationary satellite 18° below the horizon visible. The air now bends only the part of the
path below 7.35 km (ITU-R P.453's refractivity scale height). Links through the air are
unchanged, and a distant satellite shows down to 0.8° below the horizon (ITU-R P.834: 0.76°).
horizon_distance_with_refraction_muses the same model above 7.35 km;Cylindermeasured its radius flat in the centre's frame, which shrank it with height: 107 m
short at 13.7 km up on a 50 km zone. The radius is now along the ground (the geodesic), at any
height;geodesic_inversegave a distance of 0 m, notNone, for a NaN or out-of-range latitude from
some starting points (the equator at 0°, for one), andgeodesic_directgave a finite latitude
with a NaN longitude. Both now check their input;initial_bearing_deglost precision on short paths: 8e-5° out at 1 mm, and 0.02° at 1 µm. It
now keeps it at any length;ray_intersect,ground_strikeandline_of_sightwork their terms to 32 digits, so rays that
graze the Earth from far away, and points just above the surface, keep full precision;Conecompares angles, not cosines, so beams narrower than about a microradian keep their
shape;Geodetic::normalizedleft a longitude already in range very slightly changed (−0.1° became
−0.10000000000002274°); it's now exact;Ellipsoid::ep2is 300 times more precise.
The comparison results are unchanged.
-
🌐 WebAssembly (#19). CI builds for the
browser (wasm32-unknown-unknown, with and without std, and with every feature), and runs the
whole test suite under WebAssembly (wasm32-wasip1, in wasmtime). A geoid test that assumed a
64-bitusizenow holds on 32-bit too. -
📚 docs.rs. Items that need an optional feature now say so ("Available on crate feature
serde only"), and a nightly CI job builds the docs as docs.rs does. The examples show on the
items they use again: the rewrite had stopped docs.rs from scraping them. -
🌐 Every EPSG ellipsoid (#18):
Ellipsoid::from_epsg(code)gives all 50 ellipsoids in the EPSG registry, each from the
numbers EPSG defines it by, converted to metres where EPSG gives feet;- new named ones:
CGCS2000(China, BeiDou),KRASSOVSKY_1940(Pulkovo 1942, Beijing 1954),
GSK_2011(Russia),IAG_1975(Xian 1980),GRS67,AUSTRALIAN_NATIONAL(AGD66 and AGD84),
WGS66,WGS60,EVEREST_1830andEVEREST_1830_MODIFIED. Every named ellipsoid now gives
its EPSG code and where it's used; Ellipsoid::from_axes(a, b), for ellipsoids defined by their two radii.
-
🐛
CLARKE_1866now uses its defining radii. Its polar radius was 1 µm short. -
🚀 Examples, rewritten. Eight short programs, one real job each:
radar,air_traffic,
satellite,drone_camera,field_survey,search_and_rescue,flight_routeandsea_level.
They replacetracking, and CI runs them all. The README lists them. -
🎯 Range rate and intercepts (#17):
Track::range_rate_mpsandLocalFrame::range_rate_mps: how fast the distance is changing,
negative while closing, for radar Doppler. The docs give the Doppler shift for radar and
one-way signals;Track::intercept_fromandTrack::slowest_intercept_from: when and where a pursuer flying
straight at a steady speed meets a target, which way to fly, and the slowest speed that works,
as anIntercept. The equation's terms are worked out to 32 digits, so the answer is within a
few nanometres even at equal speeds, where the textbook formula divides by zero (it fails in
half of those cases). jord's sphere solver fails in 1 % to 16 % of its cases;Track::from_eci: an Earth-fixed track from an inertial (TEME) position and velocity, as SGP4
gives them. It takes the Earth's spin off at the exact rate of geo3d's sidereal time.
examples/satellite.rsnow prints the Doppler shift on the ISS's radio.
-
🌊 Heights above sea level (#16):
- the
Geoidtrait:undulation_m, the geoid's height above the ellipsoid, then
sea_level_height_mandgeodetic_from_sea_levelto convert. AnyFn(lat, lon) -> metresis
aGeoid, so national models plug straight in; GeoidGrid, which reads GeographicLib's EGM84, EGM96 and EGM2008 grids (.pgm) straight from
their bytes: from a file, frominclude_bytes!, or memory-mapped. It never copies or allocates,
and works without the standard library.
It interpolates as GeographicLib does (a cubic on 12 points by default, or bilinear), and gives
the same answers as GeographicLib's GeoidEval. Against the gravity models themselves, at
GeographicLib's 500 000 test points, it is within 2.6 mm of EGM96 and 2.2 mm of EGM2008. The
egm96 crate's default is up to 1.9 m out, and egm2008's up to 24 m. - the
-
🖨️ Printing (#15).
DisplayforGeodetic,
Aer,Ecef,Eci,Enu,Ned,Frd,Vec3,Attitude,QuaternionandUtmUps:- plain
{}keeps every digit, so a printed position reads back exactly; {:.N}gives N decimals (metres get four fewer where a type mixes them with degrees);{:#}writes a position's hemispheres as letters (33.87° S, 151.21° E, 20 m).
It works without the standard library too.
- plain
-
🗺️ UTM and UPS (#14).
Ellipsoid::to_utm_ups,to_utm_ups_zone(a chosen zone) andfrom_utm_ups. Standard zones
include the Norway and Svalbard exceptions, and UPS covers the poles. Built on two new projections
for any ellipsoid:TransverseMercator, by Krüger's series to the eighth order (Karney 2011), with grid
convergence and scale;PolarStereographic, exact.
Within 2 nm of GeographicLib on its own reference points, and 4 nm grid-and-back anywhere. The
utmcrate is up to 77 cm out on a round trip. -
🎯 Sine and cosine of whole multiples of 30° and 45° are now correctly rounded, as in current
GeographicLib. Results at those exact angles can change in the last digit. -
🔄 Quaternions.
Quaternion { w, x, y, z }in the Hamilton convention, scalar first, turning
body vectors into NED, as PX4 and MAVLink store attitude.Attitude::to_quaternionand
from_quaternion(exact pointing straight up),Quaternion::to_rotationandfrom_rotation
(Shepperd's method, precise for every rotation, half turns included),*to combine turns, and
conjugateto reverse one. Always the same sign for the same turn. Converts toglam::DQuat,
nalgebra::Quaternionandmint::Quaternion; glam's and nalgebra's own yaw, pitch and roll agree
with it. -
✈️ Yaw, pitch and roll (#13).Attitude
(aerospace yaw, pitch and roll, from North-East-Down) andFrd(forward, right, down in a
vehicle's own frame):Attitude::body_to_ned,ned_to_body,to_rotationandfrom_rotation;LocalFrame::geodetic_of_bodyandbody_of: positions around a vehicle;LocalFrame::body_to_ecef_rotationandattitude_from_ecef_rotation, the rotation into
ECEF and back (Cesium'sheadingPitchRollToFixedFrame);LocalFrame::enu_to_ecef_rotationandned_to_ecef_rotationexpose the frame's own rotation.
Exact pointing straight up or down: the angles always rebuild the same orientation, where
sguaba (and jord, after a trip through ECEF) can come back pointing the wrong way. -
🔌 Runs without the standard library (#12).
On microcontrollers and flight controllers,default-features = false, features = ["libm"]takes
the maths fromlibm. Everything else is the same, every optional feature works too, and CI
builds it for an Arm Cortex-M4F. Nothing changes for anyone else: the newstdfeature is on by
default, and the default build still has no dependencies.⚠️ If you already setdefault-features = false, addfeatures = ["std"](orlibm), or
the build will stop with a message saying so.- Keep
stdwhere you have it: its sine, cosine and atan2 round the last digit more carefully
than libm's (worst 0.5 against 0.7–1.2 units in the last place). libm is 10–20 % faster.
-
🧭 Route tools (#7), built on n-vectors so
the poles and the antimeridian need no special care:
...
geo3d 0.3.0: exact where it matters
An accuracy release. Your code keeps compiling: no public type, method or signature has changed.
Some results are now more accurate, as listed below.
🎯 More accurate
- Distance along the ground now works for every pair of points
(#3).geodesic_inverseandgeodesic_direct
now use Karney's algorithm, the one GeographicLib uses, instead of Vincenty's.- It is accurate to about 15 nm, up from about 0.5 mm.
geodesic_inverseused to returnNonefor points on nearly opposite sides of the Earth. Now
it only returnsNonefor NaN or infinite input, or a latitude beyond ±90°.- It matches geographiclib-rs to within 4 nm on 25 000 test pairs.
- The code is ported from GeographicLib and geographiclib-rs, which are both MIT-licensed. See
NOTICE.
- Four precision fixes (#5). Each has a test
that failed before the fix:- Elevation near straight up. It used to round to exactly 90°, up to 0.6 m out at
geostationary range. This affects every look angle. - Great-circle distance. It was up to 0.9 m short near the far side of the Earth. It is now
accurate to about a nanometre everywhere. Vec3::angle_to. Tiny angles used to come out as 0.- Ray–ground intersections. These lost precision when starting close to the surface. This
affectsray_intersect,ground_strikeandline_of_sight.
- Elevation near straight up. It used to round to exactly 90°, up to 0.6 m out at
📖 Clearer docs
- The README and the docs.rs front page were rewritten in plain language. They now have recipes, a
"find what you need" table, an accuracy table, a glossary, and a lookup table for MATLAB, pymap3d
and map_3d users. - There is a new comparison against five other Rust crates, with charts.
It covers accuracy, tricky cases, features and speed, including the places where geo3d is
slower. - There is a new contributing guide.
- A correction: the
eciframe is TEME, the one SGP4 satellite models use. It is not J2000, which
differs by about 0.4°. Treating UTC as UT1 can add up to 400 m. The old docs promised "tens of
metres", which wasn't true. - docs.rs now shows the optional features. Searching for familiar names like
geodetic2aer,
vdistorhaversinenow finds the matching geo3d function.
⚡ Speed
geodesic_directis a little faster than before, and about 13 % faster than geographiclib-rs.geodesic_inversetakes about twice as long as before (about 0.86 µs per call). That extra work
is what makes it exact everywhere. It is within 4 % of geographiclib-rs.- Look angles and great-circle distance are 15–20 ns slower per call, because of the precision
fixes.
🧹 Housekeeping
- The code is split into one module per topic.
- The crates.io description is simpler. The
no-std::no-alloccategory was removed, because geo3d
needsstdtoday (#12 will change that). - CI now checks that every code change comes with a CHANGELOG line.
📦 crates.io · 📖 docs.rs · 📊 How it compares