using System;
using System.Runtime.CompilerServices;
namespace ZeroLevel.ML.LocationMath
{
public static class GeoMath
{
///
/// Conversion factor degrees to radians
///
public const double DegToRad = Math.PI / 180d; //0.01745329252; // Convert Degrees to Radians
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double toRadians(double v) => v * DegToRad;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double toDegree(double v) => v * 180 / Math.PI;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double wrap90(double degrees)
{
if (-90 <= degrees && degrees <= 90) return degrees; // avoid rounding due to arithmetic ops if within range
// latitude wrapping requires a triangle wave function; a general triangle wave is
// f(x) = 4a/p ⋅ | (x-p/4)%p - p/2 | - a
// where a = amplitude, p = period, % = modulo; however, JavaScript '%' is a remainder operator
// not a modulo operator - for modulo, replace 'x%n' with '((x%n)+n)%n'
double x = degrees, a = 90, p = 360;
return 4 * a / p * Math.Abs(((x - p / 4) % p + p) % p - p / 2) - a;
}
///
/// Constrain degrees to range -180..+180 (for longitude); e.g. -181 => 179, 181 => -179.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double wrap180(double degrees)
{
if (-180 <= degrees && degrees <= 180) return degrees; // avoid rounding due to arithmetic ops if within range
// longitude wrapping requires a sawtooth wave function; a general sawtooth wave is
// f(x) = (2ax/p - p/2) % p - a
// where a = amplitude, p = period, % = modulo; however, JavaScript '%' is a remainder operator
// not a modulo operator - for modulo, replace 'x%n' with '((x%n)+n)%n'
double x = degrees, a = 180, p = 360;
return ((2 * a * x / p - p / 2) % p + p) % p - a;
}
///
/// Constrain degrees to range 0..360 (for bearings); e.g. -1 => 359, 361 => 1.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double wrap360(double degrees)
{
if (0 <= degrees && degrees < 360) return degrees; // avoid rounding due to arithmetic ops if within range
// bearing wrapping requires a sawtooth wave function with a vertical offset equal to the
// amplitude and a corresponding phase shift; this changes the general sawtooth wave function from
// f(x) = (2ax/p - p/2) % p - a
// to
// f(x) = (2ax/p) % p
// where a = amplitude, p = period, % = modulo; however, JavaScript '%' is a remainder operator
// not a modulo operator - for modulo, replace 'x%n' with '((x%n)+n)%n'
double x = degrees, a = 180, p = 360;
return (2 * a * x / p % p + p) % p;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double YawToBearing(double a)
{
return (a + 360) % 360;
}
///
/// Calculate the difference between two longitudal values constrained 0 - 180 deg
///
/// The first longitue value in degrees
/// The second longitue value in degrees
/// The distance in degrees
public static double DiffLongitude(double lon1, double lon2)
{
double diff;
if (lon1 > 180.0)
lon1 = 360.0 - lon1;
if (lon2 > 180.0)
lon2 = 360.0 - lon2;
if (lon1 >= 0.0 && lon2 >= 0.0)
diff = lon2 - lon1;
else if (lon1 < 0.0 && lon2 < 0.0)
diff = lon2 - lon1;
else
{
// different hemispheres
if (lon1 < 0)
lon1 = -1 * lon1;
if (lon2 < 0)
lon2 = -1 * lon2;
diff = lon1 + lon2;
if (diff > 180.0)
diff = 360.0 - diff;
}
return diff;
}
}
}