src/classes/Coordinate.ps1

# Need to parameterize class with "id" in order to re-load class during local testing
$Id = if ($Env:ProjectName -eq 'pwsh-prelude' -and $Env:BuildSystem -eq 'Unknown') { 'Test' } else { '' }
$TypeDefinition = @"
  using System;
  using System.Collections.ObjectModel;
  using System.Management.Automation;
 
  public class Coordinate${Id} {
 
    // All lengths are in meters
    public const double SemiMajorAxis = 6378137.0;// a
    public const double SemiMinorAxis = 6356752.31424518;// b
    public const double FlatteningFactor = 298.257223563;// 1/f
    public const double LinearEccentricity = 521854.00842339;// E
    public const double Eccentricity = 0.0818191908426215;// e
    public const double EccentricitySquared = 0.006694379990141;// e2
    public const double Radius = 6371001;// mean radius
    public const double RadiusAuthalic = 6371007.1810;// radius constant surface area
 
    private double _Latitude;
    private double _Longitude;
    private string[] _Hemisphere = new string[] { "N","E" };
    public string[] Hemisphere {
      get {
        return _Hemisphere;
      }
      private set {
        _Hemisphere = value;
      }
    }
    public double Latitude {
      get {
        return _Latitude;
      }
      set {
        this.Hemisphere[0] = value < 0 ? "S" : "N";
        _Latitude = value;
      }
    }
    public double Longitude {
      get {
        return _Longitude;
      }
      set {
        this.Hemisphere[1] = value < 0 ? "W" : "E";
        _Longitude = value;
      }
    }
    public double Height;
 
    private static double ToDegree(double value) {
      return value * (180 / Math.PI);
    }
    private static double ToRadian(double value) {
      return value * (Math.PI / 180);
    }
    private static double Hav(double value) {
      return 0.5 * (1 - Math.Cos(ToRadian(value)));
    }
    public static double[] ToSexagesimal(double value) {
      double fractionalPart = Math.Abs(value - Math.Truncate(value));
      double degree = Math.Truncate(value);
      double minute = Math.Truncate(fractionalPart * 60);
      double second = Math.Round(((fractionalPart * 60) - minute) * 60,2);
      return new double[] { degree,minute,second };
    }
    public Coordinate${Id}() {
      this.Latitude = 0.0;
      this.Longitude = 0.0;
      this.Height = 0.0;
    }
    public Coordinate${Id}(double latitude,double longitude,double height = 0.0) {
      this.Latitude = latitude;
      this.Longitude = longitude;
      this.Height = height;
    }
    public static Coordinate${Id} FromCartesian(double x,double y,double z) {
        double[] geodetic = ToGeodetic(x,y,z);
        double latitude = geodetic[0];
        double longitude = geodetic[1];
        double height = geodetic[2];
        return new Coordinate${Id}(latitude,longitude,height);
    }
    public static double[] ToGeodetic(double x,double y,double z) {
        double a = SemiMajorAxis;
        double b = SemiMinorAxis;
        double E = LinearEccentricity;
        double E2 = Math.Pow(E,2);
        double x2 = Math.Pow(x,2),y2 = Math.Pow(y,2),z2 = Math.Pow(z,2);
        double r2 = x2 + y2 + z2;
        double Q = Math.Sqrt(x2 + y2);
        double u = Math.Sqrt((0.5 * (r2 - E2)) + (0.5 * Math.Sqrt(Math.Pow(r2 - E2,2) + (4 * E2 * z2))));
        double u2 = Math.Pow(u,2);
        double beta = Math.Atan((Math.Sqrt(u2 + E2) * z) / (u * Q));
        double latitude = Math.Atan((a / b) * Math.Tan(beta));
        double longitude = Math.Atan2(y,x);
        double height = Math.Sqrt(Math.Pow(z - (b * Math.Sin(beta)),2) + Math.Pow(Q - (a * Math.Cos(beta)),2));
        return new double[] { ToDegree(latitude),ToDegree(longitude),height };
    }
    public static double[] ToCartesian(double latitude,double longitude,double height = 0) {
        double a = SemiMajorAxis;
        double e2 = EccentricitySquared;
        double lat = ToRadian(latitude);
        double lon = ToRadian(longitude);
        double h = height;
        double v = a / Math.Sqrt(1 - (e2 * Math.Pow(Math.Sin(lat),2)));
        double x = Math.Cos(lat) * Math.Cos(lon) * (v + h);
        double y = Math.Cos(lat) * Math.Sin(lon) * (v + h);
        double z = Math.Sin(lat) * ((v * (1 - e2)) + h);
        return new double[] { x,y,z };
    }
    public override string ToString() {
        double latitude = Math.Abs(this.Latitude);
        double longitude = Math.Abs(this.Longitude);
        string NS = this.Hemisphere[0];
        string WE = this.Hemisphere[1];
        string[] lat = Array.ConvertAll(ToSexagesimal(latitude),Convert.ToString);
        string[] lon = Array.ConvertAll(ToSexagesimal(longitude),Convert.ToString);
        return lat[0] + "°" + lat[1] + "'" + lat[2] + "\"" + NS + " " + lon[0] + "°" + lon[1] + "'" + lon[2] + "\"" + WE;
    }
  }
"@

if ("Coordinate${Id}" -as [Type]) {
  return
} else {
  Add-Type -TypeDefinition $TypeDefinition
  if ($Env:BuildSystem -eq 'Travis CI') {
    $Accelerators = [PowerShell].Assembly.GetType('System.Management.Automation.TypeAccelerators')
    $Accelerators::Add('CoordinateTest', 'Coordinate')
  }
}