Geodesy and Unit Conversion
Two small packages of plain functions, standard library only and on every platform. Latitudes and longitudes are decimal degrees, headings degrees true unless named magnetic.
pkg/calc
| Function | Gives |
|---|---|
HaversineMeters, HaversineNM, HaversineKM(lat1, lon1, lat2, lon2) |
Great-circle distance (mean Earth radius 6,371,000 m) |
BearingDegrees(lat1, lon1, lat2, lon2) |
Initial great-circle bearing, [0, 360) |
BearingFromOffsets(xEast, zNorth) |
Bearing to a point in a local frame (X east, Z north, as SimConnect’s airport frame); 0 at the origin |
CrossTrackMeters(latA, lonA, latB, lonB, latD, lonD) |
Distance of D from the great circle A → B: positive right of track, negative left |
AlongTrackMeters(latA, lonA, latB, lonB, latD, lonD) |
Distance from A to the point of A → B nearest D: negative behind A |
DisplaceByHeading(lat, lon, hdgDeg, distanceMeters) |
The point distanceMeters along hdgDeg (negative: backwards); sub-metre to about 50 km |
IntermediatePoint(lat1, lon1, lat2, lon2, f) |
The point a fraction f (0–1) along the great circle |
Dubins(lat1, lon1, heading1, lat2, lon2, heading2, r, step, first) |
Shortest path with turn radius r m between two positions and headings, as [lat, lon] points every step m; first +1 right, -1 left, 0 either; nil when none |
TrueToMagnetic(trueHeading, magVar), MagneticToTrue(magneticHeading, magVar) |
Heading conversion, [0, 360); magVar positive east |
WindCorrectionAngle(windDir, windSpeed, tas, course) |
WCA in degrees: fly course + WCA; 0 when tas is about 0 |
HeadwindCrosswind(windDir, windSpeed, runwayHeading) |
Headwind (negative: tailwind) and crosswind (positive: from the right); also HeadwindComponent, CrosswindComponent |
d := calc.HaversineNM(50.1008, 14.26, 48.1103, 16.5697) // LKPR → LOWW
brg := calc.BearingDegrees(50.1008, 14.26, 48.1103, 16.5697)
head, cross := calc.HeadwindCrosswind(240, 15, 243) // wind 240/15, runway 24
pkg/convert
| Group | Functions |
|---|---|
| Altitude, vertical speed | FeetToMeters, MetersToFeet, FeetPerMinuteToFeetPerSecond, FeetPerSecondToFeetPerMinute |
| Distance | NMToMeters, MetersToNM, NMToKilometers, KilometersToNM, KilometersToMeters, MetersToKilometers, NMToStatuteMiles, StatuteMilesToNM, KilometersToStatuteMiles, StatuteMilesToKilometers, StatuteMilesToMeters, MetersToStatuteMiles |
| Speed | KnotsToKilometersPerHour, KilometersPerHourToKnots, KnotsToMetersPerSecond, MetersPerSecondToKnots, KnotsToFeetPerSecond, FeetPerSecondToKnots, FeetPerMinuteToMetersPerSecond, MetersPerSecondToFeetPerMinute; Mach at sea-level ISA (661.4788 kt): KnotsToMach, MachToKnots, KilometersPerHourToMach, MachToKilometersPerHour |
| Pressure | InHgToMillibar, MillibarToInHg, InHgToHectopascal, HectopascalToInHg, InHgToPascal, PascalToInHg |
| Temperature | CelsiusToFahrenheit, FahrenheitToCelsius, CelsiusToKelvin, KelvinToCelsius, FahrenheitToKelvin, KelvinToFahrenheit |
| Weight, volume | PoundsToKilograms, KilogramsToPounds, USGallonsToLiters, LitersToUSGallons |
| Angles | DegreesToRadians, RadiansToDegrees, NormalizeHeading ([0, 360)), NormalizeAngle ((-180, 180]), AngleDifference(from, to) (signed, positive clockwise) |
| Local offsets | OffsetToLatLon(latRef, lonRef, xEast, zNorth) and LatLonToOffset(latRef, lonRef, lat, lon): metres east/north of a reference point (WGS84), round-trip |
| ICAO codes | IsICAOCode(code) (a known prefix, not a registry check), ICAORegion(code) (by the first letter), ICAOCountry(code) (by the first two letters) |
x, z := convert.LatLonToOffset(refLat, refLon, lat, lon) // metres east, north of the reference
lat2, lon2 := convert.OffsetToLatLon(refLat, refLon, x+50, z)
qnh := convert.InHgToHectopascal(29.92)
country := convert.ICAOCountry("LKPR")